1093 lines
40 KiB
C#
1093 lines
40 KiB
C#
//============================================================================
|
|
//PieItem Class
|
|
//Copyright ?2005 Bob Kaye
|
|
//
|
|
//This library is free software; you can redistribute it and/or
|
|
//modify it under the terms of the GNU Lesser General Public
|
|
//License as published by the Free Software Foundation; either
|
|
//version 2.1 of the License, or (at your option) any later version.
|
|
//
|
|
//This library is distributed in the hope that it will be useful,
|
|
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
//Lesser General Public License for more details.
|
|
//
|
|
//You should have received a copy of the GNU Lesser General Public
|
|
//License along with this library; if not, write to the Free Software
|
|
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
//=============================================================================
|
|
|
|
using System;
|
|
using System.Drawing;
|
|
using System.Collections;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Globalization;
|
|
using System.Runtime.Serialization;
|
|
using System.Security.Permissions;
|
|
|
|
namespace DrawGraph
|
|
{
|
|
/// <summary>
|
|
/// A class representing a pie chart object comprised of one or more
|
|
/// <see cref="PieItem"/>s.
|
|
/// </summary>
|
|
/// <author> Bob Kaye </author>
|
|
/// <version> $Revision: 1.31 $ $Date: 2007/01/25 07:56:09 $ </version>
|
|
[Serializable]
|
|
public class PieItem : CurveItem, ICloneable, ISerializable
|
|
{
|
|
|
|
#region Fields
|
|
/*
|
|
/// <summary>
|
|
/// Private field instance of the <see cref="PieItem"/> class indicating whether
|
|
/// the instance is displayed in 2D or 3D.(see <see cref="PieItem.PieType"/>)
|
|
/// </summary>
|
|
private PieType pieType;
|
|
*/
|
|
/// <summary>
|
|
/// Percentage (expressed as #.##) of <see cref="PieItem"/> radius to
|
|
/// which this <see cref="PieItem"/> is to be displaced from the center.
|
|
/// Displacement is done outward along the radius
|
|
/// bisecting the chord of this <see cref="PieItem"/>. Maximum allowable value
|
|
/// is 0.5.
|
|
/// </summary>
|
|
private double _displacement;
|
|
|
|
/// <summary>
|
|
/// A <see cref="ZedGraph.TextObj"/> which will customize the label display of this
|
|
/// <see cref="PieItem"/>
|
|
/// </summary>
|
|
private TextObj _labelDetail;
|
|
|
|
/// <summary>
|
|
/// Private field that stores the <see cref="ZedGraph.Fill"/> data for this
|
|
/// <see cref="PieItem"/>. Use the public property <see cref="Fill"/> to
|
|
/// access this value.
|
|
/// </summary>
|
|
private Fill _fill;
|
|
|
|
/// <summary>
|
|
/// Private field that stores the <see cref="Border"/> class that defines the
|
|
/// properties of the border around this <see cref="PieItem"/>. Use the public
|
|
/// property <see cref="Border"/> to access this value.
|
|
/// </summary>
|
|
private Border _border;
|
|
|
|
/// <summary>
|
|
/// Private field that stores the absolute value of this <see cref="PieItem"/> instance.
|
|
/// Value will be set to zero if submitted value is less than zero.
|
|
/// </summary>
|
|
private double _pieValue;
|
|
|
|
/// <summary>
|
|
/// An enum that specifies how each <see cref="CurveItem.Label"/> for this <see cref="PieItem"/> object
|
|
/// will be displayed. Use the public property <see cref="LabelType"/> to access this data.
|
|
/// Use enum <see cref="ZedGraph.PieLabelType"/>.
|
|
/// </summary>
|
|
private PieLabelType _labelType;
|
|
/// <summary>
|
|
/// The point on the arc of this <see cref="PieItem"/> representing the intersection of
|
|
/// the arc and the explosion radius.
|
|
/// </summary>
|
|
private PointF _intersectionPoint;
|
|
|
|
/// <summary>
|
|
/// The bounding rectangle for this <see cref="PieItem"/>.
|
|
/// </summary>
|
|
private RectangleF _boundingRectangle;
|
|
|
|
/// <summary>
|
|
/// The formatted string for this <see cref="PieItem"/>'s label. Formatting is
|
|
/// done based on the <see cref="PieLabelType"/>.
|
|
/// </summary>
|
|
private string _labelStr;
|
|
/// <summary>
|
|
/// The point at which the line between this <see cref="PieItem"/> and its
|
|
/// label bends to the horizontal.
|
|
/// </summary>
|
|
private PointF _pivotPoint;
|
|
/// <summary>
|
|
/// The point at the end of the line between this <see cref="PieItem"/> and
|
|
/// it's label (i.e. the beginning of the label display)
|
|
/// </summary>
|
|
private PointF _endPoint;
|
|
|
|
/// <summary>
|
|
/// Private field to hold the GraphicsPath of this <see cref="PieItem"/> to be
|
|
/// used for 'hit testing'.
|
|
/// </summary>
|
|
private GraphicsPath _slicePath;
|
|
|
|
/// <summary>
|
|
/// Private field which holds the angle (in degrees) at which the display of this <see cref="PieItem"/>
|
|
/// object will begin.
|
|
/// </summary>
|
|
private float _startAngle;
|
|
|
|
/// <summary>
|
|
///Private field which holds the length (in degrees) of the arc representing this <see cref="PieItem"/>
|
|
///object.
|
|
/// </summary>
|
|
private float _sweepAngle;
|
|
|
|
/// <summary>
|
|
///Private field which represents the angle (in degrees) of the radius along which this <see cref="PieItem"/>
|
|
///object will be displaced, if desired.
|
|
/// </summary>
|
|
private float _midAngle;
|
|
|
|
/// <summary>
|
|
///Private field which determines the number of decimal digits displayed to
|
|
///in a <see cref="PieItem"/> label containing a value.
|
|
/// </summary>
|
|
private int _valueDecimalDigits;
|
|
|
|
/// <summary>
|
|
///Private field which determines the number of decimal digits displayed
|
|
///in a <see cref="PieItem"/> label containing a percent.
|
|
/// </summary>
|
|
private int _percentDecimalDigits;
|
|
|
|
private static ColorSymbolRotator _rotator = new ColorSymbolRotator();
|
|
|
|
#endregion
|
|
|
|
#region Defaults
|
|
/// <summary>
|
|
/// Specify the default property values for the <see cref="PieItem"/> class.
|
|
/// </summary>
|
|
public struct Default
|
|
{
|
|
/// <summary>
|
|
///Default <see cref="PieItem "/> displacement.
|
|
/// </summary>
|
|
public static double Displacement = 0;
|
|
|
|
/// <summary>
|
|
/// The default pen width to be used for drawing the border around the PieItem
|
|
/// (<see cref="ZedGraph.LineBase.Width"/> property). Units are points.
|
|
/// </summary>
|
|
public static float BorderWidth = 1.0F;
|
|
/// <summary>
|
|
/// The default fill mode for this PieItem (<see cref="ZedGraph.Fill.Type"/> property).
|
|
/// </summary>
|
|
public static FillType FillType = FillType.Brush;
|
|
/// <summary>
|
|
/// The default border mode for PieItem (<see cref="ZedGraph.LineBase.IsVisible"/> property).
|
|
/// true to display frame around PieItem, false otherwise
|
|
/// </summary>
|
|
public static bool IsBorderVisible = true;
|
|
/// <summary>
|
|
/// The default color for drawing frames around PieItem
|
|
/// (<see cref="ZedGraph.LineBase.Color"/> property).
|
|
/// </summary>
|
|
public static Color BorderColor = Color.Black;
|
|
/// <summary>
|
|
/// The default color for filling in the PieItem
|
|
/// (<see cref="ZedGraph.Fill.Color"/> property).
|
|
/// </summary>
|
|
public static Color FillColor = Color.Red;
|
|
/// <summary>
|
|
/// The default custom brush for filling in the PieItem.
|
|
/// (<see cref="ZedGraph.Fill.Brush"/> property).
|
|
/// </summary>
|
|
public static Brush FillBrush = null;
|
|
|
|
/// <summary>
|
|
///Default value for controlling <see cref="PieItem"/> display.
|
|
/// </summary>
|
|
public static bool isVisible = true;
|
|
|
|
/// <summary>
|
|
/// Default value for <see cref="PieItem.LabelType"/>.
|
|
/// </summary>
|
|
public static PieLabelType LabelType = PieLabelType.Name;
|
|
|
|
/// <summary>
|
|
/// The default font size for <see cref="PieItem.LabelDetail"/> entries
|
|
/// (<see cref="ZedGraph.FontSpec.Size"/> property). Units are
|
|
/// in points (1/72 inch).
|
|
/// </summary>
|
|
public static float FontSize = 10;
|
|
|
|
/// <summary>
|
|
/// Default value for the number of decimal digits
|
|
/// to be displayed when <see cref="LabelType"/> contains a value.
|
|
/// </summary>
|
|
public static int ValueDecimalDigits = 0;
|
|
|
|
/// <summary>
|
|
/// Default value for the number of decimal digits
|
|
/// to be displayed where <see cref="LabelType"/> contains a percent.
|
|
/// </summary>
|
|
public static int PercentDecimalDigits = 2;
|
|
}
|
|
#endregion Defaults
|
|
|
|
#region PieItem Properties
|
|
/// <summary>
|
|
/// Gets or sets the a value which determines the amount, if any, of this <see cref="PieItem"/>
|
|
/// displacement.
|
|
/// </summary>
|
|
public double Displacement
|
|
{
|
|
get { return ( _displacement ); }
|
|
set { _displacement = value > .5 ? .5 : value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets a path representing this <see cref="PieItem"/>
|
|
/// </summary>
|
|
public GraphicsPath SlicePath
|
|
{
|
|
get { return _slicePath; }
|
|
// set { this.slicePath = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the <see cref="TextObj"/> to be used
|
|
/// for displaying this <see cref="PieItem"/>'s label.
|
|
/// </summary>
|
|
public TextObj LabelDetail
|
|
{
|
|
get { return _labelDetail; }
|
|
set { _labelDetail = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the <see cref="Border"/> object so as to be able to modify
|
|
/// its properties.
|
|
/// </summary>
|
|
public Border Border
|
|
{
|
|
get { return ( _border ); }
|
|
set { _border = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the <see cref="Fill" /> object which is used to fill the
|
|
/// pie slice with color.
|
|
/// </summary>
|
|
public Fill Fill
|
|
{
|
|
get { return _fill; }
|
|
set { _fill = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the arc length (in degrees) of this <see cref="PieItem"/>.
|
|
/// </summary>
|
|
private float SweepAngle
|
|
{
|
|
get { return _sweepAngle; }
|
|
set { _sweepAngle = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the starting angle (in degrees) of this <see cref="PieItem"/>.
|
|
/// </summary>
|
|
private float StartAngle
|
|
{
|
|
get { return ( _startAngle ); }
|
|
set { _startAngle = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the angle (in degrees) of the radius along which
|
|
/// this <see cref="PieItem"/> will be displaced.
|
|
/// </summary>
|
|
private float MidAngle
|
|
{
|
|
get { return ( _midAngle ); }
|
|
set { _midAngle = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the value of this <see cref="PieItem"/>.
|
|
/// Minimum value is 0.
|
|
/// </summary>
|
|
public double Value
|
|
{
|
|
get { return ( _pieValue ); }
|
|
set { _pieValue = value > 0 ? value : 0; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the <see cref="PieLabelType"/> to be used in displaying
|
|
/// <see cref="PieItem"/> labels.
|
|
/// </summary>
|
|
public PieLabelType LabelType
|
|
{
|
|
get { return ( _labelType ); }
|
|
set
|
|
{
|
|
_labelType = value;
|
|
if ( value == PieLabelType.None )
|
|
this.LabelDetail.IsVisible = false;
|
|
else
|
|
this.LabelDetail.IsVisible = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the number of decimal digits to be displayed in a <see cref="PieItem"/>
|
|
/// value label.
|
|
/// </summary>
|
|
public int ValueDecimalDigits
|
|
{
|
|
get { return ( _valueDecimalDigits ); }
|
|
set { _valueDecimalDigits = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the number of decimal digits to be displayed in a <see cref="PieItem"/>
|
|
/// percent label.
|
|
/// </summary>
|
|
public int PercentDecimalDigits
|
|
{
|
|
get { return ( _percentDecimalDigits ); }
|
|
set { _percentDecimalDigits = value; }
|
|
}
|
|
|
|
/*
|
|
/// <summary>
|
|
/// Getsor sets enum <see cref="PieType"/> to be used for drawing this <see cref="PieItem"/>.
|
|
/// </summary>
|
|
public PieType PieType
|
|
{
|
|
get { return (this.pieType); }
|
|
set { this.pieType = value; }
|
|
}
|
|
*/
|
|
/// <summary>
|
|
/// Gets a flag indicating if the Z data range should be included in the axis scaling calculations.
|
|
/// </summary>
|
|
/// <param name="pane">The parent <see cref="GraphPane" /> of this <see cref="CurveItem" />.
|
|
/// </param>
|
|
/// <value>true if the Z data are included, false otherwise</value>
|
|
override internal bool IsZIncluded( GraphPane pane )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a flag indicating if the X axis is the independent axis for this <see cref="CurveItem" />
|
|
/// </summary>
|
|
/// <param name="pane">The parent <see cref="GraphPane" /> of this <see cref="CurveItem" />.
|
|
/// </param>
|
|
/// <value>true if the X axis is independent, false otherwise</value>
|
|
override internal bool IsXIndependent( GraphPane pane )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
/// <summary>
|
|
/// Create a new <see cref="PieItem"/>, providing a gradient fill for the pie color.
|
|
/// </summary>
|
|
/// <param name="pieValue">The value associated with this <see cref="PieItem"/> instance.</param>
|
|
/// <param name="color1">The starting display color for the gradient <see cref="Fill"/> for this
|
|
/// <see cref="PieItem"/> instance.</param>
|
|
/// <param name="color2">The ending display color for the gradient <see cref="Fill"/> for this
|
|
/// <see cref="PieItem"/> instance.</param>
|
|
/// <param name="fillAngle">The angle for the gradient <see cref="Fill"/>.</param>
|
|
/// <param name="displacement">The amount this <see cref="PieItem"/> instance will be
|
|
/// displaced from the center point.</param>
|
|
/// <param name="label">Text label for this <see cref="PieItem"/> instance.</param>
|
|
public PieItem( double pieValue, Color color1, Color color2, float fillAngle,
|
|
double displacement, string label )
|
|
:
|
|
this( pieValue, color1, displacement, label )
|
|
{
|
|
if ( !color1.IsEmpty && !color2.IsEmpty )
|
|
_fill = new Fill( color1, color2, fillAngle );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new <see cref="PieItem"/>.
|
|
/// </summary>
|
|
/// <param name="pieValue">The value associated with this <see cref="PieItem"/> instance.</param>
|
|
/// <param name="color">The display color for this <see cref="PieItem"/> instance.</param>
|
|
/// <param name="displacement">The amount this <see cref="PieItem"/> instance will be
|
|
/// displaced from the center point.</param>
|
|
/// <param name="label">Text label for this <see cref="PieItem"/> instance.</param>
|
|
public PieItem( double pieValue, Color color, double displacement, string label )
|
|
: base( label )
|
|
{
|
|
_pieValue = pieValue;
|
|
_fill = new Fill( color.IsEmpty ? _rotator.NextColor : color );
|
|
_displacement = displacement;
|
|
_border = new Border( Default.BorderColor, Default.BorderWidth );
|
|
_labelDetail = new TextObj();
|
|
_labelDetail.FontSpec.Size = Default.FontSize;
|
|
_labelType = Default.LabelType;
|
|
_valueDecimalDigits = Default.ValueDecimalDigits;
|
|
_percentDecimalDigits = Default.PercentDecimalDigits;
|
|
_slicePath = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new <see cref="PieItem"/>.
|
|
/// </summary>
|
|
/// <param name="pieValue">The value associated with this <see cref="PieItem"/> instance.</param>
|
|
/// <param name="label">Text label for this <see cref="PieItem"/> instance</param>
|
|
public PieItem( double pieValue, string label )
|
|
:
|
|
this( pieValue, _rotator.NextColor, Default.Displacement, label )
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// The Copy Constructor
|
|
/// </summary>
|
|
/// <param name="rhs">The <see cref="PieItem"/> object from which to copy</param>
|
|
public PieItem( PieItem rhs )
|
|
: base( rhs )
|
|
{
|
|
_pieValue = rhs._pieValue;
|
|
_fill = rhs._fill.Clone();
|
|
this.Border = rhs._border.Clone();
|
|
_displacement = rhs._displacement;
|
|
_labelDetail = rhs._labelDetail.Clone();
|
|
_labelType = rhs._labelType;
|
|
_valueDecimalDigits = rhs._valueDecimalDigits;
|
|
_percentDecimalDigits = rhs._percentDecimalDigits;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Implement the <see cref="ICloneable" /> interface in a typesafe manner by just
|
|
/// calling the typed version of <see cref="Clone" />
|
|
/// </summary>
|
|
/// <returns>A deep copy of this object</returns>
|
|
object ICloneable.Clone()
|
|
{
|
|
return this.Clone();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Typesafe, deep-copy clone method.
|
|
/// </summary>
|
|
/// <returns>A new, independent copy of this class</returns>
|
|
public PieItem Clone()
|
|
{
|
|
return new PieItem( this );
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
#region Serialization
|
|
/// <summary>
|
|
/// Current schema value that defines the version of the serialized file
|
|
/// </summary>
|
|
public const int schema2 = 10;
|
|
|
|
/// <summary>
|
|
/// Constructor for deserializing objects
|
|
/// </summary>
|
|
/// <param name="info">A <see cref="SerializationInfo"/> instance that defines the serialized data
|
|
/// </param>
|
|
/// <param name="context">A <see cref="StreamingContext"/> instance that contains the serialized data
|
|
/// </param>
|
|
protected PieItem( SerializationInfo info, StreamingContext context )
|
|
: base( info, context )
|
|
{
|
|
// The schema value is just a file version parameter. You can use it to make future versions
|
|
// backwards compatible as new member variables are added to classes
|
|
int sch = info.GetInt32( "schema2" );
|
|
|
|
_displacement = info.GetDouble( "displacement" );
|
|
_labelDetail = (TextObj)info.GetValue( "labelDetail", typeof( TextObj ) );
|
|
_fill = (Fill)info.GetValue( "fill", typeof( Fill ) );
|
|
_border = (Border)info.GetValue( "border", typeof( Border ) );
|
|
_pieValue = info.GetDouble( "pieValue" );
|
|
_labelType = (PieLabelType)info.GetValue( "labelType", typeof( PieLabelType ) );
|
|
_intersectionPoint = (PointF)info.GetValue( "intersectionPoint", typeof( PointF ) );
|
|
_boundingRectangle = (RectangleF)info.GetValue( "boundingRectangle", typeof( RectangleF ) );
|
|
_pivotPoint = (PointF)info.GetValue( "pivotPoint", typeof( PointF ) );
|
|
_endPoint = (PointF)info.GetValue( "endPoint", typeof( PointF ) );
|
|
_slicePath = (GraphicsPath)info.GetValue( "slicePath", typeof( GraphicsPath ) );
|
|
_startAngle = (float)info.GetDouble( "startAngle" );
|
|
_sweepAngle = (float)info.GetDouble( "sweepAngle" );
|
|
_midAngle = (float)info.GetDouble( "midAngle" );
|
|
_labelStr = info.GetString( "labelStr" );
|
|
_valueDecimalDigits = info.GetInt32( "valueDecimalDigits" );
|
|
_percentDecimalDigits = info.GetInt32( "percentDecimalDigits" );
|
|
}
|
|
/// <summary>
|
|
/// Populates a <see cref="SerializationInfo"/> instance with the data needed to serialize the target object
|
|
/// </summary>
|
|
/// <param name="info">A <see cref="SerializationInfo"/> instance that defines the serialized data</param>
|
|
/// <param name="context">A <see cref="StreamingContext"/> instance that contains the serialized data</param>
|
|
[SecurityPermissionAttribute( SecurityAction.Demand, SerializationFormatter = true )]
|
|
public override void GetObjectData( SerializationInfo info, StreamingContext context )
|
|
{
|
|
base.GetObjectData( info, context );
|
|
info.AddValue( "schema2", schema2 );
|
|
info.AddValue( "displacement", _displacement );
|
|
info.AddValue( "labelDetail", _labelDetail );
|
|
info.AddValue( "fill", _fill );
|
|
info.AddValue( "border", _border );
|
|
info.AddValue( "pieValue", _pieValue );
|
|
info.AddValue( "labelType", _labelType );
|
|
info.AddValue( "intersectionPoint", _intersectionPoint );
|
|
info.AddValue( "boundingRectangle", _boundingRectangle );
|
|
info.AddValue( "pivotPoint", _pivotPoint );
|
|
info.AddValue( "endPoint", _endPoint );
|
|
info.AddValue( "slicePath", _slicePath );
|
|
info.AddValue( "startAngle", _startAngle );
|
|
info.AddValue( "sweepAngle", _sweepAngle );
|
|
info.AddValue( "midAngle", _midAngle );
|
|
info.AddValue( "labelStr", _labelStr );
|
|
info.AddValue( "valueDecimalDigits", _valueDecimalDigits );
|
|
info.AddValue( "percentDecimalDigits", _percentDecimalDigits );
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
/// <summary>
|
|
/// Do all rendering associated with this <see cref="PieItem"/> item to the specified
|
|
/// <see cref="Graphics"/> device. This method is normally only
|
|
/// called by the Draw method of the parent <see cref="ZedGraph.CurveList"/>
|
|
/// collection object.
|
|
/// </summary>
|
|
/// <param name="g">
|
|
/// A graphic device object to be drawn into. This is normally e.Graphics from the
|
|
/// PaintEventArgs argument to the Paint() method.
|
|
/// </param>
|
|
/// <param name="pane">
|
|
/// A reference to the <see cref="ZedGraph.GraphPane"/> object that is the parent or
|
|
/// owner of this object.
|
|
/// </param>
|
|
/// <param name="pos">Not used for rendering Pies</param>param>
|
|
/// <param name="scaleFactor">
|
|
/// The scaling factor to be used for rendering objects. This is calculated and
|
|
/// passed down by the parent <see cref="ZedGraph.GraphPane"/> object using the
|
|
/// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust
|
|
/// font sizes, etc. according to the actual size of the graph.
|
|
/// </param>
|
|
override public void Draw( Graphics g, GraphPane pane, int pos, float scaleFactor )
|
|
{
|
|
if ( pane.Chart._rect.Width <= 0 && pane.Chart._rect.Height <= 0 )
|
|
{
|
|
//pane.PieRect = RectangleF.Empty;
|
|
_slicePath = null;
|
|
}
|
|
else
|
|
{
|
|
//pane.PieRect = CalcPieRect( g, pane, scaleFactor, pane.ChartRect );
|
|
CalcPieRect( g, pane, scaleFactor, pane.Chart._rect );
|
|
|
|
_slicePath = new GraphicsPath();
|
|
|
|
if ( !_isVisible )
|
|
return;
|
|
|
|
RectangleF tRect = _boundingRectangle;
|
|
|
|
if ( tRect.Width >= 1 && tRect.Height >= 1 )
|
|
{
|
|
SmoothingMode sMode = g.SmoothingMode;
|
|
g.SmoothingMode = SmoothingMode.AntiAlias;
|
|
|
|
Fill tFill = _fill;
|
|
Border tBorder = _border;
|
|
if ( this.IsSelected )
|
|
{
|
|
tFill = Selection.Fill;
|
|
tBorder = Selection.Border;
|
|
}
|
|
|
|
using ( Brush brush = tFill.MakeBrush( _boundingRectangle ) )
|
|
{
|
|
g.FillPie( brush, tRect.X, tRect.Y, tRect.Width, tRect.Height, this.StartAngle, this.SweepAngle );
|
|
|
|
//add GraphicsPath for hit testing
|
|
_slicePath.AddPie( tRect.X, tRect.Y, tRect.Width, tRect.Height,
|
|
this.StartAngle, this.SweepAngle );
|
|
|
|
if ( this.Border.IsVisible )
|
|
{
|
|
using ( Pen borderPen = tBorder.GetPen( pane, scaleFactor ) )
|
|
{
|
|
g.DrawPie( borderPen, tRect.X, tRect.Y, tRect.Width, tRect.Height,
|
|
this.StartAngle, this.SweepAngle );
|
|
}
|
|
}
|
|
|
|
if ( _labelType != PieLabelType.None )
|
|
DrawLabel( g, pane, tRect, scaleFactor );
|
|
|
|
//brush.Dispose();
|
|
}
|
|
|
|
g.SmoothingMode = sMode;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculate the <see cref="RectangleF"/> that will be used to define the bounding rectangle of
|
|
/// the Pie.
|
|
/// </summary>
|
|
/// <remarks>This rectangle always lies inside of the <see cref="Chart.Rect"/>, and it is
|
|
/// normally a square so that the pie itself is not oval-shaped.</remarks>
|
|
/// <param name="g">
|
|
/// A graphic device object to be drawn into. This is normally e.Graphics from the
|
|
/// PaintEventArgs argument to the Paint() method.
|
|
/// </param>
|
|
/// <param name="pane">
|
|
/// A reference to the <see cref="ZedGraph.GraphPane"/> object that is the parent or
|
|
/// owner of this object.
|
|
/// </param>
|
|
/// <param name="scaleFactor">
|
|
/// The scaling factor to be used for rendering objects. This is calculated and
|
|
/// passed down by the parent <see cref="ZedGraph.GraphPane"/> object using the
|
|
/// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust
|
|
/// font sizes, etc. according to the actual size of the graph.
|
|
/// </param>
|
|
/// <param name="chartRect">The <see cref="RectangleF"/> (normally the <see cref="Chart.Rect"/>)
|
|
/// that bounds this pie.</param>
|
|
/// <returns></returns>
|
|
public static RectangleF CalcPieRect( Graphics g, GraphPane pane, float scaleFactor, RectangleF chartRect )
|
|
{
|
|
//want to draw the largest pie possible within ChartRect
|
|
//but want to leave 5% slack around the pie so labels will not overrun clip area
|
|
//largest pie is limited by the smaller of ChartRect.height or ChartRect.width...
|
|
//this rect (nonExplRect)has to be re-positioned so that it's in the center of ChartRect.
|
|
//Where ChartRect is almost a square - low Aspect Ratio -, need to contract pieRect so that there's some
|
|
//room for labels, if they're visible.
|
|
double maxDisplacement = 0;
|
|
RectangleF tempRect; //= new RectangleF(0,0,0,0);
|
|
|
|
RectangleF nonExplRect = chartRect;
|
|
|
|
if ( pane.CurveList.IsPieOnly )
|
|
{
|
|
if ( nonExplRect.Width < nonExplRect.Height )
|
|
{
|
|
//create slack rect
|
|
nonExplRect.Inflate( -(float)0.05F * nonExplRect.Height, -(float)0.05F * nonExplRect.Width );
|
|
//get the difference between dimensions
|
|
float delta = ( nonExplRect.Height - nonExplRect.Width ) / 2;
|
|
//make a square so we end up with circular pie
|
|
nonExplRect.Height = nonExplRect.Width;
|
|
//keep the center point the same
|
|
nonExplRect.Y += delta;
|
|
}
|
|
else
|
|
{
|
|
nonExplRect.Inflate( -(float)0.05F * nonExplRect.Height, -(float)0.05F * nonExplRect.Width );
|
|
float delta = ( nonExplRect.Width - nonExplRect.Height ) / 2;
|
|
nonExplRect.Width = nonExplRect.Height;
|
|
nonExplRect.X += delta;
|
|
}
|
|
//check aspect ratio
|
|
double aspectRatio = chartRect.Width / chartRect.Height;
|
|
//make an adjustment in rect size,as aspect ratio varies
|
|
if ( aspectRatio < 1.5 )
|
|
nonExplRect.Inflate( -(float)( .1 * ( 1.5 / aspectRatio ) * nonExplRect.Width ),
|
|
-(float)( .1 * ( 1.5 / aspectRatio ) * nonExplRect.Width ) );
|
|
|
|
//modify the rect to determine if any of the labels need to be wrapped....
|
|
//first see if there's any exploded slices and if so, what's the max displacement...
|
|
//also, might as well get all the display params we can
|
|
PieItem.CalculatePieChartParams( pane, ref maxDisplacement );
|
|
|
|
if ( maxDisplacement != 0 ) //need new rectangle if any slice exploded
|
|
CalcNewBaseRect( maxDisplacement, ref nonExplRect );
|
|
|
|
foreach ( PieItem slice in pane.CurveList )
|
|
{
|
|
slice._boundingRectangle = nonExplRect;
|
|
//if exploded, need to re-calculate rectangle for slice
|
|
if ( slice.Displacement != 0 )
|
|
{
|
|
tempRect = nonExplRect;
|
|
slice.CalcExplodedRect( ref tempRect );
|
|
slice._boundingRectangle = tempRect;
|
|
}
|
|
//now get all the other slice specific drawing details, including need for wrapping label
|
|
slice.DesignLabel( g, pane, slice._boundingRectangle, scaleFactor );
|
|
}
|
|
}
|
|
return nonExplRect;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recalculate the bounding rectangle when a piee slice is displaced.
|
|
/// </summary>
|
|
/// <param name="explRect">rectangle to be used for drawing exploded pie</param>
|
|
private void CalcExplodedRect( ref RectangleF explRect )
|
|
{
|
|
//pie exploded out along the slice bisector - modify upper left of bounding rect to account for displacement
|
|
//keep height and width same
|
|
explRect.X += (float)( this.Displacement * explRect.Width / 2 * Math.Cos( _midAngle * Math.PI / 180 ) );
|
|
explRect.Y += (float)( this.Displacement * explRect.Height / 2 * Math.Sin( _midAngle * Math.PI / 180 ) );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculate the values needed to properly display this <see cref="PieItem"/>.
|
|
/// </summary>
|
|
/// <param name="pane">
|
|
/// A graphic device object to be drawn into. This is normally e.Graphics from the
|
|
/// PaintEventArgs argument to the Paint() method.
|
|
/// </param>
|
|
/// <param name="maxDisplacement">maximum slice displacement</param>
|
|
private static void CalculatePieChartParams( GraphPane pane, ref double maxDisplacement )
|
|
{
|
|
string lblStr = " ";
|
|
|
|
//loop thru slices and get total value and maxDisplacement
|
|
double pieTotalValue = 0;
|
|
foreach ( PieItem curve in pane.CurveList )
|
|
if ( curve.IsPie )
|
|
{
|
|
pieTotalValue += curve._pieValue;
|
|
if ( curve.Displacement > maxDisplacement )
|
|
maxDisplacement = curve.Displacement;
|
|
}
|
|
|
|
double nextStartAngle = 0;
|
|
//now loop thru and calculate the various angle values
|
|
foreach ( PieItem curve in pane.CurveList )
|
|
{
|
|
lblStr = curve._labelStr;
|
|
curve.StartAngle = (float)nextStartAngle;
|
|
curve.SweepAngle = (float)( 360 * curve.Value / pieTotalValue );
|
|
curve.MidAngle = curve.StartAngle + curve.SweepAngle / 2;
|
|
nextStartAngle = curve._startAngle + curve._sweepAngle;
|
|
PieItem.BuildLabelString( curve );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Render the label for this <see cref="PieItem"/>.
|
|
/// </summary>
|
|
/// <param name="g">
|
|
/// A graphic device object to be drawn into. This is normally e.Graphics from the
|
|
/// PaintEventArgs argument to the Paint() method.
|
|
/// </param>
|
|
/// <param name="pane">
|
|
/// A graphic device object to be drawn into. This is normally e.Graphics from the
|
|
/// PaintEventArgs argument to the Paint() method.
|
|
/// </param>
|
|
/// <param name="rect">Bounding rectangle for this <see cref="PieItem"/>.</param>
|
|
/// <param name="scaleFactor">
|
|
/// The scaling factor to be used for rendering objects. This is calculated and
|
|
/// passed down by the parent <see cref="ZedGraph.GraphPane"/> object using the
|
|
/// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust
|
|
/// font sizes, etc. according to the actual size of the graph.
|
|
/// </param>
|
|
public void DrawLabel( Graphics g, GraphPane pane, RectangleF rect, float scaleFactor )
|
|
{
|
|
if ( !_labelDetail.IsVisible )
|
|
return;
|
|
|
|
using ( Pen labelPen = this.Border.GetPen( pane, scaleFactor ) )
|
|
{
|
|
//draw line from intersection point to pivot point -
|
|
g.DrawLine( labelPen, _intersectionPoint, _pivotPoint );
|
|
|
|
//draw horizontal line to move label away from pie...
|
|
g.DrawLine( labelPen, _pivotPoint, _endPoint );
|
|
}
|
|
|
|
//draw the label (TextObj)
|
|
_labelDetail.Draw( g, pane, scaleFactor );
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method collects all the data relative to rendering this <see cref="PieItem"/>'s label.
|
|
/// </summary>
|
|
/// <param name="g">
|
|
/// A graphic device object to be drawn into. This is normally e.Graphics from the
|
|
/// PaintEventArgs argument to the Paint() method.
|
|
/// </param>
|
|
/// <param name="pane">
|
|
/// A graphic device object to be drawn into. This is normally e.Graphics from the
|
|
/// PaintEventArgs argument to the Paint() method.
|
|
/// </param>
|
|
/// <param name="rect">The rectangle used for rendering this <see cref="PieItem"/>
|
|
/// </param>
|
|
/// <param name="scaleFactor">
|
|
/// The scaling factor to be used for rendering objects. This is calculated and
|
|
/// passed down by the parent <see cref="ZedGraph.GraphPane"/> object using the
|
|
/// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust
|
|
/// font sizes, etc. according to the actual size of the graph.
|
|
/// </param>
|
|
public void DesignLabel( Graphics g, GraphPane pane, RectangleF rect, float scaleFactor )
|
|
{
|
|
if ( !_labelDetail.IsVisible )
|
|
return;
|
|
|
|
_labelDetail.LayoutArea = new SizeF();
|
|
//this.labelDetail.IsWrapped = false;
|
|
|
|
//label line will come off the explosion radius and then pivot to the horizontal right or left,
|
|
//dependent on position..
|
|
//text will be at the end of horizontal segment...
|
|
CalculateLinePoints( rect, _midAngle );
|
|
|
|
//now get size of bounding rect for label
|
|
SizeF size = _labelDetail.FontSpec.BoundingBox( g, _labelStr, scaleFactor );
|
|
|
|
//how much room left for the label - most likely midangles for wrapping
|
|
//Right - 315 -> 45 degrees
|
|
//Bottom - 45 -> 135
|
|
//Left - 135 -> 225
|
|
//Top - 225 -> 315
|
|
RectangleF chartRect = pane.Chart._rect;
|
|
float fill = 0;
|
|
if ( _midAngle > 315 || _midAngle <= 45 )
|
|
{
|
|
//correct by wrapping text
|
|
fill = chartRect.X + chartRect.Width - _endPoint.X - 5;
|
|
if ( size.Width > fill )
|
|
{
|
|
//need to wrap, so create label rectangle for overloaded DrawString - two rows, max
|
|
_labelDetail.LayoutArea = new SizeF( fill, size.Height * 3.0F );
|
|
}
|
|
}
|
|
|
|
if ( _midAngle > 45 && _midAngle <= 135 )
|
|
{
|
|
//correct by moving radial line toward one or the other end of the range
|
|
fill = chartRect.Y + chartRect.Height - _endPoint.Y - 5;
|
|
//is there enuf room for the label
|
|
if ( size.Height / 2 > fill )
|
|
{
|
|
//no, so got to move explosion radius
|
|
if ( _midAngle > 90 ) //move _label clockwise one-third of way to the end of the arc
|
|
CalculateLinePoints( rect, _midAngle + ( _sweepAngle + _startAngle - _midAngle ) / 3 );
|
|
else //move _label counter-clockwise one-third of way to the start of the arc
|
|
CalculateLinePoints( rect, _midAngle - ( _midAngle - ( _midAngle - _startAngle ) / 3 ) );
|
|
}
|
|
}
|
|
|
|
if ( _midAngle > 135 && _midAngle <= 225 )
|
|
{
|
|
//wrap text
|
|
fill = _endPoint.X - chartRect.X - 5;
|
|
//need to wrap, so create label rectangle for overloaded DrawString - two rows, max
|
|
if ( size.Width > fill )
|
|
{
|
|
_labelDetail.LayoutArea = new SizeF( fill, size.Height * 3.0F );
|
|
}
|
|
}
|
|
|
|
if ( _midAngle > 225 && _midAngle <= 315 )
|
|
{
|
|
//correct by moving radial line toward one or the other end of the range
|
|
fill = _endPoint.Y - 5 - chartRect.Y;
|
|
//is there enuf room for the label
|
|
if ( size.Height / 2 > fill )
|
|
{
|
|
//no, so got to move explosion radius
|
|
if ( _midAngle < 270 ) //move _label counter-clockwise one-third of way to the start of the arc
|
|
CalculateLinePoints( rect, _midAngle - ( _sweepAngle + _startAngle - _midAngle ) / 3 );
|
|
else //move _label clockwise one-third of way to the end of the arc
|
|
CalculateLinePoints( rect, _midAngle + ( _midAngle - _startAngle ) / 3 );
|
|
}
|
|
}
|
|
|
|
//complete the location Detail info
|
|
_labelDetail.Location.AlignV = AlignV.Center;
|
|
_labelDetail.Location.CoordinateFrame = CoordType.PaneFraction;
|
|
_labelDetail.Location.X = ( _endPoint.X - pane.Rect.X ) / pane.Rect.Width;
|
|
_labelDetail.Location.Y = ( _endPoint.Y - pane.Rect.Y ) / pane.Rect.Height;
|
|
_labelDetail.Text = _labelStr;
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="rect"></param>
|
|
/// <param name="midAngle"></param>
|
|
private void CalculateLinePoints( RectangleF rect, double midAngle )
|
|
{
|
|
//get the point where the explosion radius intersects the this arc
|
|
PointF rectCenter = new PointF( ( rect.X + rect.Width / 2 ), ( rect.Y + rect.Height / 2 ) );
|
|
|
|
_intersectionPoint = new PointF( (float)( rectCenter.X + ( rect.Width / 2 * Math.Cos( ( midAngle ) * Math.PI / 180 ) ) ),
|
|
(float)( rectCenter.Y + ( rect.Height / 2 * Math.Sin( ( midAngle ) * Math.PI / 180 ) ) ) );
|
|
|
|
//draw line from intersection point to pivot point - length to be .05 * pieRect.Width pixels long
|
|
_pivotPoint = new PointF( (float)( _intersectionPoint.X + .05 * rect.Width * Math.Cos( ( midAngle ) * Math.PI / 180 ) ),
|
|
(float)( _intersectionPoint.Y + .05 * rect.Width * Math.Sin( ( midAngle ) * Math.PI / 180 ) ) );
|
|
|
|
//add horizontal line to move label away from pie...length to be 5% of rect.Width
|
|
//does line go to left or right....label alignment is to the opposite
|
|
if ( _pivotPoint.X >= rectCenter.X ) //goes to right
|
|
{
|
|
_endPoint = new PointF( (float)( _pivotPoint.X + .05 * rect.Width ), _pivotPoint.Y );
|
|
_labelDetail.Location.AlignH = AlignH.Left;
|
|
}
|
|
else
|
|
{
|
|
_endPoint = new PointF( (float)( _pivotPoint.X - .05 * rect.Width ), _pivotPoint.Y );
|
|
_labelDetail.Location.AlignH = AlignH.Right;
|
|
}
|
|
_midAngle = (float)midAngle;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build the string that will be displayed as the slice label as determined by
|
|
/// <see cref="LabelType"/>.
|
|
/// </summary>
|
|
/// <param name="curve">reference to the <see cref="PieItem"/></param>
|
|
private static void BuildLabelString( PieItem curve )
|
|
{
|
|
//set up label string formatting
|
|
NumberFormatInfo labelFormat = (NumberFormatInfo)NumberFormatInfo.CurrentInfo.Clone();
|
|
|
|
labelFormat.NumberDecimalDigits = curve._valueDecimalDigits;
|
|
labelFormat.PercentPositivePattern = 1; //no space between number and % sign
|
|
labelFormat.PercentDecimalDigits = curve._percentDecimalDigits;
|
|
|
|
switch ( curve._labelType )
|
|
{
|
|
case PieLabelType.Value:
|
|
curve._labelStr = curve._pieValue.ToString( "F", labelFormat );
|
|
break;
|
|
case PieLabelType.Percent:
|
|
curve._labelStr = ( curve._sweepAngle / 360 ).ToString( "P", labelFormat );
|
|
break;
|
|
case PieLabelType.Name_Value:
|
|
curve._labelStr = curve._label._text + ": " + curve._pieValue.ToString( "F", labelFormat );
|
|
break;
|
|
case PieLabelType.Name_Percent:
|
|
curve._labelStr = curve._label._text + ": " + ( curve._sweepAngle / 360 ).ToString( "P", labelFormat );
|
|
break;
|
|
case PieLabelType.Name_Value_Percent:
|
|
curve._labelStr = curve._label._text + ": " + curve._pieValue.ToString( "F", labelFormat ) +
|
|
" (" + ( curve._sweepAngle / 360 ).ToString( "P", labelFormat ) + ")";
|
|
break;
|
|
case PieLabelType.Name:
|
|
curve._labelStr = curve._label._text;
|
|
break;
|
|
case PieLabelType.None:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A method which calculates a new size for the bounding rectangle for the non-displaced
|
|
/// <see cref="PieItem"/>'s in the pie chart. This method is called after it is found
|
|
/// that at least one slice is displaced.
|
|
/// </summary>
|
|
/// <param name="maxDisplacement">The biggest displacement among the <see cref="PieItem"/>s
|
|
/// making up the pie chart.</param>
|
|
/// <param name="baseRect">The current bounding rectangle</param>
|
|
private static void CalcNewBaseRect( double maxDisplacement, ref RectangleF baseRect )
|
|
{
|
|
//displacement expressed in terms of % of pie radius ...do not want exploded slice to
|
|
//go beyond nonExplRect, but want to maintain the same center point...therefore, got to
|
|
//reduce the diameter of the nonexploded pie by the alue of the displacement
|
|
|
|
float xDispl = (float)( ( maxDisplacement * baseRect.Width ) );
|
|
float yDispl = (float)( ( maxDisplacement * baseRect.Height ) );
|
|
|
|
baseRect.Inflate( -(float)( ( xDispl / 10 ) ), -(float)( ( xDispl / 10 ) ) );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draw a legend key entry for this <see cref="PieItem"/> at the specified location
|
|
/// </summary>
|
|
/// <param name="g">
|
|
/// A graphic device object to be drawn into. This is normally e.Graphics from the
|
|
/// PaintEventArgs argument to the Paint() method.
|
|
/// </param>
|
|
/// <param name="pane">
|
|
/// A reference to the <see cref="ZedGraph.GraphPane"/> object that is the parent or
|
|
/// owner of this object.
|
|
/// </param>
|
|
/// <param name="rect">The <see cref="RectangleF"/> struct that specifies the
|
|
/// location for the legend key</param>
|
|
/// <param name="scaleFactor">
|
|
/// The scaling factor to be used for rendering objects. This is calculated and
|
|
/// passed down by the parent <see cref="ZedGraph.GraphPane"/> object using the
|
|
/// <see cref="PaneBase.CalcScaleFactor"/> method, and is used to proportionally adjust
|
|
/// font sizes, etc. according to the actual size of the graph.
|
|
/// </param>
|
|
override public void DrawLegendKey( Graphics g, GraphPane pane, RectangleF rect, float scaleFactor )
|
|
{
|
|
if ( !_isVisible )
|
|
return;
|
|
|
|
// Fill the slice
|
|
if ( _fill.IsVisible )
|
|
{
|
|
// just avoid height/width being less than 0.1 so GDI+ doesn't cry
|
|
using ( Brush brush = _fill.MakeBrush( rect ) )
|
|
{
|
|
g.FillRectangle( brush, rect );
|
|
//brush.Dispose();
|
|
}
|
|
}
|
|
|
|
// Border the bar
|
|
if ( !_border.Color.IsEmpty )
|
|
_border.Draw( g, pane, scaleFactor, rect );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determine the coords for the rectangle associated with a specified point for
|
|
/// this <see cref="CurveItem" />
|
|
/// </summary>
|
|
/// <param name="pane">The <see cref="GraphPane" /> to which this curve belongs</param>
|
|
/// <param name="i">The index of the point of interest</param>
|
|
/// <param name="coords">A list of coordinates that represents the "rect" for
|
|
/// this point (used in an html AREA tag)</param>
|
|
/// <returns>true if it's a valid point, false otherwise</returns>
|
|
override public bool GetCoords( GraphPane pane, int i, out string coords )
|
|
{
|
|
coords = string.Empty;
|
|
|
|
PointF pt = _boundingRectangle.Location;
|
|
pt.X += _boundingRectangle.Width / 2.0f;
|
|
pt.Y += _boundingRectangle.Height / 2.0f;
|
|
|
|
float radius = _boundingRectangle.Width / 2.0f;
|
|
Matrix matrix = new Matrix();
|
|
|
|
// Move the coordinate system to local coordinates
|
|
// of this text object (that is, at the specified
|
|
// x,y location)
|
|
matrix.Translate( pt.X, pt.Y );
|
|
|
|
matrix.Rotate( this.StartAngle );
|
|
//One mark every 5'ish degrees
|
|
int count = (int)Math.Floor ( SweepAngle / 5 ) + 1;
|
|
PointF[] pts = new PointF[2 + count];
|
|
pts[0] = new PointF( 0, 0 );
|
|
pts[1] = new PointF( radius, 0 );
|
|
double angle = 0.0;
|
|
for ( int j = 2; j < count + 2; j++ )
|
|
{
|
|
angle += SweepAngle / count;
|
|
|
|
pts[j] = new PointF(radius * (float)Math.Cos(angle * Math.PI / 180.0),
|
|
radius * (float)Math.Sin( angle * Math.PI / 180.0 ) );
|
|
}
|
|
|
|
matrix.TransformPoints( pts );
|
|
|
|
coords = String.Format("{0:f0},{1:f0},{2:f0},{3:f0},",
|
|
pts[0].X, pts[0].Y, pts[1].X, pts[1].Y );
|
|
for (int j = 2; j < count + 2; j++)
|
|
coords += String.Format(j > count ? "{0:f0},{1:f0}" : "{0:f0},{1:f0},", pts[j].X, pts[j].Y);
|
|
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
}
|