Example #1
0
        /// <summary>
        /// 1) Calculate the step value based on MinValue, MaxValue, DisplayRange and availableHeight.
        /// 2) Width is calculated based on fontSize and number of digits needed to display MinValue & MaxValue.
        /// </summary>
        private double calculateLegendWidth(double availableHeight)
        {
            //calculateLegendWidth() is called from within Measure and Arrange. Legend guarantees that MinValue, DisplayValue, DisplayValueRange and
            //MaxValue have reasonable values

            if (!hasFontChanged && heightTracked == availableHeight && !HasMinMaxOrDisplayValueRangeChanged && !double.IsNaN(requiredWidth))
            {
                return(requiredWidth);//nothing has changed
            }

            isNewWidthCalculated = true;
            hasFontChanged       = false;
            heightTracked        = availableHeight;

            //calculate step value between 2 legend values
            //--------------------------------------------

            //value between 2 display labels (=step)
            double estimatedStepValue = DisplayValueRange * FontSize * 3 / availableHeight;

            if (estimatedStepValue < 1000 * double.Epsilon)
            {
                throw new ApplicationException(string.Format("Legend: range '{0}' too close to zero." + this, estimatedStepValue));
            }

            //normalise amplitude between 1.0 and 10.0
            DoubleDigitsExponent stepDigitsExponent = new DoubleDigitsExponent(estimatedStepValue);

            if (stepDigitsExponent.Digits < 1.00 || stepDigitsExponent.Digits >= 10.00)
            {
                throw new ApplicationException(string.Format("Legend: Normalised stepValue should be between 1.0 and 9.999, but was '{0}'.", stepDigitsExponent.Digits));
            }

            if (stepDigitsExponent.Digits < 1.00)
            {
                step = new StepStruct(1, stepDigitsExponent.Exponent);
            }
            else if (stepDigitsExponent.Digits < 2.00)
            {
                step = new StepStruct(2, stepDigitsExponent.Exponent);
            }
            else if (stepDigitsExponent.Digits < 5.00)
            {
                step = new StepStruct(5, stepDigitsExponent.Exponent);
            }
            else
            {
                step = new StepStruct(1, stepDigitsExponent.Exponent + 1);
            }

            //calculate string format. It depends on:
            //---------------------------------------
            //1) biggest and smallest value in Min-/MaxValue Range
            //2) step size in DisplayValue Range

            //find maximum number of characters needed to display longest Value
            double minValue;

            if (double.IsNaN(MinValue))
            {
                minValue = DisplayValue;
            }
            else
            {
                minValue = MinValue;
            }
            //DoubleDigitsExponent minValueDigitsExponent = new DoubleDigitsExponent(minValue);
            double maxValue;

            if (double.IsNaN(MaxValue))
            {
                maxValue = DisplayValue + DisplayValueRange;
            }
            else
            {
                maxValue = MaxValue;
            }
            //DoubleDigitsExponent maxValueDigitsExponent = new DoubleDigitsExponent(maxValue);
            //int minExponent;
            //int maxExponent;
            //if (minValueDigitsExponent.Exponent<=maxValueDigitsExponent.Exponent) {
            //  //Example: Min=0.1, Max = 10000
            //  minExponent = minValueDigitsExponent.Exponent;
            //  maxExponent = maxValueDigitsExponent.Exponent;
            //} else {
            //  //Example: Min=-10000, Max = 0.1
            //  minExponent = maxValueDigitsExponent.Exponent;
            //  maxExponent = minValueDigitsExponent.Exponent;
            //}
            numberFormat = "#,0";

            if (step.Exponent < 0)
            {
                //add required digits after decimal point
                numberFormat += '.' + new string('0', -step.Exponent);
            }

            string minValueString      = minValue.ToString(numberFormat);
            double minValueStringWidth = LegendGlyphDrawer.GetLength(minValueString, FontSize);
            string maxValueString      = maxValue.ToString(numberFormat);
            double maxValueStringWidth = LegendGlyphDrawer.GetLength(maxValueString, FontSize);

            requiredWidth = Math.Max(minValueStringWidth, maxValueStringWidth);

            return(requiredWidth);
        }
Example #2
0
        protected override void OnRecalculate(ref double[]?labelValues, ref string?[]?labelStrings, ref Point[]?labelPoints)
        {
            //find first which label will need the most digits. Use MinValue or MaxValue, not DisplayValue and range, because the format
            //should stay the same for all values between min to max.
            double lowestValue;
            double highestValue;

            if (double.IsNaN(MinValue) || double.IsNaN(MaxValue))
            {
                //Min- and MaxValue are not defined. Use DisplayValue and DisplayValueRange instead
                lowestValue  = DisplayValue;
                highestValue = lowestValue + DisplayValueRange; //DisplayValueRange is guaranteed to be greater 0
            }
            else
            {
                //Min- and MaxValue are defined, use them.
                lowestValue  = MinValue;
                highestValue = MaxValue;
            }

            //use DisplayValueRange as first estimate for value difference between 2 labels.
            //normalise amplitude between 1.0 and 10.0
            DoubleDigitsExponent stepDigitsExponent = new DoubleDigitsExponent(DisplayValueRange);

#if DEBUG
            if (stepDigitsExponent.Digits < 1.00 || stepDigitsExponent.Digits >= 10.00)
            {
                System.Diagnostics.Debugger.Break();
                throw new Exception(string.Format("Legend: Normalised stepValue should be between 1.0 and 9.999, but was '{0}'.", stepDigitsExponent.Digits));
            }
#endif
            //the first digits of a step (=value difference between 2 labels) can be 1, 2 or 5. All other digits of a step are 0.
            //chose a first step which is smaller than DisplayValueRange
            StepStruct step;
            if (stepDigitsExponent.Digits < 1.00)
            {
                step = new StepStruct(5, stepDigitsExponent.Exponent - 1);
            }
            else if (stepDigitsExponent.Digits < 2.00)
            {
                step = new StepStruct(1, stepDigitsExponent.Exponent);
            }
            else if (stepDigitsExponent.Digits < 5.00)
            {
                step = new StepStruct(2, stepDigitsExponent.Exponent);
            }
            else
            {
                step = new StepStruct(5, stepDigitsExponent.Exponent);
            }
            string minMaxnumberFormat = getNumberFormat(step);

            //find the label which needs the most digits
            //MinValue might need more digits, because it can be negative (-10000) and Max might be a small number (0).
            //convert smallest and highest value to string
            string lowestValueString  = lowestValue.ToString(minMaxnumberFormat);
            string highestValueString = highestValue.ToString(minMaxnumberFormat);

            //select longer string.
            string longestValueString;
            double longestStringValue;
            if (lowestValueString.Length > highestValueString.Length)
            {
                longestValueString = lowestValueString;
                longestStringValue = lowestValue;
            }
            else
            {
                longestValueString = highestValueString;
                longestStringValue = highestValue;
            }

            //DisplayRange provides a first estimate for the distance between 2 labels (=step)
            //step gets made smaller and smaller, until the space needed to display the label is bigger than the space available between
            //2 labels
            string?    numberFormat          = null;
            double     labelWidth            = double.NaN;
            double     pixelPerValue         = RenderWidthTracked / DisplayValueRange;
            StepStruct previousStep          = step;
            string     longestValuePlusSpace = longestValueString + "    ";
            while (true)
            {
                string newNumberFormat = getNumberFormat(step);
                longestValueString = longestStringValue.ToString(newNumberFormat);
                labelWidth         = LegendGlyphDrawer.GetLength(longestValuePlusSpace, FontSize);

                if (labelWidth > step.Value * pixelPerValue)
                {
                    //not enough space for new format. Use previous step and its format.
                    //if there is not enough space for even 1 label: we come here first time going through the loop. step is already
                    //equal to previousStep. Assigning it again doesn't hurt. Even step is in this case not a meaningful value,
                    //it is ok, because the dingle label displayed will not use step for formatting.
                    step = previousStep;
                    break;
                }

                numberFormat = newNumberFormat;
                //try next smaller step
                previousStep = step;
                if (step.FirstDigit == 1)
                {
                    step = new StepStruct(5, step.Exponent - 1);
                }
                else if (step.FirstDigit == 2)
                {
                    step = new StepStruct(1, step.Exponent);
                }
                else if (step.FirstDigit == 5)
                {
                    step = new StepStruct(2, step.Exponent);
                }
                else
                {
                    throw new Exception("Illegal FirstDigit of step: " + step + ". It should be 1, 2 or 5.");
                }
            }

            //number of labels that can be displayed in window
            int estimatedLabelCount;
            if (numberFormat == null)
            {
                //not enough space to display even 1 label properly.
                estimatedLabelCount = 1;
            }
            else
            {
#if DEBUG
                if (double.IsNaN(labelWidth))
                {
                    System.Diagnostics.Debugger.Break();
                    throw new Exception("labelWidth cannot be NaN.");
                }
#endif
                estimatedLabelCount = (int)(RenderWidthTracked / labelWidth);
            }

            if (estimatedLabelCount <= 1)
            {
                //only 1 or even only the part of a label can be displayed. In this case it is better to just display the
                //very first value and not trying to find the first nicely rounded step-number
                if (labelValues == null || labelValues.Length != 1)
                {
                    labelValues  = new double[1];
                    labelStrings = new string[1];
                    labelPoints  = new Point[1];
                }
                labelValues[0]    = DisplayValue;
                labelStrings ![0] = DisplayValue.ToString();