Skip to content

Range filter slider handles snap to extremes after panel rebuild (integer ranges) #3168

Description

@mbastian

Summary

When a range filter with an integer-typed value (Degree, In/Out-Degree, Mutual Degree, integer attribute ranges, edge weight when integer, etc.) is displayed after the filter panel has been rebuilt (e.g., select another filter then click back, or any code path going through JRangeSliderPanel.setRange), the slider handles snap to the track extremes even though the labels still show the correct selected lower/upper values. Visually it looks like the slider's bounds shrank to the user's selection.

Steps to reproduce

  1. Open a graph (e.g., Les Misérables).
  2. Drag the Degree Range filter into the queries panel. The slider correctly shows e.g. min=1, max=36.
  3. Move the lower handle to the middle (e.g. lower=11). Slider looks correct.
  4. Click Filter.
  5. Add another filter (e.g. Ego Network), so the Degree Range panel is unmounted.
  6. Click back on Degree Range.

The slider now shows both handles pinned to the extremes, with labels still reading "11" and "36". The underlying Range model is still correct (min=1, max=36); only the slider rendering is wrong.

Root cause

JRangeSliderPanel.Range.refreshSlider() normalizes the lower/upper bounds with:

double normalizedLow = (lowerBoundBigDecimal.subtract(minBigDecimal))
    .divide(maxBigDecimal.subtract(minBigDecimal), RoundingMode.HALF_UP)
    .doubleValue();

BigDecimal.divide(BigDecimal, RoundingMode) returns a result with the dividend's scale. For integer-typed ranges both operands have scale 0, so the result is rounded to an integer (0 or 1). Examples for min=1, max=36:

  • lower=11 → (10).divide(35, HALF_UP) = 0 → handle at left edge
  • upper=36 → (35).divide(35, HALF_UP) = 1 → handle at right edge

The bug only shows after the panel is rebuilt, because:

  • The first time the filter is shown, lower==min and upper==max, so the divisions yield exactly 0 and 1 (no rounding error).
  • While dragging, refreshBounds() runs instead and uses double arithmetic, which is not affected.
  • setRange() → refreshSlider() (called when a new RangePanel is built) is the only path that hits the truncating divide.

Fix

Use MathContext.DECIMAL64 (or any explicit scale) so the division preserves precision regardless of operand scale.

Affected filters

All RangeFilter implementations whose range type is integer (Integer, Long, Short, Byte, BigInteger): Degree Range, In/Out-Degree Range, Mutual Degree Range, integer attribute ranges, integer edge weight ranges, etc. Float/Double ranges were unaffected because their BigDecimals have non-zero scale.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions