/// <summary> /// Check whether two labels overlap. Also calculate the horizontal and vertical offsets /// to overcome the overlap. /// </summary> /// <param name="labelInfo">Information of the label</param> /// <param name="verticalOffset">How much need to move vertically to overcome overlap</param> /// <param name="horizontalOffset">How much need to move horizontally to overcome overlap</param> /// <returns>OverlapTypes</returns> private OverlapTypes CheckOverlap(CircularLabel label, out Double verticalOffset, out Double horizontalOffset) { OverlapTypes retValue = OverlapTypes.None; // Type of overlap verticalOffset = 0; // How much need to move vertically to overcome overlap horizontalOffset = 0; // How much need to move horizontally to overcome overlap CircularLabel A = this; CircularLabel B = label; if (B.Position.Y > A.Position.Y) { if (B.Position.Y < A.Position.Y + A.Height) { // Calculate how much need to move vertically to overcome overlap verticalOffset = A.Position.Y + A.Height - B.Position.Y; retValue = OverlapTypes.Vertical; } } else if (A.Position.Y > B.Position.Y) { if (A.Position.Y < B.Position.Y + B.Height) { // Calculate how much need to move vertically to overcome overlap verticalOffset = B.Position.Y + B.Height - A.Position.Y; retValue = OverlapTypes.Vertical; } } if (B.Position.X > A.Position.X) { if (B.Position.X < A.Position.X + A.Width) { // Calculate how much need to move horizontally to overcome overlap horizontalOffset = A.Position.X + A.Width - B.Position.X; retValue = (retValue == OverlapTypes.Vertical) ? OverlapTypes.Both : OverlapTypes.Horizontal; } } else if (A.Position.X > B.Position.X) { if (A.Position.X < B.Position.X + B.Width) { // Calculate how much need to move horizontally to overcome overlap horizontalOffset = B.Position.X + B.Width - A.Position.X; retValue = (retValue == OverlapTypes.Vertical) ? OverlapTypes.Both : OverlapTypes.Horizontal; } } return(retValue); }
/// <summary> /// Ask for space to NextLabel if NextLabel can leave the space. /// NextLabel B asks for space to the next label C.. and so on /// </summary> /// <param name="labelA">CircularLabel looking for space to overcome the overlap</param> private static Boolean AskSpaceToNextLabel(CircularLabel label, Double requiredVerticalOffset, Int32 noOfIteration) { Boolean retValue = true; Double gap = 0; if (label.IsLast) { if (label.Position.Y + label.Height < label.Boundary.Height && label.Boundary.Height - (label.Position.Y + label.Height) > requiredVerticalOffset) { retValue = label.RotateByVerticalOffset(requiredVerticalOffset, SweepDirection.Clockwise); return(retValue); } else { return(false); } } gap = VerticalSpcaeBetweenLabels(label, label.NextLabel); if (gap > requiredVerticalOffset) { retValue = label.RotateByVerticalOffset(requiredVerticalOffset, SweepDirection.Clockwise); return(retValue); } else { retValue = AskSpaceToNextLabel(label.NextLabel, requiredVerticalOffset, noOfIteration); if (retValue) { retValue = label.RotateByVerticalOffset(requiredVerticalOffset, SweepDirection.Clockwise); return(retValue); } else { retValue = false; } } return(retValue); }
/// <summary> /// Ask for space to PreviusLabel B if it can leave the space inorder to overcome the overlap. /// PreviusLabel B asks for space to the previous label C.. and so on /// </summary> /// <param name="labelA">CircularLabel looking for space to overcome the overlap</param> private static Boolean AskSpaceToPreviusLabel(CircularLabel labelA, Double requiredVerticalOffset, Int32 noOfIteration) { Boolean retValue = true; Double gap = 0; if (labelA.IsFirst) { if (labelA.Position.Y > requiredVerticalOffset) { retValue = labelA.RotateByVerticalOffset(requiredVerticalOffset, SweepDirection.Counterclockwise); return(retValue); } else { return(false); } } gap = VerticalSpcaeBetweenLabels(labelA, labelA.PreviusLabel); if (gap > requiredVerticalOffset) { retValue = labelA.RotateByVerticalOffset(requiredVerticalOffset, SweepDirection.Counterclockwise); return(retValue); } else { retValue = AskSpaceToPreviusLabel(labelA.PreviusLabel, requiredVerticalOffset, noOfIteration); if (retValue) { retValue = labelA.RotateByVerticalOffset(requiredVerticalOffset, SweepDirection.Counterclockwise); return(retValue); } else { retValue = false; } } return(retValue); }
/// <summary> /// Calculate vertical space available between two CircularLabels /// </summary> /// <param name="label1">CircularLabel A</param> /// <param name="label2">CircularLabel B</param> /// <returns>vertical space available between two CircularLabels</returns> private static Double VerticalSpcaeBetweenLabels(CircularLabel labelA, CircularLabel labelB) { Double gap = 0; if (labelA.Position.Y < labelB.Position.Y) { gap = labelB.Position.Y - (labelA.Position.Y + labelA.Height); } else { gap = labelA.Position.Y - (labelB.Position.Y + labelB.Height); } if (gap < 0) { return(0); } else { return(gap); } }
/// <summary> /// Position labels for Pie /Doughnut /// </summary> /// <param name="visual">Visual element</param> /// <param name="totalSum">Total YValue sum from all dataPoints</param> /// <param name="dataPoints">List of dataPoint</param> /// <param name="labels">Dictionary of dataPoint labels</param> /// <param name="pieSize">Pie size</param> /// <param name="referenceEllipseSize">Reference ellipse size</param> /// <param name="visualCanvasSize">Visual canvas size</param> /// <param name="scaleY">Scale Y</param> /// <param name="is3D">Whether a 3D chart</param> private static void PositionLabels(Canvas visual, Double totalSum, List<DataPoint> dataPoints, Size pieSize, Size referenceEllipseSize, Size visualCanvasSize, Double scaleY, Boolean is3D) { //Point plotRadius; Double xRadiusLabel = referenceEllipseSize.Width / 2; Double yRadiusLabel = referenceEllipseSize.Height / 2; Double xRadiusChart = pieSize.Width / 2; Double yRadiusChart = pieSize.Height / 2; Point center; Chart chart = null; //if(is3D) // center = new Point(visualCanvasSize.Width / 2, visualCanvasSize.Height / 2 - (yRadiusChart * scaleY) / 2); //else center = new Point(visualCanvasSize.Width / 2, visualCanvasSize.Height / 2); Double startAngle = FixAngle(dataPoints[0].Parent.InternalStartAngle), stopAngle, meanAngle; //Graphics.DrawPointAt(center, visual, System.Windows.Media.Colors.Red); CircularLabel prevousLabel = null; CircularLabel cLabel = null; Rect boundingArea = new Rect(0, 0, visual.Width, visual.Height); // List of CircularLabels List<CircularLabel> circularLabels = new List<CircularLabel>(); // Creating the LinkList of CircularLabels foreach (DataPoint dp in dataPoints) { chart = dp.Chart as Chart; stopAngle = startAngle + Math.PI * 2 * Math.Abs(dp.InternalYValue) / totalSum; meanAngle = (startAngle + stopAngle) / 2; if (dp.LabelStyle == LabelStyles.Inside) { meanAngle = CircularLabel.ResetMeanAngle(meanAngle); PlaceLabelInside(dp, center, meanAngle, pieSize, referenceEllipseSize, scaleY, is3D); startAngle = stopAngle; continue; } // if (meanAngle > Math.PI * 2) meanAngle -= Math.PI * 2; cLabel = new CircularLabel(dp.LabelVisual, center, meanAngle, xRadiusLabel, yRadiusLabel, xRadiusChart, yRadiusChart, visual); cLabel.Boundary = boundingArea; if (prevousLabel != null) prevousLabel.NextLabel = cLabel; cLabel.PreviusLabel = prevousLabel; prevousLabel = cLabel; circularLabels.Add(cLabel); startAngle = stopAngle; } if (circularLabels.Count > 0) { circularLabels[0].PreviusLabel = cLabel; cLabel.NextLabel = circularLabels[0]; LabelPlacementHelper.CircularLabelPlacment(boundingArea, circularLabels, (chart != null) ? chart.SmartLabelEnabled : false); } }
/// <summary> /// Rearrange the labels vertically /// </summary> /// <param name="labels">List of CircularLabels</param> /// <param name="leftOfArea">Left of the bounding area</param> /// <param name="topOfArea">Top of the bounding area</param> /// <param name="areaHeight">Height of the bounding area</param> /// <param name="areaWidth">Width of the bounding area</param> private static void RearrangeLabelsVertically(List <CircularLabel> labels, Double leftOfArea, Double topOfArea, Double areaHeight, Double areaWidth) { Rect[] labelInfo = new Rect[labels.Count]; int index = 0; // Prepare label information into an array foreach (CircularLabel label in labels) { Double left = (Double)label.LabelVisual.GetValue(Canvas.LeftProperty); Double top = (Double)label.LabelVisual.GetValue(Canvas.TopProperty); labelInfo[index++] = new Rect(left, top, label.LabelVisual.Width, label.LabelVisual.Height); } // Arrange the labels vertically LabelPlacementHelper.VerticalLabelPlacement(new Rect(leftOfArea, topOfArea, areaWidth, areaHeight), ref labelInfo); index = 0; // Update position of the labels foreach (CircularLabel label in labels) { Double top = labelInfo[index].Top + topOfArea; Double left = labelInfo[index].Left; Double currentMeanAngle = label.CalculateAngleByYCoordinate(top); Double offset = 0; if (!Double.IsNaN(currentMeanAngle)) { currentMeanAngle = CircularLabel.ResetMeanAngle(currentMeanAngle); label.Position = label.GetCartesianCoordinates4Labels(currentMeanAngle); if (left < label.Center.X) { left = label.Center.X - (label.Position.X - label.Center.X); //left = areaWidth - label.Position.X - label.LabelVisual.Width; //left = areaWidth - label.Position.X - (label.Center.X - left) + label.LabelVisual.Width; //left = label.Center.X - (label.Position.X - label.Center.X) - label.LabelVisual.Width; } else { left = label.Position.X; } label.CurrentMeanAngle = currentMeanAngle; } // Move the labels towards left or right if space availlable //if ((label.BaseMeanAngle > 7 * Math.PI / 4 && label.BaseMeanAngle < Math.PI / 4 // || label.BaseMeanAngle > 3 * Math.PI / 2 && label.BaseMeanAngle < 5 * Math.PI / 4) if (top > (label.YRadiusLabel * 2) / 6 && top < 5 * (label.YRadiusLabel * 2) / 6) { if (left < label.Center.X) { Double x = left - label.LabelVisual.Width; if (x > 0) { offset = -x / 3; } } else { Double x = areaWidth - (left + label.LabelVisual.Width); if (x > 0) { offset = +x / 3; } } } label.Position = new Point(left + offset, top); label.UpdateLabelVisualPosition(); //label.LabelVisual.SetValue(Canvas.LeftProperty, left); // ); //label.LabelVisual.SetValue(Canvas.TopProperty, top); index++; } }
/// <summary> /// Check whether two labels overlap. Also calculate the horizontal and vertical offsets /// to overcome the overlap. /// </summary> /// <param name="labelInfo">Information of the label</param> /// <param name="verticalOffset">How much need to move vertically to overcome overlap</param> /// <param name="horizontalOffset">How much need to move horizontally to overcome overlap</param> /// <returns>OverlapTypes</returns> private OverlapTypes CheckOverlap(CircularLabel label, out Double verticalOffset, out Double horizontalOffset) { OverlapTypes retValue = OverlapTypes.None; // Type of overlap verticalOffset = 0; // How much need to move vertically to overcome overlap horizontalOffset = 0; // How much need to move horizontally to overcome overlap CircularLabel A = this; CircularLabel B = label; if (B.Position.Y > A.Position.Y) { if (B.Position.Y < A.Position.Y + A.Height) { // Calculate how much need to move vertically to overcome overlap verticalOffset = A.Position.Y + A.Height - B.Position.Y; retValue = OverlapTypes.Vertical; } } else if (A.Position.Y > B.Position.Y) { if (A.Position.Y < B.Position.Y + B.Height) { // Calculate how much need to move vertically to overcome overlap verticalOffset = B.Position.Y + B.Height - A.Position.Y; retValue = OverlapTypes.Vertical; } } if (B.Position.X > A.Position.X) { if (B.Position.X < A.Position.X + A.Width) { // Calculate how much need to move horizontally to overcome overlap horizontalOffset = A.Position.X + A.Width - B.Position.X; retValue = (retValue == OverlapTypes.Vertical) ? OverlapTypes.Both : OverlapTypes.Horizontal; } } else if (A.Position.X > B.Position.X) { if (A.Position.X < B.Position.X + B.Width) { // Calculate how much need to move horizontally to overcome overlap horizontalOffset = B.Position.X + B.Width - A.Position.X; retValue = (retValue == OverlapTypes.Vertical) ? OverlapTypes.Both : OverlapTypes.Horizontal; } } return retValue; }
/// <summary> /// Calculate vertical space available between two CircularLabels /// </summary> /// <param name="label1">CircularLabel A</param> /// <param name="label2">CircularLabel B</param> /// <returns>vertical space available between two CircularLabels</returns> private static Double VerticalSpcaeBetweenLabels(CircularLabel labelA, CircularLabel labelB) { Double gap = 0; if (labelA.Position.Y < labelB.Position.Y) gap = labelB.Position.Y - (labelA.Position.Y + labelA.Height); else gap = labelA.Position.Y - (labelB.Position.Y + labelB.Height); if (gap < 0) return 0; else return gap; }
/// <summary> /// Ask for space to PreviusLabel B if it can leave the space inorder to overcome the overlap. /// PreviusLabel B asks for space to the previous label C.. and so on /// </summary> /// <param name="labelA">CircularLabel looking for space to overcome the overlap</param> private static Boolean AskSpaceToPreviusLabel(CircularLabel labelA, Double requiredVerticalOffset, Int32 noOfIteration) { Boolean retValue = true; Double gap = 0; if (labelA.IsFirst) { if (labelA.Position.Y > requiredVerticalOffset) { retValue = labelA.RotateByVerticalOffset(requiredVerticalOffset, SweepDirection.Counterclockwise); return retValue; } else return false; } gap = VerticalSpcaeBetweenLabels(labelA, labelA.PreviusLabel); if (gap > requiredVerticalOffset) { retValue =labelA.RotateByVerticalOffset(requiredVerticalOffset, SweepDirection.Counterclockwise); return retValue; } else { retValue = AskSpaceToPreviusLabel(labelA.PreviusLabel, requiredVerticalOffset, noOfIteration); if (retValue) { retValue = labelA.RotateByVerticalOffset(requiredVerticalOffset, SweepDirection.Counterclockwise); return retValue; } else retValue = false; } return retValue; }
/// <summary> /// Ask for space to NextLabel if NextLabel can leave the space. /// NextLabel B asks for space to the next label C.. and so on /// </summary> /// <param name="labelA">CircularLabel looking for space to overcome the overlap</param> private static Boolean AskSpaceToNextLabel(CircularLabel label, Double requiredVerticalOffset, Int32 noOfIteration) { Boolean retValue = true; Double gap = 0; if (label.IsLast) { if (label.Position.Y + label.Height < label.Boundary.Height && label.Boundary.Height - (label.Position.Y + label.Height) > requiredVerticalOffset) { retValue = label.RotateByVerticalOffset(requiredVerticalOffset, SweepDirection.Clockwise); return retValue; } else return false; } gap = VerticalSpcaeBetweenLabels(label, label.NextLabel); if (gap > requiredVerticalOffset) { retValue = label.RotateByVerticalOffset(requiredVerticalOffset, SweepDirection.Clockwise); return retValue; } else { retValue = AskSpaceToNextLabel(label.NextLabel, requiredVerticalOffset, noOfIteration); if (retValue) { retValue = label.RotateByVerticalOffset(requiredVerticalOffset, SweepDirection.Clockwise); return retValue; } else retValue = false; } return retValue; }