/*
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.Documents;
#else
using System;
using System.Collections.Generic;
using System.Linq;
#endif
namespace Visifire.Charts
{
///
/// One or more than one DataSeries are grouped into a PlotGroup
///
internal class PlotGroup
{
#region Public Methods
///
/// Initializes a new instance of the Visifire.Charts.PlotGroup class.
///
/// RenderAs
/// axisX
/// AxisY
public PlotGroup(RenderAs renderAs, Axis axisX, Axis axisY, Chart chart)
{
DataSeriesList = new List();
XWiseStackedDataList = new Dictionary();
RenderAs = renderAs;
AxisX = axisX;
AxisY = axisY;
Chart = chart;
}
#endregion
#region Public Properties
#endregion
#region Public Events
#endregion
#region Protected Methods
#endregion
#region Internal Properties
internal Chart Chart
{
get;
set;
}
///
/// PlotGroup is enabled if atleast one dataSeries in DataSeriesList is enabled
///
internal bool IsEnabled
{
get
{
foreach (DataSeries ds in DataSeriesList)
{
if ((Boolean)ds.Enabled) return true;
}
return false;
}
}
///
/// Reference to the X-Axis for this group
///
internal Axis AxisX
{
get;
private set;
}
///
/// Reference to the Y-Axis for this group
///
internal Axis AxisY
{
get;
private set;
}
///
/// Chart type that will be rendered by this PlotGroup
///
internal RenderAs RenderAs
{
get;
private set;
}
///
/// List of DataSeries that belong to this group
///
internal List DataSeriesList
{
get;
set;
}
///
/// Data stored in a sorted order for displaying stacked chart types
///
internal Dictionary XWiseStackedDataList
{
get;
#if DEBUG
set;
#else
private set;
#endif
}
///
/// Stores the maximum InternalXValue for the group
///
internal Double MaximumX
{
get;
private set;
}
///
/// Stores the maximum YValue for the gourp
///
internal Double MaximumY
{
get;
private set;
}
///
/// Stores the maximum ZValue for the group
///
internal Double MaximumZ
{
get;
private set;
}
///
/// Stores the minimum InternalXValue for the group
///
internal Double MinimumX
{
get;
private set;
}
///
/// Stores the minimum YValue for the group
///
internal Double MinimumY
{
get;
private set;
}
///
/// Stores the minimum ZValue for the group
///
internal Double MinimumZ
{
get;
private set;
}
///
/// Stores the calculated minimum difference value for the XValues in this group
///
internal Double MinDifferenceX
{
get;
#if DEBUG
set;
#else
private set;
#endif
}
#endregion
#region Private Delegates
#endregion
#region Private Methods
///
/// Selectively adds the DataPoint to the Positive and Negative list of datapoints under a PlotGroup
///
/// Reference to the XWise data node
/// DataPoint to be added to the node
#if DEBUG
internal
#else
private
#endif
void AddXWiseStackedDataEntry(ref XWiseStackedData xWiseData, DataPoint dataPoint)
{
if (dataPoint.InternalYValue >= 0)
{
xWiseData.Positive.Add(dataPoint);
}
else
{
xWiseData.Negative.Add(dataPoint);
}
}
///
/// Gets minimum difference from Double value array.
///
/// Array of values
/// Double
#if DEBUG
internal Double GetMinDifference(Double[] values)
#else
private Double GetMinDifference(Double[] values)
#endif
{
// initialize minimum difference value with a large value
Double minDiff = Double.MaxValue;
Double[] distinctValues = values.Distinct().ToArray();
// get unique values and then sort it
Array.Sort(distinctValues);
if (distinctValues.Length <= 1 )
{
return Double.PositiveInfinity;
}
for (Int32 i = 0; i < distinctValues.Length - 1; i++)
{
// get the smallest difference between two successive elements
minDiff = Math.Min(minDiff, Math.Abs(distinctValues[i] - distinctValues[i + 1]));
}
return minDiff;
}
#endregion
#region Internal Methods
///
/// This function will find the dependent variable types from the current PlotGroup.
///
///
internal List GetDependentVariableTypes()
{
List types = new List();
var dataSeriesCount = (from dataSeries in DataSeriesList
where dataSeries.RenderAs == RenderAs.CandleStick
|| dataSeries.RenderAs == RenderAs.Stock
select dataSeries).Count();
if (dataSeriesCount == DataSeriesList.Count)
types.Add(typeof(List));
else
{
types.Add(typeof(Double));
var bubbleSeriesCount = (from dataSeries in DataSeriesList
where dataSeries.RenderAs == RenderAs.Bubble
select dataSeries).Count();
if (bubbleSeriesCount > 0)
types.Add(typeof(Double));
}
return types;
}
///
/// Updates all properties of this class by calculating each property.
///
internal void Update()
{
// List to store a concatinated set of InternalDataPoints from all DataSeries in this group
List dataPoints = new List();
// Populates the list with InternalDataPoints with all availabel InternalDataPoints from all DataSeries
// Also set the plotGropu reference to the current plot group
foreach (DataSeries dataSeries in DataSeriesList)
{
// check if data series is enabled
// if (dataSeries.Enabled == true)
{
List enabledDataPoints = (from datapoint in dataSeries.InternalDataPoints select datapoint).ToList();
// Concatenate the lists of InternalDataPoints
dataPoints.InsertRange(dataPoints.Count, enabledDataPoints);
// set the plot group reference
dataSeries.PlotGroup = this;
}
}
// variable to temporarily store the stacked Data content
XWiseStackedData xWiseData;
// Populates the Xwise sorted Stacked data list with entries from
// all the datapoints from all DataSeries from this group
foreach (DataPoint dataPoint in dataPoints)
{
if (XWiseStackedDataList.ContainsKey(dataPoint.InternalXValue))
{
// gets the existing node
xWiseData = XWiseStackedDataList[dataPoint.InternalXValue];
}
else
{
// Creates a new node
xWiseData = new XWiseStackedData();
XWiseStackedDataList.Add(dataPoint.InternalXValue, xWiseData);
}
// add the datapoint to a node
AddXWiseStackedDataEntry(ref xWiseData, dataPoint);
}
// Get a list of all XValues,YValues and ZValues from all InternalDataPoints from all the DataSeries in this Group
var xValues = (from dataPoint in dataPoints where !Double.IsNaN(dataPoint.InternalXValue) select dataPoint.InternalXValue).Distinct();
List yValues = null;
List zValues = null;
if(GetDependentVariableTypes().Count == 1 && GetDependentVariableTypes()[0] == typeof(List))
yValues = new List();
else if (GetDependentVariableTypes().Count == 2)
{
yValues = (from dataPoint in dataPoints where !Double.IsNaN(dataPoint.InternalYValue) select dataPoint.InternalYValue).Distinct().ToList();
zValues = (from dataPoint in dataPoints where !Double.IsNaN(dataPoint.ZValue) select dataPoint.ZValue).Distinct().ToList();
}
else
yValues = (from dataPoint in dataPoints where !Double.IsNaN(dataPoint.InternalYValue) select dataPoint.InternalYValue).Distinct().ToList();
List yValuesList = new List();
foreach (DataPoint dp in dataPoints)
{
if (dp.Parent.RenderAs == RenderAs.CandleStick || dp.Parent.RenderAs == RenderAs.Stock)
{
if (dp.YValues != null)
yValuesList.AddRange(dp.YValues);
}
}
yValues.AddRange(yValuesList);
// Calculate max value
MaximumX = (xValues.Count() > 0) ? (xValues).Max() : 0;
MaximumZ = (zValues != null && zValues.Count() > 0) ? (zValues).Max() : 0;
// Calculate min value
MinimumX = (xValues.Count() > 0) ? (xValues).Min() : 0;
MinimumZ = (zValues != null && zValues.Count() > 0) ? (zValues).Min() : 0;
// variables to store the yValuee sum in case of stacked type charts
// var positiveYValue;
// var negativeYValue;
// Calculating Max and Min YValue based on chart type
switch (RenderAs)
{
case RenderAs.Area:
case RenderAs.Bar:
case RenderAs.Bubble:
case RenderAs.Column:
case RenderAs.Doughnut:
case RenderAs.Line:
case RenderAs.Pie:
case RenderAs.Point:
case RenderAs.Stock:
case RenderAs.CandleStick:
case RenderAs.SectionFunnel:
case RenderAs.StreamLineFunnel:
MaximumY = (yValues.Count() > 0) ? (yValues).Max() : 0;
MinimumY = (yValues.Count() > 0) ? (yValues).Min() : 0;
break;
case RenderAs.StackedArea:
case RenderAs.StackedBar:
case RenderAs.StackedColumn:
{
var positiveYValue = from xwisedata in XWiseStackedDataList.Values select xwisedata.PositiveYValueSum;
var negativeYValue = from xwisedata in XWiseStackedDataList.Values select xwisedata.NegativeYValueSum;
MaximumY = (positiveYValue.Count() > 0) ? (positiveYValue).Max() : 0;
MinimumY = (negativeYValue.Count() > 0) ? (negativeYValue).Min() : 0;
}
break;
case RenderAs.StackedArea100:
case RenderAs.StackedBar100:
case RenderAs.StackedColumn100:
{
var positiveYValue = from xwisedata in XWiseStackedDataList.Values select xwisedata.PositiveYValueSum;
var negativeYValue = from xwisedata in XWiseStackedDataList.Values select xwisedata.NegativeYValueSum;
MaximumY = (positiveYValue.Count() > 0) ? (positiveYValue).Max() : 0;
MinimumY = (negativeYValue.Count() > 0) ? (negativeYValue).Min() : 0;
}
// Since for stacked chart the Maximum can't be greater than 100 or less then 0
// Check and set appropriate limit
if (MaximumY > 0) MaximumY = 100;
else MaximumY = 0;
// Since for stacked chart the Minimum can't be greater than 0 or less then -100
// Check and set appropriate limit
if (MinimumY >= 0) MinimumY = 0;
else MinimumY = -100;
break;
}
// Calculates and sets the min difference for XValues
MinDifferenceX = GetMinDifference(xValues.ToArray());
}
#endregion
}
}