using System; using System.Collections.Generic; using System.Drawing; using DevComponents.DotNetBar.Charts.Style; namespace DevComponents.DotNetBar.Charts { class RadialAlign { #region Private variables private List _PieLabels; #endregion public RadialAlign(List pieLabels) { _PieLabels = pieLabels; foreach (PieLabel pl in _PieLabels) pl.Bounds = Rectangle.Empty; } #region Iterate public bool Iterate(Graphics g, PieChart pieChart, Rectangle bounds) { if (_PieLabels.Count > 0) { PositionLabels(pieChart, bounds); List[] plist = SortLabels(); for (int i = 0; i < plist.Length; i++) { List pls = plist[i]; if (pls.Count > 0) { if (IsAnyOverlap(pieChart, pls) == true) { AdjustDownwards(pieChart, pls, bounds); if (IsAnyOverlap(pieChart, pls) == true) AdjustUpwards(pieChart, pls, bounds); } AdjustLabelSizes(g, pieChart, pls, bounds); if (IsAnyOverlap(pieChart, pls) == true) { AdjustDownwards(pieChart, pls, bounds); if (IsAnyOverlap(pieChart, pls) == true) AdjustUpwards(pieChart, pls, bounds); } if (pieChart.SliceLabelOverlapMode != SliceLabelOverlapMode.ShowOverlapping) { if (IsAnyOverlap(pieChart, pls) == true) HideOverLapping(pls, bounds); } UpdateBoxPoints(pieChart, pls); } } } return (true); } #region PositionLabels private void PositionLabels(PieChart pieChart, Rectangle bounds) { foreach (PieLabel pl in _PieLabels) { PieSeriesPoint psp = pl.PieSeriesPoint; PieSeriesPointCollection spc = psp.Parent as PieSeriesPointCollection; if (spc != null) { bool showWhiteSpace = psp.ShowSliceWhiteSpaceEx; int width = psp.OuterRadius - psp.InnerRadius; int outerRadius = (psp.ShowSliceWhiteSpaceEx == true) ? psp.OuterRadius : (int)(psp.InnerRadius + psp.SliceExtent * width); pl.LabelStyle = psp.GetEffectiveSliceStyle(pieChart, psp.ChartSeries).SliceOuterLabelStyle; pl.OuterRadius = outerRadius; pl.Radius = outerRadius + pl.LabelStyle.ConnectorLength; UpdateBoundingRect(pieChart, psp, pl, psp.CenterAngle); } } AdjustForChartBounds(pieChart, _PieLabels); } #region UpdateBoundingRect private void UpdateBoundingRect(PieChart pieChart, PieSeriesPoint psp, PieLabel pl, double angle) { pl.PtSliceEdge = psp.GetRayEndPoint(angle, pl.OuterRadius); Point pt = psp.GetRayEndPoint(angle, pl.Radius); Rectangle r = new Rectangle(pt, pl.LabelSize); r.Y -= (r.Size.Height / 2); angle = (angle + 360000) % 360; int n = (pl.LabelStyle.DrawConnector == Tbool.True) ? Dpi.Width(pl.LabelStyle.ConnectorTickLength) : 0; r.X += ((angle >= 270 || angle <= 90) ? n : -(r.Size.Width + n)); pl.Bounds = r; AdjustForPieIntersect(pl); } #region AdjustForPieIntersect private void AdjustForPieIntersect(PieLabel pl) { PieSeriesPoint psp = pl.PieSeriesPoint; Point pto = GetClosestOffset(psp.SliceCenter, pl.Bounds); Point ptr = psp.SliceCenter; ptr.X += pto.X; ptr.Y += pto.Y; if (Intersects(psp, ptr, pl) == true) { Rectangle r = pl.Bounds; double ihyp = Math.Sqrt(pto.X * pto.X + pto.Y * pto.Y); double ohyp = (psp.OuterRadius + pl.LabelStyle.LabelMargin) - ihyp; double scale = ohyp / ihyp; int dx = (int)(pto.X * scale); int dy = (int)(pto.Y * scale); r.X += dx; r.Y += dy; pl.Bounds = r; } } #region GetClosestOffset private Point GetClosestOffset(Point ptc, Rectangle r) { Point pt = new Point(); int dx1 = r.X - ptc.X; int dx2 = r.Right - ptc.X; int dy1 = r.Y - ptc.Y; int dy2 = r.Bottom - ptc.Y; pt.X = Math.Abs(dx1) < Math.Abs(dx2) ? dx1 : dx2; pt.Y = Math.Abs(dy1) < Math.Abs(dy2) ? dy1 : dy2; return (pt); } #endregion #region Intersects private bool Intersects( PieSeriesPoint psp, Point ptr, PieLabel pl) { int radius = psp.OuterRadius + pl.LabelStyle.LabelMargin; int dx = psp.SliceCenter.X - ptr.X; int dy = psp.SliceCenter.Y - ptr.Y; float dsq = (dx * dx) + (dy * dy); return (dsq < (radius * radius)); } #endregion #endregion #endregion #region AdjustForChartBounds private void AdjustForChartBounds(PieChart pieChart, List pls) { Rectangle r = pieChart.ContentBoundsEx; for (int i = 0; i < pls.Count; i++) { PieLabel pl = pls[i]; if (pl.Bounds.IsEmpty == false) { int margin = Dpi.Width(pl.LabelStyle.LabelMargin); if (pl.Bounds.Bottom + margin > r.Bottom) { Rectangle t = pl.Bounds; t.Y = (r.Bottom - t.Height - margin); pl.Bounds = t; } } } } #endregion #endregion #region SortLabels private List[] SortLabels() { List[] plist = new List[2]; plist[0] = new List(); plist[1] = new List(); foreach (PieLabel pl in _PieLabels) { double angle = pl.PieSeriesPoint.CenterAngle % 360; pl.IsRightlabel = (angle >= 270 || angle <= 90); if (pl.IsRightlabel == true) plist[1].Add(pl); else plist[0].Add(pl); } plist[0].Sort(new PieLabelYComparer()); plist[1].Sort(new PieLabelYComparer()); return (plist); } #region PieLabelYComparer private class PieLabelYComparer : IComparer { public int Compare(PieLabel pl1, PieLabel pl2) { return (pl1.PtSliceEdge.Y - pl2.PtSliceEdge.Y); } } #endregion #endregion #region IsAnyOverlap private bool IsAnyOverlap(PieChart pieChart, List pls) { if (pls.Count <= 0) return (false); PieSeriesPoint psp = pls[0].PieSeriesPoint; if (psp.SliceLabelVisibilityEx == SliceLabelVisibility.SliceMouseOver) return (false); int maxy = 0; for (int i = 0; i < pls.Count; i++) { PieLabel pl = pls[i]; if (pl.Bounds.IsEmpty == false) { if (maxy > pl.Bounds.Y) return (IsAnyOverlapEx(pieChart, pls)); int margin = Dpi.Width(pl.LabelStyle.LabelMargin); if (pl.Bounds.Bottom + margin > maxy) maxy = pl.Bounds.Bottom + margin; } } return (false); } #region IsAnyOverlapEx private bool IsAnyOverlapEx(PieChart pieChart, List pls) { for (int i = 0; i < pls.Count; i++) { PieLabel pl = pls[i]; if (pl.Bounds.IsEmpty == false) { int margin = pl.LabelStyle.LabelMargin; Rectangle r = pl.Bounds; r.Inflate(margin, margin); for (int j = i + 1; j < pls.Count; j++) { PieLabel pl2 = pls[j]; if (r.IntersectsWith(pl2.Bounds) == true) return (true); } } } return (false); } #endregion #endregion #region HideOverLapping private void HideOverLapping(List pls, Rectangle bounds) { for (int i = 0; i < pls.Count; i++) { PieLabel pl = pls[i]; if (pl.Bounds.IsEmpty == false) { for (int j = i + 1; j < pls.Count; j++) { PieLabel pl2 = pls[j]; if (pl.Bounds.IntersectsWith(pl2.Bounds) == true) pl2.Bounds = Rectangle.Empty; } } } } #endregion #region AdjustUpwards private void AdjustUpwards(PieChart pieChart, List pls, Rectangle bounds) { for (int i = pls.Count - 1; i > 0; i--) { PieLabel pl0 = pls[i]; PieLabel pl1 = pls[i - 1]; int margin = Dpi.Width(pl0.LabelStyle.LabelMargin); Rectangle t = pl0.Bounds; t.Inflate(margin, margin); if (pl1.Bounds.Bottom > t.Y) { if ((pl1.Bounds.X < t.Right) && (t.X < pl1.Bounds.Right)) { Rectangle r = pl1.Bounds; r.Y = Math.Max(bounds.Y + margin, pl0.Bounds.Y - pl1.Bounds.Height - margin); pl1.Bounds = r; AdjustForPieIntersect(pl1); } } } } #endregion #region AdjustDownwards private void AdjustDownwards(PieChart pieChart, List pls, Rectangle bounds) { for (int i = 0; i < pls.Count - 1; i++) { PieLabel pl0 = pls[i]; PieLabel pl1 = pls[i + 1]; int margin = Dpi.Width(pl0.LabelStyle.LabelMargin) + 4; Rectangle t = pl0.Bounds; t.Inflate(margin, margin); if (pl1.Bounds.Bottom < t.Bottom) { if ((pl1.Bounds.X < t.Right) && (t.X < pl1.Bounds.Right)) { Rectangle r = pl1.Bounds; r.Y = Math.Min(bounds.Bottom - r.Height - margin, t.Bottom); pl1.Bounds = r; AdjustForPieIntersect(pl1); } } } } #endregion #region AdjustSpread private void AdjustSpread(List pls, Rectangle bounds) { int height = 0; for (int i = 0; i < pls.Count; i++) height += pls[i].Bounds.Height; int margin = bounds.Height - height; if (pls.Count > 1) margin /= (pls.Count - 1); int y = bounds.Y; for (int i = 0; i < pls.Count; i++) { PieLabel pl = pls[i]; Rectangle r = pl.Bounds; r.Y = y; pl.Bounds = r; y = (pl.Bounds.Bottom + margin); AdjustForPieIntersect(pl); } } #endregion #region AdjustLabelSizes private void AdjustLabelSizes(Graphics g, PieChart pieChart, List pls, Rectangle bounds) { Rectangle r = pieChart.ContentBoundsEx; foreach (PieLabel pl in pls) { PieSeriesPoint psp = pl.PieSeriesPoint; ChartSliceVisualStyle sstyle = psp.GetEffectiveSliceStyle(pieChart, psp.ChartSeries); Rectangle t = GetChartBounds(pieChart, pl); if (pl.LabelSize.Width != t.Width) { PieSeries series = psp.ChartSeries; Size size = series.MeasurePieLabel(g, pieChart, psp, pl, t.Width, sstyle.SliceOuterLabelStyle); if (size != pl.Bounds.Size) { if (t.X != pl.Bounds.X && size.Width < t.Size.Width) t.X += (t.Size.Width - size.Width); if (size.Height > t.Size.Height) t.Y -= (size.Height - t.Size.Height) / 2; t.Size = size; } pl.Bounds = t; AdjustForPieIntersect(pl); } } } #region GetChartBounds private Rectangle GetChartBounds(PieChart pieChart, PieLabel pl) { Rectangle r = pl.Bounds; r.Inflate(4, 4); if (r.X < pieChart.ContentBoundsEx.X) { r.Width -= (pieChart.ContentBoundsEx.X - r.X); r.X = pieChart.ContentBoundsEx.X; if (r.Width <= 0) return (Rectangle.Empty); } if (r.Right >= pieChart.ContentBoundsEx.Right) { r.Width -= (r.Right - pieChart.ContentBoundsEx.Right + 1); if (r.Width <= 0) return (Rectangle.Empty); } if (r.Y < pieChart.ContentBoundsEx.Y) { r.Height -= (pieChart.ContentBoundsEx.Y - r.Y); r.Y = pieChart.ContentBoundsEx.Y; if (r.Height <= 0) return (Rectangle.Empty); } if (r.Bottom >= pieChart.ContentBoundsEx.Bottom) { int n = (r.Bottom - pieChart.ContentBoundsEx.Bottom + 1); r.Y -= n; r.Width++; if (r.Height <= 0) return (Rectangle.Empty); } r.Inflate(-4, -4); return (r); } #endregion #endregion #region UpdateBoxPoints private void UpdateBoxPoints(PieChart pieChart, List pls) { foreach (PieLabel pl in pls) { Rectangle t = new Rectangle(); Rectangle r = pl.Bounds; if (r.IsEmpty == false) { if (pl.LabelStyle.DrawConnector == Tbool.True) { int tick = pl.LabelStyle.ConnectorTickLength; if (pl.IsLeftlabel == true) { pl.PtBoxEdge = new Point(r.Right, r.Y + r.Height / 2); pl.PtBoxBend = new Point(pl.PtBoxEdge.X + tick, pl.PtBoxEdge.Y); } else { pl.PtBoxEdge = new Point(r.X, r.Y + r.Height / 2); pl.PtBoxBend = new Point(pl.PtBoxEdge.X - tick, pl.PtBoxEdge.Y); } Point ptMin = new Point(); Point ptMax = new Point(); if (pl.PtBoxEdge.X < pl.PtSliceEdge.X) { ptMin.X = pl.PtBoxEdge.X; ptMax.X = pl.PtSliceEdge.X; } else { ptMin.X = pl.PtSliceEdge.X; ptMax.X = pl.PtBoxEdge.X; } if (pl.PtBoxEdge.Y < pl.PtSliceEdge.Y) { ptMin.Y = pl.PtBoxEdge.Y; ptMax.Y = pl.PtSliceEdge.Y; } else { ptMin.Y = pl.PtSliceEdge.Y; ptMax.Y = pl.PtBoxEdge.Y; } t.Location = ptMin; t.Width = ptMax.X - ptMin.X + 1; t.Height = ptMax.Y - ptMin.Y + 1; } } pl.Bounds = r; pl.ConnectorBounds = t; } } #endregion #endregion } }