6324 lines
195 KiB
C#
6324 lines
195 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Represents the collection of PieSeries.
|
|
/// </summary>
|
|
[Editor("DevComponents.Charts.Design.ChartSeriesCollectionEditor, DevComponents.Charts.Design, " +
|
|
"Version=14.1.0.37, Culture=neutral, PublicKeyToken=90f470f34c89ccaf", typeof(UITypeEditor))]
|
|
public class ChartPieSeriesCollection : CustomNamedCollection<PieSeries>
|
|
{
|
|
#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<PieRing> _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
|
|
|
|
/// <summary>
|
|
/// Gets or sets whether the first pie slice is centered
|
|
/// on the starting angle or starts on the starting angle. Default is false.
|
|
/// </summary>
|
|
[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
|
|
|
|
///<summary>
|
|
/// Gets or sets the custom palette for the series.
|
|
///</summary>
|
|
[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
|
|
|
|
///<summary>
|
|
/// 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.
|
|
///</summary>
|
|
[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
|
|
|
|
/// <summary>
|
|
/// 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).
|
|
/// </summary>
|
|
[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
|
|
|
|
/// <summary>
|
|
/// Gets or sets the action used when the user clicks on a pie slice.
|
|
/// </summary>
|
|
[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
|
|
|
|
/// <summary>
|
|
/// Gets or sets the action used when the user double-clicks on a pie slice.
|
|
/// </summary>
|
|
[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
|
|
|
|
/// <summary>
|
|
/// Gets or sets the palette color group to use (Light/Medium/Dark/Color1/MonoBlue/etc).
|
|
/// </summary>
|
|
[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
|
|
|
|
/// <summary>
|
|
/// Gets or sets the mode used to perfoem pie selections.
|
|
/// </summary>
|
|
[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
|
|
|
|
///<summary>
|
|
/// Gets or sets whether default palette colors are utilized in reverse order.
|
|
///</summary>
|
|
[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
|
|
|
|
///<summary>
|
|
/// Gets or sets the 'relative' total combined thickness of all series rings, as
|
|
/// compared to the relative thickness of other series combined rings.
|
|
/// rings.
|
|
///</summary>
|
|
[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
|
|
|
|
///<summary>
|
|
/// Gets or sets the series point data collection.
|
|
///</summary>
|
|
[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
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
[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
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
[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
|
|
|
|
/// <summary>
|
|
/// Gets or sets the default layout of the subordinate or nested series slices.
|
|
/// </summary>
|
|
[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
|
|
|
|
/// <summary>
|
|
/// Gets or sets whether the series is visible or not.
|
|
/// </summary>
|
|
[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<PieRing> PieRings
|
|
{
|
|
get
|
|
{
|
|
if (_PieRings == null)
|
|
_PieRings = new List<PieRing>();
|
|
|
|
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<PieRing> 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<PieReferenceLine> 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
|
|
|
|
/// <summary>
|
|
/// Calculates the max 'a' side of the image.
|
|
/// </summary>
|
|
/// <param name="psp"></param>
|
|
/// <param name="radius"></param>
|
|
/// <param name="size"></param>
|
|
/// <returns></returns>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Gets the max radius of a circle image at the given ray radius.
|
|
/// </summary>
|
|
/// <param name="psp"></param>
|
|
/// <param name="radius"></param>
|
|
/// <returns></returns>
|
|
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<WordPosLine> 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<WordPosLine> 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<WordPosLine> 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<WordPosLine> wpsLines = null;
|
|
|
|
if (maxLineCount > 0)
|
|
{
|
|
List<WordPos> 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<WordPosLine> 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<WordPosLine> lines = FitAdaptiveLines(psp,
|
|
offset, -height, cBounds, maxLineCount, wps, out clipped, lstyle);
|
|
|
|
return (lines);
|
|
}
|
|
|
|
for (int i = 0; i < maxLineCount; i++)
|
|
{
|
|
List<WordPosLine> 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<WordPosLine> FitAdaptiveLines(
|
|
PieSeriesPoint psp, double offset, float height, RectangleF[] cBounds,
|
|
int count, List<WordPos> wps, out bool clipped, SliceInnerLabelVisualStyle lstyle)
|
|
{
|
|
List<WordPosLine> wpsLines = new List<WordPosLine>();
|
|
|
|
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<WordPosLine> 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<WordPosLine> 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<WordPosLine> 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<WordPosLine> 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<WordPosLine> wpsLines = null;
|
|
|
|
clipped = false;
|
|
|
|
if (maxLineCount > 0)
|
|
{
|
|
List<WordPos> 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<WordPosLine> 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<WordPosLine> 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<WordPos> GetWordWidths(string text, RectangleF[] cBounds)
|
|
{
|
|
List<WordPos> wps = new List<WordPos>();
|
|
|
|
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
|
|
|
|
/// <summary>
|
|
/// Gets the current 'active' SeriesPointCollection (useful when
|
|
/// 'ShowInnerRings' is set to false).
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public PieSeriesPointCollection GetActiveSeriesPointCollection()
|
|
{
|
|
PieChart pieChart = Parent as PieChart;
|
|
|
|
if (pieChart != null)
|
|
{
|
|
return ((ShowAllRingsEx(pieChart) == true)
|
|
? SeriesPoints : (ActiveSeriesPointCollection ?? SeriesPoints));
|
|
}
|
|
|
|
return (null);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SetActiveSeriesPointCollection
|
|
|
|
/// <summary>
|
|
/// Sets the current 'active' SeriesPointCollection (useful when
|
|
/// 'ShowInnerRings' is set to false).
|
|
/// </summary>
|
|
/// <param name="spc"></param>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Gets the SeriesPointCollection containing the given PieSeriesPoint
|
|
/// </summary>
|
|
/// <param name="psp"></param>
|
|
/// <returns></returns>
|
|
public PieSeriesPointCollection GetPieSeriesPointCollection(PieSeriesPoint psp)
|
|
{
|
|
return (psp.Parent as PieSeriesPointCollection);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetPieLabels
|
|
|
|
internal List<PieLabel> GetPieLabels(Graphics g, PieChart pieChart)
|
|
{
|
|
List<PieLabel> pieLabels = new List<PieLabel>();
|
|
|
|
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
|
|
|
|
/// <summary>
|
|
/// Gets the 'Other' PieSeriesPoint
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Selects or clears all series PieSeriesPoints.
|
|
/// </summary>
|
|
/// <param name="select"></param>
|
|
public void SetSelected(bool select)
|
|
{
|
|
PieSeriesPointCollection spc = SeriesPoints;
|
|
|
|
spc.SetSelected(select);
|
|
|
|
if (spc.OtherSlicePsp != null)
|
|
spc.OtherSlicePsp.IsSelected = select;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetVisibleSelectionCount
|
|
|
|
/// <summary>
|
|
/// Gets the count of the currently Visible selected pie series points.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Gets a list of the currently Visible selected pie series points.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public List<PieSeriesPoint> GetVisibleSelectedPoints()
|
|
{
|
|
List<PieSeriesPoint> list = new List<PieSeriesPoint>();
|
|
|
|
GetVisibleSelectedPointsEx(list, SeriesPoints);
|
|
|
|
return (list);
|
|
}
|
|
|
|
#region GetVisibleSelectedPointsEx
|
|
|
|
private void GetVisibleSelectedPointsEx(
|
|
List<PieSeriesPoint> 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
|
|
|
|
/// <summary>
|
|
/// Gets a count of the selected pie series points.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Gets a list of the currently selected pie series points.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public List<PieSeriesPoint> GetSelectedPoints()
|
|
{
|
|
List<PieSeriesPoint> list = new List<PieSeriesPoint>();
|
|
|
|
GetSelectedPointsEx(list, SeriesPoints);
|
|
|
|
return (list);
|
|
}
|
|
|
|
#region GetSelectedPointsEx
|
|
|
|
private void GetSelectedPointsEx(
|
|
List<PieSeriesPoint> 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<ChartLegendItem> 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<ChartLegendItem> list, List<ChartLegendItem> 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<ChartLegendItem> 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
|
|
|
|
/// <summary>
|
|
/// Gets whether legend item tracking is enabled.
|
|
/// </summary>
|
|
[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
|
|
|
|
/// <summary>
|
|
/// Formats the provided item text.
|
|
/// </summary>
|
|
[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<WordPos> Wps = new List<WordPos>();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region enums
|
|
|
|
#region SliceRenderType
|
|
|
|
public enum SliceRenderType
|
|
{
|
|
/// <summary>
|
|
/// Full slice.
|
|
/// </summary>
|
|
FullSlice,
|
|
|
|
/// <summary>
|
|
/// Extent slice (partial slice).
|
|
/// </summary>
|
|
ExtentSlice,
|
|
|
|
/// <summary>
|
|
/// Extentwhitespace.
|
|
/// </summary>
|
|
ExtentWhitespace,
|
|
|
|
/// <summary>
|
|
/// 'Other' slice whitespace.
|
|
/// </summary>
|
|
OtherWhitespace,
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ExtentFillRange
|
|
|
|
public enum ExtentFillRange
|
|
{
|
|
/// <summary>
|
|
/// Not set (default is ByPoint)
|
|
/// </summary>
|
|
NotSet = 0,
|
|
|
|
/// <summary>
|
|
/// Extents are filled according to each individual
|
|
/// PieSeriesPoint extent value.
|
|
/// </summary>
|
|
ByPoint,
|
|
|
|
/// <summary>
|
|
/// Extents are filled according to the slice
|
|
/// maximum extent value.
|
|
/// </summary>
|
|
BySlice,
|
|
|
|
/// <summary>
|
|
/// Extents are filled according to the associated
|
|
/// PieRing maximum extent value.
|
|
/// </summary>
|
|
ByRing,
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SliceImageCropMode
|
|
|
|
public enum SliceImageCropMode
|
|
{
|
|
NotSet = -1,
|
|
|
|
NoAction,
|
|
ClipImage,
|
|
HideImage,
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SliceImageRadiusAnchor
|
|
|
|
/// <summary>
|
|
/// Defines the anchor for the slice image (ie. the image's bounding edge).
|
|
/// </summary>
|
|
public enum SliceImageRadiusAnchor
|
|
{
|
|
/// <summary>
|
|
/// NotSet
|
|
/// </summary>
|
|
NotSet = -1,
|
|
|
|
/// <summary>
|
|
/// The pie InnerRadius will be the anchor.
|
|
/// </summary>
|
|
InnerRadius,
|
|
|
|
/// <summary>
|
|
/// 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).
|
|
/// </summary>
|
|
ExtentRadius,
|
|
|
|
/// <summary>
|
|
/// The pie OuterRadius will be the anchor.
|
|
/// </summary>
|
|
OuterRadius,
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region PieRefLineDisplayMode
|
|
|
|
[Flags]
|
|
/// <summary>
|
|
/// Defines the type of ReferenceLine to display.
|
|
/// </summary>
|
|
public enum PieRefLineDisplayMode
|
|
{
|
|
/// <summary>
|
|
/// NotSet
|
|
/// </summary>
|
|
NotSet = 0,
|
|
|
|
/// <summary>
|
|
/// No Reference lines
|
|
/// </summary>
|
|
None = (1 << 0),
|
|
|
|
/// <summary>
|
|
/// Extent Average Line.
|
|
/// </summary>
|
|
ExtentAverage = (1 << 3),
|
|
|
|
/// <summary>
|
|
/// Extent Minimum Line.
|
|
/// </summary>
|
|
ExtentMaximum = (1 << 2),
|
|
|
|
/// <summary>
|
|
/// Extent Minimum Line.
|
|
/// </summary>
|
|
ExtentMinimum = (1 << 1),
|
|
|
|
/// <summary>
|
|
/// Outer Extent Line.
|
|
/// </summary>
|
|
ExtentOuterRadius = (1 << 4),
|
|
|
|
/// <summary>
|
|
/// All User defined Lines.
|
|
/// </summary>
|
|
UserDefined = (1 << 5),
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
}
|