using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;
using DevComponents.DotNetBar.Charts.Style;
namespace DevComponents.DotNetBar.Charts
{
///
/// Represents the collection of PieSeries.
///
[Editor("DevComponents.Charts.Design.ChartSeriesCollectionEditor, DevComponents.Charts.Design, " +
"Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf", typeof(UITypeEditor))]
public class ChartPieSeriesCollection : CustomNamedCollection
{
#region GetUniqueName
public string GetUniqueName()
{
return (GetUniqueName("Series"));
}
#endregion
}
[TypeConverter(typeof(PieSeriesConverter))]
public class PieSeries : BaseSeries, ISeriesPointContainer, ILegendItemEx
{
#region Private variables
private States _States;
private PieSeriesPointCollection _SeriesPoints;
private double _ExplodedMargin = double.NaN;
private Point _PieCenter;
private List _PieRings;
private int _RingWeightTotal;
private Tbool _CenterFirstSlice = Tbool.NotSet;
private Tbool _ShowAllRings = Tbool.NotSet;
private Tbool _ShowOtherSlice = Tbool.NotSet;
private PieSeriesPointCollection _ActiveSeriesPointSet;
private MouseClickSliceAction _MouseClickSliceAction = MouseClickSliceAction.NotSet;
private MouseDoubleClickSliceAction _MouseDoubleClickSliceAction = MouseDoubleClickSliceAction.NotSet;
private PieSelectionMode _PieSelectionMode = PieSelectionMode.NotSet;
private PieSeriesPoint _MouseDownPsp;
private int _MouseDownRadius;
private int _MouseDownOffset;
private Tbool _EnableDragDetach = Tbool.NotSet;
private SymbolDef _ScaleSymDef = new SymbolDef();
private float _SymFontScale = -1;
private SliceVisualLayout _SliceVisualLayout;
private int _RingWeight = 100;
private int _RingWeightEx;
private PaletteGroup _PaletteGroup = PaletteGroup.NotSet;
private Color[] _CustomPalette;
private double _PieOuterExtent;
#endregion
#region Constructors
public PieSeries()
: this(null)
{
}
public PieSeries(string name)
{
Name = name;
SeriesType = SeriesType.Pie;
InitDefaultStates();
}
#endregion
#region InitDefaultStates
private void InitDefaultStates()
{
SetState(States.ShowInnerRingsEx, true);
}
#endregion
#region Public properties
#region CenterFirstSlice
///
/// Gets or sets whether the first pie slice is centered
/// on the starting angle or starts on the starting angle. Default is false.
///
[DefaultValue(Tbool.NotSet), Category("Display")]
[Description("Indicates whether the first pie slice is centered on the starting angle or starts on the starting angle. Default is false.")]
public Tbool CenterFirstSlice
{
get { return (_CenterFirstSlice); }
set
{
if (value != _CenterFirstSlice)
{
_CenterFirstSlice = value;
OnPropertyChangedEx("CenterFirstSlice", VisualChangeType.Layout);
}
}
}
#endregion
#region CustomPalette
///
/// Gets or sets the custom palette for the series.
///
[DefaultValue(null), Category("Appearance")]
[Description("Indicates the custom palette for the series.")]
public Color[] CustomPalette
{
get { return (_CustomPalette); }
set
{
_CustomPalette = value;
OnPropertyChangedEx("CustomPalette", VisualChangeType.Layout);
}
}
#endregion
#region EnableDragDetach
///
/// Gets or sets whether the user can detach associated series elements by Click and drag.
/// Note that MouseClickSelect must not be set to None or NotSet.
///
[DefaultValue(Tbool.NotSet), Category("Behavior")]
[Description("Indicates whether the user can detach associated series elements by Click and drag. Note that MouseClickSelect must not be set to None or NotSet.")]
public Tbool EnableDragDetach
{
get { return (_EnableDragDetach); }
set
{
if (value != _EnableDragDetach)
{
_EnableDragDetach = value;
OnPropertyChangedEx("EnableDragDetach", VisualChangeType.Layout);
}
}
}
#region GetEnableDragDetach
internal bool GetEnableDragDetach(PieChart pieChart)
{
Tbool enable = EnableDragDetach;
if (enable == Tbool.NotSet)
enable = pieChart.EnableDragDetach;
return (enable == Tbool.True);
}
#endregion
#endregion
#region ExplodedMargin
///
/// Gets or sets the exploded Margin for the series when the pie
/// is Exploded. The Margin is is measured as a percentage of the pie
/// radius (if less than 1) or the absolute pixel amount (if greater than 1).
///
[DefaultValue(double.NaN), Category("Layout")]
[Description("Indicates the exploded Margin for the series when the pie is Exploded. The Margin is is measured as a percentage of the pie radius (if <= 1) or the absolute pixel amount (if > 1).")]
public double ExplodedMargin
{
get { return (_ExplodedMargin); }
set
{
if (value != _ExplodedMargin)
{
_ExplodedMargin = value;
OnPropertyChangedEx("ExplodedMargin", VisualChangeType.Layout);
}
}
}
#endregion
#region MouseClickSliceAction
///
/// Gets or sets the action used when the user clicks on a pie slice.
///
[DefaultValue(MouseClickSliceAction.NotSet), Category("Behavior")]
[Description("Indicates the action used when the user clicks on a pie slice.")]
public MouseClickSliceAction MouseClickSliceAction
{
get { return (_MouseClickSliceAction); }
set
{
if (value != _MouseClickSliceAction)
{
_MouseClickSliceAction = value;
OnPropertyChanged("MouseClickSliceAction");
}
}
}
#region GetMouseClickSliceAction
private MouseClickSliceAction GetMouseClickSliceAction(PieChart pieChart)
{
MouseClickSliceAction mca =
(MouseClickSliceAction != MouseClickSliceAction.NotSet)
? MouseClickSliceAction : pieChart.MouseClickSliceAction;
return ((mca != MouseClickSliceAction.NotSet) ? mca : MouseClickSliceAction.None);
}
#endregion
#endregion
#region MouseDoubleClickSliceAction
///
/// Gets or sets the action used when the user double-clicks on a pie slice.
///
[DefaultValue(MouseDoubleClickSliceAction.NotSet), Category("Behavior")]
[Description("Indicates the action used when the user clicks on a pie slice.")]
public MouseDoubleClickSliceAction MouseDoubleClickSliceAction
{
get { return (_MouseDoubleClickSliceAction); }
set
{
if (value != _MouseDoubleClickSliceAction)
{
_MouseDoubleClickSliceAction = value;
OnPropertyChanged("MouseDoubleClickSliceAction");
}
}
}
#region GetMouseDoubleClickSliceAction
private MouseDoubleClickSliceAction GetMouseDoubleClickSliceAction(PieChart pieChart)
{
MouseDoubleClickSliceAction mca =
(MouseDoubleClickSliceAction != MouseDoubleClickSliceAction.NotSet)
? MouseDoubleClickSliceAction : pieChart.MouseDoubleClickSliceAction;
return ((mca != MouseDoubleClickSliceAction.NotSet) ? mca : MouseDoubleClickSliceAction.None);
}
#endregion
#endregion
#region PaletteGroup
///
/// Gets or sets the palette color group to use (Light/Medium/Dark/Color1/MonoBlue/etc).
///
[DefaultValue(PaletteGroup.NotSet), Category("Appearance")]
[Description("Indicates the palette color group to use (Light/Medium/Dark/Color1/MonoBlue/etc.).")]
public PaletteGroup PaletteGroup
{
get { return (_PaletteGroup); }
set
{
if (value != _PaletteGroup)
{
_PaletteGroup = value;
OnPropertyChangedEx("PaletteGroup", VisualChangeType.Recalc);
}
}
}
#endregion
#region PieSelectionMode
///
/// Gets or sets the mode used to perfoem pie selections.
///
[DefaultValue(PieSelectionMode.NotSet), Category("Behavior")]
[Description("Indicates the mode used to perform pie selections.")]
public PieSelectionMode PieSelectionMode
{
get { return (_PieSelectionMode); }
set
{
if (value != _PieSelectionMode)
{
_PieSelectionMode = value;
OnPropertyChanged("PieSelectionMode");
}
}
}
#region GetPieSelectionMode
internal PieSelectionMode GetPieSelectionMode(PieChart pieChart)
{
PieSelectionMode psm = PieSelectionMode;
if (psm == PieSelectionMode.NotSet)
psm = pieChart.PieSelectionMode;
return ((psm != PieSelectionMode.NotSet) ? psm : PieSelectionMode.Slice);
}
#endregion
#endregion
#region ReversePaletteColors
///
/// Gets or sets whether default palette colors are utilized in reverse order.
///
[DefaultValue(false), Category("Behavior")]
[Description("Indicates whether default palette colors are utilized in reverse order.")]
public virtual bool ReversePaletteColors
{
get { return (TestState(States.ReversePaletteColors)); }
set
{
if (value != ReversePaletteColors)
{
SetState(States.ReversePaletteColors, value);
OnPropertyChangedEx("ReversePaletteColors", VisualChangeType.Layout);
}
}
}
#endregion
#region RingWeight
///
/// Gets or sets the 'relative' total combined thickness of all series rings, as
/// compared to the relative thickness of other series combined rings.
/// rings.
///
[DefaultValue(100), Category("Layout")]
[Description("Indicates the 'relative' total combined thickness of all series rings, as compared to the relative thickness of other series combined rings.. Default is 100.")]
public int RingWeight
{
get { return (_RingWeight); }
set
{
if (value != _RingWeight)
{
_RingWeight = value;
OnPropertyChangedEx("RingWeight", Style.VisualChangeType.Recalc);
}
}
}
#endregion
#region SeriesPoints
///
/// Gets or sets the series point data collection.
///
[Category("Data")]
[Description("Indicates the series point data collection.")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public PieSeriesPointCollection SeriesPoints
{
get
{
if (_SeriesPoints == null)
{
_SeriesPoints = new PieSeriesPointCollection();
_SeriesPoints.Parent = this;
_SeriesPoints.CollectionChanged += SeriesPoints_CollectionChanged;
}
return (_SeriesPoints);
}
set
{
if (value != _SeriesPoints)
{
if (_SeriesPoints != null)
{
_SeriesPoints.Parent = null;
_SeriesPoints.CollectionChanged -= SeriesPoints_CollectionChanged;
}
_SeriesPoints = value;
if (_SeriesPoints != null)
{
_SeriesPoints.Parent = this;
_SeriesPoints.CollectionChanged += SeriesPoints_CollectionChanged;
}
}
}
}
#region SeriesPoints_CollectionChanged
void SeriesPoints_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
SeriesRangeChanged = true;
SetPointNotify(e.OldItems, false);
SetPointNotify(e.NewItems, true);
InvalidateLayout();
}
#region SetPointNotify
private void SetPointNotify(IList list, bool notify)
{
if (list != null)
{
for (int i = 0; i < list.Count; i++)
{
PieSeriesPoint psp = list[i] as PieSeriesPoint;
if (psp != null)
{
if (notify == true)
{
psp.Parent = SeriesPoints;
psp.PropertyChanged += Sp_PropertyChanged;
}
else
{
psp.Parent = null;
psp.PropertyChanged -= Sp_PropertyChanged;
}
}
}
}
}
#endregion
#region Sp_PropertyChanged
void Sp_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
VisualPropertyChangedEventArgs vce = e as VisualPropertyChangedEventArgs;
if (vce != null)
{
if (vce.ChangeType == VisualChangeType.Recalc)
{
InvalidateRecalc();
}
else if (vce.ChangeType == VisualChangeType.Layout)
{
InvalidateLayout();
}
else
{
PieSeriesPoint psp = sender as PieSeriesPoint;
if (psp != null)
{
InvalidateRender(psp.Bounds);
InvalidateRender(psp.PathBounds);
}
else
{
InvalidateRender();
}
}
if (ChartControl != null)
ChartControl.GlobalUpdateCount++;
}
}
#endregion
#endregion
#endregion
#region ShowAllRings
///
/// Gets or sets whether all series rings are shown when rendering the chart.
/// If false, only one ring at a time is shown, with the ability to 'drill' up
/// or down, to display adjacent rings.
///
[DefaultValue(Tbool.NotSet), Category("Appearance")]
[Description("Indicates whether all series Rings are shown when rendering the chart. If false, only one ring at a time is shown, with the ability to 'drill' up or down, to display adjacent rings.")]
public Tbool ShowAllRings
{
get { return (_ShowAllRings); }
set
{
if (value != _ShowAllRings)
{
_ShowAllRings = value;
OnPropertyChangedEx("ShowAllRings", VisualChangeType.Layout);
}
}
}
#region ShowAllRingsEx
internal bool ShowAllRingsEx(PieChart pieChart)
{
if (ShowAllRings != Tbool.NotSet)
return (ShowAllRings != Tbool.False);
return (pieChart.ShowAllRings != Tbool.False);
}
#endregion
#endregion
#region ShowOtherSlice
///
/// Gets or sets whether to consolidate smaller pie slices into a single slice that
/// represents 'other' data values, or whether to display those smaller slices
/// as separate pie slices.
///
[DefaultValue(Tbool.NotSet), Category("Appearance")]
[Description("Indicates whether to consolidate smaller pie slices into a single slice that represents 'other' data values, or whether to display those smaller slices as separate pie slices.")]
public Tbool ShowOtherSlice
{
get { return (_ShowOtherSlice); }
set
{
if (value != _ShowOtherSlice)
{
_ShowOtherSlice = value;
OnPropertyChangedEx("ShowOtherSlice", VisualChangeType.Layout);
}
}
}
#region ShowOtherSliceEx
internal bool ShowOtherSliceEx(PieChart pieChart)
{
if (ShowOtherSlice != Tbool.NotSet)
return (ShowOtherSlice == Tbool.True);
return (pieChart.ShowOtherSlice == Tbool.True);
}
#endregion
#endregion
#region SubSliceVisualLayout
///
/// Gets or sets the default layout of the subordinate or nested series slices.
///
[Category("Layout")]
[Description("Indicates the default layout of the subordinate or nested series slices.")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public SliceVisualLayout SubSliceVisualLayout
{
get
{
if (_SliceVisualLayout == null)
SubSliceVisualLayout = new SliceVisualLayout(this);
return (_SliceVisualLayout);
}
set
{
if (value != _SliceVisualLayout)
{
if (_SliceVisualLayout != null)
_SliceVisualLayout.PropertyChanged -= SliceVisualLayout_PropertyChanged;
_SliceVisualLayout = value;
if (_SliceVisualLayout != null)
_SliceVisualLayout.PropertyChanged += SliceVisualLayout_PropertyChanged;
OnPropertyChangedEx("SliceVisualLayout", VisualChangeType.Recalc);
}
}
}
#region SliceVisualLayout_PropertyChanged
void SliceVisualLayout_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
VisualPropertyChangedEventArgs vce = e as VisualPropertyChangedEventArgs;
if (vce != null)
{
OnPropertyChangedEx(vce.PropertyName, vce.ChangeType);
if (ChartControl != null)
ChartControl.GlobalUpdateCount++;
}
}
#endregion
#endregion
#region Visible
///
/// Gets or sets whether the series is visible or not.
///
[DefaultValue(true), Category("Appearance")]
[Description("Indicates whether the series is visible or not.")]
public override bool Visible
{
get { return (base.Visible); }
set
{
if (Visible != value)
{
base.Visible = value;
SeriesRangeChanged = true;
}
}
}
#endregion
#endregion
#region Internal properties
#region ActiveSeriesPointCollection
internal PieSeriesPointCollection ActiveSeriesPointCollection
{
get { return (_ActiveSeriesPointSet); }
set { _ActiveSeriesPointSet = value; }
}
#endregion
#region IsInnerRingSeries
internal bool IsInnerRingSeries
{
get { return (TestState(States.IsInnerRingSeries)); }
set { SetState(States.IsInnerRingSeries, value); }
}
#endregion
#region IsOffset
internal bool IsOffset
{
get { return (TestState(States.IsOffset)); }
set { SetState(States.IsOffset, value); }
}
#endregion
#region IsOuterRingSeries
internal bool IsOuterRingSeries
{
get { return (TestState(States.IsOuterRingSeries)); }
set { SetState(States.IsOuterRingSeries, value); }
}
#endregion
#region PieCenter
internal Point PieCenter
{
get { return (_PieCenter); }
set { _PieCenter = value; }
}
#endregion
#region PieOuterExtent
internal double PieOuterExtent
{
get { return (_PieOuterExtent); }
set { _PieOuterExtent = value; }
}
#endregion
#region PieRings
internal List PieRings
{
get
{
if (_PieRings == null)
_PieRings = new List();
return (_PieRings);
}
}
#endregion
#region RingWeightTotal
internal int RingWeightTotal
{
get { return (_RingWeightTotal); }
set { _RingWeightTotal = value; }
}
#endregion
#region RingWeightEx
internal int RingWeightEx
{
get { return (_RingWeightEx); }
set { _RingWeightEx = value; }
}
#endregion
#region SymFontScale
internal float SymFontScale
{
get { return (_SymFontScale); }
set { _SymFontScale = value; }
}
#endregion
#endregion
#region RenderOverride
protected override void RenderOverride(ChartRenderInfo renderInfo)
{
if (SeriesPoints.Count > 0)
{
PieChart pieChart = Parent as PieChart;
Point pt = pieChart.GetGlobalAdjustedPoint(pieChart.ContentBounds.Location);
RenderBounds = new Rectangle(pt, pieChart.ContentBounds.Size);
if (RenderBounds.Width > 0 && RenderBounds.Height > 0)
RenderPieSeries(renderInfo);
}
}
#region RenderPieSeries
private void RenderPieSeries(ChartRenderInfo renderInfo)
{
Graphics g = renderInfo.Graphics;
PieChart pieChart = Parent as PieChart;
PieSeriesPointCollection spc = GetActiveSeriesPointCollection();
RenderPieSeriesEx(g, renderInfo.ClipRectangle, pieChart, spc);
}
#region RenderPieSeriesEx
private void RenderPieSeriesEx(Graphics g,
Rectangle clip, PieChart pieChart, PieSeriesPointCollection spc)
{
if (spc != null)
spc = spc.PieSlices;
if (spc != null)
{
foreach (PieSeriesPoint psp in spc)
{
if (psp.PieRing != null)
{
if (psp.PieRing.IsDisplayed == true)
RenderSlice(g, clip, pieChart, spc, psp);
}
}
}
}
#region RenderSlice
private void RenderSlice(Graphics g, Rectangle clip,
PieChart pieChart, PieSeriesPointCollection spc, PieSeriesPoint psp)
{
if (PspNeedsDisplayed(pieChart, psp, clip) == true)
{
Region rgn = null;
int outerRadius = (int)psp.OuterRadius;
int innerRadius = (int)psp.InnerRadius;
ChartSliceVisualStyle sstyle = psp.GetEffectiveSliceStyle(pieChart, this);
if (psp.IsEmpty == false)
RenderReferenceLines(g, pieChart, spc, psp, false);
RenderSliceEx(g, ref rgn, pieChart,
spc, psp, outerRadius, innerRadius, sstyle);
if (psp.IsEmpty == false)
{
if (psp.IsOther == true)
{
if (ShowAllRingsEx(pieChart) == true)
{
List pieRings = psp.PieRing.ChartSeries.PieRings;
outerRadius = innerRadius;
innerRadius = pieRings[pieRings.Count - 1].InnerRadius;
RenderSliceOtherSpace(g, ref rgn,
pieChart, psp, outerRadius, innerRadius, sstyle);
}
}
}
psp.PathBounds = (rgn != null)
? Rectangle.Ceiling(rgn.GetBounds(g)) : Rectangle.Empty;
if (psp.IsEmpty == false)
{
if (sstyle.ImageOverlay == ImageOverlay.Bottom)
RenderSliceFigure(g, rgn, pieChart, psp, sstyle);
if ((sstyle.ImageOverlay == ImageOverlay.Middle) ||
(sstyle.ImageOverlay == ImageOverlay.NotSet))
{
RenderSliceFigure(g, rgn, pieChart, psp, sstyle);
}
RenderSliceLabel(g, pieChart, psp, sstyle);
if (sstyle.ImageOverlay == ImageOverlay.Top)
RenderSliceFigure(g, rgn, pieChart, psp, sstyle);
RenderReferenceLines(g, pieChart, spc, psp, true);
}
//using (StringFormat sf = new StringFormat())
//{
// sf.Alignment = StringAlignment.Center;
// sf.LineAlignment = StringAlignment.Center;
// g.DrawString(psp.RenderCount.ToString(),
// SystemFonts.DefaultFont, Brushes.Black, psp.PathBounds, sf);
//}
//psp.RenderCount++;
}
if (ShowAllRingsEx(pieChart) == true)
{
if (psp.SeriesPoints != null)
RenderPieSeriesEx(g, clip, pieChart, psp.SeriesPoints);
}
}
#region PspNeedsDisplayed
private bool PspNeedsDisplayed(PieChart pieChart, PieSeriesPoint psp, Rectangle clip)
{
if (psp.IsDisplayedEx == true)
{
if (psp.SweepAngleEx != 0)
{
Rectangle r = psp.Bounds;
if (r.Width > 0 && r.Height > 0)
{
if (psp.InnerRadius < psp.OuterRadius)
{
if (psp.PathBounds.IsEmpty == true || clip.IntersectsWith(psp.PathBounds) == true)
return (true);
}
}
}
}
return (false);
}
#endregion
#region RenderSliceEx
private void RenderSliceEx(Graphics g, ref Region rgn,
PieChart pieChart, PieSeriesPointCollection spc, PieSeriesPoint psp,
int outerRadius, int innerRadius, ChartSliceVisualStyle sstyle)
{
int width = outerRadius - innerRadius;
int extentWidth = (int)(psp.SliceExtent * width);
bool renderShading = (sstyle.EnableShading == Tbool.True);
SliceRenderType renderType = SliceRenderType.FullSlice;
if (psp.HasExtent == true)
{
int extentRadius = innerRadius + extentWidth;
if (extentWidth < width)
{
renderType = SliceRenderType.ExtentSlice;
if (psp.ShowSliceWhiteSpaceEx == true)
{
RenderSliceWhiteSpace(g, ref rgn, pieChart,
psp, outerRadius, extentRadius, renderShading, sstyle);
renderShading = false;
}
}
outerRadius = extentRadius;
}
RenderSliceExtent(g, ref rgn, pieChart,
psp, outerRadius, innerRadius, renderShading, renderType, sstyle);
}
#region RenderSliceWhiteSpace
private void RenderSliceWhiteSpace(Graphics g, ref Region rgn,
PieChart pieChart, PieSeriesPoint psp, int outerRadius,
int innerRadius, bool renderShading, ChartSliceVisualStyle sstyle)
{
Background background = sstyle.WhiteSpaceBackground;
ChartLineVisualStyle border = sstyle.WhiteSpaceBorder;
GraphicsState gs = g.Save();
g.TranslateTransform((float)psp.SliceCenter.X, (float)psp.SliceCenter.Y);
g.RotateTransform((float)(psp.CenterAngle) % 360);
Rectangle r = new Rectangle();
r.X = r.Y = -outerRadius;
r.Width = r.Height = (outerRadius * 2);
float angle = (float)GetSweepAngle(psp);
// Try to blend the edges of the slice better.
//if (background.IsDisplayable == true && border.IsDisplayable == false)
// angle += .1f;
ChartControl chartControl = ChartControl;
using (GraphicsPath path = new GraphicsPath())
{
path.AddArc(r, -angle / 2, angle);
if (psp.SliceExtent == 0)
{
path.AddLine(Point.Empty, Point.Empty);
path.CloseFigure();
if (chartControl.DoPreRenderSliceEvent(
g, path, pieChart, this, psp, SliceRenderType.ExtentWhitespace) == false)
{
Rectangle pb = Rectangle.Truncate(path.GetBounds());
if (psp.IsEmpty == false)
{
if (background.IsDisplayable == true)
{
using (Brush br = background.GetBrush(pb))
g.FillPath(br, path);
}
}
if (border.IsDisplayable == true)
{
using (Pen pen = new Pen(border.LineColor, Dpi.Width(border.LineWidth)))
{
if (border.LinePattern != LinePattern.NotSet)
pen.DashStyle = (DashStyle)border.LinePattern;
g.DrawPath(pen, path);
}
}
chartControl.DoPostRenderSliceEvent(
g, path, pieChart, this, psp, SliceRenderType.ExtentWhitespace);
}
if (psp.IsEmpty == false)
{
if (renderShading == true)
RenderShading(g, psp, path);
}
path.Transform(g.Transform);
if (rgn == null)
rgn = new Region(path);
}
else
{
int pcount = path.PointCount;
Rectangle t = new Rectangle();
t.X = t.Y = (-innerRadius);
t.Width = t.Height = (innerRadius * 2);
path.AddArc(t, angle / 2, -angle);
PointF ppt1 = path.PathPoints[pcount];
PointF ppt2 = path.GetLastPoint();
path.CloseFigure();
if (chartControl.DoPreRenderSliceEvent(
g, path, pieChart, this, psp, SliceRenderType.ExtentWhitespace) == false)
{
if (psp.IsEmpty == false)
{
if (background.IsDisplayable == true)
{
t = Rectangle.Ceiling(path.GetBounds());
Rectangle t2 = t;
t2.Width = outerRadius - (int)psp.InnerRadius;
t2.X = -t2.Width;
using (Brush br = background.GetBrush(t2))
g.FillPath(br, path);
}
if (renderShading == true)
RenderShading(g, psp, path);
}
path.Transform(g.Transform);
if (rgn == null)
rgn = new Region(path);
if (border.IsDisplayable == true)
{
path.Reset();
using (Pen pen = new Pen(border.LineColor, Dpi.Width(border.LineWidth)))
{
if (border.LinePattern != LinePattern.NotSet)
pen.DashStyle = (DashStyle)border.LinePattern;
path.AddLine(ppt2, ppt2);
path.AddArc(r, -angle / 2, angle);
path.AddLine(ppt1, ppt1);
g.DrawPath(pen, path);
}
}
chartControl.DoPostRenderSliceEvent(
g, path, pieChart, this, psp, SliceRenderType.ExtentWhitespace);
}
}
}
g.Restore(gs);
}
#endregion
#region RenderSliceExtent
private void RenderSliceExtent(Graphics g, ref Region rgn,
PieChart pieChart, PieSeriesPoint psp, int outerRadius, int innerRadius,
bool renderShading, SliceRenderType renderType, ChartSliceVisualStyle sstyle)
{
if (outerRadius - innerRadius > 0)
{
GraphicsState gs = g.Save();
g.TranslateTransform((float)psp.SliceCenter.X, (float)psp.SliceCenter.Y);
g.RotateTransform((float)(psp.CenterAngle) % 360);
Rectangle r = new Rectangle();
r.X = r.Y = -outerRadius;
r.Width = r.Height = (outerRadius * 2);
float angle = (float)GetSweepAngle(psp);
using (GraphicsPath path = new GraphicsPath())
{
path.AddArc(r, -angle / 2, angle);
if (psp.InnerRadius == 0)
{
path.AddLine(Point.Empty, Point.Empty);
}
else
{
Rectangle t = new Rectangle();
t.X = t.Y = -innerRadius;
t.Width = t.Height = innerRadius * 2;
path.AddArc(t, angle / 2, -angle);
}
path.CloseFigure();
ChartControl chartControl = ChartControl;
if (chartControl.DoPreRenderSliceEvent(g, path, pieChart, this, psp, renderType) == false)
{
if (psp.IsEmpty == false)
{
if (sstyle.Background.IsDisplayable == true)
{
r = Rectangle.Ceiling(path.GetBounds());
if (psp.HasExtent == true)
{
switch (sstyle.ExtentFillRange)
{
case ExtentFillRange.ByRing:
r.Width = psp.PieRing.ExtentRadius - innerRadius;
break;
case ExtentFillRange.BySlice:
PieSeriesPointCollection spc = psp.Parent as PieSeriesPointCollection;
if (spc != null)
{
double sliceExtent = spc.PieMaxExtent / spc.PieOuterExtent;
int width = (int)(psp.OuterRadius - psp.InnerRadius);
int extentWidth = (int)(sliceExtent * width);
r.Width = extentWidth;
}
break;
}
}
using (Brush br = sstyle.Background.GetBrush(r))
g.FillPath(br, path);
}
}
if (sstyle.Border.IsDisplayable == true)
{
using (Pen pen = new Pen(
sstyle.Border.LineColor, Dpi.Width(sstyle.Border.LineWidth)))
{
if (sstyle.Border.LinePattern != LinePattern.NotSet)
pen.DashStyle = (DashStyle)sstyle.Border.LinePattern;
g.DrawPath(pen, path);
}
}
chartControl.DoPostRenderSliceEvent(g, path, pieChart, this, psp, renderType);
}
if (psp.IsEmpty == false)
{
if (renderShading == true)
RenderShading(g, psp, path);
}
path.Transform(g.Transform);
if (rgn == null)
rgn = new Region(path);
else
rgn.Union(path);
g.Restore(gs);
}
}
}
#region GetSweepAngle
private double GetSweepAngle(PieSeriesPoint psp)
{
double sweepAngle = psp.SweepAngleEx;
if (sweepAngle > 360)
sweepAngle %= 360;
return (sweepAngle);
}
#endregion
#endregion
#endregion
#region RenderSliceOtherSpace
private void RenderSliceOtherSpace(Graphics g,
ref Region rgn, PieChart pieChart, PieSeriesPoint psp,
int outerRadius, int innerRadius, ChartSliceVisualStyle sstyle)
{
if (outerRadius - innerRadius > 0)
{
GraphicsState gs = g.Save();
g.TranslateTransform((float)psp.SliceCenter.X, (float)psp.SliceCenter.Y);
g.RotateTransform((float)(psp.CenterAngle) % 360);
Rectangle r = new Rectangle();
r.X = r.Y = -outerRadius;
r.Width = r.Height = (outerRadius * 2);
float angle = (float)GetSweepAngle(psp);
Background background = sstyle.WhiteSpaceBackground;
ChartLineVisualStyle border = sstyle.WhiteSpaceBorder;
ChartControl chartControl = ChartControl;
using (GraphicsPath path = new GraphicsPath())
{
path.AddArc(r, -angle / 2, angle);
if (innerRadius == 0)
{
path.AddLine(Point.Empty, Point.Empty);
path.CloseFigure();
if (chartControl.DoPreRenderSliceEvent(
g, path, pieChart, this, psp, SliceRenderType.OtherWhitespace) == false)
{
Rectangle pb = Rectangle.Truncate(path.GetBounds());
if (background.IsEmpty == false)
{
using (Brush br = background.GetBrush(pb))
g.FillPath(br, path);
}
if (border.IsDisplayable == true)
{
using (Pen pen = new Pen(border.LineColor, Dpi.Width(border.LineWidth)))
{
if (border.LinePattern != LinePattern.NotSet)
pen.DashStyle = (DashStyle)border.LinePattern;
g.DrawPath(pen, path);
}
}
chartControl.DoPostRenderSliceEvent(
g, path, pieChart, this, psp, SliceRenderType.OtherWhitespace);
}
path.Transform(g.Transform);
if (rgn == null)
rgn = new Region(path);
else
rgn.Union(path);
}
else
{
int pcount = path.PointCount;
Rectangle t = new Rectangle();
t.X = t.Y = -innerRadius;
t.Width = t.Height = (innerRadius * 2);
PointF ppt1 = path.PathPoints[0];
PointF ppt2 = path.GetLastPoint();
path.AddArc(t, angle / 2, -angle);
path.CloseFigure();
if (chartControl.DoPreRenderSliceEvent(
g, path, pieChart, this, psp, SliceRenderType.OtherWhitespace) == false)
{
Rectangle pb = Rectangle.Ceiling(path.GetBounds());
if (background.IsEmpty == false)
{
using (Brush br = background.GetBrush(pb))
g.FillPath(br, path);
}
path.Transform(g.Transform);
if (rgn == null)
rgn = new Region(path);
else
rgn.Union(path);
if (border.IsDisplayable == true)
{
using (Pen pen = new Pen(border.LineColor, Dpi.Width(border.LineWidth)))
{
if (border.LinePattern != LinePattern.NotSet)
pen.DashStyle = (DashStyle)border.LinePattern;
path.Reset();
path.AddLine(ppt1, ppt1);
path.AddArc(t, -angle / 2, angle);
path.AddLine(ppt2, ppt2);
g.DrawPath(pen, path);
}
}
chartControl.DoPostRenderSliceEvent(
g, path, pieChart, this, psp, SliceRenderType.OtherWhitespace);
}
else
{
path.Transform(g.Transform);
if (rgn == null)
rgn = new Region(path);
else
rgn.Union(path);
}
}
}
g.Restore(gs);
}
}
#endregion
#region RenderShading
private void RenderShading(Graphics g, PieSeriesPoint psp, GraphicsPath path)
{
PieChart pieChart = Parent as PieChart;
int radius = (int)((psp.HasExtent == true) ? psp.ExtentRadius : psp.OuterRadius);
Rectangle r = new Rectangle();
r.Inflate(radius, radius);
Region clip = g.Clip;
g.SetClip(path, CombineMode.Intersect);
using (GraphicsPath path2 = new GraphicsPath())
{
path2.AddEllipse(r);
PathGradientBrush pbr = new PathGradientBrush(path2);
ColorBlend cb = new ColorBlend(3);
Color[] colors = new Color[3];
colors[0] = Color.FromArgb(100, Color.Black);
colors[1] = Color.Transparent;
colors[2] = Color.Transparent;
cb.Colors = colors;
float[] cp = new float[3];
cp[0] = 0f;
cp[1] = .07f;
cp[2] = 1f;
cb.Positions = cp;
pbr.InterpolationColors = cb;
g.FillEllipse(pbr, r);
}
g.Clip = clip;
}
#endregion
#region RenderReferenceLines
private void RenderReferenceLines(Graphics g, PieChart pieChart,
PieSeriesPointCollection spc, PieSeriesPoint psp, bool onTop)
{
if (spc.PieOuterExtent > 0)
{
ISeriesPointContainer ispc = spc.Parent;
if (ispc != null)
{
if (psp.ReferenceLineDisplayOnTopEx == onTop)
{
PieRefLineDisplayMode mode = psp.ReferenceLineDisplayModeEx;
if ((mode != PieRefLineDisplayMode.NotSet) &&
((mode & PieRefLineDisplayMode.None) != PieRefLineDisplayMode.None))
{
ChartSliceVisualStyle sstyle = psp.GetEffectiveSliceStyle(pieChart, this);
int width = psp.OuterRadius - psp.InnerRadius;
float start = (float)psp.StartAngleEx;
float sweep = (float)psp.SweepAngleEx;
if ((mode & PieRefLineDisplayMode.ExtentAverage) == PieRefLineDisplayMode.ExtentAverage)
{
int avg = (int)((spc.PieAverageExtent / spc.PieOuterExtent) * width);
RenderReferenceLine(g, psp, start, sweep,
avg + psp.InnerRadius, sstyle.ExtentAverageRefLineStyle);
}
if ((mode & PieRefLineDisplayMode.ExtentMinimum) == PieRefLineDisplayMode.ExtentMinimum)
{
int min = (int)((spc.PieMinExtent / spc.PieOuterExtent) * width);
RenderReferenceLine(g, psp, start, sweep,
min + psp.InnerRadius, sstyle.ExtentMinRefLineStyle);
}
if ((mode & PieRefLineDisplayMode.ExtentMaximum) == PieRefLineDisplayMode.ExtentMaximum)
{
int max = (int)((spc.PieMaxExtent / spc.PieOuterExtent) * width);
RenderReferenceLine(g, psp, start, sweep,
max + psp.InnerRadius, sstyle.ExtentMaxRefLineStyle);
}
if ((mode & PieRefLineDisplayMode.ExtentOuterRadius) == PieRefLineDisplayMode.ExtentOuterRadius)
{
RenderReferenceLine(g, psp, start, sweep,
psp.OuterRadius, sstyle.ExtentOuterRefLineStyle);
}
if ((mode & PieRefLineDisplayMode.UserDefined) == PieRefLineDisplayMode.UserDefined)
{
List lines = psp.ReferenceLinesEx;
if (lines.Count > 0)
{
foreach (PieReferenceLine line in lines)
{
if (line.Visible == true)
{
double radius = (line.Value / spc.PieOuterExtent) * width;
RenderReferenceLine(g, psp, start, sweep,
radius + psp.InnerRadius, line.EffectiveStyle);
}
}
}
}
}
}
}
}
}
#region RenderReferenceLine
private void RenderReferenceLine(Graphics g, PieSeriesPoint psp,
float start, float sweep, double radius, PieReferenceLineVisualStyle rstyle)
{
if (radius > 0)
{
if (rstyle.IsDisplayable == true)
{
Rectangle r = new Rectangle();
r.Location = psp.SliceCenter;
r.Inflate((int)radius, (int)radius);
using (Pen pen = new Pen(rstyle.LineColor, rstyle.LineWidth))
{
if (rstyle.LinePattern != LinePattern.NotSet)
pen.DashStyle = (DashStyle)rstyle.LinePattern;
g.DrawArc(pen, r, start, sweep);
}
}
}
}
#endregion
#endregion
#region RenderSliceFigure
private void RenderSliceFigure(Graphics g, Region rgn,
PieChart pieChart, PieSeriesPoint psp, ChartSliceVisualStyle sstyle)
{
object figure = sstyle.GetFigure();
if (figure != null)
{
double radius = GetFigureRadius(psp, sstyle);
double centerAngle = psp.CenterAngle;
double angle = GetImageAngle(psp, centerAngle, sstyle);
Point pt = psp.GetRayEndPoint(angle, radius);
Size srcSize = sstyle.GetFigureSizeEx(g);
srcSize.Height = Dpi.DescaleHeight(srcSize.Height);
srcSize.Width = Dpi.DescaleWidth(srcSize.Width);
Rectangle srcRect = new Rectangle(pt, Size.Empty);
Rectangle destRect = srcRect;
double scaleFactor = GetImageSideFactor(psp, radius, srcSize);
Size destSize = srcRect.Size;
if (scaleFactor != 1)
{
double dh = (double)srcSize.Width / srcSize.Height;
destSize = new Size((int)(dh * scaleFactor * 2), (int)scaleFactor * 2);
}
srcRect.Inflate(srcSize.Width / 2, srcSize.Height / 2);
destRect.Inflate(destSize.Width / 2, destSize.Height / 2);
bool scaleImage = (double.IsNaN(sstyle.ImageScale) == false);
Region clip = null;
if (scaleImage == false)
{
if (sstyle.ImageCropMode == SliceImageCropMode.ClipImage)
{
clip = g.Clip;
g.SetClip(rgn, CombineMode.Intersect);
}
else if (sstyle.ImageCropMode == SliceImageCropMode.HideImage)
{
if (destRect.Contains(srcRect) == false)
return;
}
destRect = srcRect;
}
psp.PathBounds = Rectangle.Union(psp.PathBounds, destRect);
GraphicsState gState = null;
if (sstyle.ImageAutoRotate == Tbool.True || sstyle.ImageRotation != 0)
{
gState = g.Save();
destRect.X = -(destRect.Width / 2);
destRect.Y = -(destRect.Height / 2);
g.TranslateTransform(pt.X, pt.Y);
if (sstyle.ImageAutoRotate == Tbool.True)
g.RotateTransform((float)angle);
if (sstyle.ImageRotation != 0)
g.RotateTransform((float)sstyle.ImageRotation);
}
if (scaleImage == true)
{
if (sstyle.ImageScale != 1)
{
int width = (int)(destRect.Width * sstyle.ImageScale);
int height = (int)(destRect.Height * sstyle.ImageScale);
destRect.X += (destRect.Width / 2);
destRect.Y += (destRect.Height / 2);
destRect.Size = Size.Empty;
destRect.Inflate(width / 2, height / 2);
}
}
if (sstyle.IsSymbolFigure == true)
RenderSliceSymbol(g, pieChart, (SymbolDef)figure, destRect, sstyle);
else
RenderSliceImage(g, (Image)figure, srcRect, destRect);
if (sstyle.ImageAutoRotate == Tbool.True || sstyle.ImageRotation != 0)
g.Restore(gState);
if (clip != null)
g.Clip = clip;
}
}
#region GetImageAngle
private double GetImageAngle(
PieSeriesPoint psp, double centerAngle, ChartSliceVisualStyle sstyle)
{
double offset = sstyle.ImageAngleOffset;
if (offset == 0)
return (centerAngle);
if ((uint)offset < 1)
return (centerAngle + offset * psp.SweepAngleEx);
return (centerAngle + offset);
}
#endregion
#region GetImageSideFactor
///
/// Calculates the max 'a' side of the image.
///
///
///
///
///
private double GetImageSideFactor(
PieSeriesPoint psp, double radius, Size size)
{
double maxRadius = GetMaxImageRadius(psp, radius);
double scaleFactor = (double)(size.Width * size.Width) / (size.Height * size.Height);
double side = Math.Sqrt((maxRadius * maxRadius) / (scaleFactor + 1));
return (side);
}
#region GetMaxImageRadius
///
/// Gets the max radius of a circle image at the given ray radius.
///
///
///
///
private double GetMaxImageRadius(PieSeriesPoint psp, double radius)
{
double angle = psp.SweepAngleEx / 2;
if (angle >= 90)
return (radius);
double radians = MathHelper.ToRadians(angle);
double maxRad = radius * Math.Sin(radians);
//double dr = (int)(radius * radius);
//double maxRad = 2 * dr - 2 * (dr * Math.Cos(radians));
//maxRad = Math.Sqrt(maxRad);
return (maxRad);
}
#endregion
#endregion
#region GetFigureRadius
private double GetFigureRadius(PieSeriesPoint psp, ChartSliceVisualStyle sstyle)
{
double radius = psp.OuterRadius - psp.InnerRadius;
double extentRadius = psp.InnerRadius + radius * psp.SliceExtent;
double innerRadius = GetAnchorRadius(psp, extentRadius, sstyle.ImageInnerRadiusAnchor);
double outerRadius = GetAnchorRadius(psp, extentRadius, sstyle.ImageOuterRadiusAnchor);
double delta = outerRadius - innerRadius;
if (delta == 0)
delta = radius;
if (Math.Abs(sstyle.ImageRadiusOffset) <= 1)
return (innerRadius + delta * sstyle.ImageRadiusOffset);
else
return (innerRadius + sstyle.ImageRadiusOffset * (delta > 0 ? 1 : -1));
}
#region GetAnchorRadius
private double GetAnchorRadius(
PieSeriesPoint psp, double extentRadius, SliceImageRadiusAnchor anchor)
{
switch (anchor)
{
case SliceImageRadiusAnchor.ExtentRadius:
return (extentRadius);
case SliceImageRadiusAnchor.InnerRadius:
return (psp.InnerRadius);
case SliceImageRadiusAnchor.OuterRadius:
return (psp.OuterRadius);
}
return (psp.InnerRadius);
}
#endregion
#endregion
#region RenderSliceSymbol
private void RenderSliceSymbol(Graphics g,
PieChart pieChart, SymbolDef symDef, Rectangle destRect, ChartSliceVisualStyle sstyle)
{
TextRenderingHint hint = g.TextRenderingHint;
g.TextRenderingHint = TextRenderingHint.AntiAlias;
if (double.IsNaN(sstyle.ImageScale) == true)
{
using (StringFormat sf = new StringFormat())
{
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
using (Brush br = new SolidBrush(symDef.SymbolColor))
g.DrawString(symDef.SymbolRealized, symDef.SymbolFont, br, destRect, sf);
}
}
else
{
float n = Math.Max(destRect.Width, destRect.Height);
destRect.Y += Dpi.Width4;
if (_SymFontScale <= 0)
{
_ScaleSymDef.SymbolSize = n;
_ScaleSymDef.SymbolSet = symDef.SymbolSet;
_SymFontScale = n / _ScaleSymDef.SymbolFont.Height * .8f;
}
float fn = _SymFontScale * n;
_ScaleSymDef.SymbolSize = fn;
_ScaleSymDef.SymbolSet = symDef.SymbolSet;
using (StringFormat sf = new StringFormat())
{
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
using (Brush br = new SolidBrush(symDef.SymbolColor))
g.DrawString(symDef.SymbolRealized, _ScaleSymDef.SymbolFont, br, destRect, sf);
}
}
g.TextRenderingHint = hint;
}
#endregion
#region RenderSliceImage
private void RenderSliceImage(Graphics g, Image image, Rectangle srcRect, Rectangle destRect)
{
g.DrawImage(image, destRect,
new Rectangle(Point.Empty, srcRect.Size), GraphicsUnit.Pixel);
}
#endregion
#endregion
#region RenderSliceLabel
internal void RenderSliceLabel(Graphics g,
PieChart pieChart, PieSeriesPoint psp, ChartSliceVisualStyle sstyle)
{
if (IsInnerSliceLabelVisible(pieChart, psp) == true)
{
SliceInnerLabelVisualStyle lstyle = sstyle.SliceInnerLabelStyle;
psp.InnerLabelDisplayed = false;
string text = GetSliceLabel(pieChart, psp, true, psp.InnerSliceLabelEx);
ChartControl chartControl = ChartControl;
bool displayed = true;
if (chartControl.DoRenderSliceInnerLabelEvent(g, pieChart, psp, text, ref displayed) == false)
{
if (string.IsNullOrEmpty(text) == false)
{
double maxRadius = psp.OuterRadius;
if (lstyle.OuterRadiusOffsetBase == OuterRadiusOffsetBase.ExtentRadius)
maxRadius = psp.InnerRadius + (psp.OuterRadius - psp.InnerRadius) * psp.SliceExtent;
SliceLabelOrientation orientation = psp.InnerLabelOrientationEx;
switch (orientation)
{
case SliceLabelOrientation.Adaptive:
psp.InnerLabelDisplayed =
RenderAdaptiveLabel(g, pieChart, psp, maxRadius, text, lstyle);
break;
case SliceLabelOrientation.Custom:
psp.InnerLabelDisplayed =
RenderCustomLabel(g, pieChart, psp, maxRadius, text, lstyle);
break;
case SliceLabelOrientation.Parallel:
psp.InnerLabelDisplayed =
RenderParallelLabel(g, pieChart, psp, maxRadius, text, lstyle);
break;
case SliceLabelOrientation.Perpendicular:
psp.InnerLabelDisplayed =
RenderPerpendicularLabel(g, pieChart, psp, maxRadius, text, lstyle);
break;
default:
psp.InnerLabelDisplayed =
RenderHorizontalLabel(g, pieChart, psp, maxRadius, text, lstyle);
break;
}
}
}
}
else
{
psp.LastPathBounds = Rectangle.Empty;
}
}
#region IsInnerSliceLabelVisible
private bool IsInnerSliceLabelVisible(PieChart pieChart, PieSeriesPoint psp)
{
switch (psp.SliceLabelDisplayModeEx)
{
case SliceLabelDisplayMode.NotSet:
case SliceLabelDisplayMode.Inner:
case SliceLabelDisplayMode.InnerAndOuter:
case SliceLabelDisplayMode.InnerXorOuter:
return (IsLabelDisplayed(pieChart, psp));
}
return (false);
}
#region IsLabelDisplayed
internal bool IsLabelDisplayed(PieChart pieChart, PieSeriesPoint psp)
{
SliceLabelVisibility labVis = psp.SliceLabelVisibilityEx;
if ((labVis == SliceLabelVisibility.NotSet) ||
(labVis & SliceLabelVisibility.Always) == SliceLabelVisibility.Always)
return (true);
if ((labVis & SliceLabelVisibility.Never) == SliceLabelVisibility.Never)
return (false);
if (pieChart.ShowSliceLabelsOnEntry == true && pieChart.IsMouseOver == true)
return (true);
if ((labVis & SliceLabelVisibility.SliceMouseOver) == SliceLabelVisibility.SliceMouseOver)
{
if (psp == pieChart.HitPsp)
return (true);
}
if (((labVis & SliceLabelVisibility.SelectionModeMouseOver) == SliceLabelVisibility.SelectionModeMouseOver) ||
((labVis & SliceLabelVisibility.SliceMouseOver) == SliceLabelVisibility.SliceMouseOver))
{
if (IsHighLightedPsp(pieChart, psp) == true)
return (true);
}
if ((labVis & SliceLabelVisibility.SliceSelect) == SliceLabelVisibility.SliceSelect)
{
if (psp.IsSelected == true)
return (true);
}
if ((labVis & SliceLabelVisibility.SliceDetach) == SliceLabelVisibility.SliceDetach)
{
if (psp.IsDetached == true)
return (true);
}
return (false);
}
#endregion
#endregion
#region RenderAdaptiveLabel
internal bool RenderAdaptiveLabel(Graphics g, PieChart pieChart,
PieSeriesPoint psp, double maxRadius, string text, SliceInnerLabelVisualStyle lstyle)
{
double innerOffset = GetLableInnerOffset(psp, maxRadius, lstyle);
double outerOffset = GetLableOuterOffset(psp, maxRadius, lstyle);
if (outerOffset - innerOffset > 0)
{
double centerAngle = psp.CenterAngle % 360;
float fontAngle = 0;
if (lstyle.AutoOrientLabel != Tbool.False)
{
if (centerAngle < 180)
fontAngle += 180;
}
float c = (float)(Math.PI * outerOffset * 2);
if (c > 0)
{
RectangleF[] cBounds = GetAdaptiveCharBounds(g, psp, text, lstyle);
if (centerAngle < 180)
{
return (PaintAdaptiveFlipText(g, pieChart, psp,
outerOffset, innerOffset, fontAngle, text, cBounds, lstyle));
}
return (PaintAdaptiveText(g, pieChart, psp,
outerOffset, innerOffset, fontAngle, text, cBounds, lstyle));
}
}
return (false);
}
#region GetAdaptiveCharBounds
private RectangleF[] GetAdaptiveCharBounds(Graphics g,
PieSeriesPoint psp, string text, SliceInnerLabelVisualStyle lstyle)
{
RectangleF[] charBounds = psp.CharBounds;
if (charBounds == null || charBounds.Length < text.Length)
{
charBounds = new RectangleF[text.Length];
int n = (int)Math.Ceiling((double)text.Length / 32);
using (StringFormat sf = new StringFormat())
{
sf.FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.MeasureTrailingSpaces;
for (int i = 0; i < text.Length; i += 32)
{
int len = 32;
if (i + len > text.Length)
len = text.Length - i;
MeasureAdaptiveText(g, sf, text, i, len, charBounds, lstyle);
}
}
psp.CharBounds = charBounds;
}
return (charBounds);
}
#region MeasureAdaptiveText
private void MeasureAdaptiveText(Graphics g, StringFormat sf,
string text, int index, int len, RectangleF[] charBounds, SliceInnerLabelVisualStyle lstyle)
{
CharacterRange[] crs = new CharacterRange[len];
string s = text.Substring(index, len);
for (int j = 0; j < len; j++)
crs[j] = new CharacterRange(j, 1);
sf.SetMeasurableCharacterRanges(crs);
Rectangle r = new Rectangle(0, 0, 5000, 5000);
Region[] rgns = g.MeasureCharacterRanges(s, lstyle.Font, r, sf);
for (int i = 0; i < len; i++)
charBounds[index + i] = rgns[i].GetBounds(g);
if (index > 0)
{
float dx = charBounds[index - 1].X + charBounds[index - 1].Width - charBounds[index].X;
for (int i = 0; i < len; i++)
charBounds[index + i].X += dx;
}
}
#endregion
#endregion
#region PaintAdaptiveText
private bool PaintAdaptiveText(Graphics g, PieChart pieChart,
PieSeriesPoint psp, double outerOffset, double innerOffset,
float fontAngle, string text, RectangleF[] cBounds, SliceInnerLabelVisualStyle lstyle)
{
bool clipped;
List wpsLines =
GetAdaptiveLines(psp, outerOffset, innerOffset, text, cBounds, out clipped, lstyle);
if (wpsLines != null && wpsLines.Count > 0)
{
psp.InnerLabelClipped = clipped;
if (clipped == true)
{
if (psp.SliceLabelCropModeEx == SliceLabelCropMode.Hide)
return (false);
}
double startAngle;
double endAngle;
NormalizeSliceAngles(psp, out startAngle, out endAngle);
double startRadians = MathHelper.ToRadians((float)(startAngle + lstyle.AngleMargin));
double endRadians = MathHelper.ToRadians((float)(endAngle - lstyle.AngleMargin));
if (startRadians < endRadians)
{
TextRenderingHint renderingHint = g.TextRenderingHint;
g.TextRenderingHint = TextRenderingHint.AntiAlias;
float height = cBounds[0].Height;
float halfHeight = height / 2;
using (Brush br = new SolidBrush(lstyle.TextColor))
{
foreach (WordPosLine wpsLine in wpsLines)
{
double offset = wpsLine.Offset;
if (wpsLine.Wps.Count > 0)
{
double dx = GetAlignmentOffset(wpsLine, cBounds, lstyle);
double xRadians = startRadians + (dx / offset);
foreach (WordPos wp in wpsLine.Wps)
{
for (int j = 0; j < wp.Text.Length; j++)
{
RectangleF tf = cBounds[wp.Index + j];
float cRadians = (float)(tf.Width / offset);
if (xRadians + cRadians > (float)endRadians + .5)
break;
xRadians += (cRadians / 2);
float z = (float)(offset - halfHeight);
float y = (float)(psp.SliceCenter.Y + z * Math.Sin(xRadians));
float x = (float)(psp.SliceCenter.X + z * Math.Cos(xRadians));
int n = (int)Math.Max(tf.Width, tf.Height);
GraphicsState gState = g.Save();
g.TranslateTransform(x, y);
g.RotateTransform(90 + (float)MathHelper.ToDegrees((float)xRadians));
g.DrawString(wp.Text[j].ToString(), lstyle.Font, br, -tf.Width/2, -tf.Height / 2);
g.Restore(gState);
xRadians += (cRadians / 2);
}
if (wp.Index + wp.Length < cBounds.Length)
xRadians += cBounds[wp.Index + wp.Length].Width / offset;
}
}
}
}
if ((outerOffset > psp.OuterRadius) ||
(wpsLines[wpsLines.Count - 1].Offset < psp.InnerRadius))
{
Rectangle t = GetBoundingArcRect(
psp, startAngle, endAngle, outerOffset + height);
psp.PathBounds = Rectangle.Union(psp.PathBounds, t);
if (psp.PathBounds.Equals(psp.LastPathBounds) == false)
{
psp.LastPathBounds = psp.PathBounds;
InvalidateRender(psp.PathBounds);
}
}
g.TextRenderingHint = renderingHint;
return (true);
}
}
return (false);
}
#region GetBoundingArcRect
private Rectangle GetBoundingArcRect(
PieSeriesPoint psp, double startAngle, double endAngle, double outerOffset)
{
Rectangle r = new Rectangle(psp.SliceCenter, Size.Empty);
double n = startAngle;
n += 89;
n = (int)(n / 90) * 90;
while (n <= endAngle)
{
UpdateAngleBounds(psp, n, outerOffset, ref r);
n += 90;
}
if (startAngle % 90 != 0)
UpdateAngleBounds(psp, startAngle, outerOffset, ref r);
if (endAngle % 90 != 0)
UpdateAngleBounds(psp, endAngle, outerOffset, ref r);
return (r);
}
#region UpdateAngleBounds
private void UpdateAngleBounds(PieSeriesPoint psp,
double angle, double outerOffset, ref Rectangle r)
{
Point pts = psp.GetRayEndPoint(angle, outerOffset);
if (pts.X < r.X)
{
r.Width += (r.X - pts.X);
r.X = pts.X;
}
else if (pts.X > r.Right)
{
r.Width += (pts.X - r.Right);
}
if (pts.Y < r.Y)
{
r.Height += (r.Y - pts.Y);
r.Y = pts.Y;
}
else if (pts.Y > r.Bottom)
{
r.Height += (pts.Y - r.Bottom);
}
}
#endregion
#endregion
#region GetAdaptiveLines
private List GetAdaptiveLines(
PieSeriesPoint psp, double outerOffset, double innerOffset,
string text, RectangleF[] cBounds, out bool clipped, SliceInnerLabelVisualStyle lstyle)
{
if (psp.WordPosLines == null)
{
psp.WordPosLines =
GetAdaptiveLinesEx(psp, outerOffset, innerOffset, text, cBounds, out clipped, lstyle);
psp.InnerLabelClipped = clipped;
}
clipped = psp.InnerLabelClipped;
return (psp.WordPosLines);
}
#region GetAdaptiveLinesEx
private List GetAdaptiveLinesEx(
PieSeriesPoint psp, double outerOffset, double innerOffset, string text,
RectangleF[] cBounds, out bool clipped, SliceInnerLabelVisualStyle lstyle)
{
float height = cBounds[0].Height;
int maxLineCount = (int)((outerOffset - innerOffset) / height);
SliceLabelCropMode cropMode = psp.SliceLabelCropModeEx;
if (cropMode != SliceLabelCropMode.Hide)
maxLineCount = 1000;
if (lstyle.MaxLineCount >= 0)
{
if (maxLineCount > lstyle.MaxLineCount)
maxLineCount = lstyle.MaxLineCount;
}
List wpsLines = null;
if (maxLineCount > 0)
{
List wps = GetWordWidths(text, cBounds);
double offset = outerOffset;
bool middle = false;
switch (lstyle.Alignment)
{
case SliceLabelAlignment.MiddleLeft:
case SliceLabelAlignment.MiddleRight:
case SliceLabelAlignment.MiddleCenter:
offset = (outerOffset + innerOffset) / 2;
middle = true;
break;
case SliceLabelAlignment.InnerLeft:
case SliceLabelAlignment.InnerRight:
case SliceLabelAlignment.InnerCenter:
clipped = false;
while (offset > innerOffset)
{
List ilines = FitAdaptiveLines(psp,
offset, -height, cBounds, maxLineCount, wps, out clipped, lstyle);
if (ilines != null)
{
if (wpsLines == null || clipped == false)
wpsLines = ilines;
if (clipped == true)
break;
wps = GetWordWidths(text, cBounds);
offset -= height;
}
else
{
break;
}
}
return (wpsLines);
default:
List lines = FitAdaptiveLines(psp,
offset, -height, cBounds, maxLineCount, wps, out clipped, lstyle);
return (lines);
}
for (int i = 0; i < maxLineCount; i++)
{
List lines = FitAdaptiveLines(psp,
offset, -height, cBounds, (i + 1), wps, out clipped, lstyle);
if (lines != null)
{
if (clipped == false)
return (lines);
wpsLines = lines;
wps = GetWordWidths(text, cBounds);
if ((middle == false) || (i % 2 == 0))
offset += height;
}
else
{
break;
}
}
}
clipped = false;
return (wpsLines);
}
#region FitAdaptiveLines
private List FitAdaptiveLines(
PieSeriesPoint psp, double offset, float height, RectangleF[] cBounds,
int count, List wps, out bool clipped, SliceInnerLabelVisualStyle lstyle)
{
List wpsLines = new List();
int wordIndex = 0;
while (wpsLines.Count < count)
{
int lineWordCount = 0;
double arcWidth = GetArcWidth(psp, offset - cBounds[0].Height / 4, lstyle);
if (arcWidth <= 0)
break;
WordPosLine wpsLine = new WordPosLine();
wpsLine.Offset = offset;
wpsLine.ArcWidth = arcWidth;
while (wordIndex < wps.Count)
{
WordPos wp = wps[wordIndex];
double wordLength = GetWordLength(wp, cBounds, lineWordCount);
if (wp.Text.Equals("\n") == true)
{
if (wpsLine.Wps.Count == 0)
wpsLine.Wps.Add(wp);
wordIndex++;
break;
}
if (wpsLine.ArcWidth - (wpsLine.LineWidth + wordLength) < 0)
{
if (lineWordCount == 0 || wpsLines.Count + 1 == count)
{
wpsLine = FitNonBrokenLine(psp, offset,
count - wpsLines.Count, wpsLines, wpsLine, wp, cBounds, height, out clipped, lstyle);
if (clipped == true || wpsLine == null)
{
if (wpsLine != null && wpsLine.Wps.Count > 0)
wpsLines.Add(wpsLine);
return (wpsLines);
}
offset = wpsLine.Offset;
}
else
{
break;
}
}
else
{
wpsLine.LineWidth += wordLength;
wpsLine.Wps.Add(wp);
}
wordIndex++;
lineWordCount++;
}
if (wpsLine.Wps.Count > 0)
{
offset = wpsLine.Offset;
wpsLines.Add(wpsLine);
}
if (wordIndex >= wps.Count)
break;
offset += height;
}
clipped = (wordIndex < wps.Count);
return (wpsLines);
}
#region FitNonBrokenLine
private WordPosLine FitNonBrokenLine(PieSeriesPoint psp,
double offset, int maxCount, List wpsLines, WordPosLine wpsLine,
WordPos wp, RectangleF[] cBounds, float height, out bool clipped, SliceInnerLabelVisualStyle lstyle)
{
double length;
int count = GetNonBrokenCount(wpsLine, wp, cBounds, out length);
clipped = true;
if (length == 0)
return (wpsLine);
int rindex = wp.Index;
while (count > 0)
{
WordPos wp2 = new WordPos();
wp2.Index = wp.Index;
wp2.Length = count;
wp2.Text = wp.Text.Substring((wp.Index - rindex), count);
wpsLine.Wps.Add(wp2);
wp.Index += count;
wp.Length -= count;
if (--maxCount <= 0)
break;
if (wp.Length <= 0)
break;
wpsLines.Add(wpsLine);
offset += height;
wpsLine = new WordPosLine();
wpsLine.Offset = offset;
wpsLine.ArcWidth = GetArcWidth(psp, offset - cBounds[0].Height / 4, lstyle);
count = GetNonBrokenCount(wpsLine, wp, cBounds, out length);
}
clipped = (wp.Length > 0);
return (wpsLine);
}
#region GetNonBrokenCount
private int GetNonBrokenCount(
WordPosLine wpsLine, WordPos wp, RectangleF[] cBounds, out double length)
{
length = 0;
if (wpsLine.Wps.Count > 0 && wp.Index > 0)
{
double width = cBounds[wp.Index - 1].Width;
if (wpsLine.ArcWidth - (wpsLine.LineWidth + width) < 0)
return (0);
wpsLine.LineWidth += width;
length += width;
}
for (int i = 0; i < wp.Length; i++)
{
double width = cBounds[wp.Index + i].Width;
if (wpsLine.ArcWidth - (wpsLine.LineWidth + width) < 0)
return (i);
wpsLine.LineWidth += width;
length += width;
}
return (wp.Length);
}
#endregion
#endregion
#endregion
#endregion
#endregion
#endregion
#region PaintAdaptiveFlipText
private bool PaintAdaptiveFlipText(Graphics g, PieChart pieChart,
PieSeriesPoint psp, double outerOffset, double innerOffset, float fontAngle,
string text, RectangleF[] cBounds, SliceInnerLabelVisualStyle lstyle)
{
bool clipped;
List wpsLines =
GetAdaptiveFlipLines(psp, outerOffset, innerOffset, text, cBounds, out clipped, lstyle);
if (wpsLines != null && wpsLines.Count > 0)
{
if (clipped == true)
{
if (psp.SliceLabelCropModeEx == SliceLabelCropMode.Hide)
return (false);
}
double startAngle;
double endAngle;
NormalizeSliceAngles(psp, out startAngle, out endAngle);
double endRadians = MathHelper.ToRadians(startAngle + lstyle.AngleMargin);
double startRadians = MathHelper.ToRadians(endAngle - lstyle.AngleMargin);
float height = cBounds[0].Height;
float halfHeight = height / 2;
TextRenderingHint renderingHint = g.TextRenderingHint;
g.TextRenderingHint = TextRenderingHint.AntiAlias;
using (Brush br = new SolidBrush(lstyle.TextColor))
{
foreach (WordPosLine wpsLine in wpsLines)
{
double offset = wpsLine.Offset;
if (wpsLine.Wps.Count > 0)
{
double dx = GetAlignmentOffset(wpsLine, cBounds, lstyle);
double xRadians = startRadians - dx / offset;
foreach (WordPos wp in wpsLine.Wps)
{
for (int j = 0; j < wp.Text.Length; j++)
{
if (xRadians < (float)endRadians)
break;
RectangleF tf = cBounds[wp.Index + j];
float cRadians = (float)(tf.Width / offset);
xRadians -= (cRadians / 2);
float z = (float)(offset - halfHeight);
float y = (float)((float)psp.SliceCenter.Y + z * Math.Sin(xRadians));
float x = (float)((float)psp.SliceCenter.X + z * Math.Cos(xRadians));
GraphicsState gState = g.Save();
g.TranslateTransform(x, y);
g.RotateTransform(-90 + (float)MathHelper.ToDegrees(xRadians));
g.DrawString(wp.Text[j].ToString(), lstyle.Font, br, -tf.Width / 2, -tf.Height / 2);
g.Restore(gState);
xRadians -= (cRadians / 2);
}
if (wp.Index + wp.Length < cBounds.Length)
xRadians -= cBounds[wp.Index + wp.Length].Width / offset;
}
}
}
}
if ((outerOffset > psp.OuterRadius) ||
(wpsLines.Count > 0 && wpsLines[0].Offset < psp.InnerRadius))
{
Rectangle t = GetBoundingArcRect(
psp, startAngle, endAngle, outerOffset + height);
psp.PathBounds = Rectangle.Union(psp.PathBounds, t);
if (psp.PathBounds.Equals(psp.LastPathBounds) == false)
{
psp.LastPathBounds = psp.PathBounds;
InvalidateRender(psp.PathBounds);
}
}
g.TextRenderingHint = renderingHint;
return (true);
}
return (false);
}
#region GetAdaptiveFlipLines
private List GetAdaptiveFlipLines(
PieSeriesPoint psp, double outerOffset, double innerOffset,
string text, RectangleF[] cBounds, out bool clipped, SliceInnerLabelVisualStyle lstyle)
{
if (psp.WordPosLines == null)
{
psp.WordPosLines =
GetAdaptiveFlipLinesEx(psp, outerOffset, innerOffset, text, cBounds, out clipped, lstyle);
psp.InnerLabelClipped = clipped;
}
clipped = psp.InnerLabelClipped;
return (psp.WordPosLines);
}
#region GetAdaptiveFlipLinesEx
private List GetAdaptiveFlipLinesEx(
PieSeriesPoint psp, double outerOffset, double innerOffset,
string text, RectangleF[] cBounds, out bool clipped, SliceInnerLabelVisualStyle lstyle)
{
float height = cBounds[0].Height;
int maxLineCount = (int)Math.Abs((outerOffset - innerOffset) / height);
SliceLabelCropMode cropMode = psp.SliceLabelCropModeEx;
if (cropMode == SliceLabelCropMode.NoAction)
maxLineCount = 1000;
if (lstyle.MaxLineCount >= 0)
{
if (maxLineCount > lstyle.MaxLineCount)
maxLineCount = lstyle.MaxLineCount;
}
List wpsLines = null;
clipped = false;
if (maxLineCount > 0)
{
List wps = GetWordWidths(text, cBounds);
double offset = outerOffset;
bool middle = false;
switch (lstyle.Alignment)
{
case SliceLabelAlignment.MiddleLeft:
case SliceLabelAlignment.MiddleRight:
case SliceLabelAlignment.MiddleCenter:
offset = (outerOffset + innerOffset) / 2;
height = -height;
middle = true;
break;
case SliceLabelAlignment.InnerLeft:
case SliceLabelAlignment.InnerRight:
case SliceLabelAlignment.InnerCenter:
clipped = false;
offset = innerOffset;
while (offset < outerOffset)
{
List ilines = FitAdaptiveLines(psp,
offset, height, cBounds, maxLineCount, wps, out clipped, lstyle);
if (ilines != null && ilines.Count > 0)
return (ilines);
wps = GetWordWidths(text, cBounds);
offset += height;
}
return (wpsLines);
default:
height = -height;
break;
}
for (int i = 0; i < maxLineCount; i++)
{
List lines = FitAdaptiveLines(psp,
offset, Math.Abs(height), cBounds, i + 1, wps, out clipped, lstyle);
if (lines != null)
{
if (clipped == false)
return (lines);
wpsLines = lines;
wps = GetWordWidths(text, cBounds);
if ((middle == false) || (i % 2 == 0))
offset += height;
}
else
{
break;
}
}
}
return (wpsLines);
}
#endregion
#endregion
#endregion
#region NormalizeSliceAngles
private void NormalizeSliceAngles(
PieSeriesPoint psp, out double startAngle, out double endAngle)
{
startAngle = psp.StartAngleEx;
endAngle = psp.StartAngleEx + psp.SweepAngleEx;
if (startAngle > endAngle)
{
double temp = startAngle;
startAngle = endAngle;
endAngle = temp;
}
}
#endregion
#region GetArcWidth
private double GetArcWidth(
PieSeriesPoint psp, double offset, SliceInnerLabelVisualStyle lstyle)
{
double margin = lstyle.AngleMargin * 2;
double sweep = Math.Abs(psp.SweepAngleEx);
double width = offset * MathHelper.ToRadians(sweep - margin);
return (width);
}
#endregion
#region GetWordLength
private double GetWordLength(
WordPos wp, RectangleF[] cBounds, int lineWordCount)
{
double length = 0;
if (lineWordCount > 0)
length += cBounds[wp.Index - 1].Width;
for (int i = 0; i < wp.Length; i++)
length += cBounds[wp.Index + i].Width;
return (length);
}
#endregion
#region GetWordWidths
private List GetWordWidths(string text, RectangleF[] cBounds)
{
List wps = new List();
int index = 0;
while (index < text.Length)
{
int length = GetNextWord(text, ref index);
if (length > 0)
{
WordPos wp = new WordPos();
wp.Index = index;
wp.Length = length;
wp.Text = text.Substring(index, length);
wps.Add(wp);
index += length;
}
}
return (wps);
}
#region GetNextWord
private int GetNextWord(string text, ref int index)
{
while (index < text.Length)
{
if (text[index] == '\n')
return (1);
if (Char.IsWhiteSpace(text[index]) == false)
break;
index++;
}
int eindex = index;
while (eindex < text.Length)
{
if (Char.IsWhiteSpace(text[eindex]) == true)
break;
eindex++;
}
return (eindex - index);
}
#endregion
#endregion
#region GetAlignmentOffset
private double GetAlignmentOffset(
WordPosLine wpsLine, RectangleF[] tbounds, SliceInnerLabelVisualStyle lstyle)
{
switch (lstyle.Alignment)
{
case SliceLabelAlignment.OuterRight:
case SliceLabelAlignment.MiddleRight:
case SliceLabelAlignment.InnerRight:
return (wpsLine.ArcWidth - wpsLine.LineWidth);
case SliceLabelAlignment.OuterCenter:
case SliceLabelAlignment.MiddleCenter:
case SliceLabelAlignment.InnerCenter:
return ((wpsLine.ArcWidth - wpsLine.LineWidth) / 2);
default:
return (0);
}
}
#endregion
#endregion
#region RenderCustomLabel
internal bool RenderCustomLabel(Graphics g, PieChart pieChart,
PieSeriesPoint psp, double maxRadius, string text,SliceInnerLabelVisualStyle lstyle)
{
double angle = GetCustomLabelAngle(psp, lstyle);
double offset = GetCustomLableOffset(psp, maxRadius, lstyle);
Rectangle r = GetCustomLabelRect(psp, maxRadius, angle, offset, lstyle);
angle += lstyle.CustomFontAngle;
if (lstyle.AutoRotate != Tbool.True)
angle -= (psp.CenterAngle - 90);
angle = (angle + 360000) % 360;
double fontAngle = 0;
if (lstyle.AutoOrientLabel != Tbool.False)
{
if (angle < 180)
fontAngle += 180;
}
r = GetSliceLabelBounds(g, pieChart, psp, text, r, lstyle);
if (r.Width > 0 && r.Height > 0)
{
GraphicsState gState = g.Save();
TextRenderingHint renderingHint = g.TextRenderingHint;
g.TextRenderingHint = TextRenderingHint.AntiAlias;
using (StringFormat sf = new StringFormat())
{
lstyle.GetStringFormatFlags(sf, angle < 180);
g.TranslateTransform(r.X, r.Y);
g.RotateTransform((float)(angle + fontAngle + 90));
r.X = -(int)(r.Width / 2);
r.Y = -(int)(r.Height / 2);
if (lstyle.Background.IsEmpty == false)
{
using (Brush br = lstyle.Background.GetBrush(r))
g.FillRectangle(br, r);
}
using (Brush br = new SolidBrush(lstyle.TextColor))
g.DrawString(text, lstyle.Font, br, r, sf);
if (ChartControl.DesignerHosted == true)
{
using (Pen pen = new Pen(Color.Plum))
{
pen.DashStyle = DashStyle.Dash;
g.DrawRectangle(pen, r);
}
}
}
g.Restore(gState);
return (true);
}
return (false);
}
#region GetCustomLabelRect
private Rectangle GetCustomLabelRect(PieSeriesPoint psp,
double maxRadius, double angle, double offset, SliceInnerLabelVisualStyle lstyle)
{
Rectangle r = new Rectangle();
r.Location = psp.GetRayEndPoint(angle, offset);
r.Width = (int)GetCustomLabelWidth(psp, offset, lstyle);
r.Height = (int)GetCustomLabelHeight(psp, maxRadius, offset, lstyle);
return (r);
}
#endregion
#region GetCustomLabelHeight
private double GetCustomLabelHeight(PieSeriesPoint psp,
double maxRadius, double offset, SliceInnerLabelVisualStyle lstyle)
{
double height = maxRadius - psp.InnerRadius;
if (double.IsNaN(lstyle.CustomHeight) == false)
{
if (Math.Abs(lstyle.CustomHeight) < 1)
return (lstyle.CustomHeight * height);
return (lstyle.CustomHeight);
}
return (height * .5);
}
#endregion
#region GetCustomLabelWidth
private double GetCustomLabelWidth(
PieSeriesPoint psp, double offset, SliceInnerLabelVisualStyle lstyle)
{
double width = GetArcWidth(psp, offset, lstyle);
if (double.IsNaN(lstyle.CustomWidth) == false)
{
if (Math.Abs(lstyle.CustomWidth) < 1)
return (lstyle.CustomWidth * width);
return (lstyle.CustomWidth);
}
return (width * .8);
}
#endregion
#region GetCustomLableOffset
private double GetCustomLableOffset(
PieSeriesPoint psp, double maxRadius, SliceInnerLabelVisualStyle lstyle)
{
double offset = lstyle.CustomRadiusOffset;
if (double.IsNaN(offset) == false)
{
if (Math.Abs(offset) < 1)
offset = (maxRadius - psp.InnerRadius) * offset;
return (psp.InnerRadius + offset);
}
return (psp.InnerRadius + (maxRadius - psp.InnerRadius) * .7);
}
#endregion
#region GetCustomLabelAngle
private double GetCustomLabelAngle(PieSeriesPoint psp, SliceInnerLabelVisualStyle lstyle)
{
double angle = (psp.StartAngleEx + psp.SweepAngleEx / 2) % 360;
if (double.IsNaN(lstyle.CustomAngleOffset) == false)
{
double offset = lstyle.CustomAngleOffset;
if (Math.Abs(offset) < 1)
angle += (psp.SweepAngleEx * offset);
else
angle += offset;
}
return (angle);
}
#endregion
#endregion
#region RenderParallelLabel
internal bool RenderParallelLabel(Graphics g, PieChart pieChart,
PieSeriesPoint psp, double maxRadius, string text, SliceInnerLabelVisualStyle lstyle)
{
double centerAngle = psp.CenterAngle % 360;
double innerOffset = GetLableInnerOffset(psp, maxRadius, lstyle);
double outerOffset = GetLableOuterOffset(psp, maxRadius, lstyle);
float fontAngle = 0;
if (lstyle.AutoOrientLabel != Tbool.False)
{
if (centerAngle >= 90 && centerAngle < 270)
fontAngle += 180;
}
Point lpt = psp.GetRayEndPoint(
centerAngle, (innerOffset + outerOffset) / 2);
Rectangle r = new Rectangle();
r.Location = lpt;
r.Width = (int)(outerOffset - innerOffset);
r.Height = (int)GetArcWidth(psp, innerOffset, lstyle);
r = GetSliceLabelBounds(g, pieChart, psp, text, r, lstyle);
if (r.Width > 0 && r.Height > 0)
{
GraphicsState gState = g.Save();
TextRenderingHint renderingHint = g.TextRenderingHint;
g.TextRenderingHint = TextRenderingHint.AntiAlias;
using (StringFormat sf = new StringFormat())
{
GetStringFormatFlags(lstyle, sf, fontAngle != 0);
r.X = -(int)(r.Width / 2);
r.Y = -(int)(r.Height / 2);
g.TranslateTransform(lpt.X, lpt.Y);
g.RotateTransform((float)centerAngle + fontAngle);
if (lstyle.Background.IsEmpty == false)
{
using (Brush br = lstyle.Background.GetBrush(r))
g.FillRectangle(br, r);
}
using (Brush br = new SolidBrush(lstyle.TextColor))
g.DrawString(text, lstyle.Font, br, r, sf);
}
g.Restore(gState);
return (true);
}
return (false);
}
#region GetStringFormatFlags
private void GetStringFormatFlags(
SliceInnerLabelVisualStyle lstyle, StringFormat sf, bool flip)
{
if (lstyle.AllowWrap == Tbool.False || lstyle.MaxLineCount == 1)
sf.FormatFlags |= StringFormatFlags.NoWrap;
sf.Trimming = StringTrimming.EllipsisCharacter;
switch (lstyle.Alignment)
{
case SliceLabelAlignment.OuterLeft:
sf.Alignment = (flip ? StringAlignment.Near : StringAlignment.Far); // Outer/inner
sf.LineAlignment = (flip ? StringAlignment.Far : StringAlignment.Near); // Left/right
break;
case SliceLabelAlignment.InnerLeft:
sf.Alignment = (flip ? StringAlignment.Far : StringAlignment.Near);
sf.LineAlignment = (flip ? StringAlignment.Far : StringAlignment.Near);
break;
case SliceLabelAlignment.MiddleLeft:
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = (flip ? StringAlignment.Far : StringAlignment.Near);
break;
case SliceLabelAlignment.OuterRight:
sf.Alignment = (flip ? StringAlignment.Near : StringAlignment.Far);
sf.LineAlignment = (flip ? StringAlignment.Near : StringAlignment.Far);
break;
case SliceLabelAlignment.InnerRight:
sf.Alignment = (flip ? StringAlignment.Far : StringAlignment.Near);
sf.LineAlignment = (flip ? StringAlignment.Near : StringAlignment.Far);
break;
case SliceLabelAlignment.MiddleRight:
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = (flip ? StringAlignment.Near : StringAlignment.Far);
break;
case SliceLabelAlignment.OuterCenter:
sf.Alignment = (flip ? StringAlignment.Near : StringAlignment.Far);
sf.LineAlignment = StringAlignment.Center;
break;
case SliceLabelAlignment.InnerCenter:
sf.Alignment = (flip ? StringAlignment.Far : StringAlignment.Near);
sf.LineAlignment = StringAlignment.Center;
break;
case SliceLabelAlignment.NotSet:
case SliceLabelAlignment.MiddleCenter:
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
break;
}
}
#endregion
#endregion
#region RenderPerpendicularLabel
internal bool RenderPerpendicularLabel(Graphics g, PieChart pieChart,
PieSeriesPoint psp, double maxRadius, string text, SliceInnerLabelVisualStyle lstyle)
{
double innerOffset = GetLableInnerOffset(psp, maxRadius, lstyle);
double outerOffset = GetLableOuterOffset(psp, maxRadius, lstyle);
if (outerOffset - innerOffset > 0)
{
double centerAngle = psp.CenterAngle % 360;
Point lpt = psp.GetRayEndPoint(centerAngle, outerOffset);
float fontAngle = 0;
if (lstyle.AutoOrientLabel != Tbool.False)
{
if (centerAngle < 180)
fontAngle += 180;
}
Rectangle r = GetInscribedRectP(psp, maxRadius,
centerAngle, outerOffset, innerOffset, lpt, lstyle);
r = GetSliceLabelBounds(g, pieChart, psp, text, r, lstyle);
if (r.Width > 0 && r.Height > 0)
{
GraphicsState gState = g.Save();
TextRenderingHint renderingHint = g.TextRenderingHint;
g.TextRenderingHint = TextRenderingHint.AntiAlias;
g.TranslateTransform(lpt.X, lpt.Y);
g.RotateTransform((float)((centerAngle + fontAngle + 90) % 360));
if (lstyle.Background.IsEmpty == false)
{
using (Brush br = lstyle.Background.GetBrush(r))
g.FillRectangle(br, r);
}
using (StringFormat sf = new StringFormat())
{
lstyle.GetStringFormatFlags(sf, (centerAngle < 180));
using (Brush br = new SolidBrush(lstyle.TextColor))
g.DrawString(text, lstyle.Font, br, r, sf);
}
g.Restore(gState);
return (true);
}
}
return (false);
}
#region GetInscribedRectP
private Rectangle GetInscribedRectP(PieSeriesPoint psp, double maxRadius,
double centerAngle, double outerOffset, double innerOffset, Point lpt, SliceInnerLabelVisualStyle lstyle)
{
if (psp.InscribedRectValid == false)
{
double sweepAngle = psp.SweepAngleEx / 2 - lstyle.AngleMargin;
double b = (innerOffset * Math.Tan(MathHelper.ToRadians(sweepAngle)));
double h = (outerOffset - innerOffset);
double maxb = Math.Sqrt(maxRadius * maxRadius - outerOffset * outerOffset);
if (b > maxb)
b = maxb;
h = (h / lstyle.Font.Height) * lstyle.Font.Height;
Rectangle r = new Rectangle();
r.Location = new Point((int)-b, (centerAngle < 180) ? (int)-h : 0);
r.Width = (int)(b * 2);
r.Height = (int)h;
psp.InscribedRect = r;
}
return (psp.InscribedRect);
}
#endregion
#endregion
#region RenderHorizontalLabel
internal bool RenderHorizontalLabel(Graphics g, PieChart pieChart,
PieSeriesPoint psp, double maxRadius, string text, SliceInnerLabelVisualStyle lstyle)
{
double innerOffset = GetLableInnerOffset(psp, maxRadius, lstyle);
Rectangle r = GetInscribedRectH(g,
psp, psp.CenterAngle % 360, maxRadius, innerOffset, lstyle);
r = GetSliceLabelBounds(g, pieChart, psp, text, r, lstyle);
if (r.Width > 0 && r.Height > 0)
{
if (lstyle.Background.IsDisplayable == true)
{
using (Brush br = lstyle.Background.GetBrush(r))
g.FillRectangle(br, r);
}
using (StringFormat sf = new StringFormat())
{
lstyle.GetStringFormatFlags(sf, false);
using (Brush br = new SolidBrush(lstyle.TextColor))
g.DrawString(text, lstyle.Font, br, r, sf);
}
if (ChartControl.DesignerHosted == true)
{
using (Pen pen = new Pen(Color.Plum))
{
pen.DashStyle = DashStyle.Dash;
g.DrawRectangle(pen, r);
}
}
return (true);
}
return (false);
}
#region GetInscribedRectH
private Rectangle GetInscribedRectH(Graphics g, PieSeriesPoint psp,
double centerAngle, double outerOffset, double innerOffset, SliceInnerLabelVisualStyle lstyle)
{
if (psp.InscribedRectValid == false)
{
double startAngle = psp.StartAngleEx % 360;
double endAngle = startAngle + psp.SweepAngleEx;
if (psp.SweepAngleEx > 180)
{
startAngle = centerAngle - 88;
endAngle = centerAngle + 88;
}
Rectangle r;
if (centerAngle > 270)
{
r = GetInscribedRectQuad4(g, psp,
startAngle, endAngle, centerAngle, outerOffset, innerOffset, lstyle);
}
else if (centerAngle > 180)
{
r = GetInscribedRectQuad3(g, psp,
startAngle, endAngle, centerAngle, outerOffset, innerOffset, lstyle);
}
else if (centerAngle > 90)
{
r = GetInscribedRectQuad2(g, psp,
startAngle, endAngle, centerAngle, outerOffset, innerOffset, lstyle);
}
else
{
r = GetInscribedRectQuad1(g, psp,
startAngle, endAngle, centerAngle, outerOffset, innerOffset, lstyle);
}
r.Inflate(-2, -2);
psp.InscribedRect = r;
}
return (psp.InscribedRect);
}
#region GetInscribedRectQuad1
private Rectangle GetInscribedRectQuad1(Graphics g,
PieSeriesPoint psp, double startAngle, double endAngle,
double centerAngle, double outerOffset, double innerOffset, SliceInnerLabelVisualStyle lstyle)
{
Point ptCenter = psp.SliceCenter;
centerAngle = NormalizeCenterAngleQuad1(centerAngle, startAngle, endAngle);
Point ptcInner = psp.GetRayEndPoint(centerAngle, innerOffset);
Point ptcOuter = psp.GetRayEndPoint(centerAngle, outerOffset);
Point ptLeftRay = psp.GetRayEndPoint(startAngle, outerOffset);
Point ptRightRay = psp.GetRayEndPoint(endAngle, outerOffset);
Point ptPie1, ptPie2;
int count = FindCircleIntersect(ptCenter, outerOffset, ptcOuter,
new Point(ptcOuter.X + 1, ptcOuter.Y), out ptPie1, out ptPie2, true);
if (count != 2)
return (Rectangle.Empty);
Point ptLeftLower = new Point(ptPie1.X, ptcOuter.Y);
Point pt1;
if (FindLineIntersect(ptcOuter, ptLeftLower, ptCenter, ptRightRay, out pt1) == true)
{
if ((uint)(ptcOuter.X - pt1.X) < ptcOuter.X - ptLeftLower.X)
ptLeftLower.X = pt1.X;
}
Point ptPie3, ptPie4;
count = FindCircleIntersect(ptCenter, outerOffset, ptcOuter,
new Point(ptcOuter.X, ptcOuter.Y + 10), out ptPie3, out ptPie4, false);
if (count != 2)
return (Rectangle.Empty);
Point ptRightUpper = new Point(ptcOuter.X, ptPie3.Y);
if (FindLineIntersect(ptcOuter, new Point(ptcOuter.X, ptcOuter.Y + 10),
ptCenter, ptLeftRay, out pt1) == true)
{
if ((uint)(ptcOuter.Y - pt1.Y) < (ptcOuter.Y - ptRightUpper.Y))
ptRightUpper.Y = pt1.Y;
}
if (centerAngle < 40)
{
Point ray = psp.GetRayEndPoint(0, innerOffset);
if (Math.Abs(ray.X - ptcOuter.X) < Math.Abs(ptLeftLower.X - ptcOuter.X))
ptLeftLower.X = ray.X;
}
else if (centerAngle > 50)
{
Point ray = psp.GetRayEndPoint(90, innerOffset);
if (Math.Abs(ray.Y - ptcOuter.Y) < Math.Abs(ptRightUpper.Y - ptcOuter.Y))
ptRightUpper.Y = ray.Y;
}
else
{
if (ptLeftLower.X < ptcInner.X)
{
if (FindLineIntersect(ptCenter, ptLeftRay, ptcInner,
new Point(ptcInner.X, ptcInner.Y + 10), out pt1) == true)
{
if (Math.Abs(pt1.X - ptcOuter.X) < Math.Abs(ptLeftLower.X - ptcOuter.X))
ptLeftLower.X = pt1.X;
}
}
if (ptRightUpper.Y <= ptcInner.Y)
{
if (FindLineIntersect(ptCenter, ptRightRay,
ptcInner, new Point(ptcInner.X + 10, ptcInner.Y), out pt1) == true)
{
if (Math.Abs(pt1.Y - ptcOuter.Y) < Math.Abs(ptRightUpper.Y - ptcOuter.Y))
ptRightUpper.Y = pt1.Y;
}
}
}
if (psp.SweepAngleEx < 180)
{
if (FindLineIntersect(ptCenter, ptRightRay, ptRightUpper,
new Point(ptRightUpper.X + 10, ptRightUpper.Y), out pt1) == true)
{
if (Math.Abs(pt1.X - ptcOuter.X) < Math.Abs(ptLeftLower.X - ptcOuter.X))
ptLeftLower.X = pt1.X;
}
if (FindLineIntersect(ptCenter, ptLeftRay, ptLeftLower,
new Point(ptLeftLower.X, ptLeftLower.Y + 10), out pt1) == true)
{
if (Math.Abs(pt1.Y - ptcOuter.Y) < Math.Abs(ptRightUpper.Y - ptcOuter.Y))
ptRightUpper.Y = pt1.Y;
}
}
Rectangle r = new Rectangle();
r.Location = new Point(ptLeftLower.X, ptRightUpper.Y);
r.Width = ptcOuter.X - ptLeftLower.X;
r.Height = ptcOuter.Y - ptRightUpper.Y;
return (r);
}
#region NormalizeCenterAngleQuad1
private double NormalizeCenterAngleQuad1(
double centerAngle, double startAngle, double endAngle)
{
if (centerAngle > 70)
{
centerAngle -= (centerAngle - 70) / 2;
if (centerAngle - 2 < 0)
centerAngle += 360;
if (centerAngle - 2 <= startAngle)
centerAngle = (startAngle + 2) % 360;
}
else if (centerAngle < 20)
{
centerAngle += (20 - centerAngle) / 2;
if (centerAngle + 2 >= endAngle)
centerAngle = endAngle - 2;
}
return (centerAngle);
}
#endregion
#endregion
#region GetInscribedRectQuad2
private Rectangle GetInscribedRectQuad2(Graphics g,
PieSeriesPoint psp, double startAngle, double endAngle,
double centerAngle, double outerOffset, double innerOffset, SliceInnerLabelVisualStyle lstyle)
{
Point ptCenter = psp.SliceCenter;
centerAngle = NormalizeCenterAngleQuad2(centerAngle, startAngle, endAngle);
Point ptcInner = psp.GetRayEndPoint(centerAngle, innerOffset);
Point ptcOuter = psp.GetRayEndPoint(centerAngle, outerOffset);
Point ptLeftRay = psp.GetRayEndPoint(startAngle, outerOffset);
Point ptRightRay = psp.GetRayEndPoint(endAngle, outerOffset);
Point ptPie1, ptPie2;
int count = FindCircleIntersect(ptCenter, outerOffset, ptcOuter,
new Point(ptcOuter.X + 1, ptcOuter.Y), out ptPie1, out ptPie2, true);
if (count != 2)
return (Rectangle.Empty);
Point ptRightLower = new Point(ptPie2.X, ptcOuter.Y);
Point pt1;
if (FindLineIntersect(ptcOuter, ptRightLower, ptCenter, ptLeftRay, out pt1) == true)
{
if ((uint)(pt1.X - ptcOuter.X) < (ptRightLower.X - ptcOuter.X))
ptRightLower.X = pt1.X;
}
Point ptPie3, ptPie4;
count = FindCircleIntersect(ptCenter, outerOffset, ptcOuter,
new Point(ptcOuter.X, ptcOuter.Y + 10), out ptPie3, out ptPie4, false);
if (count != 2)
return (Rectangle.Empty);
Point ptLeftUpper = new Point(ptcOuter.X, ptPie3.Y);
if (FindLineIntersect(ptcOuter, new Point(ptcOuter.X, ptcOuter.Y + 10),
ptCenter, ptRightRay, out pt1) == true)
{
if ((uint)(ptcOuter.Y - pt1.Y) < (ptcOuter.Y - ptLeftUpper.Y))
ptLeftUpper.Y = pt1.Y;
}
if (centerAngle < 130)
{
Point ray = psp.GetRayEndPoint(90, innerOffset);
if (Math.Abs(ray.Y - ptcOuter.Y) < Math.Abs(ptLeftUpper.Y - ptcOuter.Y))
ptLeftUpper.Y = ray.Y;
}
else if (centerAngle > 140)
{
Point ray = psp.GetRayEndPoint(180, innerOffset);
if (Math.Abs(ray.X - ptcOuter.X) < Math.Abs(ptRightLower.X - ptcOuter.X))
ptRightLower.X = ray.X;
}
else
{
if (ptRightLower.X > ptcInner.X)
{
if (FindLineIntersect(ptCenter, ptLeftRay, ptcInner,
new Point(ptcInner.X, ptcInner.Y + 10), out pt1) == true)
{
if (Math.Abs(pt1.X - ptcOuter.X) < Math.Abs(ptRightLower.X - ptcOuter.X))
ptRightLower.X = pt1.X;
}
}
if (ptLeftUpper.Y <= ptcInner.Y)
{
if (FindLineIntersect(ptCenter, ptRightRay,
ptcInner, new Point(ptcInner.X + 10, ptcInner.Y), out pt1) == true)
{
if (Math.Abs(pt1.Y - ptcOuter.Y) < Math.Abs(ptLeftUpper.Y - ptcOuter.Y))
ptLeftUpper.Y = pt1.Y;
}
}
}
if (psp.SweepAngleEx < 180)
{
if (FindLineIntersect(ptCenter, ptRightRay, ptRightLower,
new Point(ptRightLower.X, ptRightLower.Y + 10), out pt1) == true)
{
if (Math.Abs(pt1.Y - ptcOuter.Y) < Math.Abs(ptLeftUpper.Y - ptcOuter.Y))
ptLeftUpper.Y = pt1.Y;
}
if (FindLineIntersect(ptCenter, ptLeftRay, ptLeftUpper,
new Point(ptLeftUpper.X + 10, ptLeftUpper.Y), out pt1) == true)
{
if (Math.Abs(pt1.X - ptcOuter.X) < Math.Abs(ptRightLower.X - ptcOuter.X))
ptRightLower.X = pt1.X;
}
}
Rectangle r = new Rectangle();
r.Location = ptLeftUpper;
r.Width = ptRightLower.X - ptcOuter.X;
r.Height = ptcOuter.Y - ptLeftUpper.Y;
return (r);
}
#region NormalizeCenterAngleQuad2
private double NormalizeCenterAngleQuad2(
double centerAngle, double startAngle, double endAngle)
{
if (centerAngle > 160)
{
centerAngle -= (centerAngle - 160) / 2;
if (centerAngle - 2 <= startAngle)
centerAngle = startAngle + 2;
}
else if (centerAngle < 110)
{
centerAngle += (110 - centerAngle) / 2;
if (centerAngle + 2 >= endAngle)
centerAngle = endAngle - 2;
}
return (centerAngle);
}
#endregion
#endregion
#region GetInscribedRectQuad3
private Rectangle GetInscribedRectQuad3(Graphics g,
PieSeriesPoint psp, double startAngle, double endAngle,
double centerAngle, double outerOffset, double innerOffset, SliceInnerLabelVisualStyle lstyle)
{
Point ptCenter = psp.SliceCenter;
centerAngle = NormalizeCenterAngleQuad3(centerAngle, startAngle, endAngle);
Point ptcInner = psp.GetRayEndPoint(centerAngle, innerOffset);
Point ptcOuter = psp.GetRayEndPoint(centerAngle, outerOffset);
Point ptLeftRay = psp.GetRayEndPoint(startAngle, outerOffset);
Point ptRightRay = psp.GetRayEndPoint(endAngle, outerOffset);
Point ptPie1, ptPie2;
int count = FindCircleIntersect(ptCenter, outerOffset, ptcOuter,
new Point(ptcOuter.X + 1, ptcOuter.Y), out ptPie1, out ptPie2, true);
if (count != 2)
return (Rectangle.Empty);
Point ptRightUpper = new Point(ptPie2.X, ptcOuter.Y);
Point pt1;
if (FindLineIntersect(ptcOuter, ptRightUpper, ptCenter, ptRightRay, out pt1) == true)
{
if (pt1.X - ptcOuter.X > 0)
{
if ((uint)(pt1.X - ptcOuter.X) < (ptRightUpper.X - ptcOuter.X))
ptRightUpper.X = pt1.X;
}
}
Point ptPie3, ptPie4;
count = FindCircleIntersect(ptCenter, outerOffset, ptcOuter,
new Point(ptcOuter.X, ptcOuter.Y + 10), out ptPie3, out ptPie4, false);
if (count != 2)
return (Rectangle.Empty);
Point ptLeftLower = new Point(ptcOuter.X, ptPie4.Y);
if (FindLineIntersect(ptcOuter, new Point(ptcOuter.X, ptcOuter.Y + 10),
ptCenter, ptLeftRay, out pt1) == true)
{
if (pt1.Y - ptcOuter.Y > 0)
{
if ((uint)(pt1.Y - ptcOuter.Y) < (ptLeftLower.Y - ptcOuter.Y))
ptLeftLower.Y = pt1.Y;
}
}
if (centerAngle < 220)
{
Point ray = psp.GetRayEndPoint(180, innerOffset);
if (Math.Abs(ray.X - ptcOuter.X) < Math.Abs(ptRightUpper.X - ptcOuter.X))
ptRightUpper.X = ray.X;
}
else if (centerAngle > 230)
{
Point ray = psp.GetRayEndPoint(270, innerOffset);
if (Math.Abs(ray.Y - ptcOuter.Y) < Math.Abs(ptLeftLower.Y - ptcOuter.Y))
ptLeftLower.Y = ray.Y;
}
else
{
if (ptLeftLower.Y >= ptcInner.Y)
{
if (FindLineIntersect(ptCenter, ptLeftRay, ptcInner,
new Point(ptcInner.X + 10, ptcInner.Y), out pt1) == true)
{
if (Math.Abs(pt1.Y - ptcOuter.Y) < Math.Abs(ptLeftLower.Y - ptcOuter.Y))
ptLeftLower.Y = pt1.Y;
}
}
if (ptRightUpper.X > ptcInner.X)
{
if (FindLineIntersect(ptCenter, ptRightRay,
ptcInner, new Point(ptcInner.X, ptcInner.Y + 10), out pt1) == true)
{
if (Math.Abs(pt1.X - ptcOuter.X) < Math.Abs(ptRightUpper.X - ptcOuter.X))
ptRightUpper.X = pt1.X;
}
}
}
if (psp.SweepAngleEx < 180)
{
if (FindLineIntersect(ptCenter, ptRightRay, ptLeftLower,
new Point(ptLeftLower.X + 10, ptLeftLower.Y), out pt1) == true)
{
if (Math.Abs(pt1.X - ptcOuter.X) < Math.Abs(ptRightUpper.X - ptcOuter.X))
ptRightUpper.X = pt1.X;
}
if (FindLineIntersect(ptCenter, ptLeftRay,
ptRightUpper, new Point(ptRightUpper.X, ptRightUpper.Y + 10), out pt1) == true)
{
if (Math.Abs(pt1.Y - ptcOuter.Y) < Math.Abs(ptLeftLower.Y - ptcOuter.Y))
ptLeftLower.Y = pt1.Y;
}
}
Rectangle r = new Rectangle();
r.Location = ptcOuter;
r.Width = ptRightUpper.X - ptcOuter.X;
r.Height = ptLeftLower.Y - ptcOuter.Y;
return (r);
}
#region NormalizeCenterAngleQuad3
private double NormalizeCenterAngleQuad3(
double centerAngle, double startAngle, double endAngle)
{
if (centerAngle > 250)
{
centerAngle -= (centerAngle - 250) / 2;
if (centerAngle - startAngle < 2)
centerAngle = startAngle + 2;
}
else if (centerAngle < 200)
{
centerAngle += (200 - centerAngle) / 2;
if (centerAngle + 2 >= endAngle)
centerAngle = endAngle - 2;
}
return (centerAngle);
}
#endregion
#endregion
#region GetInscribedRectQuad4
private Rectangle GetInscribedRectQuad4(Graphics g,
PieSeriesPoint psp, double startAngle, double endAngle,
double centerAngle, double outerOffset, double innerOffset, SliceInnerLabelVisualStyle lstyle)
{
Point ptCenter = psp.SliceCenter;
centerAngle = NormalizeCenterAngleQuad4(centerAngle, startAngle, endAngle);
Point ptcInner = psp.GetRayEndPoint(centerAngle, innerOffset);
Point ptcOuter = psp.GetRayEndPoint(centerAngle, outerOffset);
Point ptLeftRay = psp.GetRayEndPoint(startAngle, outerOffset);
Point ptRightRay = psp.GetRayEndPoint(endAngle, outerOffset);
Point ptPie1, ptPie2;
int count = FindCircleIntersect(ptCenter, outerOffset, ptcOuter,
new Point(ptcOuter.X + 1, ptcOuter.Y), out ptPie1, out ptPie2, true);
if (count != 2)
return (Rectangle.Empty);
Point ptLeftUpper = new
Point((ptcOuter.Y < ptCenter.Y) ? ptPie1.X : ptPie2.X, ptcOuter.Y);
Point pt1;
if (FindLineIntersect(ptcOuter, ptLeftUpper, ptCenter, ptLeftRay, out pt1) == true)
{
if (ptcOuter.X - pt1.X > 0)
{
if ((uint)(ptcOuter.X - pt1.X) < ptcOuter.X - ptLeftUpper.X)
ptLeftUpper.X = pt1.X;
}
}
Point ptPie3, ptPie4;
count = FindCircleIntersect(ptCenter, outerOffset, ptcOuter,
new Point(ptcOuter.X, ptcOuter.Y + 10), out ptPie3, out ptPie4, false);
if (count != 2)
return (Rectangle.Empty);
Point ptRightLower = new Point(ptcOuter.X,
(ptcOuter.X < ptCenter.X) ? ptPie3.Y : ptPie4.Y);
if (FindLineIntersect(ptcOuter, new Point(ptcOuter.X, ptcOuter.Y + 10),
ptCenter, ptRightRay, out pt1) == true)
{
if (pt1.Y - ptcOuter.Y > 0)
{
if ((uint)(pt1.Y - ptcOuter.Y) < (ptRightLower.Y - ptcOuter.Y))
ptRightLower.Y = pt1.Y;
}
}
if (centerAngle < 310)
{
Point ray = psp.GetRayEndPoint(270, innerOffset);
if (Math.Abs(ray.Y - ptcOuter.Y) < Math.Abs(ptRightLower.Y - ptcOuter.Y))
ptRightLower.Y = ray.Y;
}
else if (centerAngle > 320)
{
Point ray = psp.GetRayEndPoint(0, innerOffset);
if (Math.Abs(ray.X - ptcOuter.X) < Math.Abs(ptLeftUpper.X - ptcOuter.X))
ptLeftUpper.X = ray.X;
}
else
{
if (ptLeftUpper.X <= ptcInner.X)
{
if (FindLineIntersect(ptCenter, ptLeftRay, ptcInner,
new Point(ptcInner.X, ptcInner.Y + 10), out pt1) == true)
{
if (Math.Abs(pt1.X - ptcOuter.X) < Math.Abs(ptLeftUpper.X - ptcOuter.X))
ptLeftUpper.X = pt1.X;
}
}
if (ptRightLower.Y >= ptcInner.Y)
{
if (FindLineIntersect(ptCenter, ptRightRay,
ptcInner, new Point(ptcInner.X + 10, ptcInner.Y), out pt1) == true)
{
if (Math.Abs(pt1.Y - ptcOuter.Y) < Math.Abs(ptRightLower.Y - ptcOuter.Y))
ptRightLower.Y = pt1.Y;
}
}
}
if (psp.SweepAngleEx < 180)
{
if (FindLineIntersect(ptCenter, ptRightRay, ptLeftUpper,
new Point(ptLeftUpper.X, ptLeftUpper.Y + 10), out pt1) == true)
{
if (Math.Abs(pt1.Y - ptcOuter.Y) < Math.Abs(ptRightLower.Y - ptcOuter.Y))
ptRightLower.Y = pt1.Y;
}
if (FindLineIntersect(ptCenter, ptLeftRay,
ptRightLower, new Point(ptRightLower.X + 10, ptRightLower.Y), out pt1) == true)
{
if (Math.Abs(pt1.X - ptcOuter.X) < Math.Abs(ptLeftUpper.X - ptcOuter.X))
ptLeftUpper.X = pt1.X;
}
}
Rectangle r = new Rectangle();
r.Location = ptLeftUpper;
r.Width = ptRightLower.X - ptLeftUpper.X;
r.Height = ptRightLower.Y - ptLeftUpper.Y;
return (r);
}
#region NormalizeCenterAngleQuad4
private double NormalizeCenterAngleQuad4(
double centerAngle, double startAngle, double endAngle)
{
if (centerAngle > 340)
{
centerAngle -= (centerAngle - 340) / 2;
if (centerAngle - 2 <= startAngle)
centerAngle = startAngle + 2;
}
else if (centerAngle < 290)
{
centerAngle += (290 - centerAngle) / 2;
if (centerAngle + 2 >= endAngle)
centerAngle = endAngle - 2;
}
return (centerAngle);
}
#endregion
#endregion
#region FindLineIntersect
private bool FindLineIntersect(
Point ps1, Point pe1, Point ps2, Point pe2, out Point ptOut)
{
float A1 = pe1.Y - ps1.Y;
float B1 = ps1.X - pe1.X;
float C1 = A1 * ps1.X + B1 * ps1.Y;
float A2 = pe2.Y - ps2.Y;
float B2 = ps2.X - pe2.X;
float C2 = A2 * ps2.X + B2 * ps2.Y;
float delta = A1 * B2 - A2 * B1;
if (delta == 0)
{
ptOut = Point.Empty;
return (false);
}
ptOut = new Point(
(int)((B2 * C1 - B1 * C2) / delta),
(int)((A1 * C2 - A2 * C1) / delta));
return (true);
}
#endregion
#region FindCircleIntersect
private int FindCircleIntersect(Point ptc, double radius,
Point pt1, Point pt2, out Point ptOut1, out Point ptOut2, bool normx)
{
int dx = pt2.X - pt1.X;
int dy = pt2.Y - pt1.Y;
int a = dx * dx + dy * dy;
int b = 2 * (dx * (pt1.X - ptc.X) + dy * (pt1.Y - ptc.Y));
double c = (pt1.X - ptc.X) * (pt1.X - ptc.X) +
(pt1.Y - ptc.Y) * (pt1.Y - ptc.Y) - radius * radius;
double det = b * b - 4 * a * c;
if ((a <= 0.0000001) || (det < 0))
{
ptOut1 = Point.Empty;
ptOut2 = Point.Empty;
return (0);
}
else if (det == 0)
{
double t1 = -b / (2 * a);
ptOut1 = new Point((int)(pt1.X + t1 * dx), (int)(pt1.Y + t1 * dy));
ptOut2 = Point.Empty;
return (1);
}
else
{
double t2 = (float)((-b + Math.Sqrt(det)) / (2 * a));
double t3 = (float)((-b - Math.Sqrt(det)) / (2 * a));
ptOut1 = new Point((int)(pt1.X + t2 * dx), (int)(pt1.Y + t2 * dy));
ptOut2 = new Point((int)(pt1.X + t3 * dx), (int)(pt1.Y + t3 * dy));
if ((normx == true && ptOut1.X > ptOut2.X) ||
(normx == false && ptOut1.Y > ptOut2.Y))
{
Point ptTemp = ptOut1;
ptOut1 = ptOut2;
ptOut2 = ptTemp;
}
return (2);
}
}
#endregion
#endregion
#endregion
#region GetLableInnerOffset
internal double GetLableInnerOffset
(PieSeriesPoint psp, double maxRadius, SliceInnerLabelVisualStyle lstyle)
{
double offset;
if (Math.Abs(lstyle.InnerRadiusOffset) >= 1)
offset = lstyle.InnerRadiusOffset;
else
offset = (maxRadius - psp.InnerRadius) * lstyle.InnerRadiusOffset;
offset += psp.InnerRadius;
if (lstyle.MaxLineCount >= 0)
{
double outerOffset = psp.InnerRadius;
if (lstyle.OuterRadiusOffset > -1 && lstyle.OuterRadiusOffset < 1)
outerOffset += (lstyle.OuterRadiusOffset * maxRadius);
else
outerOffset += lstyle.OuterRadiusOffset;
double fh = lstyle.Font.GetHeight() * lstyle.MaxLineCount;
fh = outerOffset - fh;
if (fh > offset)
offset = fh;
}
return (offset);
}
#endregion
#region GetLableOuterOffset
internal double GetLableOuterOffset(
PieSeriesPoint psp, double maxRadius, SliceInnerLabelVisualStyle lstyle)
{
double offset;
if (Math.Abs(lstyle.OuterRadiusOffset) >= 1)
offset = lstyle.OuterRadiusOffset;
else
offset = (maxRadius * lstyle.OuterRadiusOffset);
return (maxRadius + offset);
}
#endregion
#endregion
#endregion
#endregion
#region GetSliceLabel
internal string GetSliceLabel(
PieChart pieChart, PieSeriesPoint psp, bool inner, string label)
{
if (string.IsNullOrEmpty(label) == false)
{
string text = (string)label.Clone();
if (ChartControl.DoGetSliceLabelEvent(pieChart, this, psp, inner, ref text) == false)
text = psp.GetLabelText(text);
return (text);
}
return (null);
}
#endregion
#region GetSliceLabelBounds
private Rectangle GetSliceLabelBounds(Graphics g, PieChart pieChart,
PieSeriesPoint psp, string text, Rectangle r, SliceInnerLabelVisualStyle lstyle)
{
if (r.Width > 0 && r.Height > 0)
{
SizeF sz = g.MeasureString(text, lstyle.Font, r.Width);
Size size = sz.ToSize();
psp.InnerLabelClipped = (size.Width > r.Width || size.Height > r.Height);
if (psp.InnerLabelClipped == true)
{
SliceLabelCropMode cropMode = psp.SliceLabelCropModeEx;
if (cropMode == SliceLabelCropMode.Hide)
return (Rectangle.Empty);
else if (cropMode == SliceLabelCropMode.Clip)
{
int height = GetTruncatedHeight(r.Height, lstyle);
if (height > 0)
r.Height = height;
}
else
{
r.Height = size.Height;
}
}
}
return (r);
}
#region GetTruncatedHeight
private int GetTruncatedHeight(int height, SliceInnerLabelVisualStyle lstyle)
{
int n = (int)(height / lstyle.Font.Height);
if (lstyle.MaxLineCount > 0)
{
if (n > lstyle.MaxLineCount)
n = lstyle.MaxLineCount;
}
return (lstyle.Font.Height * n);
}
#endregion
#endregion
#endregion
#region IsHighLightedPsp
internal bool IsHighLightedPsp(PieChart pieChart, PieSeriesPoint psp)
{
if (pieChart.Legend.TrackingMode == LegendTrackingMode.ChartAndLegend)
{
if (LegendItem != null && LegendItem.IsLegendHitItem == true)
return (true);
if (psp.LegendItem != null && psp.LegendItem.IsLegendHitItem == true)
return (true);
}
PieSeriesPoint hitPsp = pieChart.HitPsp;
if (hitPsp != null)
{
PieSelectionMode psm = GetPieSelectionMode(pieChart);
switch (psm)
{
case PieSelectionMode.Pie:
return (true);
case PieSelectionMode.Ring:
return (psp.PieRing == hitPsp.PieRing);
case PieSelectionMode.Series:
return (psp.ChartSeries == hitPsp.ChartSeries);
case PieSelectionMode.Slice:
if (ShowAllRingsEx(pieChart) == true)
return (psp.RootPsp == hitPsp.RootPsp);
else
return (psp == hitPsp);
case PieSelectionMode.Point:
return (psp == hitPsp);
}
}
return (false);
}
#endregion
#endregion
#region Render
internal override void Render(ChartRenderInfo renderInfo)
{
if (Displayed == true)
{
Rectangle bounds = BoundsRelative;
if (renderInfo.ClipRectangle.IntersectsWith(bounds))
RenderOverride(renderInfo);
}
}
#endregion
#region UpdatePalette
internal bool UpdatePalette(PieChart pieChart)
{
if (PaletteGroup == PaletteGroup.NotSet)
return (false);
Color[] palette = (PaletteGroup == PaletteGroup.Custom)
? (CustomPalette.Length > 0 ? CustomPalette : pieChart.CustomPalette)
: BaseChart.PaletteGroups[(int)PaletteGroup];
if (palette == null || palette.Length == 0)
palette = BaseChart.PaletteColor1;
int colorIndex = 0;
DefaultPaletteColor =
pieChart.GetPaletteColor(colorIndex++, palette, ReversePaletteColors);
if (PieRings != null)
{
foreach (PieRing pieRing in PieRings)
{
foreach (PieSeriesPoint psp in pieRing.Psps)
{
psp.DefaultPaletteColor =
pieChart.GetPaletteColor(colorIndex++, palette, ReversePaletteColors);
}
}
}
return (true);
}
#endregion
#region AddSeriesPoint
internal override void AddSeriesPoint(object valueX, object[] valuesY, object dataItem)
{
PieSeriesPoint sp = new PieSeriesPoint(valueX, valuesY);
sp.DataItem = dataItem;
SeriesPoints.Add(sp);
}
#endregion
#region ClearSeriesPoints
internal override void ClearSeriesPoints()
{
SeriesPoints.Clear();
}
#endregion
#region GetActiveSeriesPointCollection
///
/// Gets the current 'active' SeriesPointCollection (useful when
/// 'ShowInnerRings' is set to false).
///
///
public PieSeriesPointCollection GetActiveSeriesPointCollection()
{
PieChart pieChart = Parent as PieChart;
if (pieChart != null)
{
return ((ShowAllRingsEx(pieChart) == true)
? SeriesPoints : (ActiveSeriesPointCollection ?? SeriesPoints));
}
return (null);
}
#endregion
#region SetActiveSeriesPointCollection
///
/// Sets the current 'active' SeriesPointCollection (useful when
/// 'ShowInnerRings' is set to false).
///
///
public void SetActiveSeriesPointCollection(PieSeriesPointCollection spc)
{
if (spc == null || ValidSeriesPointSet(SeriesPoints, spc) == true)
ActiveSeriesPointCollection = spc;
else
throw new Exception("Not a valid PieSeriesPointSet for the series.");
InvalidateLayout();
}
#region ValidSeriesPointSet
private bool ValidSeriesPointSet(PieSeriesPointCollection spc, PieSeriesPointCollection aspc)
{
if (aspc == spc)
return (true);
if (spc.PieSlices != null)
{
foreach (PieSeriesPoint psp in spc.PieSlices)
{
if (psp.SeriesPoints == aspc)
return (true);
if (ValidSeriesPointSet(psp.SeriesPoints, aspc) == true)
return (true);
}
}
return (false);
}
#endregion
#endregion
#region GetPieSeriesPointCollection
///
/// Gets the SeriesPointCollection containing the given PieSeriesPoint
///
///
///
public PieSeriesPointCollection GetPieSeriesPointCollection(PieSeriesPoint psp)
{
return (psp.Parent as PieSeriesPointCollection);
}
#endregion
#region GetPieLabels
internal List GetPieLabels(Graphics g, PieChart pieChart)
{
List pieLabels = new List();
if (ShowAllRingsEx(pieChart) == true)
{
PieRing pieRing = GetOuterRing(pieChart);
if (pieRing != null)
{
foreach (PieSeriesPoint psp in pieRing.Psps)
{
SetPieLabel(g, pieChart, psp);
if (psp.PieLabel != null)
pieLabels.Add(psp.PieLabel);
}
}
}
else
{
PieSeriesPointCollection spc = GetActiveSeriesPointCollection();
if (spc != null)
{
foreach (PieSeriesPoint psp in spc)
{
SetPieLabel(g, pieChart, psp);
if (psp.PieLabel != null)
pieLabels.Add(psp.PieLabel);
}
}
}
return (pieLabels);
}
#region SetPieLabel
private void SetPieLabel(
Graphics g, PieChart pieChart, PieSeriesPoint psp)
{
psp.PieLabel = null;
if (psp.IsDisplayed == true && psp.SweepAngleEx != 0 &&
psp.IsInOtherEx == false && psp.IsEmpty == false)
{
ChartSliceVisualStyle sstyle = psp.GetEffectiveSliceStyle(pieChart, this);
if (IsOuterSliceLabelVisible(pieChart, psp) == true)
{
SliceOuterLabelVisualStyle lstyle = sstyle.SliceOuterLabelStyle;
SliceLabelDisplayMode displayMode = psp.SliceLabelDisplayModeEx;
if (displayMode == SliceLabelDisplayMode.Outer ||
displayMode == SliceLabelDisplayMode.InnerAndOuter ||
displayMode == SliceLabelDisplayMode.NotSet)
{
string outerlabel = psp.OuterSliceLabelEx;
if (string.IsNullOrEmpty(outerlabel) == false)
SetPieLabelEx(g, pieChart, psp, outerlabel, lstyle);
}
else
{
if (psp.InnerLabelDisplayed == false)
{
string outerlabel = psp.OuterSliceLabelEx;
if (string.IsNullOrEmpty(outerlabel) == false)
{
SetPieLabelEx(g, pieChart, psp, outerlabel, lstyle);
}
else
{
string innerlabel = psp.InnerSliceLabelEx;
if (string.IsNullOrEmpty(innerlabel) == false)
SetPieLabelEx(g, pieChart, psp, innerlabel, lstyle);
}
}
}
}
}
}
#endregion
#region GetOuterRing
private PieRing GetOuterRing(PieChart pieChart)
{
if (ShowAllRingsEx(pieChart) == false)
{
PieSeriesPointCollection spc = (ActiveSeriesPointCollection ?? SeriesPoints);
return (spc.PieRing);
}
if (PieRings.Count > 0)
{
foreach (PieRing pieRing in PieRings)
{
if (pieRing.IsOuterRing == true)
return (pieRing);
}
return (PieRings[0]);
}
return (null);
}
#endregion
#region IsOuterSliceLabelVisible
private bool IsOuterSliceLabelVisible(PieChart pieChart, PieSeriesPoint psp)
{
if (psp.SliceLabelVisibilityEx == SliceLabelVisibility.Never)
return (false);
switch (psp.SliceLabelDisplayModeEx)
{
case SliceLabelDisplayMode.Outer:
case SliceLabelDisplayMode.InnerAndOuter:
case SliceLabelDisplayMode.InnerXorOuter:
return (true);
}
return (false);
}
#endregion
#region SetPieLabelEx
private void SetPieLabelEx(Graphics g, PieChart pieChart,
PieSeriesPoint psp, string text, SliceOuterLabelVisualStyle lstyle)
{
string s = GetSliceLabel(pieChart, psp, false, text);
if (String.IsNullOrEmpty(s) == false)
{
PieLabel pl = new PieLabel(psp, s);
pl.LabelSize = MeasurePieLabel(g, pieChart, psp, pl, 0, lstyle);
psp.PieLabel = pl;
}
}
#region MeasurePieLabel
internal Size MeasurePieLabel(Graphics g, PieChart pieChart,
PieSeriesPoint psp, PieLabel pl, int cwidth, SliceOuterLabelVisualStyle lstyle)
{
float fheight = lstyle.Font.GetHeight();
int maxWidth = GetMaxPieTextWidth(g, pieChart, lstyle);
int maxHeight = GetMaxPieTextHeight(g, pieChart, fheight, lstyle);
int minWidth = GetMinPieTextWidth(g, pieChart, lstyle);
int minHeight = GetMinPieTextHeight(g, pieChart, fheight, lstyle);
using (StringFormat sf = new StringFormat())
{
lstyle.GetStringFormatFlags(sf, false);
if (cwidth != 0)
cwidth -= Dpi.Width(lstyle.Padding.Horizontal);
Size size = g.MeasureString(pl.Label,
lstyle.Font, cwidth != 0 ? cwidth : maxWidth, sf).ToSize();
size.Width += Dpi.Width1;
size = AdjustForPadding(pieChart, psp, size);
size.Height = MathHelper.Clamp(size.Height, minHeight, maxHeight);
size.Width = MathHelper.Clamp(size.Width, minWidth, maxWidth);
if (lstyle.MaxLineCount > 1)
{
int lineHeight = (int)(Math.Ceiling(fheight)) * lstyle.MaxLineCount;
if (size.Height > lineHeight)
size.Height = lineHeight;
}
return (size);
}
}
#region GetMaxPieTextWidth
private int GetMaxPieTextWidth(
Graphics g, PieChart pieChart, SliceOuterLabelVisualStyle lstyle)
{
double maxWidth = lstyle.MaxLabelWidth;
if (double.IsNaN(maxWidth) == false)
{
if (Math.Abs(maxWidth) >= 1)
return ((int)maxWidth);
}
else
{
maxWidth = .5;
}
return ((int)(pieChart.ContentBoundsEx.Width * maxWidth));
}
#endregion
#region GetMaxPieTextHeight
private int GetMaxPieTextHeight(
Graphics g, PieChart pieChart, float fheight, SliceOuterLabelVisualStyle lstyle)
{
double maxHeight = lstyle.MaxLabelHeight;
if (double.IsNaN(maxHeight) == false)
{
if (Math.Abs(maxHeight) >= 1)
return ((int)maxHeight);
}
else
{
maxHeight = .5;
}
double height = pieChart.ContentBoundsEx.Height * maxHeight;
int n = (int)(height / fheight);
return ((int)(fheight * n));
}
#endregion
#region GetMinPieTextWidth
private int GetMinPieTextWidth(
Graphics g, PieChart pieChart, SliceOuterLabelVisualStyle lstyle)
{
double minWidth = lstyle.MinLabelWidth;
if (double.IsNaN(minWidth) == false)
{
if (Math.Abs(minWidth) >= 1)
return ((int)minWidth);
return ((int)(pieChart.ContentBoundsEx.Width * minWidth));
}
return (0);
}
#endregion
#region GetMinPieTextHeight
private int GetMinPieTextHeight(
Graphics g, PieChart pieChart, float fheight, SliceOuterLabelVisualStyle lstyle)
{
double minHeight = lstyle.MinLabelHeight;
if (double.IsNaN(minHeight) == false)
{
if (Math.Abs(minHeight) >= 1)
return ((int)minHeight);
double height = pieChart.ContentBoundsEx.Height * minHeight;
int n = (int)(height / fheight);
return ((int)(fheight * n));
}
return ((int)fheight);
}
#endregion
#region AdjustForPadding
private Size AdjustForPadding(PieChart pieChart, PieSeriesPoint psp, Size size)
{
SliceOuterLabelVisualStyle sstyle =
psp.GetEffectiveSliceStyle(pieChart, psp.ChartSeries).SliceOuterLabelStyle;
size.Width += Dpi.Width(sstyle.Padding.Horizontal);
size.Height += Dpi.Width(sstyle.Padding.Vertical);
return (size);
}
#endregion
#endregion
#endregion
#endregion
#region GetOtherPieSeriesPoint
///
/// Gets the 'Other' PieSeriesPoint
///
///
public PieSeriesPoint GetOtherPieSeriesPoint()
{
return (SeriesPoints.OtherSlicePsp);
}
#endregion
#region Mouse support
#region ProcessMouseEnter
internal void ProcessMouseEnter(EventArgs e, PieChart pieChart)
{
SetToolTip(pieChart.ShowToolTips ? GetToolTipText(pieChart) : string.Empty);
}
#region SetToolTip
private static string _toolTipText;
private void SetToolTip(string toolTipText)
{
ChartControl chartControl = ChartControl;
if (chartControl != null)
{
if (chartControl.ToolTipText != _toolTipText)
chartControl.ToolTip.Hide(chartControl);
chartControl.ToolTipText = toolTipText;
}
}
#endregion
#region GetToolTipText
private string GetToolTipText(PieChart pieChart)
{
PieSeriesPoint psp = pieChart.HitPsp;
if (psp == null || "".Equals(psp.ToolTipText))
return (string.Empty);
if (_toolTipText == null)
{
ChartControl chartControl = ChartControl;
string s = "";
if (chartControl.DoGetToolTipEvent(pieChart, this, psp, ref s) == false)
s = psp.ToolTipTextEx;
_toolTipText = psp.FormatItemText(s);
}
return (_toolTipText);
}
#endregion
#endregion
#region ProcessMouseLeave
internal void ProcessMouseLeave(EventArgs e, PieChart pieChart)
{
ChartControl chartControl = ChartControl;
if (chartControl != null)
chartControl.ToolTipText = "";
_toolTipText = null;
}
#endregion
#region ProcessMouseDown
internal bool ProcessMouseDown(
MouseEventArgs e, PieChart pieChart, PieSeriesPoint psp)
{
pieChart.IsDragging = false;
pieChart.PspDragType = PspDragType.None;
if ((Control.MouseButtons & MouseButtons.Left) == MouseButtons.Left)
{
_MouseDownPsp = psp;
Point cpt = pieChart.GetLocalAdjustedPoint(pieChart.CenterPoint);
Point rpt = e.Location;
_MouseDownRadius = MathHelper.GetPointRadius(ref rpt, cpt);
if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift)
{
_MouseDownOffset = (int)pieChart.ExplodedOffsetEx;
}
else
{
PieSeriesPoint rootPsp = (ShowAllRingsEx(pieChart) == true)
? psp.RootPsp : psp;
_MouseDownOffset = (double.IsNaN(rootPsp.DetachedOffset) == false)
? (int)rootPsp.DetachedOffset : 0;
}
MouseClickSliceAction mca = GetMouseClickSliceAction(pieChart);
switch (mca)
{
case MouseClickSliceAction.Explode:
case MouseClickSliceAction.Detach:
InitPspDrag(pieChart, psp);
break;
case MouseClickSliceAction.Select:
ProcessSliceSelect(e, pieChart, psp);
break;
}
return (true);
}
return (false);
}
#region InitPspDrag
private bool InitPspDrag(PieChart pieChart, PieSeriesPoint psp)
{
if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift)
{
if (pieChart.EnableShiftDragExplode == true)
{
pieChart.PspDragType = PspDragType.Explode;
return (true);
}
}
else
{
if (GetEnableDragDetach(pieChart) == true)
{
pieChart.PspDragType = PspDragType.Detach;
return (psp.IsSelected);
}
}
return (false);
}
#endregion
#region ProcessSliceSelect
private void ProcessSliceSelect(
MouseEventArgs e, PieChart pieChart, PieSeriesPoint psp)
{
bool select = true;
if ((pieChart.MultiSelect == true) &&
(Control.ModifierKeys & Keys.Control) == Keys.Control)
{
select = !psp.IsSelected;
}
else
{
if (InitPspDrag(pieChart, psp) == true)
return;
int selCount = pieChart.GetSelectionCount();
if (selCount != 1 || psp.IsSelected == false)
pieChart.SetSelected(false);
}
ProcessSliceSelectEx(pieChart, psp, select);
}
#region ProcessSliceSelectEx
private void ProcessSliceSelectEx(
PieChart pieChart, PieSeriesPoint psp, bool select)
{
PieSelectionMode psm = GetPieSelectionMode(pieChart);
switch (psm)
{
case PieSelectionMode.Pie:
pieChart.SetSelected(select);
break;
case PieSelectionMode.Series:
SelectPspSeries(SeriesPoints, true, select);
break;
case PieSelectionMode.Ring:
SelectPspRing(psp, select);
break;
case PieSelectionMode.Slice:
SelectPspSlice(pieChart, psp, select);
break;
case PieSelectionMode.Point:
psp.IsSelected = select;
break;
}
ChartControl.DoPieSelectionChangedEvent(pieChart, this, psp, psm);
}
#region SelectPspSeries
private void SelectPspSeries(
PieSeriesPointCollection spc, bool showInner, bool select)
{
if (spc != null)
{
foreach (PieSeriesPoint psp in spc)
{
if (psp.AllowSelect == true)
{
psp.IsSelected = select;
if (showInner == true)
SelectPspSeries(psp.SeriesPoints, true, select);
}
}
}
}
#endregion
#region SelectPspRing
private void SelectPspRing(PieSeriesPoint psp, bool select)
{
if (psp.PieRing != null)
{
if (psp.PieRing.AllowSelect == true)
{
foreach (PieSeriesPoint rpsp in psp.PieRing.Psps)
{
if (rpsp.AllowSelect == true)
rpsp.IsSelected = select;
}
}
}
}
#endregion
#region SelectPspSlice
private void SelectPspSlice(
PieChart pieChart, PieSeriesPoint psp, bool select)
{
PieSeriesPoint rootPsp = (ShowAllRingsEx(pieChart) == true)
? psp.RootPsp : psp;
if (rootPsp != null)
{
if (rootPsp.AllowSelect == true)
{
rootPsp.IsSelected = select;
SelectPspSliceEx(rootPsp.SeriesPoints, select);
}
}
}
#region SelectPspSliceEx
private void SelectPspSliceEx(PieSeriesPointCollection spc, bool select)
{
if (spc != null)
{
foreach (PieSeriesPoint psp in spc)
{
if (psp.AllowSelect == true)
{
psp.IsSelected = select;
SelectPspSliceEx(psp.SeriesPoints, select);
}
}
}
}
#endregion
#endregion
#endregion
#endregion
#endregion
#region ProcessMouseMove
internal void ProcessMouseMove(MouseEventArgs e, PieChart pieChart)
{
if (IsMouseDown == true && _MouseDownPsp != null)
ProcessMouseDownMove(e, pieChart);
}
#region ProcessMouseDownMove
private bool ProcessMouseDownMove(MouseEventArgs e, PieChart pieChart)
{
Point pt = e.Location;
if (pieChart.IsDragging == false)
{
if (Math.Abs(MouseDownPoint.X - pt.X) > 3 ||
Math.Abs(MouseDownPoint.Y - pt.Y) > 3)
{
pieChart.IsDragging = true;
}
}
else
{
int deltaOffset = GetDeltaOffset(pieChart, pt);
MouseClickSliceAction mca = GetMouseClickSliceAction(pieChart);
switch (pieChart.PspDragType)
{
case PspDragType.Detach:
if (GetEnableDragDetach(pieChart) == true)
DetachSelectedPsps(pieChart, mca, deltaOffset);
break;
case PspDragType.Explode:
if (pieChart.EnableShiftDragExplode == true)
ExplodePie(pieChart, deltaOffset);
break;
}
}
return (true);
}
#region GetDeltaOffset
private int GetDeltaOffset(PieChart pieChart, Point pt)
{
PieSeriesPoint psp = pieChart.HitPsp;
if (psp != null)
{
Point cpt = pieChart.GetLocalAdjustedPoint(pieChart.CenterPoint);
Point rpt = pt;
double radius = MathHelper.GetPointRadius(ref rpt, cpt);
if (ShowAllRingsEx(pieChart) == true)
{
double pspAngle = MathHelper.GetPointAngle(rpt);
double angle = psp.CenterAngle - pspAngle;
double delta = Math.Cos(MathHelper.ToRadians(angle)) * radius;
return ((int)delta);
}
return ((int)radius);
}
return (0);
}
#endregion
#region DetachSelectedPsps
private void DetachSelectedPsps(
PieChart pieChart, MouseClickSliceAction mca, int deltaOffset)
{
int offset = Math.Max(0, deltaOffset - _MouseDownRadius + _MouseDownOffset);
offset = Math.Min(offset, pieChart.MaxDetachedOffset);
PieSeriesPoint pspRoot =
ShowAllRingsEx(pieChart) ? _MouseDownPsp.RootPsp : _MouseDownPsp;
PieSelectionMode psm = GetPieSelectionMode(pieChart);
bool detachChange = false;
switch (psm)
{
case PieSelectionMode.Pie:
foreach (PieSeries series in pieChart.ChartSeries)
{
if (series.IsDisplayed == true)
{
foreach (PieSeriesPoint psp in series.SeriesPoints)
detachChange |= DetachPsp(psp, offset);
}
}
break;
case PieSelectionMode.Series:
case PieSelectionMode.Ring:
if (pspRoot.PieRing.AllowDetach == true)
{
foreach (PieSeriesPoint psp in pspRoot.PieRing.Psps)
detachChange |= DetachPsp(psp, offset);
}
break;
case PieSelectionMode.Slice:
case PieSelectionMode.Point:
if (mca == MouseClickSliceAction.Select)
{
if (pspRoot.PieRing.AllowDetach == true)
{
foreach (PieSeriesPoint psp in pspRoot.PieRing.Psps)
{
if (psp.IsSelected == true)
detachChange |= DetachPsp(psp, offset);
}
}
}
else
{
detachChange |= DetachPsp(pspRoot, offset);
}
break;
}
ChartControl.DoPieDetachChangedEvent(pieChart, this, psm);
}
#region DetachPsp
private void DetachPsp(PieSeriesPoint psp)
{
DetachPsp(psp, psp.DetachedOffset);
}
private bool DetachPsp(PieSeriesPoint psp, double offset)
{
if (psp.AllowDetach == true)
{
if (psp.DetachedOffset != offset || psp.IsDetached != (offset > 0))
{
psp.DetachedOffset = offset;
psp.IsDetached = (offset > 0);
return (true);
}
}
return (false);
}
#endregion
#endregion
#region ExplodePie
private void ExplodePie(PieChart pieChart, int deltaOffset)
{
int offset = Math.Max(0, deltaOffset - _MouseDownRadius + _MouseDownOffset);
offset = Math.Min(offset, pieChart.MaxExplodedOffset);
if (pieChart.ExplodedOffset != offset || pieChart.IsExploded != (offset > 0))
{
pieChart.IsExploded = (offset > 0);
pieChart.ExplodedOffset = offset;
ChartControl.DoPieExplodeChangedEvent(pieChart, this);
}
}
#endregion
#endregion
#endregion
#region ProcessMouseUp
internal void ProcessMouseUp(MouseEventArgs e, PieChart pieChart)
{
if (pieChart.IsDragging == false)
{
PieSeriesPoint psp = pieChart.HitPsp;
if (psp != null && psp == _MouseDownPsp)
{
ChartControl chartControl = ChartControl;
if (chartControl.DoChartMouseClickEvent(pieChart, psp, e) == false)
{
MouseClickSliceAction mca = GetMouseClickSliceAction(pieChart);
switch (mca)
{
case MouseClickSliceAction.Explode:
pieChart.IsExploded = !pieChart.IsExploded;
break;
case MouseClickSliceAction.Detach:
DetachPsp(ShowAllRingsEx(pieChart) ? psp.RootPsp : psp);
break;
case MouseClickSliceAction.Select:
if ((Control.ModifierKeys & Keys.Control) == Keys.None)
{
int selCount = pieChart.GetSelectionCount();
if (selCount != 1 || psp.IsSelected == false)
pieChart.SetSelected(false);
ProcessSliceSelectEx(pieChart, psp, true);
}
break;
}
}
}
}
}
#endregion
#region ProcessMouseDoubleClick
internal bool ProcessMouseDoubleClick(
MouseEventArgs e, PieChart pieChart, PieSeriesPoint psp)
{
if (e.Button == MouseButtons.Left)
{
MouseDoubleClickSliceAction mdca = GetMouseDoubleClickSliceAction(pieChart);
switch (mdca)
{
case MouseDoubleClickSliceAction.Detach:
psp.IsDetached = !psp.IsDetached;
break;
case MouseDoubleClickSliceAction.Explode:
pieChart.IsExploded = !pieChart.IsExploded;
break;
case MouseDoubleClickSliceAction.ChangeActiveRing:
Keys keys = Control.ModifierKeys;
if ((keys & Keys.Shift) == Keys.Shift)
ProcessPieDrillUp(pieChart, psp);
else if ((keys & Keys.Control) == Keys.None)
ProcessPieDrillDown(pieChart, psp);
break;
}
return (true);
}
return (false);
}
#region ProcessPieDrillUp
private void ProcessPieDrillUp(PieChart pieChart, PieSeriesPoint psp)
{
PieSeriesPointCollection spc1 = psp.Parent as PieSeriesPointCollection;
if (spc1 != null)
{
PieSeriesPoint psp2 = spc1.Parent as PieSeriesPoint;
if (psp2 != null)
{
PieSeriesPointCollection spc2 = GetPieSeriesPointCollection(psp2);
if (spc2 != null)
{
ChartControl chartControl = ChartControl;
if (chartControl.DoPieRingLevelChangingEvent(pieChart, spc1, spc2) == false)
{
SetActiveSeriesPointCollection(spc2);
chartControl.DoPieRingLevelChangedEvent(pieChart, spc1, spc2);
}
}
}
}
}
#endregion
#region ProcessPieDrillDown
private void ProcessPieDrillDown(PieChart pieChart, PieSeriesPoint psp)
{
if (psp.ChartSeries.ShowAllRingsEx(pieChart) == false)
{
PieSeriesPointCollection spc1 = psp.Parent as PieSeriesPointCollection;
if (psp.SeriesPoints.Count > 0)
{
ChartControl chartControl = ChartControl;
PieSeriesPointCollection spc2 = psp.SeriesPoints;
if (chartControl.DoPieRingLevelChangingEvent(pieChart, spc1, spc2) == false)
{
SetActiveSeriesPointCollection(psp.SeriesPoints);
chartControl.DoPieRingLevelChangedEvent(pieChart, spc1, spc2);
}
}
}
}
#endregion
#endregion
#endregion
#region SetSelected
///
/// Selects or clears all series PieSeriesPoints.
///
///
public void SetSelected(bool select)
{
PieSeriesPointCollection spc = SeriesPoints;
spc.SetSelected(select);
if (spc.OtherSlicePsp != null)
spc.OtherSlicePsp.IsSelected = select;
}
#endregion
#region GetVisibleSelectionCount
///
/// Gets the count of the currently Visible selected pie series points.
///
///
public int GetVisibleSelectionCount()
{
int count = GetVisibleSelectionCountEx(SeriesPoints);
return (count);
}
#region GetVisibleSelectionCountEx
private int GetVisibleSelectionCountEx(PieSeriesPointCollection spc)
{
int count = 0;
if (spc != null && spc.Count > 0)
{
foreach (PieSeriesPoint psp in spc)
{
if (psp.IsSelected == true && psp.Visible == true)
count++;
count += GetVisibleSelectionCountEx(psp.SeriesPoints);
}
if (spc.OtherSlicePsp != null)
{
if (spc.OtherSlicePsp.IsSelected == true && spc.OtherSlicePsp.Visible == true)
count++;
}
}
return (count);
}
#endregion
#endregion
#region GetVisibleSelectedPoints
///
/// Gets a list of the currently Visible selected pie series points.
///
///
public List GetVisibleSelectedPoints()
{
List list = new List();
GetVisibleSelectedPointsEx(list, SeriesPoints);
return (list);
}
#region GetVisibleSelectedPointsEx
private void GetVisibleSelectedPointsEx(
List list, PieSeriesPointCollection spc)
{
if (spc != null && spc.Count > 0)
{
foreach (PieSeriesPoint psp in spc)
{
if (psp.IsSelected == true && psp.Visible == true)
list.Add(psp);
GetVisibleSelectedPointsEx(list, psp.SeriesPoints);
}
if (spc.OtherSlicePsp != null)
{
if (spc.OtherSlicePsp.IsSelected == true && spc.OtherSlicePsp.Visible == true)
list.Add(spc.OtherSlicePsp);
}
}
}
#endregion
#endregion
#region GetSelectionCount
///
/// Gets a count of the selected pie series points.
///
///
public int GetSelectionCount()
{
int count = GetSelectionCountEx(SeriesPoints);
return (count);
}
#region GetSelectionCountEx
private int GetSelectionCountEx(PieSeriesPointCollection spc)
{
int count = 0;
if (spc != null && spc.Count > 0)
{
foreach (PieSeriesPoint psp in spc)
{
if (psp.IsSelected == true)
count++;
count += GetSelectionCountEx(psp.SeriesPoints);
}
if (spc.OtherSlicePsp != null)
{
if (spc.OtherSlicePsp.IsSelected == true )
count++;
}
}
return (count);
}
#endregion
#endregion
#region GetSelectedPoints
///
/// Gets a list of the currently selected pie series points.
///
///
public List GetSelectedPoints()
{
List list = new List();
GetSelectedPointsEx(list, SeriesPoints);
return (list);
}
#region GetSelectedPointsEx
private void GetSelectedPointsEx(
List list, PieSeriesPointCollection spc)
{
if (spc != null && spc.Count > 0)
{
foreach (PieSeriesPoint psp in spc)
{
if (psp.IsSelected == true)
list.Add(psp);
GetSelectedPointsEx(list, psp.SeriesPoints);
}
if (spc.OtherSlicePsp != null)
{
if (spc.OtherSlicePsp.IsSelected == true)
list.Add(spc.OtherSlicePsp);
}
}
}
#endregion
#endregion
#region Style handling
#region StyleChanged
protected override void StyleChanged(object sender, PropertyChangedEventArgs e)
{
base.StyleChanged(sender, e);
if (sender is ChartLegendItemVisualStyles && LegendItem != null)
InvalidateRender(LegendItem.Bounds);
}
#endregion
#endregion
#region InvalidateRender
public override void InvalidateRender()
{
BaseChart chart = Parent as BaseChart;
if (chart != null)
chart.InvalidateRender();
}
#endregion
#region InvalidateRecalc
internal override void InvalidateRecalc()
{
SeriesRangeChanged = true;
base.InvalidateRecalc();
}
#endregion
#region ILegendItem
#region GetLegendItem
public override ChartLegendItem GetLegendItem()
{
LegendItem = null;
if (ShowInLegend == true)
{
PieChart pieChart = Parent as PieChart;
if (pieChart != null)
{
if (pieChart.VisibleSeriesCount > 1)
{
if (pieChart.Legend.ShowPieSeriesInLegend == true)
{
LegendItem = base.GetLegendItem();
LegendItem.ShowCheckBox = pieChart.Legend.ShowPieSeriesCheckBoxes;
}
}
}
}
return (LegendItem);
}
#endregion
#region AddSubLegendItems
internal override void AddSubLegendItems(List list)
{
PieChart pieChart = Parent as PieChart;
if (pieChart != null)
{
if (ShowInLegend == true)
{
ChartLegend legend = pieChart.Legend;
if (ShowAllRingsEx(pieChart) == true)
{
if (PieRings != null && PieRings.Count > 0)
{
bool addRings =
((PieRings.Count > 1) &&
(pieChart.Legend.ShowPieRingsInLegend == true));
foreach (PieRing pieRing in PieRings)
{
if (addRings == true)
{
ChartLegendItem litem = pieRing.GetLegendItem();
if (litem != null)
{
litem.ShowCheckBox = legend.ShowPieRingCheckBoxes;
list.Add(litem);
}
}
if (legend.ShowPieSeriesPointsInLegend == true)
AddSubLegendItemsEx(legend, list, pieRing.GetLegendItems());
}
}
}
else
{
PieSeriesPointCollection spc = GetActiveSeriesPointCollection();
if (spc != null)
{
if (spc.PieSlices != null)
spc = spc.PieSlices;
if (spc != null)
{
foreach (PieSeriesPoint psp in spc)
{
if (psp.IsDisplayable == true)
AddSubLegendItemsEx(legend, list, psp.GetLegendItems());
}
}
}
}
}
}
}
#region AddSubLegendItemsEx
private void AddSubLegendItemsEx(ChartLegend legend,
List list, List items)
{
if (items != null && items.Count > 0)
{
foreach (ChartLegendItem item in items)
{
item.ShowCheckBox = legend.ShowPieSeriesPointCheckBoxes;
if (legend.CombineLikeItems == true)
{
ChartLegendItem likeItem = FindLikeItems(list, item);
if (likeItem == null)
{
item.ChartControl = ChartControl;
likeItem = new ChartLegendItem();
likeItem.Parent = legend;
likeItem.ChartControl = ChartControl;
likeItem.Name = item.Name;
likeItem.ItemText = item.ItemText;
likeItem.IsEnabled = item.IsEnabled;
list.Add(likeItem);
}
item.LikeItem = likeItem;
likeItem.ChartItems.AddRange(item.ChartItems);
}
else
{
list.Add(item);
}
}
}
}
#region FindLikeItems
private ChartLegendItem FindLikeItems(List list, ChartLegendItem item)
{
string itemName = item.Name ?? item.ItemText;
if (String.IsNullOrEmpty(itemName) == false)
{
foreach (ChartLegendItem likeItem in list)
{
string likeName = likeItem.Name ?? likeItem.ItemText;
if (String.IsNullOrEmpty(likeName) == false)
{
if (likeName.Equals(itemName) == true)
return (likeItem);
}
}
}
return (null);
}
#endregion
#endregion
#endregion
#region GetLegendItemColorEx
internal override Color GetLegendItemColorEx()
{
return (Color.Empty);
}
#endregion
#region RenderLegendItemMarkerEx
internal override void RenderLegendItemMarkerEx(
Graphics g, ChartLegendItem litem, ChartLegendItemVisualStyle style)
{
Rectangle bounds = litem.MarkerBounds;
bounds.Width--;
bounds.Height--;
Color color = GetLegendItemColor();
using (GraphicsPath path = new GraphicsPath())
{
path.AddEllipse(bounds);
using (Brush br = new SolidBrush(color))
g.FillPath(br, path);
}
}
#endregion
#region TrackLegendItem
///
/// Gets whether legend item tracking is enabled.
///
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool TrackLegendItem
{
get
{
PieChart pieChart = Parent as PieChart;
if (pieChart != null)
{
return (pieChart.Legend.TrackingMode == LegendTrackingMode.ChartAndLegend ||
pieChart.Legend.TrackingMode == LegendTrackingMode.Legend);
}
return (false);
}
}
#endregion
#region FormatItemText
///
/// Formats the provided item text.
///
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string FormatItemText(string text)
{
return (text);
}
#endregion
#region OnCheckStateChanged
internal override void OnCheckStateChanged()
{
if (LegendItem != null)
LegendItem.UpdateCheckState();
foreach (PieRing pieRing in PieRings)
{
if (pieRing.LegendItem != null)
{
pieRing.LegendItem.IsEnabled = pieRing.IsEnabled;
}
else
{
foreach (PieSeriesPoint psp in pieRing.Psps)
{
if (psp.LegendItem != null)
psp.LegendItem.IsEnabled = psp.IsEnabled;
}
}
}
base.OnCheckStateChanged();
}
#endregion
#endregion
#region Copy/CopyTo
public override ChartVisualElement Copy()
{
ChartSeries copy = new ChartSeries();
CopyTo(copy);
return (copy);
}
public override void CopyTo(ChartVisualElement copy)
{
PieSeries c = copy as PieSeries;
if (c != null)
{
base.CopyTo(c);
c.CenterFirstSlice = CenterFirstSlice;
if (_CustomPalette != null)
{
c.CustomPalette = new Color[CustomPalette.Length];
CustomPalette.CopyTo(c.CustomPalette, 0);
}
c.EnableDragDetach = EnableDragDetach;
c.ExplodedMargin = ExplodedMargin;
c.MouseClickSliceAction = MouseClickSliceAction;
c.MouseDoubleClickSliceAction = MouseDoubleClickSliceAction;
c.PaletteGroup = PaletteGroup;
c.PieSelectionMode = PieSelectionMode;
c.ReversePaletteColors = ReversePaletteColors;
c.RingWeight = RingWeight;
c.SubSliceVisualLayout = (_SliceVisualLayout != null) ? _SliceVisualLayout.Copy() : null;
foreach (PieSeriesPoint psp in SeriesPoints)
c.SeriesPoints.Add(psp.Copy());
c.ShowAllRings = ShowAllRings;
c.ShowOtherSlice = ShowOtherSlice;
c.SubSliceVisualLayout = (_SliceVisualLayout != null) ? SubSliceVisualLayout.Copy() : null;
}
}
#endregion
#region GetSerialData
internal override SerialElementCollection GetSerialData(string serialName)
{
SerialElementCollection sec = new SerialElementCollection();
if (serialName != null)
{
if (serialName.Equals("") == true)
serialName = "PieSeries";
sec.AddStartElement(serialName);
}
sec.AddValue("CenterFirstSlice", CenterFirstSlice, Tbool.NotSet);
if (_CustomPalette != null && _CustomPalette.Length > 0)
{
sec.AddStartElement("CustomPalette count=\"" + _CustomPalette.Length + "\"");
foreach (Color color in _CustomPalette)
sec.AddValue("PaletteColor", color);
sec.AddEndElement("CustomPalette");
}
sec.AddValue("EnableDragDetach", EnableDragDetach, Tbool.NotSet);
sec.AddValue("ExplodedMargin", ExplodedMargin, double.NaN);
sec.AddValue("MouseClickSliceAction", MouseClickSliceAction, MouseClickSliceAction.NotSet);
sec.AddValue("MouseDoubleClickSliceAction", MouseDoubleClickSliceAction, MouseDoubleClickSliceAction.NotSet);
sec.AddValue("PaletteGroup", PaletteGroup, PaletteGroup.NotSet);
sec.AddValue("PieSelectionMode", PieSelectionMode, PieSelectionMode.NotSet);
sec.AddValue("ReversePaletteColors", ReversePaletteColors, false);
sec.AddValue("RingWeight", RingWeight, 100);
if (_SeriesPoints != null)
{
PieSeriesPointCollection spc = _SeriesPoints;
if (spc.Count > 0)
{
sec.AddStartElement("SeriesPoints count=\"" + spc.Count + "\"");
foreach (PieSeriesPoint psp in spc)
sec.AddElement(psp.GetSerialData());
sec.AddEndElement("SeriesPoints");
}
}
sec.AddValue("ShowAllRings", ShowAllRings, Tbool.NotSet);
sec.AddValue("ShowOtherSlice", ShowOtherSlice, Tbool.NotSet);
if (_SliceVisualLayout != null)
sec.AddElement(_SliceVisualLayout.GetSerialData(true));
sec.AddElement(base.GetSerialData(null));
if (serialName != null)
sec.AddEndElement(serialName);
return (sec);
}
#endregion
#region PutSerialData
#region ProcessValue
internal override void ProcessValue(SerialElement se)
{
ChartXy chartXy = Parent as ChartXy;
switch (se.Name)
{
case "CenterFirstSlice":
CenterFirstSlice = (Tbool)se.GetValueEnum(typeof(Tbool));
break;
case "EnableDragDetach":
EnableDragDetach = (Tbool)se.GetValueEnum(typeof(Tbool));
break;
case "ExplodedMargin":
ExplodedMargin = double.Parse(se.StringValue);
break;
case "MouseClickSliceAction":
MouseClickSliceAction = (MouseClickSliceAction)se.GetValueEnum(typeof(MouseClickSliceAction));
break;
case "MouseDoubleClickSliceAction":
MouseDoubleClickSliceAction = (MouseDoubleClickSliceAction)se.GetValueEnum(typeof(MouseDoubleClickSliceAction));
break;
case "PaletteColor":
CustomPalette[se.ValueIndex] = se.GetValueColor();
break;
case "PaletteGroup":
PaletteGroup = (PaletteGroup)se.GetValueEnum(typeof(PaletteGroup));
break;
case "PieSelectionMode":
PieSelectionMode = (PieSelectionMode)se.GetValueEnum(typeof(PieSelectionMode));
break;
case "ReversePaletteColors":
ReversePaletteColors = bool.Parse(se.StringValue);
break;
case "RingWeight":
RingWeight = int.Parse(se.StringValue);
break;
case "ShowAllRings":
ShowAllRings = (Tbool)se.GetValueEnum(typeof(Tbool));
break;
case "ShowOtherSlice":
ShowOtherSlice = (Tbool)se.GetValueEnum(typeof(Tbool));
break;
default:
base.ProcessValue(se);
break;
}
}
#endregion
#region ProcessCollection
internal override void ProcessCollection(SerialElement se)
{
SerialElementCollection sec = se.Sec;
switch (se.Name)
{
case "CustomPalette":
if (se.ArrayCount > 0)
{
CustomPalette = new Color[se.ArrayCount];
sec.PutSerialData(this);
}
break;
case "SeriesPoint":
PieSeriesPoint psp = new PieSeriesPoint();
sec.PutSerialData(psp);
SeriesPoints.Add(psp);
break;
case "SeriesPoints":
if (se.ArrayCount > 0)
{
SeriesPoints = new PieSeriesPointCollection();
sec.PutSerialData(this);
}
break;
case "SliceVisualLayout":
sec.PutSerialData(SubSliceVisualLayout);
break;
default:
base.ProcessCollection(se);
break;
}
}
#endregion
#endregion
#region Series States
[Flags]
private enum States : uint
{
FilterIgnoreMatchCase = (1U << 0),
IsSorted = (1U << 1),
IsRotated = (1U << 2),
StackQualitativePoints = (1U << 3),
DisplayLinePointsOnTop = (1U << 4),
EnableEmptyValues = (1U << 5),
ShowEmptyLines = (1U << 6),
ShowEmptyPoints = (1U << 7),
ShowOriginValueLabels = (1U << 8),
ShowHiLoBarMedianLines = (1U << 9),
NeedToUpdateBindings = (1U << 10),
IsInnerRingSeries = (1U << 11),
IsOuterRingSeries = (1U << 12),
IsOffset = (1U << 13),
IsDetached = (1U << 14),
ShowInnerRingsEx = (1U << 15),
ReversePaletteColors = (1U << 16),
}
#region TestState
private bool TestState(States state)
{
return ((_States & state) == state);
}
#endregion
#region SetState
private void SetState(States state, bool value)
{
if (value == true)
_States |= state;
else
_States &= ~state;
}
#endregion
#endregion
#region IDisposable
public override void Dispose()
{
Visible = false;
SeriesPoints = null;
SubSliceVisualLayout = null;
base.Dispose();
}
#endregion
}
#region WordPos
internal class WordPos
{
public string Text;
public int Index;
public int Length;
}
#endregion
#region WordPosLine
internal class WordPosLine
{
public double ArcWidth;
public double LineWidth;
public double Offset;
public List Wps = new List();
}
#endregion
#region enums
#region SliceRenderType
public enum SliceRenderType
{
///
/// Full slice.
///
FullSlice,
///
/// Extent slice (partial slice).
///
ExtentSlice,
///
/// Extentwhitespace.
///
ExtentWhitespace,
///
/// 'Other' slice whitespace.
///
OtherWhitespace,
}
#endregion
#region ExtentFillRange
public enum ExtentFillRange
{
///
/// Not set (default is ByPoint)
///
NotSet = 0,
///
/// Extents are filled according to each individual
/// PieSeriesPoint extent value.
///
ByPoint,
///
/// Extents are filled according to the slice
/// maximum extent value.
///
BySlice,
///
/// Extents are filled according to the associated
/// PieRing maximum extent value.
///
ByRing,
}
#endregion
#region SliceImageCropMode
public enum SliceImageCropMode
{
NotSet = -1,
NoAction,
ClipImage,
HideImage,
}
#endregion
#region SliceImageRadiusAnchor
///
/// Defines the anchor for the slice image (ie. the image's bounding edge).
///
public enum SliceImageRadiusAnchor
{
///
/// NotSet
///
NotSet = -1,
///
/// The pie InnerRadius will be the anchor.
///
InnerRadius,
///
/// The pie ExtentRadius will be the anchor. The 'ExtentRadius' denotes
/// how far the slice extends out from the slice center - which may not
/// be as far as the OuterRadius (specified by the ValuesY[1] value).
///
ExtentRadius,
///
/// The pie OuterRadius will be the anchor.
///
OuterRadius,
}
#endregion
#region PieRefLineDisplayMode
[Flags]
///
/// Defines the type of ReferenceLine to display.
///
public enum PieRefLineDisplayMode
{
///
/// NotSet
///
NotSet = 0,
///
/// No Reference lines
///
None = (1 << 0),
///
/// Extent Average Line.
///
ExtentAverage = (1 << 3),
///
/// Extent Minimum Line.
///
ExtentMaximum = (1 << 2),
///
/// Extent Minimum Line.
///
ExtentMinimum = (1 << 1),
///
/// Outer Extent Line.
///
ExtentOuterRadius = (1 << 4),
///
/// All User defined Lines.
///
UserDefined = (1 << 5),
}
#endregion
#endregion
}