615 lines
17 KiB
C#
615 lines
17 KiB
C#
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<PieLabel> _PieLabels;
|
|
|
|
#endregion
|
|
|
|
public RadialAlign(List<PieLabel> 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<PieLabel>[] plist = SortLabels();
|
|
|
|
for (int i = 0; i < plist.Length; i++)
|
|
{
|
|
List<PieLabel> 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<PieLabel> 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<PieLabel>[] SortLabels()
|
|
{
|
|
List<PieLabel>[] plist = new List<PieLabel>[2];
|
|
|
|
plist[0] = new List<PieLabel>();
|
|
plist[1] = new List<PieLabel>();
|
|
|
|
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<PieLabel>
|
|
{
|
|
public int Compare(PieLabel pl1, PieLabel pl2)
|
|
{
|
|
return (pl1.PtSliceEdge.Y - pl2.PtSliceEdge.Y);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region IsAnyOverlap
|
|
|
|
private bool IsAnyOverlap(PieChart pieChart, List<PieLabel> 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<PieLabel> 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<PieLabel> 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<PieLabel> 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<PieLabel> 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<PieLabel> 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<PieLabel> 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<PieLabel> 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
|
|
}
|
|
}
|
|
|