/// <summary> /// Gets index of segment that should be started from zero. /// </summary> /// <param name="axisSegments">Axis scale segment collection.</param> /// <returns>Index axis segment or -1.</returns> private int GetStartScaleFromZeroSegmentIndex(AxisScaleSegmentCollection axisSegments) { if (this.StartFromZero == StartFromZero.Auto || this.StartFromZero == StartFromZero.Yes) { int index = 0; foreach (AxisScaleSegment axisScaleSegment in axisSegments) { // Check if zero value is already part of the scale if (axisScaleSegment.ScaleMinimum < 0.0 && axisScaleSegment.ScaleMaximum > 0.0) { return(-1); } // As soon as we get first segment with positive minimum value or // we reached last segment adjust scale to start from zero. if (axisScaleSegment.ScaleMinimum > 0.0 || index == (axisSegments.Count - 1)) { // Check if setting minimum scale to zero will make the // data points in the segment hard to read. This may hapen // when the distance from zero to current minimum is // significantly larger than current scale size. if (this.StartFromZero == StartFromZero.Auto && axisScaleSegment.ScaleMinimum > 2.0 * (axisScaleSegment.ScaleMaximum - axisScaleSegment.ScaleMinimum)) { return(-1); } return(index); } // Increase segment index ++index; } } return(-1); }
/// <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; } } }
/// <summary> /// Sets position of all scale segments in the axis. /// </summary> /// <param name="axisSegments">Collection of axis scale segments.</param> private void SetAxisSegmentPosition(AxisScaleSegmentCollection axisSegments) { // Calculate total number of points int totalPointNumber = 0; foreach (AxisScaleSegment axisScaleSegment in axisSegments) { if (axisScaleSegment.Tag is int) { totalPointNumber += (int)axisScaleSegment.Tag; } } // Calculate segment minimum size double minSize = Math.Min(this._minSegmentSize, Math.Floor(100.0 / axisSegments.Count)); // Set segment position double currentPosition = 0.0; for (int index = 0; index < axisSegments.Count; index++) { axisSegments[index].Position = (currentPosition > 100.0) ? 100.0 : currentPosition; axisSegments[index].Size = Math.Round(((int)axisSegments[index].Tag) / (totalPointNumber / 100.0), 5); if (axisSegments[index].Size < minSize) { axisSegments[index].Size = minSize; } // Set spacing for all segments except the last one if (index < (axisSegments.Count - 1)) { axisSegments[index].Spacing = this._segmentSpacing; } // Advance current position currentPosition += axisSegments[index].Size; } // Make sure we do not exceed the 100% axis length double totalHeight = 0.0; do { // Calculate total height totalHeight = 0.0; double maxSize = double.MinValue; int maxSizeIndex = -1; for (int index = 0; index < axisSegments.Count; index++) { totalHeight += axisSegments[index].Size; if (axisSegments[index].Size > maxSize) { maxSize = axisSegments[index].Size; maxSizeIndex = index; } } // If height is too large find largest segment if (totalHeight > 100.0) { // Adjust segment size axisSegments[maxSizeIndex].Size -= totalHeight - 100.0; if (axisSegments[maxSizeIndex].Size < minSize) { axisSegments[maxSizeIndex].Size = minSize; } // Adjust position of the next segment double curentPosition = axisSegments[maxSizeIndex].Position + axisSegments[maxSizeIndex].Size; for (int index = maxSizeIndex + 1; index < axisSegments.Count; index++) { axisSegments[index].Position = curentPosition; curentPosition += axisSegments[index].Size; } } } while(totalHeight > 100.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); } } } }