@@ -182,10 +182,14 @@ void CartesianPlane::addXAxisLabel (double value, const String& text, const Colo
182182void CartesianPlane::setXAxisLabels (const std::vector<double >& values, const Color& color, float fontSize)
183183{
184184 xAxisLabels.clear ();
185- for ( auto value : values)
185+ if (! values. empty () )
186186 {
187- String text = formatAxisValue (value, xScaleType);
188- xAxisLabels.emplace_back (value, text, color, fontSize);
187+ int precision = determineAxisPrecision (values, xScaleType);
188+ for (auto value : values)
189+ {
190+ String text = formatAxisValueWithPrecision (value, xScaleType, precision);
191+ xAxisLabels.emplace_back (value, text, color, fontSize);
192+ }
189193 }
190194 repaint ();
191195}
@@ -205,10 +209,14 @@ void CartesianPlane::addYAxisLabel (double value, const String& text, const Colo
205209void CartesianPlane::setYAxisLabels (const std::vector<double >& values, const Color& color, float fontSize)
206210{
207211 yAxisLabels.clear ();
208- for ( auto value : values)
212+ if (! values. empty () )
209213 {
210- String text = formatAxisValue (value, yScaleType);
211- yAxisLabels.emplace_back (value, text, color, fontSize);
214+ int precision = determineAxisPrecision (values, yScaleType);
215+ for (auto value : values)
216+ {
217+ String text = formatAxisValueWithPrecision (value, yScaleType, precision);
218+ yAxisLabels.emplace_back (value, text, color, fontSize);
219+ }
212220 }
213221 repaint ();
214222}
@@ -594,8 +602,8 @@ void CartesianPlane::drawLegend (Graphics& g, const Rectangle<float>& bounds)
594602 const int legendHeight = visibleSignalCount * (itemHeight + itemSpacing) - itemSpacing + 2 * padding;
595603
596604 // Calculate legend position
597- float legendX = bounds.getX () + legendPosition.getX () * bounds. getWidth ( ) - legendWidth;
598- float legendY = bounds.getY () + legendPosition.getY () * bounds. getHeight ( );
605+ float legendX = bounds.getX () + bounds. proportionOfWidth ( legendPosition.getX ()) - legendWidth;
606+ float legendY = bounds.getY () + bounds. proportionOfHeight ( legendPosition.getY ());
599607
600608 // Keep legend within bounds
601609 legendX = jlimit (bounds.getX (), bounds.getRight () - legendWidth, legendX);
@@ -640,26 +648,100 @@ void CartesianPlane::drawLegend (Graphics& g, const Rectangle<float>& bounds)
640648 }
641649}
642650
643- String CartesianPlane::formatAxisValue (double value, AxisScaleType scaleType) const
651+ int CartesianPlane::determineAxisPrecision (const std::vector<double >& values, AxisScaleType scaleType) const
652+ {
653+ if (values.empty ())
654+ return 0 ;
655+
656+ // Find the range and characteristics of values
657+ double minVal = *std::min_element (values.begin (), values.end ());
658+ double maxVal = *std::max_element (values.begin (), values.end ());
659+ double range = maxVal - minVal;
660+
661+ if (scaleType == AxisScaleType::logarithmic)
662+ {
663+ // For log scales (frequency), use consistent precision based on range
664+ if (maxVal >= 10000.0 )
665+ return 0 ; // 10k, 20k (no decimals)
666+ else if (maxVal >= 1000.0 )
667+ return 1 ; // 1.0k, 2.5k (one decimal)
668+ else if (maxVal >= 100.0 )
669+ return 0 ; // 100, 500 (integers)
670+ else if (maxVal >= 10.0 )
671+ return 1 ; // 10.0, 50.5 (one decimal)
672+ else
673+ return 2 ; // 1.25, 5.50 (two decimals)
674+ }
675+ else
676+ {
677+ // For linear scales (dB, etc.), determine precision based on typical values
678+ double maxAbs = std::max (std::abs (minVal), std::abs (maxVal));
679+
680+ if (range < 0.1 )
681+ return 3 ; // Very small range needs high precision
682+ else if (range < 1.0 )
683+ return 2 ; // Small range
684+ else if (range < 10.0 || maxAbs < 10.0 )
685+ return 1 ; // Medium range or small absolute values
686+ else
687+ return 0 ; // Large range or values, use integers
688+ }
689+ }
690+
691+ String CartesianPlane::formatAxisValueWithPrecision (double value, AxisScaleType scaleType, int precision) const
644692{
693+ // Handle zero specially
694+ if (std::abs (value) < 1e-10 )
695+ return " 0" ;
696+
645697 if (scaleType == AxisScaleType::logarithmic)
646698 {
699+ // Logarithmic scale formatting (typically frequency)
647700 if (value >= 1000.0 )
648- return String (value / 1000.0 , (value >= 10000.0 ) ? 0 : 1 ) + " k" ;
701+ {
702+ double kValue = value / 1000.0 ;
703+ if (precision == 0 )
704+ return String (static_cast <int > (std::round (kValue ))) + " k" ;
705+ else
706+ return String (kValue , precision) + " k" ;
707+ }
649708 else
650- return String (value, (value >= 100.0 ) ? 0 : 1 );
709+ {
710+ if (precision == 0 )
711+ return String (static_cast <int > (std::round (value)));
712+ else
713+ return String (value, precision);
714+ }
651715 }
652716 else
653717 {
718+ // Linear scale formatting (typically dB, gain, etc.)
654719 if (std::abs (value) >= 1000.0 )
655- return String (value / 1000.0 , 1 ) + " k" ;
656- else if (std::abs (value) >= 1.0 )
657- return String (value, (std::abs (value) >= 10.0 ) ? 0 : 1 );
720+ {
721+ double kValue = value / 1000.0 ;
722+ if (precision == 0 )
723+ return String (static_cast <int > (std::round (kValue ))) + " k" ;
724+ else
725+ return String (kValue , precision) + " k" ;
726+ }
658727 else
659- return String (value, 3 );
728+ {
729+ if (precision == 0 )
730+ return String (static_cast <int > (std::round (value)));
731+ else
732+ return String (value, precision);
733+ }
660734 }
661735}
662736
737+ String CartesianPlane::formatAxisValue (double value, AxisScaleType scaleType) const
738+ {
739+ // Legacy method - determine precision for single value
740+ std::vector<double > singleValue = { value };
741+ int precision = determineAxisPrecision (singleValue, scaleType);
742+ return formatAxisValueWithPrecision (value, scaleType, precision);
743+ }
744+
663745std::optional<Point<float >> CartesianPlane::findBoundsIntersection (const Point<float >& p1, const Point<float >& p2, const Rectangle<float >& bounds) const
664746{
665747 // Find intersection of line segment p1-p2 with rectangle bounds
0 commit comments