using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Linq;
using System.Collections.Generic;
using Visifire.Commons;
namespace Visifire.Charts
{
internal static class FunnelChart
{
///
/// Returns the visual object for funnel chart
///
/// PlotArea width
/// PlotArea height
/// PlotDetails
/// List of line series
/// Chart
/// Whether animation is enabled
/// Whether funnel chart is a Streamline funnel chart
///
public static Grid GetVisualObjectForFunnelChart(Double width, Double height, PlotDetails plotDetails, List seriesList, Chart chart, bool animationEnabled, Boolean isStreamLine)
{
if (seriesList.Count > 0)
{
DataSeries funnelSeries; // DataSeries used for drawing funnel chart
List funnelDataPoints; // DataPoints considered for drawing funnel chart
// Select DataSeries for render
List selectedDataSeriesList = (from ds in seriesList where (Boolean)ds.Enabled == true select ds).ToList();
if (selectedDataSeriesList.Count > 0)
funnelSeries = selectedDataSeriesList.First();
else
return null;
List tempDataPoints = (from dp in funnelSeries.DataPoints where dp.Enabled == true && dp.YValue >= 0 select dp).ToList();
if ((from dp in tempDataPoints where dp.YValue == 0 select dp).Count() == tempDataPoints.Count)
return null;
// If number of DataPoints is equals to 0 then dont do any operation
if (tempDataPoints.Count == 0 || (tempDataPoints.Count == 1 && tempDataPoints[0].YValue == 0))
return null;
if (isStreamLine)
{
if (tempDataPoints.Count <= 1)
throw new Exception("Invalid DataSet. StreamLineFunnel chart must have more than one DataPoint in a DataSeries with YValue > 0.");
funnelDataPoints = (from dp in tempDataPoints orderby dp.YValue descending select dp).ToList();
}
else
funnelDataPoints = tempDataPoints.ToList();
// Create funnel chart canvas
Grid _funnelChartGrid = new Grid() { Height = height, Width = width };
#region Create layout for Funnel chart and labels
// Create canvas for label
Canvas labelCanvas = new Canvas() { Height = height };
// Create canvas for funnel
Canvas funnelCanvas = new Canvas() { Height = height, HorizontalAlignment = HorizontalAlignment.Left };
_funnelChartGrid.Children.Add(funnelCanvas);
_funnelChartGrid.Children.Add(labelCanvas);
#endregion
if ((funnelSeries.Chart as Chart).AnimationEnabled)
funnelSeries.Storyboard = new Storyboard();
// Creating labels for
CreateLabelsAndSetFunnelCanvasSize(isStreamLine, _funnelChartGrid, labelCanvas, funnelCanvas, funnelDataPoints);
Double minPointHeight = funnelSeries.MinPointHeight;
Double yScale = 40;
Boolean isSameSlantAngle = true;
Double bottomRadius = 5;
Double gapRatio = (chart.View3D) ? 0.04 : 0.02;
funnelCanvas = CreateFunnelChart(_funnelChartGrid, funnelSeries, funnelDataPoints, isStreamLine, funnelCanvas, minPointHeight, chart.View3D, yScale, gapRatio, isSameSlantAngle, bottomRadius, animationEnabled);
// here
// funnelChartCanvas.Background = new SolidColorBrush(Colors.Red);
RectangleGeometry clipRectangle = new RectangleGeometry();
clipRectangle.Rect = new Rect(0, 0, width, height);
_funnelChartGrid.Clip = clipRectangle;
return _funnelChartGrid;
}
return null;
}
///
/// Create labels and set width for the label canvas
///
/// Main Funnel chart canvas
/// Label canvas for funnel chart placed in side funnelChartCanvas
/// DataSeries reference
private static void CreateLabelsAndSetFunnelCanvasSize(Boolean isStreamLine, Grid funnelChartCanvas, Canvas labelCanvas, Canvas funnelCanvas, List funnelDataPoints)
{
Int32 index = 0;
Double totalLabelsHeight = 0;
_streamLineParentTitleSize = new Size(0, 0);
labelCanvas.Width = 0;
for (; index < funnelDataPoints.Count; index++)
{
// Create label for a funnel slice
funnelDataPoints[index].LabelVisual = CreateLabelForDataPoint(funnelDataPoints[index], isStreamLine, index);
// Calculate label size
Size labelSize = Graphics.CalculateVisualSize(funnelDataPoints[index].LabelVisual);
labelSize.Width += 2.5;
if ((Boolean)funnelDataPoints[index].LabelEnabled && funnelDataPoints[index].LabelStyle == LabelStyles.OutSide)
totalLabelsHeight += labelSize.Height;
if (isStreamLine && index == 0)
_streamLineParentTitleSize = labelSize;
else if (labelSize.Width > labelCanvas.Width && (Boolean)funnelDataPoints[index].LabelEnabled && funnelDataPoints[index].LabelStyle == LabelStyles.OutSide) // && !(isStreamLine && index == 0))
labelCanvas.Width = labelSize.Width;
funnelDataPoints[index].LabelVisual.Height = labelSize.Height;
funnelDataPoints[index].LabelVisual.Width = labelSize.Width;
// labelCanvas.Children.Add(funnelDataPoints[index].LabelVisual);
}
labelCanvas.Width += Chart.BEVEL_DEPTH;
if (labelCanvas.Width > .6 * funnelChartCanvas.Width)
{
// Do some optimization here
}
// if funnelcanvas height is less than total labels height reduce the funnel canvas height
//if (funnelCanvas.Height < totalLabelsHeight)
// funnelCanvas.Height -= (totalLabelsHeight - funnelCanvas.Height);
funnelCanvas.Width = funnelChartCanvas.Width - labelCanvas.Width;
labelCanvas.SetValue(Canvas.LeftProperty, funnelCanvas.Width);
if (isStreamLine)
{
funnelCanvas.Height -= _streamLineParentTitleSize.Height;
labelCanvas.Height -= _streamLineParentTitleSize.Height;
funnelCanvas.SetValue(Canvas.TopProperty, _streamLineParentTitleSize.Height);
labelCanvas.SetValue(Canvas.TopProperty, _streamLineParentTitleSize.Height);
//funnelChartCanvas.Children.Add(funnelDataPoints[0].LabelVisual);
funnelDataPoints[0].LabelVisual.SetValue(Canvas.LeftProperty, (Double)(funnelCanvas.Width - _streamLineParentTitleSize.Width) / 2);
funnelDataPoints[0].Faces = new Faces();
funnelDataPoints[0].Faces.VisualComponents.Add(funnelDataPoints[0].LabelVisual);
funnelDataPoints[0].Faces.Visual = funnelDataPoints[0].LabelVisual;
if ((funnelDataPoints[0].Chart as Chart).AnimationEnabled)
{
// funnelDataPoints[0].Parent.Storyboard = AnimationHelper.ApplyOpacityAnimation(funnelDataPoints[0].LabelVisual, funnelDataPoints[0].Parent, funnelDataPoints[0].Parent.Storyboard, 1, funnelDataPoints[0].Opacity, 1);
}
}
}
///
/// Create labels for DataPoint
///
/// DataPoint
/// Border
private static Border CreateLabelForDataPoint(DataPoint dataPoint, Boolean isStreamLine, Int32 sliceIndex)
{
Title title = new Title()
{
IsNotificationEnable = false,
Chart = dataPoint.Chart,
Text = dataPoint.TextParser(dataPoint.LabelText),
InternalFontSize = (Double)dataPoint.LabelFontSize,
InternalFontColor = (isStreamLine && sliceIndex == 0) ? Chart.CalculateDataPointLabelFontColor(dataPoint.Chart as Chart, dataPoint, null, LabelStyles.OutSide) : Chart.CalculateDataPointLabelFontColor(dataPoint.Chart as Chart, dataPoint, dataPoint.LabelFontColor, (LabelStyles)dataPoint.LabelStyle),
InternalFontFamily = dataPoint.LabelFontFamily,
InternalFontStyle = (FontStyle)dataPoint.LabelFontStyle,
InternalFontWeight = (FontWeight)dataPoint.LabelFontWeight,
InternalBackground = dataPoint.LabelBackground
};
title.CreateVisualObject(new ElementData(){Element = dataPoint});
if (!(Boolean)dataPoint.LabelEnabled)
title.Visual.Visibility = Visibility.Collapsed;
return title.Visual;
}
///
/// Creates the funnel Chart
///
/// DataSeries reference
/// List of sorted reference(If required)
/// Whether its a streamline funnel chart
/// Funnel Canvas reference
/// Min height of a funnel slice
/// Whether chart is a 3D chart
/// YScale of the chart
/// Gap between two data points while a particular datapoint is Exploded
/// Whether the same slant angle to be used while drawing each slice
/// Bottom most raduis of a funnel
/// Whether animation is enabled for chart
/// Canvas with funnel
private static Canvas CreateFunnelChart(Grid _funnelChartGrid, DataSeries dataSeries, List dataPoints, Boolean isStreamLine, Canvas funnelCanvas, Double minPointHeight, Boolean is3D, Double yScale, Double gapRatio, Boolean isSameSlantAngle, Double bottomRadius, Boolean animationEnabled)
{
Boolean isAnimationEnabled = (dataSeries.Chart as Chart).AnimationEnabled;
Double plotHeight = funnelCanvas.Height;
Double plotWidth = funnelCanvas.Width;
// Canvas funnelCanvas = new Canvas() { Height = plotHeight, Width = plotWidth }; //, Background = new SolidColorBrush(Colors.LightGray) };
FunnelSliceParms[] funnelSlices = CalculateFunnelSliceParmsInfo(isStreamLine, dataSeries, dataPoints, plotHeight, plotWidth - Chart.BEVEL_DEPTH, minPointHeight, is3D, yScale, gapRatio, isSameSlantAngle, bottomRadius);
dataSeries.VisualParams = funnelSlices;
Double topRadius = plotWidth / 2;
Int32 zIndex = funnelSlices.Count() + 1;
Random rand = new Random(DateTime.Now.Millisecond);
Brush fillColor;
Int32 sliceCount = funnelSlices.Count();
Double totalFunnelActualHeight = 0;
for (Int32 index = 0; index < sliceCount; index++)
{
fillColor = funnelSlices[index].DataPoint.Color;
Double yScaleTop = yScale * (funnelSlices[index].TopRadius / topRadius);
Double yScaleBottom = yScale * (funnelSlices[index].BottomRadius / topRadius);
if (Double.IsNaN(yScaleTop))
yScaleTop = 0.0000001;
if (Double.IsNaN(yScaleBottom))
yScaleBottom = 0.0000001;
Canvas sliceCanvas = GetFunnelSliceVisual(index, topRadius, is3D, funnelSlices[index], yScaleTop, yScaleBottom, fillColor, animationEnabled);
funnelSlices[index].Top = funnelSlices[index].TopGap + ((index == 0) ? 0 : (funnelSlices[index - 1].Top + funnelSlices[index - 1].Height + funnelSlices[index - 1].BottomGap));
sliceCanvas.SetValue(Canvas.TopProperty, funnelSlices[index].Top);
sliceCanvas.SetValue(Canvas.ZIndexProperty, zIndex--);
sliceCanvas.Height = funnelSlices[index].Height;
sliceCanvas.Width = topRadius * 2;
totalFunnelActualHeight += (funnelSlices[index].Height + funnelSlices[index].TopGap);
// sliceCanvas.Background = new SolidColorBrush(Color.FromArgb((byte)255, (byte)rand.Next(228), (byte)rand.Next(128), (byte)rand.Next(228)));
// here
//sliceCanvas.Background = new SolidColorBrush(Color.FromArgb((byte)255, (byte)rand.Next(200 * index % 226), (byte)rand.Next(200 * index % 250), (byte)rand.Next(200 * index % 223)));
funnelCanvas.Children.Add(sliceCanvas);
if (isStreamLine && index == 0)
{
// funnelCanvas.Height -= _streamLineParentTitleSize.Height;
// labelCanvas.Height -= _streamLineParentTitleSize.Height;
// funnelCanvas.SetValue(Canvas.TopProperty, _streamLineParentTitleSize.Height);
// labelCanvas.SetValue(Canvas.TopProperty, _streamLineParentTitleSize.Height);
sliceCanvas.Children.Add(dataPoints[0].LabelVisual);
dataPoints[0].Faces.Visual = dataPoints[0].LabelVisual;
dataPoints[0].LabelVisual.SetValue(Canvas.TopProperty, (Double)(-(_streamLineParentTitleSize.Height + (is3D ? yScale / 2 : 0))));
funnelSlices[index].DataPoint.VisualParams = null;
}
funnelSlices[index].DataPoint.Faces.Visual = sliceCanvas;
funnelSlices[index].DataPoint.VisualParams = funnelSlices[index];
}
CalcutateExplodedPosition(ref funnelSlices, isStreamLine, yScale, dataSeries);
// here
//funnelCanvas.Background = new SolidColorBrush(Colors.Yellow);
//if (!dataSeries.Exploded)
{
funnelCanvas.Height = totalFunnelActualHeight - _streamLineParentTitleSize.Height;
}
ArrangeLabels(funnelSlices, Double.NaN, _funnelChartGrid.Height);
return funnelCanvas;
}
///
/// Arrange labels to overcome overlaps
///
private static void ArrangeLabels(FunnelSliceParms[] funnelSlices, Double width, Double height)
{
if (funnelSlices == null || funnelSlices.Length < 0)
return;
FunnelSliceParms[] selectedfunnelSlices = (from fs in funnelSlices where fs.DataPoint.LabelStyle == LabelStyles.OutSide select fs).ToArray();
Rect baseArea = new Rect(0, 0, width, height);
Rect[] labelInfo = new Rect[selectedfunnelSlices.Length];
for (Int32 index = 0; index < selectedfunnelSlices.Length; index++)
{
Double left = (Double)selectedfunnelSlices[index].DataPoint.LabelVisual.GetValue(Canvas.LeftProperty);
Double top = (Double)selectedfunnelSlices[index].DataPoint.LabelVisual.GetValue(Canvas.TopProperty);
labelInfo[index] = new Rect
(left,
selectedfunnelSlices[index].Top + top,
selectedfunnelSlices[index].DataPoint.LabelVisual.Width,
selectedfunnelSlices[index].DataPoint.LabelVisual.Height);
}
Visifire.Commons.LabelPlacementHelper.VerticalLabelPlacement(baseArea, ref labelInfo);
for (Int32 index = 0; index < selectedfunnelSlices.Length; index++)
{
Double labelTop = labelInfo[index].Top - selectedfunnelSlices[index].Top;
selectedfunnelSlices[index].DataPoint.LabelVisual.SetValue(Canvas.LeftProperty, labelInfo[index].Left);
selectedfunnelSlices[index].DataPoint.LabelVisual.SetValue(Canvas.TopProperty, labelTop);
selectedfunnelSlices[index].LabelLineEndPoint = new Point(selectedfunnelSlices[index].LabelLineEndPoint.X, labelTop + selectedfunnelSlices[index].DataPoint.LabelVisual.Height / 2);
UpdateLabelLineEndPoint(selectedfunnelSlices[index]);
}
}
private static void UpdateLabelLineEndPoint(FunnelSliceParms funnelSlice)
{
Path labelLine = funnelSlice.DataPoint.LabelLine;
if (labelLine != null && labelLine.Data != null)
{
LineSegment ls = (((labelLine.Data as PathGeometry).Figures[0] as PathFigure).Segments[0] as LineSegment);
ls.Point = funnelSlice.LabelLineEndPoint;
}
}
///
/// Create exploding in animation
///
/// DataPoint
/// Stroyboard used for animation
/// Path elements reference
/// Label reference
/// Label line reference
/// Unexploded points
/// Exploded points
/// X offset
/// Y offset
/// Storyboard
internal static Storyboard CreateExplodingAnimation(DataSeries dataSeries, DataPoint dataPoint, Storyboard storyboard, Panel visual, Double targetValue, Double beginTime)
{
#if WPF
if (storyboard != null && storyboard.GetValue(System.Windows.Media.Animation.Storyboard.TargetProperty) != null)
storyboard.Stop();
#else
if (storyboard != null)
storyboard.Stop();
#endif
//Double fromValue = (Double) visual.GetValue(Canvas.TopProperty);
DoubleCollection values = Graphics.GenerateDoubleCollection(targetValue);
DoubleCollection frames = Graphics.GenerateDoubleCollection(.2);
List splines = AnimationHelper.GenerateKeySplineList(frames.Count);
DoubleAnimationUsingKeyFrames topAnimation = PieChart.CreateDoubleAnimation(dataSeries, dataPoint, visual, "(Canvas.Top)", beginTime, frames, values, splines);
storyboard.Children.Add(topAnimation);
return storyboard;
}
internal static Storyboard CreateUnExplodingAnimation(DataSeries dataSeries, DataPoint dataPoint, Storyboard storyboard, Panel visual, Double targetValue)
{
#if WPF
if (storyboard != null && storyboard.GetValue(System.Windows.Media.Animation.Storyboard.TargetProperty) != null)
storyboard.Stop();
#else
if (storyboard != null)
storyboard.Stop();
#endif
Double fromValue = (Double)visual.GetValue(Canvas.TopProperty);
DoubleCollection values = Graphics.GenerateDoubleCollection(fromValue, targetValue);
DoubleCollection frames = Graphics.GenerateDoubleCollection(0, .2);
List splines = AnimationHelper.GenerateKeySplineList(frames.Count);
DoubleAnimationUsingKeyFrames topAnimation = PieChart.CreateDoubleAnimation(dataSeries, dataPoint, visual, "(Canvas.Top)", 0, frames, values, splines);
storyboard.Children.Add(topAnimation);
return storyboard;
}
///
/// Calculates Exploded DataPoint Positions
///
///
private static void CalcutateExplodedPosition(ref FunnelSliceParms[] funnelSlices, Boolean isStreamLine, Double yScale, DataSeries dataSeries)
{
Int32 sliceCount = funnelSlices.Count();
Int32 index = 0;
if (funnelSlices[0].DataPoint.Parent.Exploded)
{
if (!funnelSlices[0].DataPoint.Chart.IsInDesignMode)
{
Int32 midIndex = sliceCount / 2;
Double beginTime = 0.4;
if ((dataSeries.Chart as Chart).ChartArea._isFirstTimeRender)
beginTime = 1;
for (index = midIndex; index >= 0; index--)
{
if (index < 0)
break;
Double yPosition = funnelSlices[index].Top - (midIndex - index) * _singleGap;// -yScale / 2;
dataSeries.Storyboard = CreateExplodingAnimation(dataSeries, funnelSlices[index].DataPoint, dataSeries.Storyboard, funnelSlices[index].DataPoint.Faces.Visual as Panel, yPosition, beginTime);
}
for (index = midIndex + 1; index < sliceCount; index++)
{
Double yPosition = funnelSlices[index].Top + (index - midIndex) * _singleGap;// -yScale / 2;
dataSeries.Storyboard = CreateExplodingAnimation(dataSeries, funnelSlices[index].DataPoint, dataSeries.Storyboard, funnelSlices[index].DataPoint.Faces.Visual as Panel, yPosition, beginTime);
}
if (dataSeries.Chart != null && !(dataSeries.Chart as Chart).ChartArea._isFirstTimeRender)
#if WPF
dataSeries.Storyboard.Begin(dataSeries.Chart._rootElement, true);
#else
dataSeries.Storyboard.Begin();
#endif
}
}
else
{
Storyboard unExplodeStoryBoard = new Storyboard();
for (; index < sliceCount; index++)
{
funnelSlices[index].ExplodedPoints = new List();
funnelSlices[index].DataPoint.ExplodeAnimation = new Storyboard();
// For top slice
if (index == 0)
{
funnelSlices[index].ExplodedPoints.Add(new Point(0, funnelSlices[index].Top - _singleGap / 2));
funnelSlices[index].DataPoint.ExplodeAnimation = CreateExplodingAnimation(dataSeries, funnelSlices[index].DataPoint, funnelSlices[index].DataPoint.ExplodeAnimation, funnelSlices[index].DataPoint.Faces.Visual as Panel, (funnelSlices[index].Top - _singleGap / 2), 0);
//unExplodeStoryBoard = CreateExplodingAnimation(unExplodeStoryBoard, funnelSlices[index].DataPoint.Faces.Visual as Panel, funnelSlices[index].Top);
for (Int32 i = 1; i < funnelSlices.Length; i++)
{
funnelSlices[index].ExplodedPoints.Add(new Point(0, funnelSlices[i].Top + _singleGap / 2));
funnelSlices[index].DataPoint.ExplodeAnimation = CreateExplodingAnimation(dataSeries, funnelSlices[index].DataPoint, funnelSlices[index].DataPoint.ExplodeAnimation, funnelSlices[i].DataPoint.Faces.Visual as Panel, (funnelSlices[i].Top + _singleGap / 2), 0);
//unExplodeStoryBoard = CreateExplodingAnimation(unExplodeStoryBoard, funnelSlices[i].DataPoint.Faces.Visual as Panel, funnelSlices[i].Top);
}
}
// For bottom slice
else if (index == funnelSlices.Length - 1)
{
Int32 i = 0;
for (; i < index; i++)
{
funnelSlices[index].ExplodedPoints.Add(new Point(0, funnelSlices[i].Top - _singleGap / 2 + _singleGap / 6));
funnelSlices[index].DataPoint.ExplodeAnimation = CreateExplodingAnimation(dataSeries, funnelSlices[index].DataPoint, funnelSlices[index].DataPoint.ExplodeAnimation, funnelSlices[i].DataPoint.Faces.Visual as Panel, (funnelSlices[i].Top - _singleGap / 2 + _singleGap / 6), 0);
}
funnelSlices[index].ExplodedPoints.Add(new Point(0, funnelSlices[i].Top + _singleGap / 2 + _singleGap / 6));
funnelSlices[index].DataPoint.ExplodeAnimation = CreateExplodingAnimation(dataSeries, funnelSlices[index].DataPoint, funnelSlices[i].DataPoint.ExplodeAnimation, funnelSlices[i].DataPoint.Faces.Visual as Panel, (funnelSlices[i].Top + _singleGap / 2 + _singleGap / 6), 0);
}
// For other slice
else
{
Int32 i;
for (i = 0; i < index; i++)
{
funnelSlices[index].ExplodedPoints.Add(new Point(0, funnelSlices[i].Top - _singleGap / 2));
funnelSlices[index].DataPoint.ExplodeAnimation = CreateExplodingAnimation(dataSeries, funnelSlices[index].DataPoint, funnelSlices[index].DataPoint.ExplodeAnimation, funnelSlices[i].DataPoint.Faces.Visual as Panel, (funnelSlices[i].Top - _singleGap / 2), 0);
}
funnelSlices[index].ExplodedPoints.Add(new Point(0, funnelSlices[i].Top));
funnelSlices[index].DataPoint.ExplodeAnimation = CreateExplodingAnimation(dataSeries, funnelSlices[index].DataPoint, funnelSlices[index].DataPoint.ExplodeAnimation, funnelSlices[index].DataPoint.Faces.Visual as Panel, funnelSlices[index].Top, 0);
for (++i; i < funnelSlices.Length; i++)
{
funnelSlices[index].ExplodedPoints.Add(new Point(0, funnelSlices[i].Top + _singleGap / 2));
funnelSlices[index].DataPoint.ExplodeAnimation = CreateExplodingAnimation(dataSeries, funnelSlices[index].DataPoint, funnelSlices[index].DataPoint.ExplodeAnimation, funnelSlices[i].DataPoint.Faces.Visual as Panel, (funnelSlices[i].Top + _singleGap / 2), 0);
}
}
}
}
}
///
/// Calculate funnelSliceParms information
///
/// Whether its a streamline funnel chart
/// DataSeries reference
/// List of sorted reference(If required)
/// Total height of the plot canvas
/// Total width of the plot canvas
/// Min height of a funnel slice
/// Whether chart is a 3D chart
/// YScale of the chart
/// Gap between two data points while a particular datapoint is Exploded
/// Whether the same slant angle to be used while drawing each slice
/// Bottom most raduis of a funnel
/// FunnelSliceParms[]
private static FunnelSliceParms[] CalculateFunnelSliceParmsInfo(Boolean isStreamLine, DataSeries dataSeries, List dataPoints, Double plotHeight, Double plotWidth, Double minPointHeight, Boolean is3D, Double yScale, Double gapRatio, Boolean isSameSlantAngle, Double bottomRadius)
{
// Initialize funnel Slices parameters
FunnelSliceParms[] funnelSlicesParms;
// Actual funnel height
// For 3d funnel height will be reduced to maintain yScale
Double funnelHeight;
if (dataSeries.Exploded)
{
gapRatio = 0.02;
// Single gap height
_singleGap = gapRatio * plotHeight;
_totalGap = _singleGap * ((isStreamLine) ? dataPoints.Count : (dataPoints.Count + 1));
}
else
{
// Single gap height
_singleGap = gapRatio * plotHeight;
_totalGap = _singleGap * 2;
}
// Actual funnel height
// For 3d funnel height will be reduced to maintain yScale
funnelHeight = plotHeight - _totalGap - (is3D ? yScale / 2 : 0);
if (!isStreamLine)
{
#region Sectional Funnel
// Initialization of funnelSliceParms
funnelSlicesParms = new FunnelSliceParms[dataPoints.Count];
// Calculate sum of values
Double sum = (from dp in dataPoints select dp.YValue).Sum();
// Min YValue
Double min = (from dp in dataPoints select dp.YValue).Min();
// Funnel angle
Double theta = Math.Atan((plotWidth / 2 - bottomRadius) / funnelHeight);
// Height of the funnel drawn considering bottom radius
funnelHeight -= Math.Tan(theta) / ((bottomRadius == 0) ? 1 : bottomRadius);
// Creating prams for each funnel slice
for (Int32 index = 0; index < dataPoints.Count; index++)
{
funnelSlicesParms[index] = new FunnelSliceParms() { DataPoint = dataPoints[index], TopAngle = Math.PI / 2 - theta, BottomAngle = Math.PI / 2 + theta };
funnelSlicesParms[index].Height = funnelHeight * (dataPoints[index].YValue / sum);
funnelSlicesParms[index].TopRadius = index == 0 ? plotWidth / 2 : funnelSlicesParms[index - 1].BottomRadius;
funnelSlicesParms[index].BottomRadius = funnelSlicesParms[index].TopRadius - funnelSlicesParms[index].Height * Math.Tan(theta);
/*
// Set topgap and bottom gap
if (index == 0)
funnelSlicesParms[index].TopGap = singleGap + ((index == 0 && is3D) ? yScale / 2 : 0);
else if ((Boolean)funnelSlicesParms[index].DataPoint.Exploded)
funnelSlicesParms[index].TopGap = singleGap + ((index == 0 && is3D) ? yScale / 2 : 0);
if (index == dataPoints.Count - 1)
funnelSlicesParms[index].BottomGap = singleGap;
else if ((Boolean)funnelSlicesParms[index].DataPoint.Exploded)
funnelSlicesParms[index].BottomGap = singleGap;*/
//--------------
funnelSlicesParms[index].TopGap = ((index == 0 && is3D) ? yScale / 2 : 0);
//--------------
}
if (!Double.IsNaN(minPointHeight))
{
Boolean isFixedSize = false; // Whether to fix height of all slice
Double fixedSliceHeight = 0;
Double totalSumOfHeight = (from funnelSlice in funnelSlicesParms select funnelSlice.Height).Sum();
fixedSliceHeight = totalSumOfHeight / funnelSlicesParms.Length;
// Calculate minPointHeight in terms of pixel value
minPointHeight = (minPointHeight / 100) * funnelHeight;
// Funnel slices where height is less than the minPointHeight
List fixedHeightFunnelSlices = (from funnelSlice in funnelSlicesParms where funnelSlice.Height < minPointHeight select funnelSlice).ToList();
List variableHeightFunnelSlices = (from funnelSlice in funnelSlicesParms
where !(from slice in fixedHeightFunnelSlices select slice).Contains(funnelSlice)
select funnelSlice).ToList();
if (minPointHeight > fixedSliceHeight || fixedHeightFunnelSlices.Count == funnelSlicesParms.Count())
{
isFixedSize = true;
}
Double sumOfHeightOfSlice2BeFixed = (from funnelSlice in fixedHeightFunnelSlices select funnelSlice.Height).Sum();
Double sumOfVariableHeightSlices = (from funnelSlice in variableHeightFunnelSlices select funnelSlice.Height).Sum();
Double totalHeight2Reduce = minPointHeight * fixedHeightFunnelSlices.Count() - sumOfHeightOfSlice2BeFixed;
// Creating prams for each funnel slice
for (Int32 index = 0; index < dataPoints.Count; index++)
{
if (isFixedSize)
{
funnelSlicesParms[index].Height = fixedSliceHeight;
}
else
{
if (funnelSlicesParms[index].Height < minPointHeight)
funnelSlicesParms[index].Height = minPointHeight;
else
funnelSlicesParms[index].Height -= totalHeight2Reduce * (funnelSlicesParms[index].Height / sumOfVariableHeightSlices);
}
funnelSlicesParms[index].TopRadius = index == 0 ? plotWidth / 2 : funnelSlicesParms[index - 1].BottomRadius;
funnelSlicesParms[index].BottomRadius = funnelSlicesParms[index].TopRadius - funnelSlicesParms[index].Height * Math.Tan(theta);
// Set topgap and bottom gap
/*if (index == 0 || (Boolean)funnelSlicesParms[index].DataPoint.Exploded)
funnelSlicesParms[index].TopGap = _singleGap + ((index == 0 && is3D) ? yScale / 2 : 0);
if(index == dataPoints.Count -1 || (Boolean) funnelSlicesParms[index].DataPoint.Exploded)
funnelSlicesParms[index].BottomGap = _singleGap;
*/
}
}
#endregion
}
else
{
#region StreamLineFunnel
funnelHeight -= _streamLineParentTitleSize.Height;
// Initialization of iOValuePairs
IOValuePair[] iOValuePairs = new IOValuePair[dataPoints.Count];
for (Int32 index = 0; index < dataPoints.Count; index++)
{
iOValuePairs[index] = new IOValuePair();
iOValuePairs[index].InputValue = ((index == 0) ? Double.NaN : dataPoints[index - 1].YValue);
iOValuePairs[index].OutPutValue = dataPoints[index].YValue;
}
// Initialization of funnelSliceParms
funnelSlicesParms = new FunnelSliceParms[dataPoints.Count - 1];
// Creating prams for each funnel slice
Int32 slicesIndex;
for (Int32 index = 1; index < iOValuePairs.Count(); index++)
{
slicesIndex = index - 1;
funnelSlicesParms[slicesIndex] = new FunnelSliceParms() { DataPoint = dataPoints[index] };
funnelSlicesParms[slicesIndex].Height = (((iOValuePairs[index].InputValue - iOValuePairs[index].OutPutValue) / iOValuePairs[0].OutPutValue) * funnelHeight);
funnelSlicesParms[slicesIndex].TopRadius = slicesIndex == 0 ? plotWidth / 2 : funnelSlicesParms[slicesIndex - 1].BottomRadius;
if (!isSameSlantAngle)
funnelSlicesParms[slicesIndex].BottomRadius = (funnelSlicesParms[slicesIndex].TopRadius * Math.Sqrt(iOValuePairs[index].OutPutValue / iOValuePairs[index].InputValue));
else
funnelSlicesParms[slicesIndex].BottomRadius = (funnelSlicesParms[slicesIndex].TopRadius * (iOValuePairs[index].OutPutValue / iOValuePairs[index].InputValue));
Double theta = Math.Atan((funnelSlicesParms[slicesIndex].TopRadius - funnelSlicesParms[slicesIndex].BottomRadius) / funnelSlicesParms[slicesIndex].Height);
funnelSlicesParms[slicesIndex].TopAngle = Math.PI / 2 - theta;
funnelSlicesParms[slicesIndex].BottomAngle = Math.PI / 2 + theta;
/*
// Set top and bottom gap
if(index == 1 || (Boolean)funnelSlicesParms[slicesIndex].DataPoint.Exploded)
funnelSlicesParms[slicesIndex].TopGap = singleGap + ((slicesIndex == 0 && is3D) ? yScale / 2 : 0);
if (index == iOValuePairs.Count() - 1 || (Boolean)funnelSlicesParms[slicesIndex].DataPoint.Exploded)
funnelSlicesParms[slicesIndex].BottomGap = singleGap;
*/
FixTopAndBottomRadiusForStreamLineFunnel(ref funnelSlicesParms[slicesIndex]);
}
// Enlarge Funnel Height-----------
Double totalSumOfHeight = (from funnelSlice in funnelSlicesParms select funnelSlice.Height).Sum();
if (totalSumOfHeight < funnelHeight)
{
for (Int32 index = 1; index < iOValuePairs.Count(); index++)
{
slicesIndex = index - 1;
//funnelSlicesParms[slicesIndex] = new FunnelSliceParms() { DataPoint = dataPoints[index] };
funnelSlicesParms[slicesIndex].Height += (funnelHeight - totalSumOfHeight) * (funnelSlicesParms[slicesIndex].Height / totalSumOfHeight);
funnelSlicesParms[slicesIndex].TopRadius = slicesIndex == 0 ? plotWidth / 2 : funnelSlicesParms[slicesIndex - 1].BottomRadius;
if (!isSameSlantAngle)
funnelSlicesParms[slicesIndex].BottomRadius = Math.Round(funnelSlicesParms[slicesIndex].TopRadius * Math.Sqrt(iOValuePairs[index].OutPutValue / iOValuePairs[index].InputValue));
else
funnelSlicesParms[slicesIndex].BottomRadius = Math.Round(funnelSlicesParms[slicesIndex].TopRadius * (iOValuePairs[index].OutPutValue / iOValuePairs[index].InputValue));
Double theta = Math.Atan((funnelSlicesParms[slicesIndex].TopRadius - funnelSlicesParms[slicesIndex].BottomRadius) / funnelSlicesParms[slicesIndex].Height);
funnelSlicesParms[slicesIndex].TopAngle = Math.PI / 2 - theta;
funnelSlicesParms[slicesIndex].BottomAngle = Math.PI / 2 + theta;
/*
// Set top and bottom gap
if(index == 1 || (Boolean)funnelSlicesParms[slicesIndex].DataPoint.Exploded)
funnelSlicesParms[slicesIndex].TopGap = singleGap + ((slicesIndex == 0 && is3D) ? yScale / 2 : 0);
if (index == iOValuePairs.Count() - 1 || (Boolean)funnelSlicesParms[slicesIndex].DataPoint.Exploded)
funnelSlicesParms[slicesIndex].BottomGap = singleGap;
*/
FixTopAndBottomRadiusForStreamLineFunnel(ref funnelSlicesParms[slicesIndex]);
}
}
// End Enlarge funnel Height-------
if (!Double.IsNaN(minPointHeight))
{
Boolean isFixedSize = false; // Whether to fix height of all slice
Double fixedSliceHeight = 0;
Double funnelActualHeight = funnelHeight - _streamLineParentTitleSize.Height;
fixedSliceHeight = funnelActualHeight / funnelSlicesParms.Length;
// Calculate minPointHeight in terms of pixel value
minPointHeight = (minPointHeight / 100) * funnelHeight;
// Funnel slices where height is less than the minPointHeight
var fixedHeightFunnelSlices = (from funnelSlice in funnelSlicesParms where funnelSlice.Height < minPointHeight select funnelSlice);
var variableHeightFunnelSlices = (from funnelSlice in funnelSlicesParms
where !(from slice in fixedHeightFunnelSlices select slice).Contains(funnelSlice)
select funnelSlice);
if (minPointHeight > fixedSliceHeight || fixedHeightFunnelSlices.Count() == funnelSlicesParms.Count())
{
isFixedSize = true;
}
//Double totalSumOfHeight = (from funnelSlice in funnelSlicesParms select funnelSlice.Height).Sum();
Double sumOfHeightOfSlice2BeFixed = (from funnelSlice in fixedHeightFunnelSlices select funnelSlice.Height).Sum();
Double sumOfVariableHeightSlices = (from funnelSlice in variableHeightFunnelSlices select funnelSlice.Height).Sum();
Double totalHeight2Reduce = minPointHeight * fixedHeightFunnelSlices.Count() - sumOfHeightOfSlice2BeFixed;
for (Int32 index = 1; index < iOValuePairs.Count(); index++)
{
slicesIndex = index - 1;
if (isFixedSize)
{
funnelSlicesParms[slicesIndex].Height = fixedSliceHeight;
}
else
{
if (funnelSlicesParms[slicesIndex].Height < minPointHeight)
funnelSlicesParms[slicesIndex].Height = minPointHeight;
else
funnelSlicesParms[slicesIndex].Height -= totalHeight2Reduce * (funnelSlicesParms[slicesIndex].Height / sumOfVariableHeightSlices);
}
funnelSlicesParms[slicesIndex].TopRadius = slicesIndex == 0 ? plotWidth / 2 : funnelSlicesParms[slicesIndex - 1].BottomRadius;
if (!isSameSlantAngle)
funnelSlicesParms[slicesIndex].BottomRadius = Math.Round(funnelSlicesParms[slicesIndex].TopRadius * Math.Sqrt(iOValuePairs[index].OutPutValue / iOValuePairs[index].InputValue));
else
funnelSlicesParms[slicesIndex].BottomRadius = Math.Round(funnelSlicesParms[slicesIndex].TopRadius * (iOValuePairs[index].OutPutValue / iOValuePairs[index].InputValue));
// Set top and bottom gap
/*if(index == 1 || (Boolean)funnelSlicesParms[slicesIndex].DataPoint.Exploded)
funnelSlicesParms[slicesIndex].TopGap = _singleGap + ((slicesIndex == 0 && is3D) ? yScale / 2 : 0);
if(index == iOValuePairs.Count() -1 || (Boolean)funnelSlicesParms[slicesIndex].DataPoint.Exploded)
funnelSlicesParms[slicesIndex].BottomGap = _singleGap;
*/
// Calculate funnel angle
// funnelSlicesParms[slicesIndex].TopAngle = Math.PI / 2 - Math.Atan((funnelSlicesParms[slicesIndex].TopRadius - funnelSlicesParms[slicesIndex].BottomRadius) / funnelSlicesParms[slicesIndex].Height);
FixTopAndBottomRadiusForStreamLineFunnel(ref funnelSlicesParms[slicesIndex]);
Double theta = Math.Atan((funnelSlicesParms[slicesIndex].TopRadius - funnelSlicesParms[slicesIndex].BottomRadius) / funnelSlicesParms[slicesIndex].Height);
funnelSlicesParms[slicesIndex].TopAngle = Math.PI / 2 - theta;
funnelSlicesParms[slicesIndex].BottomAngle = Math.PI / 2 + theta;
}
}
}
#endregion
return funnelSlicesParms;
}
public static void FixTopAndBottomRadiusForStreamLineFunnel(ref FunnelSliceParms funnelSlice)
{
if (Double.IsNaN(funnelSlice.TopRadius))
{
funnelSlice.TopRadius = 0.00000001;
funnelSlice.Height = 0;
}
if (Double.IsNaN(funnelSlice.BottomRadius))
funnelSlice.BottomRadius = 0.0000001;
if (Double.IsNaN(funnelSlice.Height))
funnelSlice.Height = 0.0000001;
}
///
/// Returns the visual of a funnel slice
///
/// Slice index
/// Top radius of the Funnel
/// Whether the chart is a 3D Chart
/// Funnel Slice reference
/// Top y-scale for 3D slice
/// Bottom y-scale for 3D slice
/// Fill Color of the funnel slice
/// Whether the animation is enabled
/// Funnel slice canvas
private static Canvas GetFunnelSliceVisual(Int32 funnelSliceIndex, Double topRadius, Boolean is3D, FunnelSliceParms funnelSlice, Double yScaleTop, Double yScaleBottom, Brush fillColor, Boolean animationEnabled)
{
funnelSlice.Index = funnelSliceIndex;
Canvas sliceCanvas = CreateFunnelSlice(false, topRadius, is3D, funnelSlice, yScaleTop, yScaleBottom, fillColor, fillColor, fillColor, animationEnabled);
if ((Boolean)funnelSlice.DataPoint.LightingEnabled)
{
Brush highlightBrush4Stroke = GetLightingBrushForStroke(fillColor, funnelSlice.Index);
Brush sideFillColor = (Boolean)funnelSlice.DataPoint.LightingEnabled ? GetSideBrush() : fillColor;
Brush topFillColor = is3D ? GetTopBrush(fillColor) : fillColor;
Canvas gradientCanvas = CreateFunnelSlice(true, topRadius, is3D, funnelSlice, yScaleTop, yScaleBottom, sideFillColor, topFillColor, highlightBrush4Stroke, animationEnabled);
sliceCanvas.Children.Add(gradientCanvas);
}
return sliceCanvas;
}
///
/// Get stroke brush for Top lighting Ellipse
///
///
///
///
private static Brush GetLightingBrushForStroke(Brush fillColor, Int32 funnelSliceIndex)
{
SolidColorBrush fillColorGradientBrush = fillColor as SolidColorBrush;
Brush highlightBrush4Stroke = fillColor;
if (funnelSliceIndex > 0 && fillColorGradientBrush != null)
highlightBrush4Stroke = new SolidColorBrush(Graphics.GetLighterColor(fillColorGradientBrush.Color, 100));
else
highlightBrush4Stroke = Graphics.GetBevelTopBrush(new SolidColorBrush(Graphics.GetLighterColor(fillColorGradientBrush.Color, 100)), 0.12);
return highlightBrush4Stroke;
}
///
/// Get new Brush for Bevel
///
///
///
///
///
internal static void ReCalculateAndApplyTheNewBrush(Shape shape, Brush newBrush, Boolean isLightingEnabled, Boolean is3D, FunnelSliceParms funnelSliceParms)
{
switch ((shape.Tag as ElementData).VisualElementName)
{
case "FunnelBase":
case "FunnelTop": shape.Fill = newBrush; shape.Stroke = newBrush; break;
case "TopBevel": shape.Fill = Graphics.GetBevelTopBrush(newBrush, 90); break;
case "LeftBevel": shape.Fill = Graphics.GetBevelSideBrush(45, newBrush); break;
case "RightBevel": shape.Fill = Graphics.GetBevelSideBrush(45, newBrush); break;
case "BottomBevel": shape.Fill = Graphics.GetBevelSideBrush(180, newBrush); break;
case "Lighting": shape.Fill = isLightingEnabled ? GetSideBrush() : newBrush; break;
case "FunnelTopLighting":
shape.Fill = is3D ? GetTopBrush(newBrush) : newBrush;
shape.Stroke = GetLightingBrushForStroke(newBrush, funnelSliceParms.Index);
break;
}
}
///
/// Apply Bevel effect for a funnel slice
///
/// Parent canvas of Bevel layer
/// funnelSlice
/// Side fill color
/// Funnel Points
private static void ApplyFunnelBevel(Canvas parentVisual, FunnelSliceParms funnelSlice, Brush sideFillColor, Point[] points)
{
if (funnelSlice.DataPoint.Parent.Bevel && funnelSlice.Height > Chart.BEVEL_DEPTH)
{
// Generate Inner Points
CalculateBevelInnerPoints(funnelSlice, points);
Path topBevelPath = ExtendedGraphics.GetPathFromPoints(Graphics.GetBevelTopBrush(sideFillColor, 90), points[0], points[4], points[5], points[1]);
Path leftBevelPath = ExtendedGraphics.GetPathFromPoints(Graphics.GetBevelSideBrush(45, sideFillColor), points[0], points[4], points[7], points[3]);
Path rightBevelPath = ExtendedGraphics.GetPathFromPoints(Graphics.GetBevelSideBrush(45, sideFillColor), points[1], points[5], points[6], points[2]);
Path bottomBevelPath = ExtendedGraphics.GetPathFromPoints(Graphics.GetBevelSideBrush(180, sideFillColor), points[7], points[6], points[2], points[3]);
topBevelPath.IsHitTestVisible = false;
leftBevelPath.IsHitTestVisible = false;
rightBevelPath.IsHitTestVisible = false;
bottomBevelPath.IsHitTestVisible = false;
parentVisual.Children.Add(topBevelPath);
parentVisual.Children.Add(leftBevelPath);
parentVisual.Children.Add(rightBevelPath);
parentVisual.Children.Add(bottomBevelPath);
topBevelPath.Tag = new ElementData() { Element = funnelSlice.DataPoint, VisualElementName = "TopBevel" };
leftBevelPath.Tag = new ElementData() { Element = funnelSlice.DataPoint, VisualElementName = "LeftBevel" };
rightBevelPath.Tag = new ElementData() { Element = funnelSlice.DataPoint, VisualElementName = "RightBevel" };
bottomBevelPath.Tag = new ElementData() { Element = funnelSlice.DataPoint, VisualElementName = "BottomBevel" };
funnelSlice.DataPoint.Faces.Parts.Add(topBevelPath);
funnelSlice.DataPoint.Faces.Parts.Add(leftBevelPath);
funnelSlice.DataPoint.Faces.Parts.Add(rightBevelPath);
funnelSlice.DataPoint.Faces.Parts.Add(bottomBevelPath);
funnelSlice.DataPoint.Faces.VisualComponents.Add(topBevelPath);
funnelSlice.DataPoint.Faces.VisualComponents.Add(leftBevelPath);
funnelSlice.DataPoint.Faces.VisualComponents.Add(rightBevelPath);
funnelSlice.DataPoint.Faces.VisualComponents.Add(bottomBevelPath);
if ((funnelSlice.DataPoint.Chart as Chart).AnimationEnabled)
{
funnelSlice.DataPoint.Parent.Storyboard = AnimationHelper.ApplyOpacityAnimation(topBevelPath, funnelSlice.DataPoint.Parent, funnelSlice.DataPoint.Parent.Storyboard, 0, funnelSlice.DataPoint.InternalOpacity, 1);
funnelSlice.DataPoint.Parent.Storyboard = AnimationHelper.ApplyOpacityAnimation(leftBevelPath, funnelSlice.DataPoint.Parent, funnelSlice.DataPoint.Parent.Storyboard, 0, funnelSlice.DataPoint.InternalOpacity, 1);
funnelSlice.DataPoint.Parent.Storyboard = AnimationHelper.ApplyOpacityAnimation(rightBevelPath, funnelSlice.DataPoint.Parent, funnelSlice.DataPoint.Parent.Storyboard, 0, funnelSlice.DataPoint.InternalOpacity, 1);
funnelSlice.DataPoint.Parent.Storyboard = AnimationHelper.ApplyOpacityAnimation(bottomBevelPath, funnelSlice.DataPoint.Parent, funnelSlice.DataPoint.Parent.Storyboard, 0, funnelSlice.DataPoint.InternalOpacity, 1);
}
}
}
///
/// Create a slice of a funnel
///
/// Whether CreateFunnelSlice() function should create a layer for lighting
/// Top Radius of the funnel
/// Whether the chart is a 3D Chart
/// FunnelSlice canvas reference
/// Top YScale for 3D view
/// Bottom YScale for 3D view
/// Side surface fill color
/// Top surface fill color
/// Top surface stroke color
/// Whether animation is enabled
/// Return funnel slice canvas
private static Canvas CreateFunnelSlice(Boolean isLightingGradientLayer, Double topRadius, Boolean is3D, FunnelSliceParms funnelSlice, Double yScaleTop, Double yScaleBottom, Brush sideFillColor, Brush topFillColor, Brush topSurfaceStroke, Boolean animationEnabled)
{
Canvas sliceCanvas = new Canvas() { Tag = new ElementData() { Element = funnelSlice.DataPoint } };
Canvas visual = new Canvas() { Width = topRadius * 2, Height = funnelSlice.Height, Tag = new ElementData() { Element = funnelSlice.DataPoint } }; // Canvas holds a slice of a funnel chart
Faces faces = null;
// GeometryGroup for for a funnel slice path
GeometryGroup geometryGroup = new GeometryGroup();
// PathGeometry for for a funnel slice path
PathGeometry pathGeometry = new PathGeometry();
// pathFigure for for a funnel slice path
PathFigure pathFigure = new PathFigure() { StartPoint = new Point(topRadius - funnelSlice.TopRadius, 0) }; // PathFigure of a funnel slice
// Path for for a funnel slice
Path path4Slice = new Path() { Fill = sideFillColor };
path4Slice.Tag = new ElementData() { Element = funnelSlice.DataPoint };
// Add PathGeometry to GeometryGroup
geometryGroup.Children.Add(pathGeometry);
// Set path data
path4Slice.Data = geometryGroup;
// Set properties for path
path4Slice.StrokeThickness = 0;
path4Slice.Stroke = new SolidColorBrush(Colors.Black);
// Add path elements to its parent canvas
pathGeometry.Figures.Add(pathFigure);
visual.Children.Add(path4Slice);
if (is3D)
{
#region 3D
geometryGroup.FillRule = FillRule.Nonzero;
// Create top arc left
ArcSegment arcSegment = new ArcSegment();
arcSegment.Point = new Point(topRadius, yScaleTop / 2);
arcSegment.Size = new Size(funnelSlice.TopRadius, yScaleTop / 2);
pathFigure.Segments.Add(arcSegment);
// Create top arc right
arcSegment = new ArcSegment();
arcSegment.Point = new Point(topRadius + funnelSlice.TopRadius, 0);
arcSegment.Size = new Size(funnelSlice.TopRadius, yScaleTop / 2);
pathFigure.Segments.Add(arcSegment);
// Create right Plain
LineSegment lineSegment = new LineSegment() { Point = new Point(topRadius + funnelSlice.BottomRadius, funnelSlice.Height) };
pathFigure.Segments.Add(lineSegment);
lineSegment = new LineSegment() { Point = new Point(topRadius - funnelSlice.BottomRadius, funnelSlice.Height) };
pathFigure.Segments.Add(lineSegment);
// Create left Plain
lineSegment = new LineSegment() { Point = new Point(topRadius - funnelSlice.TopRadius, 0) };
pathFigure.Segments.Add(lineSegment);
EllipseGeometry ellipseGeometry = new EllipseGeometry();
ellipseGeometry.Center = new Point(topRadius, funnelSlice.Height);
ellipseGeometry.RadiusX = funnelSlice.BottomRadius;
ellipseGeometry.RadiusY = yScaleBottom / 2;
geometryGroup.Children.Add(ellipseGeometry);
// Create ellips for the funnel top
Ellipse funnelTopEllipse = new Ellipse() { Height = yScaleTop, Width = funnelSlice.TopRadius * 2, Fill = topFillColor, Tag = new ElementData() { Element = funnelSlice.DataPoint } };
funnelTopEllipse.SetValue(Canvas.TopProperty, -yScaleTop / 2);
funnelTopEllipse.SetValue(Canvas.LeftProperty, topRadius - funnelSlice.TopRadius);
if (funnelSlice.DataPoint.Parent.Bevel)
funnelTopEllipse.StrokeThickness = 1.24;
else
funnelTopEllipse.StrokeThickness = 0.24;
funnelTopEllipse.Stroke = Graphics.GetBevelTopBrush(topSurfaceStroke, 0);
visual.Children.Add(funnelTopEllipse);
if (!isLightingGradientLayer)
{
// Update faces for the DataPoint
faces = new Faces();
faces.VisualComponents.Add(path4Slice);
faces.VisualComponents.Add(funnelTopEllipse);
(path4Slice.Tag as ElementData).VisualElementName = "FunnelBase";
(funnelTopEllipse.Tag as ElementData).VisualElementName = "FunnelTop";
#region Creating Seperate BorderLine
GeometryGroup borderGeometryGroup = new GeometryGroup();
// PathGeometry for for a funnel slice path
PathGeometry leftRightBorderPathGeometry = new PathGeometry();
// LeftLine Border
PathFigure leftBorderPathFigure = new PathFigure() { StartPoint = pathFigure.StartPoint };
LineSegment leftBorderLineSegment = new LineSegment() { Point = new Point(topRadius - funnelSlice.BottomRadius, funnelSlice.Height) };
leftBorderPathFigure.Segments.Add(leftBorderLineSegment);
leftRightBorderPathGeometry.Figures.Add(leftBorderPathFigure);
// RightLine Border
PathGeometry rightRightBorderPathGeometry = new PathGeometry();
PathFigure rightBorderPathFigure = new PathFigure() { StartPoint = new Point(topRadius + funnelSlice.TopRadius, 0) };
LineSegment rightBorderLineSegment = new LineSegment() { Point = new Point(topRadius + funnelSlice.BottomRadius, funnelSlice.Height) };
rightBorderPathFigure.Segments.Add(rightBorderLineSegment);
rightRightBorderPathGeometry.Figures.Add(rightBorderPathFigure);
// Bottom border Ellipse
EllipseGeometry ellipseGeometryBorder = new EllipseGeometry();
ellipseGeometryBorder.Center = new Point(topRadius, funnelSlice.Height);
ellipseGeometryBorder.RadiusX = funnelSlice.BottomRadius;
ellipseGeometryBorder.RadiusY = yScaleBottom / 2;
borderGeometryGroup.Children.Add(ellipseGeometryBorder);
// Bottom border Ellipse
ellipseGeometryBorder = new EllipseGeometry();
ellipseGeometryBorder.Center = new Point(topRadius, 0);
ellipseGeometryBorder.RadiusX = funnelSlice.TopRadius;
ellipseGeometryBorder.RadiusY = yScaleTop / 2;
borderGeometryGroup.Children.Add(ellipseGeometryBorder);
borderGeometryGroup.Children.Add(leftRightBorderPathGeometry);
borderGeometryGroup.Children.Add(rightRightBorderPathGeometry);
Path borderPath = new Path() { Data = borderGeometryGroup, IsHitTestVisible = false };
borderPath.SetValue(Canvas.ZIndexProperty, (Int32)(-1));
visual.Children.Add(borderPath);
faces.BorderElements.Add(borderPath);
faces.BorderElements.Add(funnelTopEllipse);
#endregion
faces.Parts = new List();
faces.Parts.Add(path4Slice);
faces.Parts.Add(funnelTopEllipse);
funnelSlice.DataPoint.Faces = faces;
}
else
{
path4Slice.IsHitTestVisible = false;
funnelTopEllipse.IsHitTestVisible = false;
funnelSlice.DataPoint.Faces.Parts.Add(path4Slice);
funnelSlice.DataPoint.Faces.Parts.Add(funnelTopEllipse);
funnelSlice.DataPoint.Faces.VisualComponents.Add(path4Slice);
funnelSlice.DataPoint.Faces.VisualComponents.Add(funnelTopEllipse);
(path4Slice.Tag as ElementData).VisualElementName = "Lighting";
(funnelTopEllipse.Tag as ElementData).VisualElementName = "FunnelTopLighting";
}
// Apply animation for the 3D funnel slice
if (animationEnabled)
{
funnelSlice.DataPoint.Parent.Storyboard = AnimationHelper.ApplyOpacityAnimation(funnelTopEllipse, funnelSlice.DataPoint.Parent, funnelSlice.DataPoint.Parent.Storyboard, 0, funnelSlice.DataPoint.InternalOpacity, 1);
funnelSlice.DataPoint.Parent.Storyboard = AnimationHelper.ApplyOpacityAnimation(path4Slice, funnelSlice.DataPoint.Parent, funnelSlice.DataPoint.Parent.Storyboard, 0, funnelSlice.DataPoint.InternalOpacity, 1);
}
#endregion
}
else
{
// Points of a 2D funnel slice
Point[] funnelCornerPoints = new Point[8];
// Top line
LineSegment lineSegment = new LineSegment() { Point = new Point(topRadius - funnelSlice.BottomRadius, funnelSlice.Height) };
pathFigure.Segments.Add(lineSegment);
funnelCornerPoints[3] = lineSegment.Point;
// Right line
lineSegment = new LineSegment() { Point = new Point(topRadius + funnelSlice.BottomRadius, funnelSlice.Height) };
pathFigure.Segments.Add(lineSegment);
funnelCornerPoints[2] = lineSegment.Point;
// Bottom line
lineSegment = new LineSegment() { Point = new Point(topRadius + funnelSlice.TopRadius, 0) };
pathFigure.Segments.Add(lineSegment);
funnelCornerPoints[1] = lineSegment.Point;
// Left line
lineSegment = new LineSegment() { Point = new Point(topRadius - funnelSlice.TopRadius, 0) };
pathFigure.Segments.Add(lineSegment);
funnelCornerPoints[0] = lineSegment.Point;
// Apply animation for the 2D funnel slice
if (animationEnabled)
funnelSlice.DataPoint.Parent.Storyboard = AnimationHelper.ApplyOpacityAnimation(path4Slice, funnelSlice.DataPoint.Parent, funnelSlice.DataPoint.Parent.Storyboard, 0, funnelSlice.DataPoint.InternalOpacity, 1);
if (!isLightingGradientLayer)
{
// Update faces for the DataPoint
faces = new Faces();
faces.VisualComponents.Add(path4Slice);
(path4Slice.Tag as ElementData).VisualElementName = "FunnelBase";
faces.Parts = new List();
faces.Parts.Add(path4Slice);
faces.BorderElements.Add(path4Slice);
funnelSlice.DataPoint.Faces = faces;
// Apply bevel effect for the 2D funnel Slice
if (funnelSlice.DataPoint.Parent.Bevel)
{
ApplyFunnelBevel(visual, funnelSlice, sideFillColor, funnelCornerPoints);
}
}
else
{
path4Slice.IsHitTestVisible = false;
funnelSlice.DataPoint.Faces.VisualComponents.Add(path4Slice);
(path4Slice.Tag as ElementData).VisualElementName = "Lighting";
}
}
if (isLightingGradientLayer)
{
visual.IsHitTestVisible = false;
}
else
{
// Drawing LabelLine
Canvas labelLineCanvas = CreateLabelLine(funnelSlice, topRadius, animationEnabled);
if (labelLineCanvas != null)
{
sliceCanvas.Children.Add(labelLineCanvas);
faces.VisualComponents.Add(labelLineCanvas);
}
// Add label visual to the visual
if ((Boolean)funnelSlice.DataPoint.LabelEnabled)
{
Canvas labelCanvas = new Canvas();
labelCanvas.SetValue(Canvas.ZIndexProperty, (Int32)10);
faces.VisualComponents.Add(funnelSlice.DataPoint.LabelVisual);
// Label placement
funnelSlice.DataPoint.LabelVisual.SetValue(Canvas.TopProperty, funnelSlice.LabelLineEndPoint.Y - funnelSlice.DataPoint.LabelVisual.Height / 2);
if (funnelSlice.DataPoint.LabelStyle == LabelStyles.OutSide)
{
funnelSlice.DataPoint.LabelVisual.SetValue(Canvas.TopProperty, funnelSlice.LabelLineEndPoint.Y - funnelSlice.DataPoint.LabelVisual.Height / 2);
funnelSlice.DataPoint.LabelVisual.SetValue(Canvas.LeftProperty, funnelSlice.LabelLineEndPoint.X);
}
else
{
funnelSlice.DataPoint.LabelVisual.SetValue(Canvas.TopProperty, funnelSlice.LabelLineEndPoint.Y - funnelSlice.DataPoint.LabelVisual.Height / 2 + (is3D ? yScaleTop / 2 : 0));
funnelSlice.DataPoint.LabelVisual.SetValue(Canvas.LeftProperty, topRadius - funnelSlice.DataPoint.LabelVisual.Width / 2);
}
if (animationEnabled)
funnelSlice.DataPoint.Parent.Storyboard = AnimationHelper.ApplyOpacityAnimation(funnelSlice.DataPoint.LabelVisual, funnelSlice.DataPoint.Parent, funnelSlice.DataPoint.Parent.Storyboard, 1.2, 0.5, 1);
labelCanvas.Children.Add(funnelSlice.DataPoint.LabelVisual);
sliceCanvas.Children.Add(labelCanvas);
}
}
// if (!isLightingGradientLayer)
// faces.Visual = visual;
sliceCanvas.Children.Add(visual);
// sliceCanvas.Background = new SolidColorBrush(Color.FromArgb((byte)rand.Next(0,200),(byte)rand.Next(0,200),(byte)rand.Next(0,200),(byte)rand.Next(0,200)));
return sliceCanvas;
}
private static Random rand = new Random();
///
/// Creates a LabelLine for Funnel Chart
///
/// FunnelSliceParms
/// Top most radius of the funnel
/// Whether animation is enabled
/// Canvas for labelline
private static Canvas CreateLabelLine(FunnelSliceParms funnelSlice, Double topRadius, Boolean animationEnabled)
{
Canvas labelLineCanvas = null;
Point topRightPoint = new Point(topRadius + funnelSlice.TopRadius, 0);
Point bottomRightPoint = new Point(topRadius + funnelSlice.BottomRadius, funnelSlice.Height);
funnelSlice.RightMidPoint = Graphics.MidPointOfALine(topRightPoint, bottomRightPoint);
if (funnelSlice.DataPoint.Parent.RenderAs == RenderAs.StreamLineFunnel)
funnelSlice.LabelLineEndPoint = new Point(2 * topRadius, (bottomRightPoint.Y - 1.5 * Chart.BEVEL_DEPTH) < 0 ? bottomRightPoint.Y * .9 : (bottomRightPoint.Y - 1.5 * Chart.BEVEL_DEPTH));
else
funnelSlice.LabelLineEndPoint = new Point(2 * topRadius, funnelSlice.RightMidPoint.Y);
if ((Boolean)funnelSlice.DataPoint.LabelLineEnabled && funnelSlice.DataPoint.LabelStyle == LabelStyles.OutSide)
{
labelLineCanvas = new Canvas();
labelLineCanvas.Width = topRadius * 2;
labelLineCanvas.Height = funnelSlice.Height;
funnelSlice.DataPoint.LabelLine = null;
Path line = new Path()
{
Stroke = funnelSlice.DataPoint.LabelLineColor,
Fill = funnelSlice.DataPoint.LabelLineColor,
StrokeDashArray = ExtendedGraphics.GetDashArray((LineStyles)funnelSlice.DataPoint.LabelLineStyle),
StrokeThickness = (Double)funnelSlice.DataPoint.LabelLineThickness,
};
PathGeometry linePathGeometry = new PathGeometry();
// Set first point of the line
PathFigure linePathFigure = new PathFigure()
{
StartPoint = (funnelSlice.DataPoint.Parent.RenderAs == RenderAs.StreamLineFunnel) ? bottomRightPoint : funnelSlice.RightMidPoint
};
// Set second point of line
linePathFigure.Segments.Add(new LineSegment() { Point = funnelSlice.LabelLineEndPoint });
linePathGeometry.Figures.Add(linePathFigure);
line.Data = linePathGeometry;
funnelSlice.DataPoint.LabelLine = line;
labelLineCanvas.Children.Add(line);
if (animationEnabled)
funnelSlice.DataPoint.Parent.Storyboard = ApplyLabeLineAnimation(labelLineCanvas, funnelSlice.DataPoint.Parent, funnelSlice.DataPoint.Parent.Storyboard);
}
return labelLineCanvas;
}
///
/// Apply animation for line chart
///
/// Line chart canvas
/// DataSeries
/// Storyboard
/// Whether canvas is line canvas
/// Storyboard
private static Storyboard ApplyLabeLineAnimation(Panel canvas, DataSeries dataSeries, Storyboard storyboard)
{
LinearGradientBrush opacityMaskBrush = new LinearGradientBrush() { StartPoint = new Point(0, 0.5), EndPoint = new Point(1, 0.5) };
// Create gradients for opacity mask animation
GradientStop GradStop1 = new GradientStop() { Color = Colors.White, Offset = 0 };
GradientStop GradStop2 = new GradientStop() { Color = Colors.White, Offset = 0 };
GradientStop GradStop3 = new GradientStop() { Color = Colors.Transparent, Offset = 0.01 };
GradientStop GradStop4 = new GradientStop() { Color = Colors.Transparent, Offset = 1 };
// Add gradients to gradient stop list
opacityMaskBrush.GradientStops.Add(GradStop1);
opacityMaskBrush.GradientStops.Add(GradStop2);
opacityMaskBrush.GradientStops.Add(GradStop3);
opacityMaskBrush.GradientStops.Add(GradStop4);
canvas.OpacityMask = opacityMaskBrush;
double beginTime = 1;
DoubleCollection values = Graphics.GenerateDoubleCollection(0, 1);
DoubleCollection timeFrames = Graphics.GenerateDoubleCollection(0, 0.5);
List splines = AnimationHelper.GenerateKeySplineList(new Point(0, 0), new Point(1, 1), new Point(0, 0), new Point(1, 1));
storyboard.Children.Add(AnimationHelper.CreateDoubleAnimation(dataSeries, GradStop2, "(GradientStop.Offset)", beginTime, timeFrames, values, splines));
values = Graphics.GenerateDoubleCollection(0.01, 1);
timeFrames = Graphics.GenerateDoubleCollection(0, 0.5);
splines = AnimationHelper.GenerateKeySplineList(new Point(0, 0), new Point(1, 1), new Point(0, 0), new Point(1, 1));
storyboard.Children.Add(AnimationHelper.CreateDoubleAnimation(dataSeries, GradStop3, "(GradientStop.Offset)", beginTime, timeFrames, values, splines));
return storyboard;
}
///
/// Calculate Bevel innter points
///
/// funnelSlice
/// Array of points
private static void CalculateBevelInnerPoints(FunnelSliceParms funnelSlice, Point[] points)
{
Double a, b, h = Chart.BEVEL_DEPTH;
a = h * Math.Sin(funnelSlice.TopAngle / 2);
b = h * Math.Cos(funnelSlice.TopAngle / 2);
points[4] = new Point(points[0].X + b, a);
points[5] = new Point(points[1].X - b, a);
// a = h * Math.Cos(funnelSlice.BottomAngle / 2);
b = h * Math.Sin(funnelSlice.TopAngle / 2);
points[6] = new Point(points[2].X - a, points[2].Y - b);
points[7] = new Point(points[3].X + a, points[3].Y - b);
}
///
/// Get side lighting brush for the funnel slice
///
///
private static Brush GetSideBrush()
{
LinearGradientBrush gb = new LinearGradientBrush() { EndPoint = new Point(1.016, 0.558), StartPoint = new Point(0.075, 0.708) };
gb.GradientStops.Add(new GradientStop() { Color = Color.FromArgb(0, 0, 0, 0), Offset = 0.491 });
gb.GradientStops.Add(new GradientStop() { Color = Color.FromArgb(135, 8, 8, 8), Offset = 0.938 });
gb.GradientStops.Add(new GradientStop() { Color = Color.FromArgb(71, 71, 71, 71), Offset = 0 });
return gb;
}
///
/// Get top lighting brush for funnel slice
///
/// Current brush
/// Return new Brush
private static Brush GetTopBrush(Brush fillBrush)
{
if ((fillBrush as SolidColorBrush) != null)
{
// SolidColorBrush solidBrush = fillBrush as SolidColorBrush;
// LinearGradientBrush gb = new LinearGradientBrush() { EndPoint = new Point(0.5, 1), StartPoint = new Point(0.5, 0) };
// gb.GradientStops.Add(new GradientStop() { Color = Graphics.GetLighterColor(solidBrush.Color, 0.8), Offset = 1 });
// gb.GradientStops.Add(new GradientStop() { Color = Graphics.GetLighterColor(solidBrush.Color, 1), Offset = 0 });
SolidColorBrush solidBrush = fillBrush as SolidColorBrush;
Double r, g, b;
List colors = new List();
List stops = new List();
r = ((double)solidBrush.Color.R / (double)255) * 0.9999;
g = ((double)solidBrush.Color.G / (double)255) * 0.9999;
b = ((double)solidBrush.Color.B / (double)255) * 0.9999;
LinearGradientBrush gb = new LinearGradientBrush() { StartPoint = new Point(0.5, 1), EndPoint = new Point(0.5, 0) };
gb.GradientStops.Add(new GradientStop() { Color = Graphics.GetLighterColor(solidBrush.Color, 1 - r, 1 - g, 1 - b), Offset = 0 });
gb.GradientStops.Add(new GradientStop() { Color = solidBrush.Color, Offset = 0.9 });
gb.GradientStops.Add(new GradientStop() { Color = Graphics.GetLighterColor(solidBrush.Color, 1), Offset = 0.99 });
gb.Opacity = 0.8;
return gb;
}
else
return fillBrush;
}
private static Double _singleGap = 0;// Single gap height
private static Double _totalGap = 0;// Total height used for introducing gap among funnel slice
///
/// Size of the parent title of the StreamLine funnel Chart
///
private static Size _streamLineParentTitleSize;
}
///
/// Visifire.Charts.FunnelSliceParms
///
internal class FunnelSliceParms
{
public Int32 Index;
///
/// DataPoint reference
///
public DataPoint DataPoint;
///
/// Height of the funnel slice
///
public Double Height;
///
/// Top radius of the funnel slice
///
public Double TopRadius;
///
/// Bottom radius of the funnel slice
///
public Double BottomRadius;
///
/// Top angle for the funnel slice
///
public Double TopAngle;
///
/// Bottom angle for funnel slice
///
public Double BottomAngle;
///
/// Top position of the funnel slice canvas
///
public Double Top;
///
/// Top gap for the funnel slice canvas
///
public Double TopGap;
///
/// Bottom gap of the funnel slice canvas
///
public Double BottomGap;
///
/// Right mid point of the funnel slice
///
public Point RightMidPoint;
///
/// Left mid point of the funnel slice
///
public Point LeftMidPoint;
///
/// End point of the label line
///
public Point LabelLineEndPoint;
///
/// Holds the DataPoint visual position if a other DataPoints (funnel slices)
///
public System.Collections.Generic.List ExplodedPoints
{
get;
set;
}
}
///
/// IOValuePair fro Streamline Funnel slice
///
internal struct IOValuePair
{
public Double InputValue;
public Double OutPutValue;
}
}