feat(RenderWindowInteractor): add optional mouseWheelSpinYBuffering - #3623
feat(RenderWindowInteractor): add optional mouseWheelSpinYBuffering#3623yehor-murza-lx wants to merge 5 commits into
Conversation
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.
| model.mouseWheelSpinYBuffering && | ||
| Math.abs(scrollBuffer) >= SCROLL_THRESHOLD | ||
| ) { | ||
| callData.spinY = Math.sign(scrollBuffer); |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Can you please take the opportunity to move wheeltimeoutid out of model?
There was a problem hiding this comment.
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
|
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.
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
left a comment
There was a problem hiding this comment.
Thanks looks good.
Minor points though.
…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) { |
There was a problem hiding this comment.
if (model.wheelEndDebounceDelay === 0) {
| vtkWarningMacro( | ||
| 'mouseScrollDebounceByPass is deprecated. Use wheelEndDebounceDelay instead.' | ||
| ); | ||
| return setMouseScrollDebounceByPass(...args); |
There was a problem hiding this comment.
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);
There was a problem hiding this comment.
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.
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
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.