Skip to content

feat(RenderWindowInteractor): add optional mouseWheelSpinYBuffering - #3623

Open
yehor-murza-lx wants to merge 5 commits into
Kitware:masterfrom
yehor-murza-lx:feat/optional-mouse-wheel-spinY-buffering
Open

feat(RenderWindowInteractor): add optional mouseWheelSpinYBuffering#3623
yehor-murza-lx wants to merge 5 commits into
Kitware:masterfrom
yehor-murza-lx:feat/optional-mouse-wheel-spinY-buffering

Conversation

@yehor-murza-lx

Copy link
Copy Markdown

Motivation

High-frequency wheel input devices (e.g. trackpads) can emit many small fractional spinY deltas in quick succession. Consumers of �tkRenderWindowInteractor that map wheel events directly to camera zoom/dolly can end up with jittery or overly sensitive interactions as a result.

Change

  • Adds an opt-in mouseWheelSpinYBuffering flag (default alse). When enabled, fractional spinY deltas are accumulated per interactor instance and a mouseWheelEvent is only dispatched once the buffered spin reaches a full step (rounded via Math.sign). The buffer resets when the scroll direction reverses and when the wheel-end debounce fires.
  • Adds a configurable wheelEndDebounceDelay (default 200, matching the previous hardcoded value) so consumers can tune how long to wait after the last wheel event before the wheel-end event fires.
  • Updates the corresponding TypeScript definitions (index.d.ts).

Backward compatibility

Both new properties default to values that reproduce the current behavior exactly (mouseWheelSpinYBuffering: false disables buffering entirely; wheelEndDebounceDelay: 200 matches the previous hardcoded timeout), so no existing consumer behavior changes unless they opt in.

Testing

pm run reformat and lint (oxlint) pass with no issues.

  • Manually verified against a downstream application that previously carried this behavior as a local patch; setting mouseWheelSpinYBuffering(true) reproduces that smoothing behavior.

High-frequency wheel devices (e.g. trackpads) can emit many small fractional
spinY deltas in quick succession, causing jittery or overly sensitive zoom/
rotate interactions in consumers of vtkRenderWindowInteractor.

Add an opt-in mouseWheelSpinYBuffering flag (default false) that, when
enabled, accumulates fractional spinY deltas per interactor instance and
only dispatches a mouseWheelEvent once the buffered spin reaches a full
step. The buffer resets on direction change and on wheel-end.

Also add a configurable wheelEndDebounceDelay (default 200, matching the
previous hardcoded value) so consumers can tune how long to wait after the
last wheel event before firing the wheel-end event.

Both properties default to the existing behavior, so this change is fully
backward compatible.
Comment thread Sources/Rendering/Core/RenderWindowInteractor/index.js
model.mouseWheelSpinYBuffering &&
Math.abs(scrollBuffer) >= SCROLL_THRESHOLD
) {
callData.spinY = Math.sign(scrollBuffer);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think spinY should be 3 (and not 1) if the scroll buffer is 3.2 (maybe the last event added 2.3 to the buffer)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the buffered event to preserve the complete integer portion of the accumulated scroll amount. For example, a buffer of 3.2 now emits spinY: 3 and retains 0.2; negative values are handled symmetrically with Math.trunc.
In 203b687

model.wheelTimeoutID = setTimeout(() => {
publicAPI.extendAnimation(600);
publicAPI.endMouseWheelEvent();
model.wheelTimeoutID = 0;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please take the opportunity to move wheeltimeoutid out of model?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved wheelTimeoutID into the vtkRenderWindowInteractor closure so it is private per interactor instance. It is cleared during unbind and is no longer exposed through DEFAULT_VALUES or IRenderWindowInteractorInitialValues.
In 203b687

Comment thread Sources/Rendering/Core/RenderWindowInteractor/index.js
@finetjul

finetjul commented Sep 2, 2026

Copy link
Copy Markdown
Member

We now have a case when a startmousewheelevent may not be followed by a mouse event. Please check in the code base that it is fine. I think I remember that assumption to be expected

Move wheel timeout state out of the public model and simplify wheel event
dispatching for buffered and unbuffered input.

Emit the complete integer portion of buffered spinY values and use
wheelEndDebounceDelay as the canonical wheel-end configuration. Keep
mouseScrollDebounceByPass for backward compatibility while marking it
deprecated in the TypeScript API.
@YehorMurza-elucid

Copy link
Copy Markdown

We now have a case when a startmousewheelevent may not be followed by a mouse event. Please check in the code base that it is fine. I think I remember that assumption to be expected

The buffering behavior intentionally allows startMouseWheelEvent to occur before the first mouseWheelEvent when the initial wheel delta is fractional and does not yet reach the threshold. This is required to retain the accumulated input and avoid dispatching a partial event. The buffer is reset on direction changes, wheel-end, and unbind. Existing mouse-move handling already uses the same burst-marker pattern, where a start event can precede later interaction events.

Reformat the .d.ts file (handledEvents enum member quoting and doc-comment
indentation) to satisfy the repo's format check.

@finetjul finetjul left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks looks good.
Minor points though.

Comment thread Sources/Rendering/Core/RenderWindowInteractor/index.js
Comment thread Sources/Rendering/Core/RenderWindowInteractor/index.js
…d deprecation warning

Dispatch mouseWheelEvent from a single call site using a triggerMouseWheelEvent
flag, instead of separate calls in the buffered and unbuffered branches.

Warn when the deprecated mouseScrollDebounceByPass setter is used, wrapping
the original setter so its boolean return value is preserved (macro.chain
would have changed it to an array).
}

if (model.mouseScrollDebounceByPass) {
if (model.mouseScrollDebounceByPass || model.wheelEndDebounceDelay === 0) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (model.wheelEndDebounceDelay === 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed in 35c8c21

Comment thread Sources/Rendering/Core/RenderWindowInteractor/index.js Outdated
vtkWarningMacro(
'mouseScrollDebounceByPass is deprecated. Use wheelEndDebounceDelay instead.'
);
return setMouseScrollDebounceByPass(...args);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apologies for my erroneous suggestion earlier, I meant:

publicAPI.setMouseScrollDebounceByPass = (byPass) => {
    vtkWarningMacro(
      'mouseScrollDebounceByPass is deprecated. Use wheelEndDebounceDelay instead.'
    );
    return publicAPI.setWheelEndDebounceDelay(byPass ? 0 : DEFAULT_VALUES.wheelEndDebounceDelay);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed in 35c8c21 and removed the typescript definitions as well

…del state

Remove mouseScrollDebounceByPass from DEFAULT_VALUES and the setGet macro list;
wheelEndDebounceDelay is now the sole source of truth for wheel-end behavior.

Keep getMouseScrollDebounceByPass/setMouseScrollDebounceByPass as deprecated
compatibility shims that delegate to wheelEndDebounceDelay, warning on use of
the setter.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants