/// <summary>
        /// Draw this <see cref="CurveItem"/> to the specified <see cref="Graphics"/>
        /// device as a symbol at each defined point.  The routine
        /// only draws the symbols; the lines are draw by the
        /// <see cref="Line.DrawCurve"/> method.  This method
        /// is normally only called by the Draw method of the
        /// <see cref="CurveItem"/> 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="GraphPane"/> object that is the parent or
        /// owner of this object.
        /// </param>
        /// <param name="curve">A <see cref="LineItem"/> representing this
        /// curve.</param>
        /// <param name="scaleFactor">
        /// The scaling factor to be used for rendering objects.  This is calculated and
        /// passed down by the parent <see cref="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 Draw(Graphics g, GraphPane pane, LineItem curve, float scaleFactor)
        {
            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 );
                Pen pen = _border.MakePen(pane.IsPenWidthScaled, scaleFactor);
                if (curve.IsHighlighted)
                {
                    pen.Width *= 3;
                }
                //Pen pen = new Pen( this.border.Color, pane.ScaledPenWidth(_border.PenWidth * scaleFactor) );

                GraphicsPath path         = MakePath(g, scaleFactor, curve.IsHighlighted, pen.Width);
                RectangleF   rect         = path.GetBounds();
                Brush        brush        = this.Fill.MakeBrush(rect);
                ValueHandler valueHandler = new ValueHandler(pane, false);
                Scale        xScale       = pane.XAxis.Scale;
                Scale        yScale       = curve.GetYAxis(pane).Scale;

                bool xIsLog = xScale.IsLog;
                bool yIsLog = yScale.IsLog;

                // If it's highlighted, draw a black "border" first.  Actually not a border but rather the shape
                //  drawn with a thicker pen, over which the thinner pen will be used to draw the same shape in the
                //  main color.
                if (curve.IsHighlighted)
                {
                    const int highlightWidthIncrement = 4;
                    pen.Width += highlightWidthIncrement;
                    Color oldColor = pen.Color;
                    pen.Color = Color.Black;
                    GraphicsPath borderPath = MakePath(g, scaleFactor, curve.IsHighlighted, pen.Width);
                    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))
                        {
                            // 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)
                            {
                                brush = _fill.MakeBrush(rect, points[i]);
                            }
                            // Otherwise, the brush is already defined
                            // Draw the symbol at the specified pixel location
                            this.DrawSymbol(g, tmpX, tmpY, borderPath, pen, Brushes.Black);
                        }
                    }
                    pen.Width -= highlightWidthIncrement;
                    pen.Color  = oldColor;
                }

                // 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))
                    {
                        // 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)
                        {
                            brush = _fill.MakeBrush(rect, points[i]);
                        }
                        // 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;
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Draw this <see cref="CurveItem"/> to the specified <see cref="Graphics"/>
        /// device as a symbol at each defined point.  The routine
        /// only draws the symbols; the lines are draw by the
        /// <see cref="Line.DrawCurve"/> method.  This method
        /// is normally only called by the Draw method of the
        /// <see cref="CurveItem"/> 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="GraphPane"/> object that is the parent or
        /// owner of this object.
        /// </param>
        /// <param name="curve">A <see cref="LineItem"/> representing this
        /// curve.</param>
        /// <param name="scaleFactor">
        /// The scaling factor to be used for rendering objects.  This is calculated and
        /// passed down by the parent <see cref="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="isSelected">Indicates that the <see cref="Symbol" /> should be drawn
        /// with attributes from the <see cref="Selection" /> class.
        /// </param>
        public void Draw(Graphics g, GraphPane pane, LineItem curve, float scaleFactor, bool isSelected)
        {
            var source = isSelected ? Selection.Symbol : this;

            var minX = (int)pane.Chart.Rect.Left;
            var maxX = (int)pane.Chart.Rect.Right;
            var minY = (int)pane.Chart.Rect.Top;
            var maxY = (int)pane.Chart.Rect.Bottom;

            var points = curve.Points;

            if (points == null || !IsVisible || (!Border.IsVisible && !Fill.IsVisible))
            {
                return;
            }

            var 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 (var pen = source.Border.GetPen(pane, scaleFactor))
                using (var path = MakePath(g, scaleFactor))
                {
                    var rect = path.GetBounds();

                    using (var brush = source.Fill.MakeBrush(rect))
                    {
                        var valueHandler = new ValueHandler(pane, false);
                        var xScale       = curve.GetXAxis(pane).Scale;
                        var yScale       = curve.GetYAxis(pane).Scale;

                        var xIsLog     = xScale.IsLog;
                        var yIsLog     = yScale.IsLog;
                        var xIsOrdinal = xScale.IsAnyOrdinal;

                        var xMin = xScale.Min;
                        var xMax = xScale.Max;

                        // (Dale-a-b) we'll set an element to true when it has been drawn
                        var isPixelDrawn = new bool[maxX + 1, maxY + 1];

                        // Loop over each defined point
                        for (var i = 0; i < points.Count; i++)
                        {
                            // Get the user scale values for the current point
                            // use the valueHandler only for stacked types
                            double curX;
                            double curY;
                            if (pane.LineType == LineType.Stack)
                            {
                                double lowVal;
                                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;
                                curY = curve is StickItem ? points[i].Z : 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 ||
                                double.IsNaN(curX) || double.IsNaN(curY) || double.IsInfinity(curX) ||
                                double.IsInfinity(curY) || (!(curX > 0) && xIsLog) ||
                                (yIsLog && !(curY > 0.0)) ||
                                (!xIsOrdinal && (!(curX >= xMin) || !(curX <= xMax))))
                            {
                                continue;
                            }

                            // Transform the user scale values to pixel locations
                            var tmpX = (int)xScale.Transform(curve.IsOverrideOrdinal, i, curX);
                            var tmpY = (int)yScale.Transform(curve.IsOverrideOrdinal, i, curY);

                            // Maintain an array of "used" pixel locations to avoid duplicate drawing operations
                            if (tmpX >= minX && tmpX <= maxX && tmpY >= minY && tmpY <= maxY) // guard against the zoom-in case
                            {
                                if (isPixelDrawn[tmpX, tmpY])
                                {
                                    continue;
                                }
                                isPixelDrawn[tmpX, tmpY] = true;
                            }

                            // 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 (var tBrush = Fill.MakeBrush(rect, points[i]))
                                    using (var tPen = Border.GetPen(pane, scaleFactor, points[i]))
                                        drawSymbol(g, tmpX, tmpY, path, tPen, tBrush, Type, Fill, Border);
                            }
                            else
                            {
                                // Otherwise, the brush is already defined
                                // Draw the symbol at the specified pixel location
                                drawSymbol(g, tmpX, tmpY, path, pen, brush, Type, Fill, Border);
                            }
                        }
                    }
                }

            g.SmoothingMode = sModeSave;
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Draw this <see cref="CurveItem"/> to the specified <see cref="Graphics"/>
        /// device as a symbol at each defined point.  The routine
        /// only draws the symbols; the lines are draw by the
        /// <see cref="Line.DrawCurve"/> method.  This method
        /// is normally only called by the Draw method of the
        /// <see cref="CurveItem"/> 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="GraphPane"/> object that is the parent or
        /// owner of this object.
        /// </param>
        /// <param name="curve">A <see cref="LineItem"/> representing this
        /// curve.</param>
        /// <param name="scaleFactor">
        /// The scaling factor to be used for rendering objects.  This is calculated and
        /// passed down by the parent <see cref="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="isSelected">Indicates that the <see cref="Symbol" /> should be drawn
        /// with attributes from the <see cref="Selection" /> class.
        /// </param>
        public void Draw(Graphics g, GraphPane pane, LineItem curve, float scaleFactor,
                         bool isSelected)
        {
            Symbol source = this;

            if (isSelected)
            {
                source = Selection.Symbol;
            }

            int tmpX, tmpY;

            int minX = (int)pane.Chart.Rect.Left;
            int maxX = (int)pane.Chart.Rect.Right;
            int minY = (int)pane.Chart.Rect.Top;
            int maxY = (int)pane.Chart.Rect.Bottom;

            // (Dale-a-b) we'll set an element to true when it has been drawn
            bool[,] isPixelDrawn = new bool[maxX + 1, maxY + 1];

            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;
                            bool xIsOrdinal = xScale.IsAnyOrdinal;

                            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
                                object tag = null;
                                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;
                                    }
                                    tag = points[i].Tag;
                                }

                                // 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) &&
                                    (xIsOrdinal || (curX >= xMin && curX <= xMax)))
                                {
                                    // Transform the user scale values to pixel locations
                                    tmpX = (int)xScale.Transform(curve.IsOverrideOrdinal, i, curX);
                                    tmpY = (int)yScale.Transform(curve.IsOverrideOrdinal, i, curY);
                                    tag  = TransformTag(pane, tag, curX, curY, xScale, yScale, curve.IsOverrideOrdinal, i);

                                    // Maintain an array of "used" pixel locations to avoid duplicate drawing operations
                                    if (tmpX >= minX && tmpX <= maxX && tmpY >= minY && tmpY <= maxY)                               // guard against the zoom-in case
                                    {
                                        if (isPixelDrawn[tmpX, tmpY])
                                        {
                                            continue;
                                        }
                                        isPixelDrawn[tmpX, tmpY] = true;
                                    }

                                    // 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, tag);
                                    }
                                }
                            }
                        }
                    }

                g.SmoothingMode = sModeSave;
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Draw this <see cref="CurveItem"/> to the specified <see cref="Graphics"/>
        /// device as a symbol at each defined point.  The routine
        /// only draws the symbols; the lines are draw by the
        /// <see cref="Line.DrawCurve"/> method.  This method
        /// is normally only called by the Draw method of the
        /// <see cref="CurveItem"/> 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="GraphPane"/> object that is the parent or
        /// owner of this object.
        /// </param>
        /// <param name="curve">A <see cref="LineItem"/> representing this
        /// curve.</param>
        /// <param name="scaleFactor">
        /// The scaling factor to be used for rendering objects.  This is calculated and
        /// passed down by the parent <see cref="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="isSelected">Indicates that the <see cref="Symbol" /> should be drawn
        /// with attributes from the <see cref="Selection" /> class.
        /// </param>
        public void Draw( Graphics g, GraphPane pane, LineItem curve, float scaleFactor,
			bool isSelected )
        {
            Symbol source = this;
            if ( isSelected )
                source = Selection.Symbol;

            int tmpX, tmpY;

            int minX = (int)pane.Chart.Rect.Left;
            int maxX = (int)pane.Chart.Rect.Right;
            int minY = (int)pane.Chart.Rect.Top;
            int maxY = (int)pane.Chart.Rect.Bottom;

            // (Dale-a-b) we'll set an element to true when it has been drawn
            bool[,] isPixelDrawn = new bool[maxX + 1, maxY + 1];

            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;
                        bool xIsOrdinal = xScale.IsAnyOrdinal;

                        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 ) &&
                                    ( xIsOrdinal || ( curX >= xMin && curX <= xMax ) ) )
                            {
                                // Transform the user scale values to pixel locations
                                tmpX = (int) xScale.Transform( curve.IsOverrideOrdinal, i, curX );
                                tmpY = (int) yScale.Transform( curve.IsOverrideOrdinal, i, curY );

                                // Maintain an array of "used" pixel locations to avoid duplicate drawing operations
                                if ( tmpX >= minX && tmpX <= maxX && tmpY >= minY && tmpY <= maxY ) // guard against the zoom-in case
                                {
                                    if ( isPixelDrawn[tmpX, tmpY] )
                                        continue;
                                    isPixelDrawn[tmpX, tmpY] = true;
                                }

                                // 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;
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Draw this <see cref="CurveItem"/> to the specified <see cref="Graphics"/>
        /// device as a symbol at each defined point.  The routine
        /// only draws the symbols; the lines are draw by the
        /// <see cref="Line.DrawCurve"/> method.  This method
        /// is normally only called by the Draw method of the
        /// <see cref="CurveItem"/> 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="GraphPane"/> object that is the parent or
        /// owner of this object.
        /// </param>
        /// <param name="curve">A <see cref="LineItem"/> representing this
        /// curve.</param>
        /// <param name="scaleFactor">
        /// The scaling factor to be used for rendering objects.  This is calculated and
        /// passed down by the parent <see cref="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 Draw(Graphics g, GraphPane pane, LineItem curve, float scaleFactor)
        {
            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 = _border.MakePen(pane.IsPenWidthScaled, scaleFactor))
                using (GraphicsPath path = MakePath(g, scaleFactor))
                {
                    RectangleF rect = path.GetBounds();

                    using (Brush brush = Fill.MakeBrush(rect))
                    {
                        var valueHandler = new ValueHandler(pane, false);
                        Scale xScale = pane.XAxis.Scale;
                        Scale yScale = curve.GetYAxis(pane).Scale;

                        bool xIsLog = xScale.IsLog;
                        bool yIsLog = yScale.IsLog;

                        // 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 &&
                                !Double.IsNaN(curX) &&
                                !Double.IsNaN(curY) &&
                                !Double.IsInfinity(curX) &&
                                !Double.IsInfinity(curY) &&
                                (curX > 0 || !xIsLog) &&
                                (!yIsLog || curY > 0.0))
                            {
                                // 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)
                                {
                                    using (Brush tBrush = _fill.MakeBrush(rect, points[i]))
                                        DrawSymbol(g, tmpX, tmpY, path, pen, tBrush);
                                }
                                else
                                {
                                    // Otherwise, the brush is already defined
                                    // Draw the symbol at the specified pixel location
                                    DrawSymbol(g, tmpX, tmpY, path, pen, brush);
                                }
                            }
                        }
                    }
                }

                g.SmoothingMode = sModeSave;
            }
        }