@@ -97,6 +97,10 @@ public class RTTank extends Canvas
9797 /** Current value, i.e. fill level */
9898 private volatile double value = 5.0 ;
9999
100+ /** Requested value range, see {@link #setRange}; NaN until set */
101+ private volatile double rangeLow = Double .NaN ;
102+ private volatile double rangeHigh = Double .NaN ;
103+
100104 /** Does layout need to be re-computed? */
101105 protected final AtomicBoolean need_layout = new AtomicBoolean (true );
102106
@@ -273,6 +277,7 @@ public void setLogScale(final boolean logscale)
273277 {
274278 scale .setLogarithmic (logscale );
275279 right_scale .setLogarithmic (logscale );
280+ applyRange ();
276281 requestUpdate ();
277282 }
278283
@@ -441,34 +446,65 @@ public void setPerpendicularTickLabels(final boolean perpendicular)
441446 requestUpdate ();
442447 }
443448
444- /** Set value range
445- * @param low Lower limit
446- * @param high Upper limit
449+ /** Set value range.
450+ *
451+ * <p>An inverted range ({@code low > high}) runs the scale top-down
452+ * and fills the tank from the top. A logarithmic scale always runs
453+ * bottom-up. Non-finite and zero-width ranges are ignored.
454+ *
455+ * @param low Value at the bottom of the tank
456+ * @param high Value at the top of the tank
447457 */
448458 public void setRange (final double low , final double high )
449459 {
450- // Guard against NaN, Infinite, or inverted/flat range
451- if (!Double .isFinite (low ) || !Double .isFinite (high ) || low >= high )
460+ if (!Double .isFinite (low ) || !Double .isFinite (high ) || low == high )
461+ return ;
462+ rangeLow = low ;
463+ rangeHigh = high ;
464+ applyRange ();
465+ }
466+
467+ /** @return Current value range of the scale */
468+ public AxisRange <Double > getValueRange ()
469+ {
470+ return scale .getValueRange ();
471+ }
472+
473+ /** Push the requested range to both scales, ascending for a log scale */
474+ private void applyRange ()
475+ {
476+ double low = rangeLow ;
477+ double high = rangeHigh ;
478+ if (Double .isNaN (low ))
452479 return ;
480+ if (scale .isLogarithmic () && low > high )
481+ {
482+ low = rangeHigh ;
483+ high = rangeLow ;
484+ }
453485 scale .setValueRange (low , high );
454486 right_scale .setValueRange (low , high );
455487 }
456488
457- /** @param value Set value */
489+ /** @param value Set value; a non-finite value shows an empty tank */
458490 public void setValue (final double value )
459491 {
460492 if (Double .isFinite (value ))
461493 this .value = value ;
462494 else
463- this .value = scale .getValueRange ().getLow ();
495+ {
496+ final AxisRange <Double > range = scale .getValueRange ();
497+ this .value = Math .min (range .getLow (), range .getHigh ());
498+ }
464499 requestUpdate ();
465500 }
466501
467- /** Map a value to a Y pixel within the plot bounds (low value at bottom) .
502+ /** Map a value to a Y pixel within the plot bounds.
468503 * Returns -1 when the mapping is undefined (e.g. log scale with non-positive inputs).
504+ * @param normal Range runs bottom-up? Otherwise the low value is at the top
469505 */
470506 private int valueToY (final Rectangle pb , final double min , final double max ,
471- final double v , final boolean logscale )
507+ final double v , final boolean logscale , final boolean normal )
472508 {
473509 final double frac ;
474510 if (logscale )
@@ -479,17 +515,17 @@ private int valueToY(final Rectangle pb, final double min, final double max,
479515 }
480516 else
481517 frac = (v - min ) / (max - min );
482- return (int ) (pb .y + pb .height * (1.0 - frac ));
518+ return (int ) (pb .y + pb .height * (normal ? 1.0 - frac : frac ));
483519 }
484520
485521 /** Draw a single horizontal limit line across the tank area at the given value. */
486522 private void drawLimitLineAt (final Graphics2D gc , final Rectangle pb ,
487- final double min , final double max ,
523+ final double min , final double max , final boolean normal ,
488524 final double limit , final Color color )
489525 {
490526 if (!Double .isFinite (limit ) || limit <= min || limit >= max )
491527 return ;
492- final int y = valueToY (pb , min , max , limit , scale .isLogarithmic ());
528+ final int y = valueToY (pb , min , max , limit , scale .isLogarithmic (), normal );
493529 if (y < pb .y || y > pb .y + pb .height )
494530 return ;
495531 gc .setColor (color );
@@ -642,18 +678,18 @@ protected Image updateImageBuffer()
642678 final double lim_lo = limit_lo ;
643679 final double lim_hi = limit_hi ;
644680 final double lim_hihi = limit_hihi ;
645- if (normal && ( !Double .isNaN (lim_lolo ) || !Double .isNaN (lim_lo ) ||
646- !Double .isNaN (lim_hi ) || !Double .isNaN (lim_hihi ) ))
681+ if (!Double .isNaN (lim_lolo ) || !Double .isNaN (lim_lo ) ||
682+ !Double .isNaN (lim_hi ) || !Double .isNaN (lim_hihi ))
647683 {
648684 if (limits_from_pv )
649685 gc .setStroke (new BasicStroke (2f ));
650686 else
651687 gc .setStroke (new BasicStroke (2f , BasicStroke .CAP_BUTT ,
652688 BasicStroke .JOIN_MITER , 10f , new float []{6f , 4f }, 0f ));
653- drawLimitLineAt (gc , plot_bounds , min , max , lim_lolo , limit_major_color );
654- drawLimitLineAt (gc , plot_bounds , min , max , lim_lo , limit_minor_color );
655- drawLimitLineAt (gc , plot_bounds , min , max , lim_hi , limit_minor_color );
656- drawLimitLineAt (gc , plot_bounds , min , max , lim_hihi , limit_major_color );
689+ drawLimitLineAt (gc , plot_bounds , min , max , normal , lim_lolo , limit_major_color );
690+ drawLimitLineAt (gc , plot_bounds , min , max , normal , lim_lo , limit_minor_color );
691+ drawLimitLineAt (gc , plot_bounds , min , max , normal , lim_hi , limit_minor_color );
692+ drawLimitLineAt (gc , plot_bounds , min , max , normal , lim_hihi , limit_major_color );
657693 gc .setStroke (new BasicStroke (1f ));
658694 }
659695
0 commit comments