/*
Copyright (C) 2008 Webyog Softworks Private Limited
This file is a part of Visifire Charts.
Visifire is a free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
You should have received a copy of the GNU General Public License
along with Visifire Charts. If not, see .
If GPL is not suitable for your products or company, Webyog provides Visifire
under a flexible commercial license designed to meet your specific usage and
distribution requirements. If you have already obtained a commercial license
from Webyog, you can use this file under those license terms.
*/
#if WPF
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Diagnostics;
#else
using System;
using System.Windows;
using System.Linq;
using System.Windows.Controls;
using System.Collections.Generic;
using System.Diagnostics;
#endif
namespace Visifire.Charts
{
///
/// Containes all the details about the data required for various plotting puposes
///
internal class PlotDetails
{
#region Public Methods
///
/// Initializes a new instance of the Visifire.Charts.PlotDetails class
///
public PlotDetails(Chart chart)
{
// Create a plot groups list
this.PlotGroups = new List();
// To store all the axisX labels for the primary axisX;
this.AxisXPrimaryLabels = new Dictionary();
// To store all the axisX labels for the secondary axisX;
this.AxisXSecondaryLabels = new Dictionary();
// Store the chart reference
this.Chart = chart;
// Set default chart orientation
this.ChartOrientation = ChartOrientationType.Undefined;
// Validate XValue type of the DataPoint and DataSeries
SetDataPointsNameAndValidateDataPointXValueType();
// Calculate all the required details
this.Calculate();
}
#endregion
#region Public Properties
#endregion
#region Public Events
#endregion
#region Protected Methods
#endregion
#region Internal Properties
///
/// List of all DataPoints in chart
///
internal List ListOfAllDataPoints
{
get;
set;
}
///
/// List of different types of plot groups based on RenderAs, AxisXType and AxisYType
///
internal List PlotGroups
{
get;
#if DEBUG
set;
#else
private set;
#endif
}
///
/// Reference to the chart element
///
internal Chart Chart
{
get;
private set;
}
///
/// Stores the overall orientation of the chart
///
internal ChartOrientationType ChartOrientation
{
get;
set;
}
///
/// Stores the number of divisions to Divide the height/width available for each datapoint while rendering in multiseries combinations.
/// Also used for calculating axisX limits
///
internal Int32 DrawingDivisionFactor
{
get;
private set;
}
///
/// Stores the value which will be used to calculate the thickness in the case of 3D charts
///
internal Int32 Layer3DCount
{
get;
private set;
}
///
/// Dictionary that stores the Series Drawing Indexes, the Key is the DataSeries itself
///
internal Dictionary SeriesDrawingIndex
{
get;
set;
}
///
/// List of unique axisX primary labels
///
internal Dictionary AxisXPrimaryLabels
{
get;
private set;
}
///
/// List of unique axisX secondary labels
///
internal Dictionary AxisXSecondaryLabels
{
get;
private set;
}
///
/// Whether all primary axis labels are present
///
internal Boolean IsAllPrimaryAxisXLabelsPresent
{
get;
set;
}
///
/// Whether all secondary axis labels are present
///
internal Boolean IsAllSecondaryAxisXLabelsPresent
{
get;
set;
}
#endregion
#region Private Delegates
#endregion
#region Private Methods
///
/// Validate XValue type of the DataPoint and DataSeries
///
private void SetDataPointsNameAndValidateDataPointXValueType()
{
Int32 dsIndex = 0;
foreach (DataSeries ds in Chart.InternalSeries)
{
foreach (DataPoint dp in ds.InternalDataPoints)
{
if (ds.XValueType == ChartValueTypes.Auto)
{
ds.InternalXValueType = (dp.XValueType == ChartValueTypes.DateTime) ? ChartValueTypes.Date : dp.XValueType;
}
else if ((ds.XValueType == ChartValueTypes.Date || ds.XValueType == ChartValueTypes.DateTime || ds.XValueType == ChartValueTypes.Time)
&& dp.XValueType != ChartValueTypes.DateTime)
{
throw new Exception("Error occurred due to incorrect XValue format. XValue can be Double or DateTime for all DataPoints in a DataSeries.");
}
else if (ds.XValueType == ChartValueTypes.Numeric && dp.XValueType != ChartValueTypes.Numeric)
{
throw new Exception("Error occurred due to incorrect XValue format. XValue can be Double or DateTime for all DataPoints in a DataSeries according to XValueType of the DataSeries.");
}
}
dsIndex++;
}
}
///
/// Calculate PlotDetails
///
private void Calculate()
{
// Create Axis incase if it doesnt exist
CreateMissingAxes();
Axis axisXSecondary = GetAxisXFromChart(Chart, AxisTypes.Secondary);
if (axisXSecondary != null)
throw new Exception("Note: Secondary AxisX is not yet supported in Visifire.");
Axis axisX = GetAxisXFromChart(Chart, AxisTypes.Primary);
// Generate XValues for DataTime axis
GenerateXValueForDataTimeAxis(axisX);
// Identifies the various plot groups and populates the list
PopulatePlotGroups();
SetTrendLineValues(axisX);
// Generates a index set that identifies the order in which the series must be drawn(layering order)
SeriesDrawingIndex = GenerateDrawingOrder();
// Create list of datapoints from all series
CreateListOfDataPoints();
// Gets a unique set of axis labels by axisX type
AxisXPrimaryLabels = GetAxisXLabels(AxisTypes.Primary);
AxisXSecondaryLabels = GetAxisXLabels(AxisTypes.Secondary);
// Set Labels count state
SetLabelsCountState();
}
///
/// Get List of DateTime from all DataSeries
///
private List GetListOfXValue(Axis axis)
{
List xValuesAsDateTimeList = new List();
int dataSeriesIndex = 0, dataPointIndex = 0;
foreach (DataSeries ds in Chart.InternalSeries)
{
dataPointIndex = 0;
foreach (DataPoint dp in ds.InternalDataPoints)
{
if (dp.InternalXValueAsDateTime == null)
{
throw new Exception("Wrong property value as date for XValue in DataPoint position " + dataPointIndex.ToString() + " and DataSeries position " + dataSeriesIndex.ToString());
}
try
{
if (dp.XValueType == ChartValueTypes.Numeric)
throw new Exception();
if (ds.InternalXValueType == ChartValueTypes.Auto || ds.InternalXValueType == ChartValueTypes.Date)
{
dp.InternalXValueAsDateTime = new DateTime(dp.InternalXValueAsDateTime.Date.Year, dp.InternalXValueAsDateTime.Date.Month, dp.InternalXValueAsDateTime.Date.Day);
xValuesAsDateTimeList.Add(dp.InternalXValueAsDateTime);
}
else if (ds.InternalXValueType == ChartValueTypes.DateTime)
{
// if (axis.IntervalType == IntervalTypes.Years || axis.IntervalType == IntervalTypes.Months || axis.IntervalType == IntervalTypes.Weeks || axis.IntervalType == IntervalTypes.Days)
// xValuesAsDateTimeList.Add((DateTime)dp.InternalXValueAsDateTime.Date);
// else
xValuesAsDateTimeList.Add((DateTime)dp.InternalXValueAsDateTime);
}
else if (ds.InternalXValueType == ChartValueTypes.Time)
{
System.Diagnostics.Debug.WriteLine(dp.InternalXValueAsDateTime.TimeOfDay.ToString());
dp.InternalXValueAsDateTime = DateTime.Parse("12/30/1899 " + dp.InternalXValueAsDateTime.TimeOfDay.ToString(), System.Globalization.CultureInfo.InvariantCulture);
xValuesAsDateTimeList.Add(dp.InternalXValueAsDateTime);
}
}
catch
{
throw new Exception("Wrong property value as date for XValue in DataPoint position " + dataPointIndex.ToString() + " and DataSeries position " + dataSeriesIndex.ToString());
}
dataPointIndex++;
}
dataSeriesIndex++;
}
return xValuesAsDateTimeList;
}
///
/// Whether axis is a date time axis.
/// Also set up common XValue Type for axis
///
///
///
private Boolean CheckIsDateTimeAxis(Axis axis)
{
Boolean isDateTimeAxis = false; // Whether axisX is DateTime axis
axis.XValueType = ChartValueTypes.Numeric;
Boolean isAnyDateTimeSeriesExist = false;
int seriesCount = (from dataSeries in Chart.InternalSeries
where dataSeries.InternalXValueType == ChartValueTypes.Date
select dataSeries).Count();
isAnyDateTimeSeriesExist = seriesCount > 0 ? true : false;
if (seriesCount == Chart.InternalSeries.Count) // Is chart having a DateTime Axis
{
isDateTimeAxis = true;
axis.XValueType = ChartValueTypes.Date;
}
else
{
seriesCount = (from dataSeries in Chart.InternalSeries
where dataSeries.InternalXValueType == ChartValueTypes.DateTime
select dataSeries).Count();
isAnyDateTimeSeriesExist = seriesCount > 0 ? true : isAnyDateTimeSeriesExist;
if (seriesCount == Chart.InternalSeries.Count) // Is chart having a DateTime Axis
{
isDateTimeAxis = true;
axis.XValueType = ChartValueTypes.DateTime;
}
else
{
seriesCount = (from dataSeries in Chart.InternalSeries
where dataSeries.InternalXValueType == ChartValueTypes.Time
select dataSeries).Count();
isAnyDateTimeSeriesExist = seriesCount > 0 ? true : isAnyDateTimeSeriesExist;
if (seriesCount == Chart.InternalSeries.Count) // Is chart having a DateTime Axis
{
isDateTimeAxis = true;
axis.XValueType = ChartValueTypes.Time;
}
}
}
if (!isDateTimeAxis && isAnyDateTimeSeriesExist)
{
throw new Exception("Error occurred, different types of XValue type found in DataSeries. Check for property value applied for XValueType property of different DataSeries.");
}
return isDateTimeAxis;
}
///
/// Set TrendLine values
///
/// Axis
private void SetTrendLineValues(Axis axisX)
{
if (axisX.IsDateTimeAxis)
{
foreach (TrendLine trendLine in Chart.TrendLines)
SetTrendLineValue(trendLine, axisX);
}
}
///
/// Set TrendLine value
///
/// Axis
internal void SetTrendLineValue(TrendLine trendLine, Axis axisX)
{
if (axisX != null && axisX.IsDateTimeAxis)
{
if ((Boolean)trendLine.Enabled &&
((trendLine.Orientation == Orientation.Vertical && axisX.PlotDetails.ChartOrientation == ChartOrientationType.Vertical)
|| (trendLine.Orientation == Orientation.Horizontal && axisX.PlotDetails.ChartOrientation == ChartOrientationType.Horizontal)
)
)
trendLine.InternalNumericValue = DateTimeHelper.DateDiff(trendLine.InternalDateValue, axisX.MinDate, axisX.MinDateRange, axisX.MaxDateRange, axisX.InternalIntervalType, axisX.XValueType);
}
}
///
/// Reset DateTime for AxisMinimum and Axis AxisMaximum
///
///
private void ResetDateTime4AxisMinimumAndMaximum(Axis axis)
{
if (axis.AxisMinimum != null)
{
if (axis._axisMinimumValueType == ChartValueTypes.Numeric)
throw new Exception("AxisMinimum should have a value of type Date/DateTime/Time");
if (axis.XValueType == ChartValueTypes.Date)
axis.AxisMinimumDateTime = new DateTime(axis.AxisMinimumDateTime.Year, axis.AxisMinimumDateTime.Month, axis.AxisMinimumDateTime.Day);
else if (axis.XValueType == ChartValueTypes.Time)
axis.AxisMinimumDateTime = DateTime.Parse("12/30/1899 " + axis.AxisMinimumDateTime.TimeOfDay.ToString(), System.Globalization.CultureInfo.InvariantCulture);
}
if (axis.AxisMaximum != null)
{
if (axis._axisMaximumValueType == ChartValueTypes.Numeric)
throw new Exception("AxisMaximum should have a value of type Date/DateTime/Time");
if (axis.XValueType == ChartValueTypes.Date)
axis.AxisMaximumDateTime = new DateTime(axis.AxisMaximumDateTime.Year, axis.AxisMaximumDateTime.Month, axis.AxisMaximumDateTime.Day);
else if (axis.XValueType == ChartValueTypes.Time)
axis.AxisMaximumDateTime = DateTime.Parse("12/30/1899 " + axis.AxisMaximumDateTime.TimeOfDay.ToString(), System.Globalization.CultureInfo.InvariantCulture);
}
}
///
/// Generates XValues for DataTime axis
///
private void GenerateXValueForDataTimeAxis(Axis axisX)
{
if (axisX != null)
{
axisX._isDateTimeAutoInterval = false;// Minimum difference between two DataTimes
axisX.IsDateTimeAxis = CheckIsDateTimeAxis(axisX);
if (axisX.IsDateTimeAxis)
{
DateTime minDate, maxDate; // Min and max date
TimeSpan minDateRange, maxDateRange;
List xValuesAsDateTimeList = GetListOfXValue(axisX);
ResetDateTime4AxisMinimumAndMaximum(axisX);
if (xValuesAsDateTimeList.Count != 0)
{
DateTimeHelper.CalculateMinMaxDate(xValuesAsDateTimeList, out minDate, out maxDate, out minDateRange, out maxDateRange);
IntervalTypes autoIntervalType = axisX.InternalIntervalType;
Double maxInterval = (axisX.XValueType == ChartValueTypes.Date || axisX.XValueType == ChartValueTypes.Time) ? 8 : 8;
if (minDate != maxDate)
{
axisX.InternalInterval = DateTimeHelper.CalculateAutoInterval(Chart.ActualWidth, Chart.ActualHeight, axisX.AxisOrientation, minDate, maxDate, out autoIntervalType, maxInterval, axisX.XValueType);
}
else
{
if (axisX.XValueType != ChartValueTypes.Time)
{
//if (axisX.AxisMinimum == null)
//{
// minDate = minDate.AddDays(-1);
// autoIntervalType = IntervalTypes.Days;
//}
if (axisX.AxisMinimum == null)
{
if (axisX.InternalIntervalType == IntervalTypes.Hours)
{
minDate = minDate.AddHours(-1);
autoIntervalType = axisX.InternalIntervalType;
}
else if (axisX.InternalIntervalType == IntervalTypes.Minutes)
{
minDate = minDate.AddMinutes(-1);
autoIntervalType = axisX.InternalIntervalType;
}
else if (axisX.InternalIntervalType == IntervalTypes.Seconds)
{
minDate = minDate.AddSeconds(-1);
autoIntervalType = axisX.InternalIntervalType;
}
else if (axisX.InternalIntervalType == IntervalTypes.Milliseconds)
{
minDate = minDate.AddMilliseconds(-1);
autoIntervalType = axisX.InternalIntervalType;
}
else
{
minDate = minDate.AddDays(-1);
autoIntervalType = IntervalTypes.Days;
}
}
else
{
TimeSpan minDateDifference = minDate.Subtract((DateTime)axisX.AxisMinimumDateTime);
if (minDateDifference.TotalDays <= 30)
{
autoIntervalType = IntervalTypes.Days;
minDate = minDate.AddDays(-1);
}
else if ((minDateDifference.TotalDays / 30) < 12)
{
autoIntervalType = IntervalTypes.Months;
minDate = minDate.AddMonths(-1);
}
else if (minDateDifference.TotalDays / 365.242199 > 0)
{
autoIntervalType = IntervalTypes.Years;
minDate = minDate.AddYears(-1);
}
else if (minDateDifference.TotalHours > 0)
{
autoIntervalType = IntervalTypes.Hours;
minDate = minDate.AddHours(-1);
}
else if (minDateDifference.TotalMinutes > 0)
{
autoIntervalType = IntervalTypes.Minutes;
minDate = minDate.AddMinutes(-1);
}
else if (minDateDifference.TotalSeconds > 0)
{
autoIntervalType = IntervalTypes.Seconds;
minDate = minDate.AddSeconds(-1);
}
else if (minDateDifference.TotalMilliseconds > 0)
{
autoIntervalType = IntervalTypes.Milliseconds;
minDate = minDate.AddMilliseconds(-1);
}
}
}
axisX.InternalInterval = 1;
axisX._isDateTimeAutoInterval = true;
}
//if (minDate == maxDate)
//{
// //if (axisX.XValueType != ChartValueTypes.Time)
// // minDate = minDate.AddDays(-1);
// autoIntervalType = IntervalTypes.Days;
//}
if (axisX.XValueType == ChartValueTypes.DateTime || axisX.XValueType == ChartValueTypes.Date)
{
if (axisX.IntervalType == IntervalTypes.Auto || Double.IsNaN((Double)axisX.Interval))
{
axisX._isDateTimeAutoInterval = true;
axisX.InternalIntervalType = autoIntervalType;
}
else
{
Boolean isAcceptAutoIntervalType = false;
if (axisX.XValueType == ChartValueTypes.Date && (
axisX.IntervalType == IntervalTypes.Hours || axisX.IntervalType == IntervalTypes.Minutes
|| axisX.IntervalType == IntervalTypes.Seconds || axisX.IntervalType == IntervalTypes.Milliseconds
|| axisX.IntervalType == IntervalTypes.Auto))
isAcceptAutoIntervalType = true;
if (axisX.IntervalType == IntervalTypes.Auto)
{
isAcceptAutoIntervalType = true;
}
if (axisX.Interval == null || Double.IsNaN((Double)axisX.Interval))
{
if (axisX.IntervalType == IntervalTypes.Years)
{
if (autoIntervalType == IntervalTypes.Months || autoIntervalType == IntervalTypes.Weeks
|| autoIntervalType == IntervalTypes.Days || autoIntervalType == IntervalTypes.Hours
|| autoIntervalType == IntervalTypes.Minutes || autoIntervalType == IntervalTypes.Seconds
|| autoIntervalType == IntervalTypes.Milliseconds
)
isAcceptAutoIntervalType = true;
}
else if (axisX.IntervalType == IntervalTypes.Months)
{
if (autoIntervalType == IntervalTypes.Weeks || autoIntervalType == IntervalTypes.Days
|| autoIntervalType == IntervalTypes.Hours || autoIntervalType == IntervalTypes.Minutes
|| autoIntervalType == IntervalTypes.Seconds || autoIntervalType == IntervalTypes.Milliseconds
)
isAcceptAutoIntervalType = true;
}
else if (axisX.IntervalType == IntervalTypes.Weeks)
{
if (autoIntervalType == IntervalTypes.Days || autoIntervalType == IntervalTypes.Hours
|| autoIntervalType == IntervalTypes.Minutes || autoIntervalType == IntervalTypes.Seconds
|| autoIntervalType == IntervalTypes.Milliseconds
)
isAcceptAutoIntervalType = true;
}
else if (axisX.IntervalType == IntervalTypes.Days)
{
if (autoIntervalType == IntervalTypes.Days || autoIntervalType == IntervalTypes.Hours
|| autoIntervalType == IntervalTypes.Minutes || autoIntervalType == IntervalTypes.Seconds
|| autoIntervalType == IntervalTypes.Milliseconds
)
isAcceptAutoIntervalType = true;
}
else if (axisX.IntervalType == IntervalTypes.Hours)
{
if (autoIntervalType == IntervalTypes.Minutes || autoIntervalType == IntervalTypes.Seconds
|| autoIntervalType == IntervalTypes.Milliseconds
)
isAcceptAutoIntervalType = true;
}
else if (axisX.IntervalType == IntervalTypes.Minutes)
{
if (autoIntervalType == IntervalTypes.Seconds || autoIntervalType == IntervalTypes.Milliseconds)
isAcceptAutoIntervalType = true;
}
else if (axisX.IntervalType == IntervalTypes.Seconds)
{
if (autoIntervalType == IntervalTypes.Milliseconds)
isAcceptAutoIntervalType = true;
}
else if (axisX.IntervalType == IntervalTypes.Milliseconds)
{
if (autoIntervalType == IntervalTypes.Milliseconds)
isAcceptAutoIntervalType = true;
}
axisX._isDateTimeAutoInterval = true;
}
if (axisX.InternalIntervalType != autoIntervalType)
{
axisX.InternalInterval = UpdateTimeIterval(autoIntervalType, axisX.InternalIntervalType, axisX.InternalInterval);
}
if (isAcceptAutoIntervalType)
axisX.InternalIntervalType = autoIntervalType;
}
if (axisX.AxisMinimum == null)
{
//DateTime newMinDate = DateTimeHelper.AlignDateTime(minDate, axisX.InternalInterval, axisX.InternalIntervalType);
//if (newMinDate != minDate)
//{
// // Chart.InternalSeries[0].IsNotificationEnable = false;
// // Chart.InternalSeries[0].InternalDataPoints.Add(new DataPoint() { XValueType = ChartValueTypes.DateTime, InternalXValueAsDateTime = newMinDate, Enabled = false, Parent = Chart.InternalSeries[0] });
// // Chart.InternalSeries[0].IsNotificationEnable = true;
// minDate = newMinDate;
//}
}
}
if (axisX.XValueType == ChartValueTypes.Time)
{
if (axisX.IntervalType == IntervalTypes.Auto || Double.IsNaN((Double)axisX.Interval))
{
axisX._isDateTimeAutoInterval = true;
axisX.InternalIntervalType = autoIntervalType;
}
else
{
if (axisX.IntervalType == IntervalTypes.Years || axisX.IntervalType == IntervalTypes.Months
|| axisX.IntervalType == IntervalTypes.Weeks || axisX.IntervalType == IntervalTypes.Days
|| axisX.IntervalType == IntervalTypes.Auto)
{
if (autoIntervalType == IntervalTypes.Years || autoIntervalType == IntervalTypes.Months
|| autoIntervalType == IntervalTypes.Weeks || autoIntervalType == IntervalTypes.Days)
axisX.InternalIntervalType = IntervalTypes.Hours;
else
axisX.InternalIntervalType = autoIntervalType;
}
else
{
if (axisX.Interval == null || Double.IsNaN((Double)axisX.Interval))
{
if ((axisX.InternalIntervalType == IntervalTypes.Hours && maxDateRange.Hours == 0)
|| (axisX.InternalIntervalType == IntervalTypes.Minutes && maxDateRange.Minutes == 0)
|| (axisX.InternalIntervalType == IntervalTypes.Seconds && maxDateRange.Seconds == 0))
{
axisX.InternalIntervalType = autoIntervalType;
}
}
}
if (axisX.Interval == null || Double.IsNaN((Double)axisX.Interval))
{
if (axisX.InternalIntervalType != autoIntervalType && minDate.TimeOfDay != maxDate.TimeOfDay)
{
axisX.InternalInterval = UpdateTimeIterval(autoIntervalType, axisX.InternalIntervalType, axisX.InternalInterval);
}
else
axisX.InternalInterval = 1;
axisX._isDateTimeAutoInterval = true;
}
else
axisX.InternalInterval = (Double)axisX.Interval;
}
if (axisX.InternalIntervalType == IntervalTypes.Hours || axisX.InternalIntervalType == IntervalTypes.Minutes || axisX.InternalIntervalType == IntervalTypes.Seconds || axisX.InternalIntervalType == IntervalTypes.Milliseconds)
{
if (axisX.AxisMinimum == null)
{
//DateTime newMinDate = DateTimeHelper.AlignDateTime(minDate, axisX.InternalInterval, axisX.InternalIntervalType);
//if (newMinDate != minDate && minDate.TimeOfDay != maxDate.TimeOfDay)
//{
// // Chart.InternalSeries[0].IsNotificationEnable = false;
// // Chart.InternalSeries[0].InternalDataPoints.Add(new DataPoint() { XValueType = ChartValueTypes.DateTime, InternalXValueAsDateTime = newMinDate, Enabled = false, Parent = Chart.InternalSeries[0] });
// // Chart.InternalSeries[0].IsNotificationEnable = true;
// minDate = newMinDate;
//}
}
}
}
axisX.MinDate = minDate;
axisX.MaxDate = maxDate;
axisX.MinDateRange = minDateRange;
axisX.MaxDateRange = maxDateRange;
if (Chart.InternalSeries.Count == 1 && Chart.InternalSeries[0].InternalDataPoints.Count == 1)
{
axisX._isAllXValueZero = false;
DataPoint dp = Chart.InternalSeries[0].InternalDataPoints[0] as DataPoint;
dp.InternalXValue = DateTimeHelper.DateDiff((DateTime)dp.InternalXValueAsDateTime, minDate, minDateRange, maxDateRange, axisX.InternalIntervalType, axisX.XValueType);
if (dp.InternalXValue == 0)
{
axisX._isAllXValueZero = true;
dp.InternalXValue = 1;
}
}
else
{
axisX._isAllXValueZero = true;
foreach (DataSeries ds in Chart.InternalSeries)
{
foreach (DataPoint dp in ds.InternalDataPoints)
{
dp.InternalXValue = DateTimeHelper.DateDiff((DateTime)dp.InternalXValueAsDateTime, minDate, minDateRange, maxDateRange, axisX.InternalIntervalType, axisX.XValueType);
if (dp.InternalXValue == 0 && axisX._isAllXValueZero == true)
axisX._isAllXValueZero = true;
else
axisX._isAllXValueZero = false;
// System.Diagnostics.Debug.WriteLine("XValue =" + dp.InternalXValue.ToString());
}
}
if (axisX._isAllXValueZero == true)
{
foreach (DataSeries ds in Chart.InternalSeries)
{
foreach (DataPoint dp in ds.InternalDataPoints)
{
dp.InternalXValue = 1;
}
}
}
}
}
}
}
}
///
/// Update interval in terms of time according to interval type
///
/// Auto interval type
/// Current interval type
/// Interval to update
/// Returns interval in terms of current interval type
private Double UpdateTimeIterval(IntervalTypes autoIntervalType, IntervalTypes currentIntervalTypes, Double intervalToUpdate)
{
if (autoIntervalType == IntervalTypes.Hours)
{
if (currentIntervalTypes == IntervalTypes.Minutes)
intervalToUpdate = intervalToUpdate * 60;
else if (currentIntervalTypes == IntervalTypes.Seconds)
intervalToUpdate = intervalToUpdate * 60 * 60;
else if (currentIntervalTypes == IntervalTypes.Milliseconds)
intervalToUpdate = intervalToUpdate * 60 * 60 * 1000;
}
else if (autoIntervalType == IntervalTypes.Minutes)
{
if (currentIntervalTypes == IntervalTypes.Hours)
intervalToUpdate = Math.Ceiling(intervalToUpdate / 60);
if (currentIntervalTypes == IntervalTypes.Seconds)
intervalToUpdate = Math.Ceiling(intervalToUpdate * 60);
else if (currentIntervalTypes == IntervalTypes.Milliseconds)
intervalToUpdate = Math.Ceiling(intervalToUpdate * 60 * 1000);
}
else if (autoIntervalType == IntervalTypes.Seconds)
{
if (currentIntervalTypes == IntervalTypes.Hours)
intervalToUpdate = Math.Ceiling(intervalToUpdate / 3600);
if (currentIntervalTypes == IntervalTypes.Minutes)
intervalToUpdate = Math.Ceiling(intervalToUpdate / 60);
else if (currentIntervalTypes == IntervalTypes.Milliseconds)
intervalToUpdate = Math.Ceiling(intervalToUpdate * 1000);
}
else if (autoIntervalType == IntervalTypes.Milliseconds)
{
if (currentIntervalTypes == IntervalTypes.Hours)
intervalToUpdate = Math.Ceiling(intervalToUpdate / 3600000);
if (currentIntervalTypes == IntervalTypes.Minutes)
intervalToUpdate = Math.Ceiling(intervalToUpdate / 60000);
else if (currentIntervalTypes == IntervalTypes.Seconds)
intervalToUpdate = Math.Ceiling(intervalToUpdate / 1000);
}
return intervalToUpdate;
}
///
/// Set labels count state
///
private void SetLabelsCountState()
{
//Get all dataPoints with series reference to dataseries
List dataPointsListPri = new List();
List dataPointsListSec = new List();
// Populates the list with InternalDataPoints with all availabel InternalDataPoints from all DataSeries
foreach (DataSeries dataSeries in Chart.InternalSeries)
{
List enabledDataPoints = (from datapoint in dataSeries.InternalDataPoints select datapoint).ToList(); // where datapoint.Enabled == true
// Concatinate the lists of InternalDataPoints if the axis type matches
if (dataSeries.AxisXType == AxisTypes.Primary)
dataPointsListPri.InsertRange(dataPointsListPri.Count, enabledDataPoints);
else
dataPointsListSec.InsertRange(dataPointsListSec.Count, enabledDataPoints);
}
var uniqueLabels4PrimaryAxisX = (from dataPoint in dataPointsListPri group dataPoint by dataPoint.InternalXValue);
IsAllPrimaryAxisXLabelsPresent = (AxisXPrimaryLabels.Count == uniqueLabels4PrimaryAxisX.Count());
var uniqueLabels4SecondaryAxisX = (from dataPoint in dataPointsListSec group dataPoint by dataPoint.InternalXValue);
IsAllSecondaryAxisXLabelsPresent = (AxisXSecondaryLabels.Count == uniqueLabels4SecondaryAxisX.Count());
}
///
/// Create missing axis
///
private void CreateMissingAxes()
{
ChartOrientationType chartOrientation = GetChartOrientation();
Orientation axisXOrientation = (chartOrientation == ChartOrientationType.Vertical) ? Orientation.Horizontal : Orientation.Vertical;
Orientation axisYOrientation = (chartOrientation == ChartOrientationType.Vertical) ? Orientation.Vertical : Orientation.Horizontal;
#region Check primary Axis X
if (GetCountOfSeriesUsingAxisXPrimary() > 0)
{
Axis axisX = GetAxisXFromChart(Chart, AxisTypes.Primary);
if (axisX == null)
{
axisX = new Axis();
axisX._isAutoGenerated = true;
axisX.Chart = Chart;
axisX.AxisOrientation = axisXOrientation;
axisX.AxisType = AxisTypes.Primary;
axisX.PlotDetails = this;
axisX.AxisRepresentation = AxisRepresentations.AxisX;
Chart.InternalAxesX.Add(axisX);
Chart.AxesX.Add(axisX);
}
else
{
axisX.AxisOrientation = axisXOrientation;
axisX.AxisType = AxisTypes.Primary;
axisX.PlotDetails = this;
axisX.AxisRepresentation = AxisRepresentations.AxisX;
}
}
else
{
Axis axisX = GetAxisXFromChart(Chart, AxisTypes.Primary);
while (axisX != null)
{
Chart.InternalAxesX.Remove(axisX);
axisX = GetAxisXFromChart(Chart, AxisTypes.Primary);
}
}
#endregion
#region Check Secondary axis X
if (GetCountOfSeriesUsingAxisXSecondary() > 0)
{
Axis axisX = GetAxisXFromChart(Chart, AxisTypes.Secondary);
if (axisX == null)
{
axisX = new Axis();
axisX._isAutoGenerated = true;
axisX.Chart = Chart;
axisX.AxisOrientation = axisXOrientation;
axisX.AxisType = AxisTypes.Secondary;
axisX.PlotDetails = this;
axisX.AxisRepresentation = AxisRepresentations.AxisX;
Chart.InternalAxesX.Add(axisX);
Chart.AxesX.Add(axisX);
}
else
{
axisX.AxisOrientation = axisXOrientation;
axisX.AxisType = AxisTypes.Secondary;
axisX.PlotDetails = this;
axisX.AxisRepresentation = AxisRepresentations.AxisX;
}
}
else
{
Axis axisX = GetAxisXFromChart(Chart, AxisTypes.Secondary);
while (axisX != null)
{
Chart.InternalAxesX.Remove(axisX);
axisX = GetAxisXFromChart(Chart, AxisTypes.Secondary);
}
}
#endregion
#region Check primary Axis Y
if (GetCountOfSeriesUsingAxisYPrimary() > 0)
{
Axis axisY = GetAxisYFromChart(Chart, AxisTypes.Primary);
if (axisY == null)
{
axisY = new Axis();
axisY._isAutoGenerated = true;
axisY.Chart = Chart;
axisY.AxisOrientation = axisYOrientation;
axisY.AxisType = AxisTypes.Primary;
axisY.PlotDetails = this;
axisY.AxisRepresentation = AxisRepresentations.AxisY;
Chart.InternalAxesY.Add(axisY);
Chart.AxesY.Add(axisY);
}
else
{
axisY.AxisOrientation = axisYOrientation;
axisY.AxisType = AxisTypes.Primary;
axisY.PlotDetails = this;
axisY.AxisRepresentation = AxisRepresentations.AxisY;
}
}
else
{
Axis axisY = GetAxisYFromChart(Chart, AxisTypes.Primary);
while (axisY != null)
{
Chart.InternalAxesY.Remove(axisY);
axisY = GetAxisYFromChart(Chart, AxisTypes.Primary);
}
}
#endregion
#region Check Secondary axis Y
if (GetCountOfSeriesUsingAxisYSecondary() > 0)
{
Axis axisY = GetAxisYFromChart(Chart, AxisTypes.Secondary);
if (axisY == null)
{
axisY = new Axis() { AxisOrientation = axisYOrientation, AxisType = AxisTypes.Secondary };
axisY._isAutoGenerated = true;
axisY.Chart = Chart;
axisY.AxisOrientation = axisYOrientation;
axisY.AxisType = AxisTypes.Secondary;
axisY.PlotDetails = this;
axisY.AxisRepresentation = AxisRepresentations.AxisY;
Chart.InternalAxesY.Add(axisY);
Chart.AxesY.Add(axisY);
}
else
{
axisY.AxisOrientation = axisYOrientation;
axisY.AxisType = AxisTypes.Secondary;
axisY.PlotDetails = this;
axisY.AxisRepresentation = AxisRepresentations.AxisY;
}
}
else
{
Axis axisY = GetAxisYFromChart(Chart, AxisTypes.Secondary);
if (axisY != null)
{
Chart.InternalAxesY.Remove(axisY);
axisY = GetAxisYFromChart(Chart, AxisTypes.Secondary);
}
}
#endregion
}
///
/// Create default legends
///
private void CreateLegends()
{
foreach (Legend oldLegends in Chart.Legends)
{
oldLegends.Entries.Clear();
oldLegends.Visual = null;
}
List SeriesToBeShownInLegend;
if (Chart.InternalSeries.Count > 1)
SeriesToBeShownInLegend = (from entry in Chart.InternalSeries where entry.Enabled == true && (Boolean)entry.ShowInLegend == true select entry).ToList();
else
SeriesToBeShownInLegend = Chart.InternalSeries;
Legend legend = null;
if (SeriesToBeShownInLegend.Count > 0)
{
//List SeriesWithNoReferingLegend = (from entry in SeriesToBeShownInLegend where String.IsNullOrEmpty(entry.Legend) select entry).ToList();
List SeriesWithReferingLegend = (from entry in SeriesToBeShownInLegend where !String.IsNullOrEmpty(entry.Legend) select entry).ToList();
//if (SeriesWithNoReferingLegend.Count > 0)
//{
// legend = new Legend() { _isAutoGenerated = true };
// legend.Chart = Chart;
// Chart.Legends.Add(legend);
// legend.SetValue(FrameworkElement.NameProperty, "Legend" + Chart.Legends.IndexOf(legend));
// foreach (DataSeries series in SeriesWithNoReferingLegend)
// {
// series.InternalLegendName = legend.Name;
// }
//}
if (SeriesWithReferingLegend.Count > 0)
{
foreach (DataSeries series in SeriesWithReferingLegend)
{
var legends = (from entry in Chart.Legends where entry.Name == series.Legend select entry);
if (legends.Count() == 0)
{
legend = new Legend() { _isAutoGenerated = true };
legend.Chart = Chart;
legend.SetValue(FrameworkElement.NameProperty, series.Legend);
Chart.Legends.Add(legend);
}
}
}
}
}
///
/// Get orientation of Chart
///
/// ChartOrientationType
private ChartOrientationType GetChartOrientation()
{
ChartOrientationType chartOrientation = ChartOrientationType.Undefined;
foreach (DataSeries dataSeries in Chart.InternalSeries)
{
if (chartOrientation == ChartOrientationType.Undefined)
chartOrientation = GetChartOrientation(dataSeries.RenderAs);
else if (chartOrientation == GetChartOrientation(dataSeries.RenderAs))
continue;
else
throw new Exception("Invalid chart combination");
}
return chartOrientation;
}
///
/// Creates and updates the plot groups
///
private void PopulatePlotGroups()
{
// Creates any required legends
CreateLegends();
// From the series generate groups based on RenderAs, AxisXType,AxisYType
var plotGroupsData = (from dataSeries in Chart.InternalSeries
group dataSeries by
new
{
dataSeries.RenderAs,
dataSeries.AxisXType,
dataSeries.AxisYType
});
// Populate the plot groups by checking for validity of charts
foreach (var plotGroup in plotGroupsData)
{
// Get the overall orientation of the chart
ChartOrientationType plotGroupChartOrientation = GetChartOrientation(plotGroup.Key.RenderAs);
// Retrieve the reference for the axisX and AxisY
Axis axisX = GetAxisXFromChart(Chart, plotGroup.Key.AxisXType);
Axis axisY = GetAxisYFromChart(Chart, plotGroup.Key.AxisYType);
// Perfrom tasks based on orientation setting
if (ChartOrientation == ChartOrientationType.Undefined)
{
// if orientation is not set then set it first
ChartOrientation = plotGroupChartOrientation;
// create and update a plot group and add to the plot group list
AddToPlotGroupsList(plotGroup.Key.RenderAs, axisX, axisY, plotGroup.ToList());
}
else if (ChartOrientation == plotGroupChartOrientation)
{
// if orientation is already set and the current group also is of the same orientation then
// create and update a plot group and add to the plot group list
AddToPlotGroupsList(plotGroup.Key.RenderAs, axisX, axisY, plotGroup.ToList());
}
else
{
// if the chart orientation do not match then assert.
Debug.Assert(false, "Invalid chart combination. See Documentation for Combination Charts.");
throw new Exception("Invalid chart combination");
}
}
// Select DataSeries and group them by RenderAs type
var seriesGroupByRenderAs = (from series in Chart.InternalSeries group series by series.RenderAs);
// Apply sibling count based on the chart types
foreach (var seriesGroup in seriesGroupByRenderAs)
{
// Convert the group to a list
List seriesList = seriesGroup.ToList();
// Get the number of series within the list
Int32 seriesCount = seriesList.Count;
// Set the count as series count for all series within the list
seriesList.ForEach(delegate(DataSeries dataSeries) { dataSeries.SeriesCountOfSameRenderAs = seriesCount; });
}
// If the chart contains charts of type Bar, StackedBar, StackedBar100
if (ChartOrientation == ChartOrientationType.Horizontal)
{
// Get the count of number of series with RenderAs Bar
Int32 countOfBarCharts = GetSeriesCountByRenderAs(RenderAs.Bar);
// Get the count of number of plot groups with RenderAs type as StackedBar
Int32 countOfStackedBarGroups = GetPlotGroupCountByRenderAs(RenderAs.StackedBar);
// Get the count of number of plot groups with RenderAs type as StackedBar100
Int32 countOfStackedBar100Groups = GetPlotGroupCountByRenderAs(RenderAs.StackedBar100);
// Set a count of siblings by selecting the maximum out of the 3 counts
DrawingDivisionFactor = Math.Max(countOfBarCharts, Math.Max(countOfStackedBarGroups, countOfStackedBar100Groups));
}
else if (ChartOrientation == ChartOrientationType.Vertical)
{
// If the chart contains any chart other than bar type or pie or doughnut
// Get the count of number of series with RenderAs Column
Int32 countOfColumnCharts = GetSeriesCountByRenderAs(RenderAs.Column);
// Get the count of number of plot groups with RenderAs type as StackedColumn
Int32 countOfStackedColumnGroups = GetPlotGroupCountByRenderAs(RenderAs.StackedColumn);
// Get the count of number of plot groups with RenderAs type as StackedColumn100
Int32 countOfStackedColumn100Groups = GetPlotGroupCountByRenderAs(RenderAs.StackedColumn100);
Int32 countOfStockChart = GetPlotGroupCountByRenderAs(RenderAs.Stock);
if (countOfStockChart > 1)
countOfStockChart = 1;
Int32 countOfCandleStickCharts = GetPlotGroupCountByRenderAs(RenderAs.CandleStick);
if (countOfCandleStickCharts > 1)
countOfCandleStickCharts = 1;
countOfStockChart = (countOfStockChart == 0) ? countOfCandleStickCharts : countOfStockChart;
// Set a count of siblings by selecting the maximum out of the 3 counts
DrawingDivisionFactor = Math.Max(countOfColumnCharts, Math.Max(countOfStackedColumnGroups, countOfStackedColumn100Groups));
DrawingDivisionFactor = Math.Max(DrawingDivisionFactor, countOfStockChart);
}
else if (ChartOrientation == ChartOrientationType.NoAxis)
{
// if chart type is NoAxis then set sibling count as zero
DrawingDivisionFactor = 0;
}
}
///
/// Creates and adds a PlotGroup to the PlotGroups list
///
/// RenderAs type of the PlotGroup
/// axisX reference for the PlotGroup
/// AxisY reference for the PlotGroup
/// List of DataSeries belonging to the PlotGroup
private void AddToPlotGroupsList(RenderAs renderAs, Axis axisX, Axis axisY, List series)
{
// Create a new PlotGroup
PlotGroup plotGroupEntry = new PlotGroup(renderAs, axisX, axisY, Chart);
// Assign the series list to the PlotGroup
plotGroupEntry.DataSeriesList = series;
// refresh or update the PlotGroup details
plotGroupEntry.Update();
// Add the PlotGroup the PlotGroups
PlotGroups.Add(plotGroupEntry);
}
///
/// Converts RenderAs to chart orientation type
///
///
///
private ChartOrientationType GetChartOrientation(RenderAs renderAs)
{
ChartOrientationType chartOrientation;
switch (renderAs)
{
case RenderAs.Area:
case RenderAs.Bubble:
case RenderAs.Column:
case RenderAs.Line:
case RenderAs.Stock:
case RenderAs.CandleStick:
case RenderAs.Point:
case RenderAs.StackedArea:
case RenderAs.StackedColumn:
case RenderAs.StackedArea100:
case RenderAs.StackedColumn100:
chartOrientation = ChartOrientationType.Vertical;
break;
case RenderAs.Bar:
case RenderAs.StackedBar:
case RenderAs.StackedBar100:
chartOrientation = ChartOrientationType.Horizontal;
break;
case RenderAs.Pie:
case RenderAs.Doughnut:
case RenderAs.SectionFunnel:
case RenderAs.StreamLineFunnel:
chartOrientation = ChartOrientationType.NoAxis;
break;
default:
chartOrientation = ChartOrientationType.Undefined;
break;
}
return chartOrientation;
}
///
/// Returns the axis reference for a axis by selecting it from the axis collection in the chart
///
/// Reference to the chart
/// Axis type to be selected
/// Axis
internal Axis GetAxisXFromChart(Chart chart, AxisTypes axisType)
{
var axisList = from axis in chart.InternalAxesX where axis.AxisType == axisType select axis;
if (axisList.Count() > 0)
return (axisList).Last();
else return null;
}
///
/// Returns the axis reference for a axis by selecting it from the axis collection in the chart
///
/// Reference to the chart
/// Axis type to be selected
/// Axis
internal Axis GetAxisYFromChart(Chart chart, AxisTypes axisType)
{
var axisList = from axis in chart.InternalAxesY where axis.AxisType == axisType select axis;
if (axisList.Count() > 0)
return (axisList).Last();
else return null;
}
///
/// Sets an incremental ZIndex to the series
///
private void SetIncrementalZIndexForSeries()
{
Int32 index = 0;
Int32 seriesIndex = 0;
foreach (DataSeries dataSeries in Chart.InternalSeries)
{
if (dataSeries.IsZIndexSet)
dataSeries.InternalZIndex = dataSeries.ZIndex - seriesIndex;
else
dataSeries.InternalZIndex = dataSeries.ZIndex - Chart.InternalSeries.Count;
dataSeries.InternalZIndex = dataSeries.InternalZIndex + index++;
seriesIndex++;
//dataSeries.InternalZIndex = dataSeries.InternalZIndex - Chart.InternalSeries.Count;
//dataSeries.InternalZIndex += index++;
}
}
///
/// Calculates and returns the drawing order for all the series for a given chart
///
/// A dictionary containing DataSeries as key and its corresponding drawing index
private Dictionary GenerateDrawingOrder()
{
SetIncrementalZIndexForSeries();
// These charts will be drawn in the same plane hence the depth for each chart type increases by one
Int32 layer3DCount = 0;
layer3DCount += (GetSeriesCountByRenderAs(RenderAs.Column) > 0) ? 1 : 0;
layer3DCount += (GetSeriesCountByRenderAs(RenderAs.StackedColumn) > 0) ? 1 : 0;
layer3DCount += (GetSeriesCountByRenderAs(RenderAs.StackedColumn100) > 0) ? 1 : 0;
layer3DCount += (GetSeriesCountByRenderAs(RenderAs.Bar) > 0) ? 1 : 0;
layer3DCount += (GetSeriesCountByRenderAs(RenderAs.StackedBar) > 0) ? 1 : 0;
layer3DCount += (GetSeriesCountByRenderAs(RenderAs.StackedBar100) > 0) ? 1 : 0;
//layer3DCount += (GetSeriesCountByRenderAs(RenderAs.Bubble) > 0) ? 1 : 0;
// Each Area chart has to be drawn in a different plane hence the depth incereases
// by the amount equal to the number of area charts
layer3DCount += GetSeriesCountByRenderAs(RenderAs.Area);
//layer3DCount += GetSeriesCountByRenderAs(RenderAs.Line);
//layer3DCount += GetSeriesCountByRenderAs(RenderAs.Point);
//layer3DCount += GetSeriesCountByRenderAs(RenderAs.Bubble);
// These charts are drawn on a different plane for each plot group hence the depth increases
// by the amount equal to the number of plots for these charts
layer3DCount += GetPlotGroupCountByRenderAs(RenderAs.StackedArea);
layer3DCount += GetPlotGroupCountByRenderAs(RenderAs.StackedArea100);
// Set the depth factor as the depth count
Layer3DCount = layer3DCount;
// Functions to select the key and value from the grouped list obtained from LINQ.
Func, DataSeries> KeySelector = delegate(IGrouping entry) { return entry.Key; };
Func, Int32> ElementSelector = delegate(IGrouping entry) { return entry.Last(); };
// create a dictionary of seriesIndex by converting the result into a dictionary
Dictionary sortedSeriesIndexGroupedBySeries = (from series in Chart.InternalSeries orderby series.InternalZIndex group series.InternalZIndex by series).ToDictionary(KeySelector, ElementSelector);
// Generate index for each chart type
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.Column, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.StackedColumn, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.StackedColumn100, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.Bar, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.StackedBar, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.StackedBar100, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.Line, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.Point, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.Stock, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.CandleStick, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.Bubble, sortedSeriesIndexGroupedBySeries);
// Generate index for each chart type based on the AxisXType and AxisYType
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.StackedArea, AxisTypes.Primary, AxisTypes.Primary, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.StackedArea, AxisTypes.Primary, AxisTypes.Secondary, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.StackedArea, AxisTypes.Secondary, AxisTypes.Primary, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.StackedArea, AxisTypes.Secondary, AxisTypes.Secondary, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.StackedArea100, AxisTypes.Primary, AxisTypes.Primary, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.StackedArea100, AxisTypes.Primary, AxisTypes.Secondary, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.StackedArea100, AxisTypes.Secondary, AxisTypes.Primary, sortedSeriesIndexGroupedBySeries);
sortedSeriesIndexGroupedBySeries = GenerateIndexByRenderAs(RenderAs.StackedArea100, AxisTypes.Secondary, AxisTypes.Secondary, sortedSeriesIndexGroupedBySeries);
// create a List out of the result obtained by sorting the seriesIndex by ZIndex value
List> seriesIndexList = (from entry in sortedSeriesIndexGroupedBySeries orderby entry.Value select entry).ToList();
// Temporary list to swap lists
List> seriesIndexTempList = new List>();
// This list will accumilte the final index for each DataSeries
List> seriesIndexFinal = new List>();
Int32 drawingIndex = 0; // Decides which chart shoud come in front (rendering will be done from series with lowest index )
Int32 lowestIndex; // used to get the lowest index for a chart type
Boolean ignore = false; // is used to indicate whether the series has any affect on index or not
// This is a array of ignorable render as types while calculating drawing index
RenderAs[] ignorableCharts = { RenderAs.Line, RenderAs.Point, RenderAs.Bubble, RenderAs.Stock, RenderAs.CandleStick };
// repeat the loop until the seriesIndexList becomes empty
while (seriesIndexList.Count > 0)
{
// Do not ignore any series type ( this will change based on chart type later )
ignore = false;
// Get the lowest seriesIndex from the seriesIndexList
lowestIndex = (from series in seriesIndexList select series.Value).Min();
// Select all series with the lowest index
var seriesWithLowestIndex = from series in seriesIndexList where series.Value == lowestIndex select series.Key;
// Insert the series with lowest index into the final index list, Update the index by setting it to the drawingIndex
seriesIndexFinal.InsertRange(seriesIndexFinal.Count, (from series in seriesWithLowestIndex select new KeyValuePair(series, drawingIndex)).ToList());
// Get the count of series with RenderAs value in the ignorable charts list
Int32 ignorableChartCount = (from series in seriesWithLowestIndex where ignorableCharts.Contains(series.RenderAs) select series.RenderAs).Count();
// If the count of ignorable charts is gretaer than 0 then set ignore flag to true
ignore = (ignorableChartCount > 0) ? true : false;
// Get a list of series which are not same as the lowest index
var seriesWithoutLowestIndex = from series in seriesIndexList where series.Value != lowestIndex select series;
// Generate a list of series whose index is not same as the lowest index
seriesIndexTempList = seriesWithoutLowestIndex.ToList();
// Clear the list so that a updated list can be assigned to it
seriesIndexList.Clear();
// Assign the reference of the updated list to the setiesIndex
seriesIndexList = seriesIndexTempList;
// do not increase the drawing index
if (!ignore)
drawingIndex++;
}
// Functions to select the key and value from the grouped list obtained from LINQ.
Func, DataSeries> KeySelector2 = delegate(KeyValuePair entry) { return entry.Key; };
Func, Int32> ElementSelector2 = delegate(KeyValuePair entry) { return entry.Value; };
// Generate a dictionary from the result obtained from the LINQ.
sortedSeriesIndexGroupedBySeries = seriesIndexFinal.ToDictionary(KeySelector2, ElementSelector2);
// Return the generated dictionary
return sortedSeriesIndexGroupedBySeries;
}
///
/// From a given list all series with a particular RenderAs type are selected.
/// From such a list the largest index is obtainded. This index is applied to all series with same RenderAs type
///
/// RenderAs type for selecting the series
/// List which will be used to select the series
/// Return the updated list
private Dictionary GenerateIndexByRenderAs(RenderAs renderAs, Dictionary seriesIndexDictionary)
{
// Get all series of a particular render as type
var seriesByRenderAs = from entry in seriesIndexDictionary where entry.Key.RenderAs == renderAs select entry;
// If there atleast one series in the list then continue to update the list
if (seriesByRenderAs.Count() > 0)
{
// Get the highest index from the seriesByRenderAs list
Int32 highestIndex = (from entry in seriesByRenderAs select entry.Value).Max();
// Convert list to array
KeyValuePair[] seriesArray = seriesByRenderAs.ToArray();
// Update the series index
for (Int32 i = 0; i < seriesArray.Length; i++)
seriesIndexDictionary[seriesArray[i].Key] = highestIndex;
}
// Return the updated list
return seriesIndexDictionary;
}
///
/// From a given list all series with a particular RenderAs type and same reference to axisX and axisY are selected.
/// From such a list the largest index is obtainded. This index is applied to all series with same RenderAs type
///
/// RenderAs type for selecting the series
/// Reference to the axisX
/// Reference to the axisY
/// List which will be used to select the series
/// Return the updated list
private Dictionary GenerateIndexByRenderAs(RenderAs renderAs, AxisTypes axisXType, AxisTypes axisYType, Dictionary seriesIndexDictionary)
{
// Get all series of a particular render as typeand same reference to axisX and axisY
var seriesByRenderAs = from entry in seriesIndexDictionary
where entry.Key.RenderAs == renderAs && entry.Key.AxisXType == axisXType && entry.Key.AxisYType == axisYType
select entry;
// If there atleast one series in the list then continue to update the list
if (seriesByRenderAs.Count() > 0)
{
// Get the highest index fron the seriesByRenderAs list
Int32 highestIndex = (from entry in seriesByRenderAs select entry.Value).Max();
// Convert list to array
KeyValuePair[] seriesArray = seriesByRenderAs.ToArray();
// Update the series index
for (Int32 i = 0; i < seriesArray.Length; i++)
seriesIndexDictionary[seriesArray[i].Key] = highestIndex;
}
// Return the updated list
return seriesIndexDictionary;
}
///
/// Returns a count of DataSeries for a particulat render as type
///
internal Int32 GetSeriesCountByRenderAs(RenderAs renderAs)
{
return (from series in Chart.InternalSeries where series.RenderAs == renderAs select series).Count();
}
///
/// Returns a count of DataSeries for a particulat render as type
///
internal Int32 GetPlotGroupCountByRenderAs(RenderAs renderAs)
{
return (from plotGroup in PlotGroups where plotGroup.RenderAs == renderAs && plotGroup.IsEnabled select plotGroup).Count();
}
///
/// Create list of datapoints from all series
///
private void CreateListOfDataPoints()
{
// List of all datapoints in the chart
ListOfAllDataPoints = new List();
// Populates the list with InternalDataPoints with all availabel InternalDataPoints from all DataSeries
foreach (DataSeries dataSeries in Chart.InternalSeries)
{
// Concatinate the lists of InternalDataPoints if the axis type matches
if (dataSeries.Enabled == true)
{
List enabledDataPoints = (from datapoint in dataSeries.InternalDataPoints select datapoint).ToList(); //where datapoint.Enabled == true
ListOfAllDataPoints.InsertRange(ListOfAllDataPoints.Count, enabledDataPoints);
}
}
}
///
/// Generates AxisLabels for this PlotGroup and returns a dictionary
/// that holds InternalXValue as key, AxisLabel as value
///
private Dictionary GetAxisXLabels(AxisTypes axisXType)
{
// List of all datapoints in the chart
List listOfAllDataPoints = (from dataPoint in ListOfAllDataPoints where dataPoint.Parent.AxisXType == axisXType select dataPoint).ToList();
//// Populates the list with InternalDataPoints with all availabel InternalDataPoints from all DataSeries
//foreach (DataSeries dataSeries in Chart.InternalSeries)
//{
// // Concatinate the lists of InternalDataPoints if the axis type matches
// if (dataSeries.AxisXType == axisXType && dataSeries.Enabled == true)
// {
// List enabledDataPoints = (from datapoint in dataSeries.InternalDataPoints select datapoint).ToList(); //where datapoint.Enabled == true
// listOfAllDataPoints.InsertRange(listOfAllDataPoints.Count, enabledDataPoints);
// }
//}
// Contains a table which hold unique XValues and all the Axis Labels availabel for each XVAlue
var uniqueXValueDataPoints = (from dataPoint in listOfAllDataPoints where !String.IsNullOrEmpty(dataPoint.AxisXLabel) orderby dataPoint.InternalXValue group dataPoint.AxisXLabel by dataPoint.InternalXValue);
// A function to select a appropriate key for creating the final dictionary
Func, Double> GetXValue = delegate(IGrouping entry) { return entry.Key; };
// A function to get the last axis label for a particular InternalXValue for an available set of axis labels
Func, String> GetAxisLabel = delegate(IGrouping entry) { return entry.Last(); };
// Generates the dictionary with InternalXValue as key, AxisLabel as value
return uniqueXValueDataPoints.ToDictionary(GetXValue, GetAxisLabel);
}
#endregion
#region Internal Methods
///
/// Returns the maximum data value for axisX, no need to check primary or secondary
///
/// Returns the maximum data value as Double
internal Double GetAxisXMaximumDataValue(Axis axisX)
{
return (from plotData in PlotGroups
where (!Double.IsNaN(plotData.MaximumX) && plotData.AxisX == axisX)
select plotData.MaximumX).Max();
}
///
/// Returns the maximum data value for AxisY, no need to check primary or secondary
///
///
/// Returns the maximum data value as Double
internal Double GetAxisYMaximumDataValue(Axis axisY)
{
return (from plotData in PlotGroups
where (!Double.IsNaN(plotData.MaximumY) && plotData.AxisY == axisY)
select plotData.MaximumY).Max();
}
///
/// Returns the minimum data value for axisX, no need to check primary or secondary
///
///
/// Returns the minimum data value as Double
internal Double GetAxisXMinimumDataValue(Axis axisX)
{
Double dataSeriesCount = 0;
Double min = Double.PositiveInfinity;
foreach (PlotGroup plotGroup in PlotGroups)
{
dataSeriesCount = (from dataSeries in plotGroup.DataSeriesList
where dataSeries.DataPoints.Count > 0
select dataSeries).Count();
if (dataSeriesCount > 0)
{
if ((!Double.IsNaN(plotGroup.MinimumX) && plotGroup.AxisX == axisX))
{
min = Math.Min(min, plotGroup.MinimumX);
}
}
}
if (!Double.IsPositiveInfinity(min))
return min;
else
return 0;
//return (from plotData in PlotGroups
// where (!Double.IsNaN(plotData.MinimumX) && plotData.AxisX == axisX)
// select plotData.MinimumX).Min();
}
///
/// Returns the minimum data value for AxisY, no need to check primary or secondary
///
///
/// Returns the minimum data value as Double
internal Double GetAxisYMinimumDataValue(Axis axisY)
{
Double dataSeriesCount = 0;
Double min = Double.PositiveInfinity;
foreach (PlotGroup plotGroup in PlotGroups)
{
dataSeriesCount = (from dataSeries in plotGroup.DataSeriesList
where dataSeries.DataPoints.Count > 0
select dataSeries).Count();
if (dataSeriesCount > 0)
{
if ((!Double.IsNaN(plotGroup.MinimumY) && plotGroup.AxisY == axisY))
{
min = Math.Min(min, plotGroup.MinimumY);
}
}
}
if (!Double.IsPositiveInfinity(min))
return min;
else
return 0;
//return (from plotData in PlotGroups
// where (!Double.IsNaN(plotData.MinimumY) && plotData.AxisY == axisY)
// select plotData.MinimumY).Min();
}
///
/// Returns the maximum ZValue
///
/// Returns the maximum ZValue as Double
internal Double GetMaximumZValue()
{
return (from plotData in PlotGroups
where !Double.IsNaN(plotData.MinimumZ)
select plotData.MaximumZ).Max();
}
///
/// Returns the minimum ZValue
///
/// Returns the minimum ZValue as Double
internal Double GetMinimumZValue()
{
return (from plotData in PlotGroups
where !Double.IsNaN(plotData.MinimumZ)
select plotData.MinimumZ).Min();
}
///
/// Returns the maximum value from a given set of minimum differences
///
/// Double
internal Double GetMaxOfMinDifferencesForXValue()
{
Double max = Double.NegativeInfinity;
foreach (PlotGroup plotGroup in PlotGroups)
{
var dsList = (from dataSeries in plotGroup.DataSeriesList
where dataSeries.DataPoints.Count > 0
select dataSeries);
if (dsList != null && dsList.Count() > 0)
{
var plotGroups = (from d in dsList select d.PlotGroup);
if (plotGroups != null && plotGroups.Count() > 0)
{
Double tempMax = (from plotData in plotGroups
where !Double.IsNaN(plotData.MinDifferenceX)
select plotData.MinDifferenceX).Max();
if (tempMax > max)
max = tempMax;
}
}
}
return Double.IsNegativeInfinity(max) ? Double.PositiveInfinity : max;
//return (from plotData in PlotGroups
// where !Double.IsNaN(plotData.MinDifferenceX)
// select plotData.MinDifferenceX).Max();
}
///
/// Returns the maximum value from a given set of minimum differences by renderas type
///
/// Double
internal Double GetMaxOfMinDifferencesForXValue(RenderAs renderAs)
{
return (from plotData in PlotGroups
where !Double.IsNaN(plotData.MinDifferenceX) && plotData.RenderAs == renderAs
select plotData.MinDifferenceX).Max();
}
///
/// Returns the minimum value from a given set of minimum differences
///
/// Double
internal Double GetMinOfMinDifferencesForXValue()
{
return (from plotData in PlotGroups
where !Double.IsNaN(plotData.MinDifferenceX)
select plotData.MinDifferenceX).Min();
}
///
/// Returns the minimum value from a given set of minimum differences by render as type
///
/// RenderAs
/// Double
internal Double GetMinOfMinDifferencesForXValue(RenderAs renderAs)
{
return (from plotData in PlotGroups
where !Double.IsNaN(plotData.MinDifferenceX) && plotData.RenderAs == renderAs
select plotData.MinDifferenceX).Min();
}
///
/// Returns the minimum value from a given set of minimum differences by render as types
///
/// RenderAs
/// Double
internal Double GetMinOfMinDifferencesForXValue(params RenderAs[] renderAs)
{
return (from plotData in PlotGroups
where !Double.IsNaN(plotData.MinDifferenceX) && renderAs.Contains(plotData.RenderAs)
select plotData.MinDifferenceX).Min();
}
///
/// Returns a series list based on the render as type
///
/// RenderAs
/// List of dataseries
internal List GetSeriesListByRenderAs(RenderAs renderAs)
{
return (from series in Chart.InternalSeries where series.Enabled == true && series.RenderAs == renderAs select series).ToList();
}
///
/// Returns series count using secondary axis-x
///
/// Series count
internal Int32 GetCountOfSeriesUsingAxisXSecondary()
{
return (from series in Chart.InternalSeries where series.AxisXType == AxisTypes.Secondary select series).Count();
}
///
/// Get count of series using primary axis-x
///
/// Series count
internal Int32 GetCountOfSeriesUsingAxisXPrimary()
{
return (from series in Chart.InternalSeries where series.AxisXType == AxisTypes.Primary select series).Count();
}
///
/// Return series count using secondary axis-y
///
/// Series count
internal Int32 GetCountOfSeriesUsingAxisYSecondary()
{
return (from series in Chart.InternalSeries where series.AxisYType == AxisTypes.Secondary select series).Count();
}
///
/// Returns series count using primary axis-y
///
/// Series count
internal Int32 GetCountOfSeriesUsingAxisYPrimary()
{
return (from series in Chart.InternalSeries where series.AxisYType == AxisTypes.Primary select series).Count();
}
///
/// Returns true if series count is equal to no of dataSeries having renderAs type StackedArea100
/// or StackedBar100 or StackedColumn100, else false
///
/// Boolean
internal Boolean GetStacked100OverrideState()
{
RenderAs[] stacked100Types = { RenderAs.StackedArea100, RenderAs.StackedBar100, RenderAs.StackedColumn100 };
Int32 countOfStack100Types = (from series in Chart.InternalSeries where stacked100Types.Contains(series.RenderAs) select series).Count();
return (countOfStack100Types == Chart.InternalSeries.Count());
}
///
/// Returns datapoints grouped by InternalXValue
///
/// RenderAs
/// Dictionary[Double, SortedDataPoints]
internal Dictionary GetDataPointsGroupedByXValue(RenderAs renderAs)
{
List selectedGroup = (from plotGroup in PlotGroups where plotGroup.RenderAs == renderAs select plotGroup).ToList();
List dataPoints = new List();
foreach (PlotGroup plotGroup in selectedGroup)
{
foreach (DataSeries dataSeries in plotGroup.DataSeriesList)
{
if (dataSeries.Enabled == true)
{
List enabledDataPoints = (from datapoint in dataSeries.InternalDataPoints where datapoint.Enabled == true select datapoint).ToList();
dataPoints.InsertRange(dataPoints.Count, enabledDataPoints);
}
}
}
var dataPointsGroupedByXValues = (from datapoint in dataPoints group datapoint by datapoint.InternalXValue);
Dictionary entries = new Dictionary();
foreach (var groupEntry in dataPointsGroupedByXValues)
{
List positiveDataPoints = (from data in groupEntry where data.InternalYValue > 0 select data).ToList();
List negativeDataPoints = (from data in groupEntry where data.InternalYValue <= 0 select data).ToList();
entries.Add(groupEntry.Key, new SortDataPoints(positiveDataPoints, negativeDataPoints));
}
return entries;
}
///
/// Returns datapoints grouped by InternalXValue
///
/// dataseries renderas type
/// axis-x
/// axis-y
/// Returns datapoints grouped by InternalXValue
internal Dictionary GetDataPointsGroupedByXValue(RenderAs renderAs, Axis axisX, Axis axisY)
{
List selectedGroup = (from plotGroup in PlotGroups where plotGroup.RenderAs == renderAs && plotGroup.AxisX == axisX && plotGroup.AxisY == axisY select plotGroup).ToList();
List dataPoints = new List();
foreach (PlotGroup plotGroup in selectedGroup)
{
foreach (DataSeries dataSeries in plotGroup.DataSeriesList)
{
if (dataSeries.Enabled == true)
{
List enabledDataPoints = (from datapoint in dataSeries.InternalDataPoints where datapoint.Enabled == true select datapoint).ToList();
dataPoints.InsertRange(dataPoints.Count, enabledDataPoints);
}
}
}
var dataPointsGroupedByXValues = (from datapoint in dataPoints group datapoint by datapoint.InternalXValue);
Dictionary entries = new Dictionary();
foreach (var groupEntry in dataPointsGroupedByXValues)
{
List positiveDataPoints = (from data in groupEntry where data.InternalYValue > 0 select data).ToList();
List negativeDataPoints = (from data in groupEntry where data.InternalYValue <= 0 select data).ToList();
entries.Add(groupEntry.Key, new SortDataPoints(positiveDataPoints, negativeDataPoints));
}
return entries;
}
///
/// Returns minimum interval of axis-x
///
/// Axis
internal Axis GetAxisXMinimumInterval()
{
Double minInterval = (Double)(from axis in Chart.InternalAxesX select axis.InternalInterval).Min();
return (from axis in Chart.InternalAxesX where axis.InternalInterval == minInterval select axis).First();
}
///
/// Returns division factor (Number of distinct parent) from sorted datapoints set
///
/// SortedDataPoints
/// Int32
internal Int32 GetDivisionFactor(SortDataPoints sortedSet)
{
List lists = new List();
lists.InsertRange(lists.Count, sortedSet.Positive);
lists.InsertRange(lists.Count, sortedSet.Negative);
// Number of distinct parent
return (from entry in lists group entry by entry.Parent).Count();
}
///
/// Returns dataseries list from sorted datapoints
///
/// SortedDataPoints
/// List of dataseries
internal List GetSeriesFromSortedPoints(SortDataPoints sortedSet)
{
List lists = new List();
lists.InsertRange(lists.Count, sortedSet.Positive);
lists.InsertRange(lists.Count, sortedSet.Negative);
return (from entry in lists select entry.Parent).Distinct().ToList();
}
///
/// Returns the list of Series belonging to a DataPoint value
///
/// DataPoint
/// List of dataseries
internal List GetSeriesFromDataPoint(DataPoint dataPoint)
{
List lists = new List();
//Boolean IsDataSeriesExist = false;
foreach (DataSeries ds in Chart.InternalSeries)
{
if (ds.RenderAs == dataPoint.Parent.RenderAs && ds.Enabled == true)
{
//foreach (DataPoint dp in ds.InternalDataPoints)
//{
// if (dp.InternalXValue == dataPoint.InternalXValue)
// {
// IsDataSeriesExist = true;
// break;
// }
// else
// IsDataSeriesExist = false;
//}
if (ds.InternalDataPoints.Count > 0)
lists.Add(ds);
}
}
return lists;
}
///
/// Returns max division factor,
/// Where division factor is the number of distinct parent(dataseries) from sorted datapoints set
///
/// Dictionary of SortedDataPoints
/// Int32
internal Int32 GetMaxDivision(Dictionary sortedDataPointList)
{
List values = sortedDataPointList.Keys.ToList();
Int32 factor = 0;
foreach (Double value in values)
{
factor = Math.Max(factor, GetDivisionFactor(sortedDataPointList[value]));
}
return factor;
}
///
/// Returns absolute sum of datapoints
///
/// List of datapoints
/// Double
internal Double GetAbsoluteSumOfDataPoints(List dataPoints)
{
if (dataPoints.Count > 0 && (dataPoints[0].Parent.RenderAs == RenderAs.SectionFunnel || dataPoints[0].Parent.RenderAs == RenderAs.StreamLineFunnel))
return (from dataPoint in dataPoints where !Double.IsNaN(dataPoint.InternalYValue) && dataPoint.InternalYValue >=0 select Math.Abs(dataPoint.InternalYValue)).Sum();
else
return (from dataPoint in dataPoints where !Double.IsNaN(dataPoint.InternalYValue) select Math.Abs(dataPoint.InternalYValue)).Sum();
}
///
/// Returns datapoint value in stacked order (positive to negative order)
///
/// PlotGroup
/// Dictionary[Double, List[Double]]
internal Dictionary> GetDataPointValuesInStackedOrder(PlotGroup plotGroup)
{
Double[] xValues = plotGroup.XWiseStackedDataList.Keys.ToArray();
Array.Sort(xValues);
Dictionary> dataPointsInStackOrder = new Dictionary>();
for (Int32 i = 0; i < xValues.Length; i++)
{
var yValuePositive = (from entry in plotGroup.XWiseStackedDataList[xValues[i]].Positive group entry.InternalYValue by entry.Parent);
var yValueNegative = (from entry in plotGroup.XWiseStackedDataList[xValues[i]].Negative group entry.InternalYValue by entry.Parent);
Double[] indexedYValues = new Double[yValuePositive.Count() + yValueNegative.Count()];
for (Int32 index = 0; index < indexedYValues.Length; index++)
indexedYValues[index] = 0;
foreach (var entry in yValuePositive)
{
Int32 index = plotGroup.DataSeriesList.IndexOf(entry.Key);
if (index < indexedYValues.Count())
indexedYValues[index] += entry.Sum();
}
foreach (var entry in yValueNegative)
{
Int32 index = plotGroup.DataSeriesList.IndexOf(entry.Key);
if (index < indexedYValues.Count())
indexedYValues[index] += entry.Sum();
}
dataPointsInStackOrder.Add(xValues[i], indexedYValues.ToList());
}
return dataPointsInStackOrder;
}
///
/// Returns datapoint in stacked order (positive to negative order)
///
/// PlotGroup
/// Dictionary[Double, List[DataPoint]]
internal Dictionary> GetDataPointInStackOrder(PlotGroup plotGroup)
{
Double[] xValues = plotGroup.XWiseStackedDataList.Keys.ToArray();
Array.Sort(xValues);
Dictionary> dataPointsInStackOrder = new Dictionary>();
Int32 enabledSeriesCount = (from series in plotGroup.DataSeriesList where series.Enabled == true select series).Count();
for (Int32 i = 0; i < xValues.Length; i++)
{
var yPositiveDataPoints = (from entry in plotGroup.XWiseStackedDataList[xValues[i]].Positive group entry by entry.Parent);
var yNegativeDataPoints = (from entry in plotGroup.XWiseStackedDataList[xValues[i]].Negative group entry by entry.Parent);
DataPoint[] indexedDataPoints = new DataPoint[yPositiveDataPoints.Count() + yNegativeDataPoints.Count()];
foreach (var entry in yPositiveDataPoints)
{
Int32 index = plotGroup.DataSeriesList.IndexOf(entry.Key);
if (index < indexedDataPoints.Count())
indexedDataPoints[index] = entry.First();
}
foreach (var entry in yNegativeDataPoints)
{
Int32 index = plotGroup.DataSeriesList.IndexOf(entry.Key);
if (index < indexedDataPoints.Count())
indexedDataPoints[index] = entry.First();
}
dataPointsInStackOrder.Add(xValues[i], indexedDataPoints.ToList());
}
return dataPointsInStackOrder;
}
#endregion
#region Internal Events
#endregion
#region Data
#endregion
}
}