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
}
}