/// <summary>
        /// Gets position of the axis break line. Break line may be shown as a single
        /// line or two lines separated with a spacing.
        /// </summary>
        /// <param name="graph">Chart graphics.</param>
        /// <param name="nextSegment">Next segment reference.</param>
        /// <returns>Position of the axis break line in pixel coordinates.</returns>
        internal RectangleF GetBreakLinePosition(ChartGraphics graph, AxisScaleSegment nextSegment)
        {
            // Start with the plotting rectangle position
            RectangleF breakPosition = this.axis.PlotAreaPosition.ToRectangleF();

            // Find maximum scale value of the current segment and minimuj of the next
            double from = this.axis.GetLinearPosition(nextSegment.ScaleMinimum);
            double to   = this.axis.GetLinearPosition(this.ScaleMaximum);

            if (this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left)
            {
                breakPosition.Y      = (float)Math.Min(from, to);
                breakPosition.Height = (float)Math.Max(from, to);
            }
            else
            {
                breakPosition.X     = (float)Math.Min(from, to);
                breakPosition.Width = (float)Math.Max(from, to);;
            }

            // Convert to pixels
            breakPosition = Rectangle.Round(graph.GetAbsoluteRectangle(breakPosition));

            // Add border width
            if (this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left)
            {
                breakPosition.Height = (float)Math.Abs(breakPosition.Y - breakPosition.Height);
                breakPosition.X     -= this.axis.ChartArea.BorderWidth;
                breakPosition.Width += 2 * this.axis.ChartArea.BorderWidth;
            }
            else
            {
                breakPosition.Width   = (float)Math.Abs(breakPosition.X - breakPosition.Width);
                breakPosition.Y      -= this.axis.ChartArea.BorderWidth;
                breakPosition.Height += 2 * this.axis.ChartArea.BorderWidth;
            }

            return(breakPosition);
        }
 /// <summary>
 /// Ensures that specified axis scale segment is used for all coordinate transformations.
 /// Set tot NULL to reset.
 /// </summary>
 /// <param name="segment"></param>
 internal void EnforceSegment(AxisScaleSegment segment)
 {
     this._enforcedSegment = segment;
 }
 /// <summary>
 /// Adds a segment to the end of the collection.
 /// </summary>
 /// <param name="segment">
 /// <see cref="AxisScaleSegment"/> object to add.
 /// </param>
 /// <returns>
 /// Index of the newly added object.
 /// </returns>
 public int Add(AxisScaleSegment segment)
 {
     return(this.List.Add(segment));
 }
        /// <summary>
        /// Paints the axis break line.
        /// </summary>
        /// <param name="graph">Chart graphics to use.</param>
        /// <param name="nextSegment">Axis scale segment next to current.</param>
        internal void PaintBreakLine(ChartGraphics graph, AxisScaleSegment nextSegment)
        {
            // Get break line position
            RectangleF breakPosition = this.GetBreakLinePosition(graph, nextSegment);

            // Get top line graphics path
            GraphicsPath breakLinePathTop    = this.GetBreakLinePath(breakPosition, true);
            GraphicsPath breakLinePathBottom = null;

            // Clear break line space using chart color behind the area
            if (breakPosition.Width > 0f && breakPosition.Height > 0f)
            {
                // Get bottom line graphics path
                breakLinePathBottom = this.GetBreakLinePath(breakPosition, false);

                // Clear plotting area background
                using (GraphicsPath fillPath = new GraphicsPath())
                {
                    // Create fill path out of top and bottom break lines
                    fillPath.AddPath(breakLinePathTop, true);
                    fillPath.Reverse();
                    fillPath.AddPath(breakLinePathBottom, true);
                    fillPath.CloseAllFigures();

                    // Use chart back color to fill the area
                    using (Brush fillBrush = this.GetChartFillBrush(graph))
                    {
                        graph.FillPath(fillBrush, fillPath);

                        // Check if shadow exsits in chart area
                        if (this.axis.ChartArea.ShadowOffset != 0 && !this.axis.ChartArea.ShadowColor.IsEmpty)
                        {
                            // Clear shadow
                            RectangleF shadowPartRect = breakPosition;
                            if (this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left)
                            {
                                shadowPartRect.Y      += this.axis.ChartArea.ShadowOffset;
                                shadowPartRect.Height -= this.axis.ChartArea.ShadowOffset;
                                shadowPartRect.X       = shadowPartRect.Right - 1;
                                shadowPartRect.Width   = this.axis.ChartArea.ShadowOffset + 2;
                            }
                            else
                            {
                                shadowPartRect.X     += this.axis.ChartArea.ShadowOffset;
                                shadowPartRect.Width -= this.axis.ChartArea.ShadowOffset;
                                shadowPartRect.Y      = shadowPartRect.Bottom - 1;
                                shadowPartRect.Height = this.axis.ChartArea.ShadowOffset + 2;
                            }
                            graph.FillRectangle(fillBrush, shadowPartRect);

                            // Draw new shadow
                            using (GraphicsPath shadowPath = new GraphicsPath())
                            {
                                shadowPath.AddPath(breakLinePathTop, false);

                                // Define maximum size
                                float size = this.axis.ChartArea.ShadowOffset;
                                if (this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left)
                                {
                                    size = Math.Min(size, breakPosition.Height);
                                }
                                else
                                {
                                    size = Math.Min(size, breakPosition.Width);
                                }

                                // Define step to increase transperancy
                                int transparencyStep = (int)(this.axis.ChartArea.ShadowColor.A / size);

                                // Set clip region to achieve spacing of the shadow
                                // Start with the plotting rectangle position
                                RectangleF clipRegion = graph.GetAbsoluteRectangle(this.axis.PlotAreaPosition.ToRectangleF());
                                if (this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left)
                                {
                                    clipRegion.X     += this.axis.ChartArea.ShadowOffset;
                                    clipRegion.Width += this.axis.ChartArea.ShadowOffset;
                                }
                                else
                                {
                                    clipRegion.Y      += this.axis.ChartArea.ShadowOffset;
                                    clipRegion.Height += this.axis.ChartArea.ShadowOffset;
                                }
                                graph.SetClip(graph.GetRelativeRectangle(clipRegion));

                                // Draw several lines to form shadow
                                for (int index = 0; index < size; index++)
                                {
                                    using (Matrix newMatrix = new Matrix())
                                    {
                                        // Shift top break line by 1 pixel
                                        if (this.axis.AxisPosition == AxisPosition.Right || this.axis.AxisPosition == AxisPosition.Left)
                                        {
                                            newMatrix.Translate(0f, 1f);
                                        }
                                        else
                                        {
                                            newMatrix.Translate(1f, 0f);
                                        }
                                        shadowPath.Transform(newMatrix);
                                    }

                                    // Get line color
                                    Color color = Color.FromArgb(
                                        this.axis.ChartArea.ShadowColor.A - transparencyStep * index,
                                        this.axis.ChartArea.ShadowColor);

                                    using (Pen shadowPen = new Pen(color, 1))
                                    {
                                        // Draw shadow
                                        graph.DrawPath(shadowPen, shadowPath);
                                    }
                                }

                                graph.ResetClip();
                            }
                        }
                    }
                }
            }

            // Draw Separator Line(s)
            if (this.axis.ScaleBreakStyle.BreakLineStyle != BreakLineStyle.None)
            {
                using (Pen pen = new Pen(this.axis.ScaleBreakStyle.LineColor, this.axis.ScaleBreakStyle.LineWidth))
                {
                    // Set line style
                    pen.DashStyle = graph.GetPenStyle(this.axis.ScaleBreakStyle.LineDashStyle);

                    // Draw break lines
                    graph.DrawPath(pen, breakLinePathTop);
                    if (breakPosition.Width > 0f && breakPosition.Height > 0f)
                    {
                        graph.DrawPath(pen, breakLinePathBottom);
                    }
                }
            }

            // Dispose break line paths
            breakLinePathTop.Dispose();
            breakLinePathTop = null;
            if (breakLinePathBottom != null)
            {
                breakLinePathBottom.Dispose();
                breakLinePathBottom = null;
            }
        }
Beispiel #5
0
        /// <summary>
        /// Fill collection of axis scale segments.
        /// </summary>
        /// <param name="axisSegments">Collection of axis segments.</param>
        private void FillAxisSegmentCollection(AxisScaleSegmentCollection axisSegments)
        {
            // Clear axis segments collection
            axisSegments.Clear();

            // Get statistics for the series attached to the axis
            double minYValue   = 0.0;
            double maxYValue   = 0.0;
            double segmentSize = 0.0;

            double[] segmentMaxValue    = null;
            double[] segmentMinValue    = null;
            int[]    segmentPointNumber = GetSeriesDataStatistics(
                this._totalNumberOfSegments,
                out minYValue,
                out maxYValue,
                out segmentSize,
                out segmentMaxValue,
                out segmentMinValue);
            if (segmentPointNumber == null)
            {
                return;
            }

            // Calculate scale maximum and minimum
            double minimum = minYValue;
            double maximum = maxYValue;

            this.axis.EstimateNumberAxis(
                ref minimum,
                ref maximum,
                this.axis.IsStartedFromZero,
                this.axis.prefferedNumberofIntervals,
                true,
                true);

            // Make sure max/min Y values are not the same
            if (maxYValue == minYValue)
            {
                return;
            }

            // Calculate the percentage of the scale range covered by the data range.
            double dataRangePercent = (maxYValue - minYValue) / ((maximum - minimum) / 100.0);

            // Get sequences of empty segments
            ArrayList emptySequences = new ArrayList();
            bool      doneFlag       = false;

            while (!doneFlag)
            {
                doneFlag = true;

                // Get longest sequence of segments with no points
                int startSegment     = 0;
                int numberOfSegments = 0;
                this.GetLargestSequenseOfSegmentsWithNoPoints(
                    segmentPointNumber,
                    out startSegment,
                    out numberOfSegments);

                // Adjust minimum empty segments  number depending on current segments
                int minEmptySegments = (int)(this._minimumNumberOfEmptySegments * (100.0 / dataRangePercent));
                if (axisSegments.Count > 0 && numberOfSegments > 0)
                {
                    // Find the segment which contain newly found empty segments sequence
                    foreach (AxisScaleSegment axisScaleSegment in axisSegments)
                    {
                        if (startSegment > 0 && (startSegment + numberOfSegments) <= segmentMaxValue.Length - 1)
                        {
                            if (segmentMaxValue[startSegment - 1] >= axisScaleSegment.ScaleMinimum &&
                                segmentMinValue[startSegment + numberOfSegments] <= axisScaleSegment.ScaleMaximum)
                            {
                                // Get percentage of segment scale that is empty and suggested for collapsing
                                double segmentScaleRange = axisScaleSegment.ScaleMaximum - axisScaleSegment.ScaleMinimum;
                                double emptySpaceRange   = segmentMinValue[startSegment + numberOfSegments] - segmentMaxValue[startSegment - 1];
                                double emptySpacePercent = emptySpaceRange / (segmentScaleRange / 100.0);
                                emptySpacePercent = emptySpacePercent / 100 * axisScaleSegment.Size;

                                if (emptySpacePercent > minEmptySegments &&
                                    numberOfSegments > this._minSegmentSize)
                                {
                                    minEmptySegments = numberOfSegments;
                                }
                            }
                        }
                    }
                }

                // Check if found sequence is long enough
                if (numberOfSegments >= minEmptySegments)
                {
                    doneFlag = false;

                    // Store start segment and number of segments in the list
                    emptySequences.Add(startSegment);
                    emptySequences.Add(numberOfSegments);

                    // Check if there are any emty segments sequence found
                    axisSegments.Clear();
                    if (emptySequences.Count > 0)
                    {
                        double segmentFrom = double.NaN;
                        double segmentTo   = double.NaN;

                        // Based on the segments that need to be excluded create axis segments that
                        // will present on the axis scale.
                        int numberOfPoints = 0;
                        for (int index = 0; index < segmentPointNumber.Length; index++)
                        {
                            // Check if current segment is excluded
                            bool excludedSegment = this.IsExcludedSegment(emptySequences, index);

                            // If not excluded segment - update from/to range if they were set
                            if (!excludedSegment &&
                                !double.IsNaN(segmentMinValue[index]) &&
                                !double.IsNaN(segmentMaxValue[index]))
                            {
                                // Calculate total number of points
                                numberOfPoints += segmentPointNumber[index];

                                // Set From/To of the visible segment
                                if (double.IsNaN(segmentFrom))
                                {
                                    segmentFrom = segmentMinValue[index];
                                    segmentTo   = segmentMaxValue[index];
                                }
                                else
                                {
                                    segmentTo = segmentMaxValue[index];
                                }
                            }

                            // If excluded or last segment - add current visible segment range
                            if (!double.IsNaN(segmentFrom) &&
                                (excludedSegment || index == (segmentPointNumber.Length - 1)))
                            {
                                // Make sure To and From do not match
                                if (segmentTo == segmentFrom)
                                {
                                    segmentFrom -= segmentSize;
                                    segmentTo   += segmentSize;
                                }

                                // Add axis scale segment
                                AxisScaleSegment axisScaleSegment = new AxisScaleSegment();
                                axisScaleSegment.ScaleMaximum = segmentTo;
                                axisScaleSegment.ScaleMinimum = segmentFrom;
                                axisScaleSegment.Tag          = numberOfPoints;
                                axisSegments.Add(axisScaleSegment);

                                // Reset segment range
                                segmentFrom    = double.NaN;
                                segmentTo      = double.NaN;
                                numberOfPoints = 0;
                            }
                        }
                    }

                    // Calculate the position of each segment
                    this.SetAxisSegmentPosition(axisSegments);
                }

                // Make sure we do not exceed specified number of breaks
                if ((axisSegments.Count - 1) >= this._maximumNumberOfBreaks)
                {
                    doneFlag = true;
                }
            }
        }
Beispiel #6
0
        /// <summary>
        /// Get collection of axis segments to present scale breaks.
        /// </summary>
        /// <param name="axisSegments">Collection of axis scale segments.</param>
        internal void GetAxisSegmentForScaleBreaks(AxisScaleSegmentCollection axisSegments)
        {
            // Clear segment collection
            axisSegments.Clear();

            // Check if scale breaks are enabled
            if (this.IsEnabled())
            {
                // Fill collection of segments
                this.FillAxisSegmentCollection(axisSegments);

                // Check if more than 1 segments were defined
                if (axisSegments.Count >= 1)
                {
                    // Get index of segment which scale should start from zero
                    int startFromZeroSegmentIndex = this.GetStartScaleFromZeroSegmentIndex(axisSegments);

                    // Calculate segment interaval and round the scale
                    int index = 0;
                    foreach (AxisScaleSegment axisScaleSegment in axisSegments)
                    {
                        // Check if segment scale should start from zero
                        bool startFromZero = (index == startFromZeroSegmentIndex) ? true : false;

                        // Calculate interval and round scale
                        double minimum = axisScaleSegment.ScaleMinimum;
                        double maximum = axisScaleSegment.ScaleMaximum;
                        axisScaleSegment.Interval = this.axis.EstimateNumberAxis(
                            ref minimum, ref maximum, startFromZero, this.axis.prefferedNumberofIntervals, true, true);
                        axisScaleSegment.ScaleMinimum = minimum;
                        axisScaleSegment.ScaleMaximum = maximum;

                        // Make sure new scale break value range do not exceed axis current scale
                        if (axisScaleSegment.ScaleMinimum < this.axis.Minimum)
                        {
                            axisScaleSegment.ScaleMinimum = this.axis.Minimum;
                        }
                        if (axisScaleSegment.ScaleMaximum > this.axis.Maximum)
                        {
                            axisScaleSegment.ScaleMaximum = this.axis.Maximum;
                        }

                        // Increase segment index
                        ++index;
                    }

                    // Defined axis scale segments cannot overlap.
                    // Check for overlapping and join segments or readjust min/max.
                    bool             adjustPosition = false;
                    AxisScaleSegment prevSegment    = axisSegments[0];
                    for (int segmentIndex = 1; segmentIndex < axisSegments.Count; segmentIndex++)
                    {
                        AxisScaleSegment currentSegment = axisSegments[segmentIndex];
                        if (currentSegment.ScaleMinimum <= prevSegment.ScaleMaximum)
                        {
                            if (currentSegment.ScaleMaximum > prevSegment.ScaleMaximum)
                            {
                                // If segments are partially overlapping make sure the previous
                                // segment scale is extended
                                prevSegment.ScaleMaximum = currentSegment.ScaleMaximum;
                            }

                            // Remove the overlapped segment
                            adjustPosition = true;
                            axisSegments.RemoveAt(segmentIndex);
                            --segmentIndex;
                        }
                        else
                        {
                            prevSegment = currentSegment;
                        }
                    }

                    // Calculate the position of each segment
                    if (adjustPosition)
                    {
                        this.SetAxisSegmentPosition(axisSegments);
                    }
                }
            }
        }