//============================================================================ //ZedGraph Class Library - A Flexible Line Graph/Bar Graph Library in C# //Copyright ?2004 John Champion // //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.Drawing.Drawing2D; using System.Runtime.Serialization; using System.Security.Permissions; namespace DrawGraph { /// /// This class handles the drawing of the curve objects. /// The symbols are the small shapes that appear over each defined point /// along the curve. /// /// /// John Champion /// $Revision: 3.34 $ $Date: 2007/04/16 00:03:02 $ [Serializable] public class Symbol : ICloneable, ISerializable { #region Fields /// /// Private field that stores the size of this /// in points (1/72 inch). Use the public /// property to access this value. /// private float _size; /// /// Private field that stores the for this /// . Use the public /// property to access this value. /// private SymbolType _type; /// /// private field that determines if the symbols are drawn using /// Anti-Aliasing capabilities from the class. /// Use the public property to access /// this value. /// private bool _isAntiAlias; /// /// Private field that stores the visibility of this /// . Use the public /// property to access this value. If this value is /// false, the symbols will not be shown (but the may /// still be shown). /// private bool _isVisible; /// /// Private field that stores the data for this /// . Use the public property to /// access this value. /// private Fill _fill; /// /// Private field that stores the data for this /// . Use the public property to /// access this value. /// private Border _border; #endregion #region Defaults /// /// A simple struct that defines the /// default property values for the class. /// public struct Default { // Default Symbol properties /// /// The default size for curve symbols ( property), /// in units of points. /// public static float Size = 7; /// /// The default pen width to be used for drawing curve symbols /// ( property). Units are points. /// public static float PenWidth = 1.0F; /// /// The default color for filling in this /// ( property). /// public static Color FillColor = Color.Red; /// /// The default custom brush for filling in this /// ( property). /// public static Brush FillBrush = null; /// /// The default fill mode for the curve ( property). /// public static FillType FillType = FillType.None; /// /// The default symbol type for curves ( property). /// This is defined as a enumeration. /// public static SymbolType Type = SymbolType.Square; /// /// The default value for the /// property. /// public static bool IsAntiAlias = false; /// /// The default display mode for symbols ( property). /// true to display symbols, false to hide them. /// public static bool IsVisible = true; /// /// The default for drawing frames around symbols ( property). /// true to display symbol frames, false to hide them. /// public static bool IsBorderVisible = true; /// /// The default color for drawing symbols ( property). /// public static Color BorderColor = Color.Red; } #endregion #region Properties /// /// Gets or sets the size of the /// /// Size in points (1/72 inch) /// public float Size { get { return _size; } set { _size = value; } } /// /// Gets or sets the type (shape) of the /// /// A enum value indicating the shape /// public SymbolType Type { get { return _type; } set { _type = value; } } /// /// Gets or sets a value that determines if the symbols are drawn using /// Anti-Aliasing capabilities from the class. /// /// /// If this value is set to true, then the /// property will be set to only while /// this is drawn. A value of false will leave the value of /// unchanged. /// public bool IsAntiAlias { get { return _isAntiAlias; } set { _isAntiAlias = value; } } /// /// Gets or sets a property that shows or hides the . /// /// true to show the symbol, false to hide it /// public bool IsVisible { get { return _isVisible; } set { _isVisible = value; } } /// /// Gets or sets the data for this /// . /// public Fill Fill { get { return _fill; } set { _fill = value; } } /// /// Gets or sets the data for this /// , which controls the border outline of the symbol. /// public Border Border { get { return _border; } set { _border = value; } } #endregion #region Constructors /// /// Default constructor that sets all properties to default /// values as defined in the class. /// public Symbol() : this( SymbolType.Default, Color.Empty ) { } /// /// Default constructor that sets the and /// as specified, and the remaining /// properties to default /// values as defined in the class. /// /// A enum value /// indicating the shape of the symbol /// A value indicating /// the color of the symbol /// public Symbol( SymbolType type, Color color ) { _size = Default.Size; _type = type; _isAntiAlias = Default.IsAntiAlias; _isVisible = Default.IsVisible; _border = new Border( Default.IsBorderVisible, color, Default.PenWidth ); _fill = new Fill( color, Default.FillBrush, Default.FillType ); } /// /// The Copy Constructor /// /// The Symbol object from which to copy public Symbol( Symbol rhs ) { _size = rhs.Size; _type = rhs.Type; _isAntiAlias = rhs._isAntiAlias; _isVisible = rhs.IsVisible; _fill = rhs.Fill.Clone(); _border = rhs.Border.Clone(); } /// /// Implement the interface in a typesafe manner by just /// calling the typed version of /// /// A deep copy of this object object ICloneable.Clone() { return this.Clone(); } /// /// Typesafe, deep-copy clone method. /// /// A new, independent copy of this class public Symbol Clone() { return new Symbol( this ); } #endregion #region Serialization /// /// Current schema value that defines the version of the serialized file /// public const int schema = 10; /// /// Constructor for deserializing objects /// /// A instance that defines the serialized data /// /// A instance that contains the serialized data /// protected Symbol( SerializationInfo info, StreamingContext 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( "schema" ); _size = info.GetSingle( "size" ); _type = (SymbolType) info.GetValue( "type", typeof(SymbolType) ); _isAntiAlias = info.GetBoolean( "isAntiAlias" ); _isVisible = info.GetBoolean( "isVisible" ); _fill = (Fill) info.GetValue( "fill", typeof(Fill) ); _border = (Border) info.GetValue( "border", typeof(Border) ); } /// /// Populates a instance with the data needed to serialize the target object /// /// A instance that defines the serialized data /// A instance that contains the serialized data [SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)] public virtual void GetObjectData( SerializationInfo info, StreamingContext context ) { info.AddValue( "schema", schema ); info.AddValue( "size", _size ); info.AddValue( "type", _type ); info.AddValue( "isAntiAlias", _isAntiAlias ); info.AddValue( "isVisible", _isVisible ); info.AddValue( "fill", _fill ); info.AddValue( "border", _border ); } #endregion #region Rendering Methods /// /// Draw the to the specified device /// at the specified location. This routine draws a single symbol. /// /// /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// /// The x position of the center of the symbol in /// pixel units /// The y position of the center of the symbol in /// pixel units /// A previously constructed by /// for this symbol /// A class representing the standard pen for this symbol /// A class representing a default solid brush for this symbol /// If this symbol uses a , it will be created on the fly for /// each point, since it has to be scaled to the individual point coordinates. private void DrawSymbol( Graphics g, float x, float y, GraphicsPath path, Pen pen, Brush brush ) { // Only draw if the symbol is visible if ( _isVisible && this.Type != SymbolType.None && x < 100000 && x > -100000 && y < 100000 && y > -100000 ) { Matrix saveMatrix = g.Transform; g.TranslateTransform( x, y ); // Fill or draw the symbol as required if ( _fill.IsVisible) g.FillPath( brush, path ); //FillPoint( g, x, y, scaleFactor, pen, brush ); if ( _border.IsVisible ) g.DrawPath( pen, path ); //DrawPoint( g, x, y, scaleFactor, pen ); g.Transform = saveMatrix; } } /// /// Draw the to the specified device /// at the specified location. This routine draws a single symbol. /// /// /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// /// /// A reference to the object that is the parent or /// owner of this object. /// /// The x position of the center of the symbol in /// pixel units /// The y position of the center of the symbol in /// pixel units /// /// The scaling factor for the features of the graph based on the . This /// scaling factor is calculated by the method. The scale factor /// represents a linear multiple to be applied to font sizes, symbol sizes, etc. /// /// The data value to be used for a value-based /// color gradient. This is only applicable for , /// or . /// Indicates that the should be drawn /// with attributes from the class. /// public void DrawSymbol( Graphics g, GraphPane pane, float x, float y, float scaleFactor, bool isSelected, PointPair dataValue ) { Symbol source = this; if ( isSelected ) source = Selection.Symbol; // Only draw if the symbol is visible if ( _isVisible && this.Type != SymbolType.None && x < 100000 && x > -100000 && y < 100000 && y > -100000 ) { SmoothingMode sModeSave = g.SmoothingMode; if ( _isAntiAlias ) g.SmoothingMode = SmoothingMode.HighQuality; using ( Pen pen = _border.GetPen( pane, scaleFactor, dataValue ) ) using ( GraphicsPath path = this.MakePath( g, scaleFactor ) ) using ( Brush brush = this.Fill.MakeBrush( path.GetBounds(), dataValue ) ) { DrawSymbol( g, x, y, path, pen, brush ); } g.SmoothingMode = sModeSave; } } /// /// Create a struct for the current symbol based on the /// specified scaleFactor and assuming the symbol will be centered at position 0,0. /// /// /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// /// /// The scaling factor for the features of the graph based on the . This /// scaling factor is calculated by the method. The scale factor /// represents a linear multiple to be applied to font sizes, symbol sizes, etc. /// Returns the for the current symbol public GraphicsPath MakePath( Graphics g, float scaleFactor ) { float scaledSize = (float) ( _size * scaleFactor ); float hsize = scaledSize / 2, hsize1 = hsize + 1; GraphicsPath path = new GraphicsPath(); switch( _type == SymbolType.Default ? Default.Type : _type ) { case SymbolType.Square: path.AddLine( -hsize, -hsize, hsize, -hsize ); path.AddLine( hsize, -hsize, hsize, hsize ); path.AddLine( hsize, hsize, -hsize, hsize ); path.AddLine( -hsize, hsize, -hsize, -hsize ); break; case SymbolType.Diamond: path.AddLine( 0, -hsize, hsize, 0 ); path.AddLine( hsize, 0, 0, hsize ); path.AddLine( 0, hsize, -hsize, 0 ); path.AddLine( -hsize, 0, 0, -hsize ); break; case SymbolType.Triangle: path.AddLine( 0, -hsize, hsize, hsize ); path.AddLine( hsize, hsize, -hsize, hsize ); path.AddLine( -hsize, hsize, 0, -hsize ); break; case SymbolType.Circle: path.AddEllipse( -hsize, -hsize, scaledSize, scaledSize ); break; case SymbolType.XCross: path.AddLine( -hsize, -hsize, hsize1, hsize1 ); path.StartFigure(); path.AddLine( hsize, -hsize, -hsize1, hsize1 ); break; case SymbolType.Plus: path.AddLine( 0, -hsize, 0, hsize1 ); path.StartFigure(); path.AddLine( -hsize, 0, hsize1, 0 ); break; case SymbolType.Star: path.AddLine( 0, -hsize, 0, hsize1 ); path.StartFigure(); path.AddLine( -hsize, 0, hsize1, 0 ); path.StartFigure(); path.AddLine( -hsize, -hsize, hsize1, hsize1 ); path.StartFigure(); path.AddLine( hsize, -hsize, -hsize1, hsize1 ); break; case SymbolType.TriangleDown: path.AddLine( 0, hsize, hsize, -hsize ); path.AddLine( hsize, -hsize, -hsize, -hsize ); path.AddLine( -hsize, -hsize, 0, hsize ); break; case SymbolType.HDash: path.AddLine( -hsize, 0, hsize1, 0 ); break; case SymbolType.VDash: path.AddLine( 0, -hsize, 0, hsize1 ); break; } return path; } /// /// Draw this to the specified /// device as a symbol at each defined point. The routine /// only draws the symbols; the lines are draw by the /// method. This method /// is normally only called by the Draw method of the /// object /// /// /// A graphic device object to be drawn into. This is normally e.Graphics from the /// PaintEventArgs argument to the Paint() method. /// /// /// A reference to the object that is the parent or /// owner of this object. /// /// A representing this /// curve. /// /// The scaling factor to be used for rendering objects. This is calculated and /// passed down by the parent object using the /// method, and is used to proportionally adjust /// font sizes, etc. according to the actual size of the graph. /// /// Indicates that the should be drawn /// with attributes from the class. /// public void Draw( Graphics g, GraphPane pane, LineItem curve, float scaleFactor, bool isSelected ) { Symbol source = this; if ( isSelected ) source = Selection.Symbol; float tmpX, tmpY; double curX, curY, lowVal; IPointList points = curve.Points; if ( points != null && ( _border.IsVisible || _fill.IsVisible ) ) { SmoothingMode sModeSave = g.SmoothingMode; if ( _isAntiAlias ) g.SmoothingMode = SmoothingMode.HighQuality; // For the sake of speed, go ahead and create a solid brush and a pen // If it's a gradient fill, it will be created on the fly for each symbol //SolidBrush brush = new SolidBrush( this.fill.Color ); using ( Pen pen = source._border.GetPen( pane, scaleFactor ) ) using ( GraphicsPath path = MakePath( g, scaleFactor ) ) { RectangleF rect = path.GetBounds(); using ( Brush brush = source.Fill.MakeBrush( rect ) ) { ValueHandler valueHandler = new ValueHandler( pane, false ); Scale xScale = curve.GetXAxis( pane ).Scale; Scale yScale = curve.GetYAxis( pane ).Scale; bool xIsLog = xScale.IsLog; bool yIsLog = yScale.IsLog; double xMin = xScale.Min; double xMax = xScale.Max; // Loop over each defined point for ( int i = 0; i < points.Count; i++ ) { // Get the user scale values for the current point // use the valueHandler only for stacked types if ( pane.LineType == LineType.Stack ) { valueHandler.GetValues( curve, i, out curX, out lowVal, out curY ); } // otherwise, just access the values directly. Avoiding the valueHandler for // non-stacked types is an optimization to minimize overhead in case there are // a large number of points. else { curX = points[i].X; if ( curve is StickItem ) curY = points[i].Z; else curY = points[i].Y; } // Any value set to double max is invalid and should be skipped // This is used for calculated values that are out of range, divide // by zero, etc. // Also, any value <= zero on a log scale is invalid if ( curX != PointPair.Missing && curY != PointPair.Missing && !System.Double.IsNaN( curX ) && !System.Double.IsNaN( curY ) && !System.Double.IsInfinity( curX ) && !System.Double.IsInfinity( curY ) && ( curX > 0 || !xIsLog ) && ( !yIsLog || curY > 0.0 ) && curX >= xMin && curX <= xMax ) { // Transform the user scale values to pixel locations tmpX = xScale.Transform( curve.IsOverrideOrdinal, i, curX ); tmpY = yScale.Transform( curve.IsOverrideOrdinal, i, curY ); // If the fill type for this symbol is a Gradient by value type, // the make a brush corresponding to the appropriate current value if ( _fill.IsGradientValueType || _border._gradientFill.IsGradientValueType ) { using ( Brush tBrush = _fill.MakeBrush( rect, points[i] ) ) using ( Pen tPen = _border.GetPen( pane, scaleFactor, points[i] ) ) this.DrawSymbol( g, tmpX, tmpY, path, tPen, tBrush ); } else { // Otherwise, the brush is already defined // Draw the symbol at the specified pixel location this.DrawSymbol( g, tmpX, tmpY, path, pen, brush ); } } } } } g.SmoothingMode = sModeSave; } } #endregion } }