/// <summary> /// Calculate funnelSliceParms information /// </summary> /// <param name="isStreamLine">Whether its a streamline funnel chart</param> /// <param name="dataSeries">DataSeries reference</param> /// <param name="dataPoints">List of sorted reference(If required)</param> /// <param name="plotHeight">Total height of the plot canvas</param> /// <param name="plotWidth">Total width of the plot canvas</param> /// <param name="minPointHeight">Min height of a funnel slice</param> /// <param name="is3D">Whether chart is a 3D chart</param> /// <param name="yScale">YScale of the chart</param> /// <param name="gapRatio">Gap between two data points while a particular datapoint is Exploded</param> /// <param name="isSameSlantAngle">Whether the same slant angle to be used while drawing each slice</param> /// <param name="bottomRadius">Bottom most raduis of a funnel</param> /// <returns>FunnelSliceParms[]</returns> private static FunnelSliceParms[] CalculateFunnelSliceParmsInfo(Boolean isStreamLine, DataSeries dataSeries, List<DataPoint> 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<FunnelSliceParms> fixedHeightFunnelSlices = (from funnelSlice in funnelSlicesParms where funnelSlice.Height < minPointHeight select funnelSlice).ToList(); List<FunnelSliceParms> 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; }
/// <summary> /// Calculates Exploded DataPoint Positions /// </summary> /// <param name="funnelSlices"></param> 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<Point>(); 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); } } } } }