Skip to content

Make InputMapper.setInteraction auto-add when matched entry is broader#18461

Draft
georginahalpern wants to merge 3 commits into
BabylonJS:masterfrom
georginahalpern:setInteractionAutoAdd
Draft

Make InputMapper.setInteraction auto-add when matched entry is broader#18461
georginahalpern wants to merge 3 commits into
BabylonJS:masterfrom
georginahalpern:setInteractionAutoAdd

Conversation

@georginahalpern
Copy link
Copy Markdown
Contributor

Summary

Follow-up to #18379. Makes InputMapper.setInteraction smart about the catch-all-mutation footgun raised by @AmoebaChant in the docs PR review (BabylonJS/Documentation#1578).

Problem

With #18379 as merged, setInteraction always mutated the matched entry's interaction in place. If the conditions you supplied were stricter than any existing entry — for example, asking setInteraction("pointer", { button: 0, modifiers: { ctrl: true } }, "rotate") against the GeospatialCamera default map (which has no ctrl-modified pointer entry) — the call would resolve the catch-all { button: 0, interaction: "pan" }, mutate it, and silently break unmodified left-drag too.

The fix is to detect that the matched entry is broader than the conditions and fall through to addEntry, inserting a more-specific entry that wins for the requested combo without clobbering the broader one.

Changes

  • InputMapper.setInteraction:
    • If a matching entry exists and is at least as specific as the conditions, mutate in place (unchanged).
    • If the matched entry is broader than the conditions, addEntry instead.
    • If nothing matches at all, addEntry.
  • New private helpers _entryIsAtLeastAsSpecificAsConditions and _modifiersAtLeastAsSpecific implement the broader-than detection.
  • Five new unit tests in cameraMovement.test.ts covering: mutate-in-place, modifier-broader add, button-broader add, no-match add, and the less-specific-conditions add path.

Backward compatibility

  • Existing call sites that setInteraction against an entry that already exists with matching specificity continue to mutate in place. This is the primary case in the existing arc-rotate playgrounds.
  • Call sites that previously relied on setInteraction returning false to detect a missed match now always get true. The boolean return is preserved for source compatibility, but its semantics change from "found and updated" to "the requested mapping is now in effect" (true is the only outcome since the call now self-heals).

Validation

  • npm run lint:check -w @dev/core: clean
  • npx vitest run packages/dev/core/test/unit/Cameras: 179/179 pass
  • The unit test add a new specific entry when the matched entry is broader (modifier-broader case) reproduces exactly the geospatial scenario AmoebaChant flagged.

Companion docs change

Per AmoebaChant follow-up review feedback: the previous setInteraction
silently mutated the catch-all entry when called with conditions stricter
than any existing entry, which broke unrelated gestures. Example: calling
`setInteraction("pointer", { button: 0, modifiers: { ctrl: true } }, "rotate")`
on the GeospatialCamera default map (which has no ctrl-modifier entry)
mutated the unmodified `{ button: 0, interaction: "pan" }` catch-all and
broke plain left-drag.

New behavior:
- If the matched entry is at least as specific as the supplied conditions,
  mutate its interaction in place (unchanged from before).
- If the matched entry is broader than the conditions (e.g. catch-all
  matched a ctrl-modified request), add a new more-specific entry via
  addEntry instead. The new entry wins for the requested combination
  without clobbering the broader entry.
- If no entry matches at all, also add a new entry.

Internal helpers _entryIsAtLeastAsSpecificAsConditions and
_modifiersAtLeastAsSpecific implement the broader-than detection.

Tests: 5 new cases in cameraMovement.test.ts covering mutate-in-place,
modifier-broader add, button-broader add, no-match add, and the
less-specific-conditions add path. All 189 camera tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 15, 2026

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s).
To prevent this PR from going to the changelog marked it with the "skip changelog" label.

Collapse _entryIsAtLeastAsSpecificAsConditions and _modifiersAtLeastAsSpecific
into one _entryConstrainsAllOf helper. InputConditions is a flat type with all
source-specific fields optional, so a string-keyed loop over the four fields
(button, key, touchCount, modifiers) covers every source naturally -- the
irrelevant fields for a given source are undefined on both entry and
conditions and harmlessly skip the early return.

Saves ~25 lines, no behavior change. All 5 setInteraction tests still pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 15, 2026

Please make sure to label your PR with "bug", "new feature" or "breaking change" label(s).
To prevent this PR from going to the changelog marked it with the "skip changelog" label.

@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 15, 2026

Snapshot stored with reference name:
refs/pull/18461/merge

Test environment:
https://snapshots-cvgtc2eugrd3cgfd.z01.azurefd.net/refs/pull/18461/merge/index.html

To test a playground add it to the URL, for example:

https://snapshots-cvgtc2eugrd3cgfd.z01.azurefd.net/refs/pull/18461/merge/index.html#WGZLGJ#4600

Links to test your changes to core in the published versions of the Babylon tools (does not contain changes you made to the tools themselves):

https://playground.babylonjs.com/?snapshot=refs/pull/18461/merge
https://sandbox.babylonjs.com/?snapshot=refs/pull/18461/merge
https://gui.babylonjs.com/?snapshot=refs/pull/18461/merge
https://nme.babylonjs.com/?snapshot=refs/pull/18461/merge

To test the snapshot in the playground with a playground ID add it after the snapshot query string:

https://playground.babylonjs.com/?snapshot=refs/pull/18461/merge#BCU1XR#0

If you made changes to the sandbox or playground in this PR, additional comments will be generated soon containing links to the dev versions of those tools.

@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 15, 2026

The movement system is unconditionally on for ArcRotateCamera (and
GeospatialCamera). The setter has always been a no-op and the getter has
always returned true, so leaving the accessor pair around only added
noise to the public surface and invited misreading of the API.

Soft breaking change: TypeScript users that read or assign
`camera.useMovementSystem` will now see a compile error. Runtime behavior
is unchanged for plain JS users -- writes silently set an instance
property that has no effect (same as before, where the setter no-op'd).

Drops the corresponding 'useMovementSystem backward compat' test block
in arcRotateCameraMovement.test.ts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 15, 2026

@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 15, 2026

georginahalpern pushed a commit to georginahalpern/BabylonDocumentation that referenced this pull request May 15, 2026
…n.js#18461)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 15, 2026

@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 15, 2026

@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 15, 2026

⚡ Performance Test Results

🟢 All performance tests passed — no regressions detected.

@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 15, 2026

@bjsplat
Copy link
Copy Markdown
Collaborator

bjsplat commented May 15, 2026

🟢 Memory Leak Test Results

13 passed, 0 leaked out of 13 scenarios

🟢 All memory leak tests passed — no leaks detected.

Passed Scenarios (13)
Scenario Package
Core Feature Stack @babylonjs/core
Core Rendering Materials Shadows Stack @babylonjs/core
Core Textures Render Targets PostProcess Stack @babylonjs/core
GUI Fullscreen UI Controls @babylonjs/gui
GUI Mesh ADT Controls @babylonjs/gui
Loaders Boombox Import @babylonjs/loaders
Loaders OBJ Direct Load @babylonjs/loaders
Loaders STL Direct Load @babylonjs/loaders
Materials Library Stack @babylonjs/materials
Serializers glTF Export @babylonjs/serializers
Serializers GLB Export @babylonjs/serializers
PostProcesses Digital Rain Stack @babylonjs/post-processes
Procedural Textures Stack @babylonjs/procedural-textures

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.

2 participants