Skip to content

Add applyToGroupOther option to onIntersect with generator support and tests#522

Merged
tracygardner merged 6 commits intomainfrom
codex/add-on-collision-support-for-matching-meshes
Apr 11, 2026
Merged

Add applyToGroupOther option to onIntersect with generator support and tests#522
tracygardner merged 6 commits intomainfrom
codex/add-on-collision-support-for-matching-meshes

Conversation

@tracygardner
Copy link
Copy Markdown
Contributor

@tracygardner tracygardner commented Apr 11, 2026

Motivation

  • Enable collision handlers to expand a right-hand argument into all meshes that belong to the same group root so a single collision block can target an entire group of targets.

Description

  • Added an applyToGroupOther option to flock.onIntersect to expand the otherMeshName into all matching scene meshes sharing the same group root and register intersections for each match, skipping the original source when appropriate, and preventing recursive expansion by resetting the flag for recursive calls.
  • Implemented a getGroupRoot helper that derives group roots by splitting the mesh name on __ or _ and using the first segment.
  • Updated the Blockly JS generator for the on_collision block to emit applyToGroupOther: true for top-level blocks so generated code automatically expands right-hand groups at runtime.

Testing

  • Ran the physics unit tests including the existing intersection test and two new tests: should register intersections for all matching right-hand group meshes and should skip self-pair when expanding right-hand collision group, and all tests passed.

Codex Task

Summary by CodeRabbit

  • New Features

    • Target intersections can be registered against a group so handlers fire for all matching objects (including ones created later).
  • Improvements

    • Deferred wiring and replay ensure handlers applied when targets appear; group targets are expanded without duplicating self-pairs and tolerate common name formats.
  • Tests

    • Added coverage for group expansion, delayed targets, canonicalized names, and deduplication behavior.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Apr 11, 2026

Deploying flockxr with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6a60c3f
Status: ✅  Deploy successful!
Preview URL: https://6949d2d3.flockxr.pages.dev
Branch Preview URL: https://codex-add-on-collision-suppo.flockxr.pages.dev

View logs

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 11, 2026

Warning

Rate limit exceeded

@tracygardner has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 59 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 10 minutes and 59 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0097857f-a04c-435a-8def-a6f258a41429

📥 Commits

Reviewing files that changed from the base of the PR and between c2f384d and 6a60c3f.

📒 Files selected for processing (3)
  • api/physics.js
  • flock.js
  • tests/physics.test.js
📝 Walkthrough

Walkthrough

Adds group-aware intersection registration: flockPhysics.onIntersect accepts applyToGroupOther to expand a target name into a canonical group root, queue pending intersections or immediately register handlers for all matching scene meshes (deduplicated and excluding the source), and supports deferred wiring when meshes arrive later.

Changes

Cohort / File(s) Summary
Physics API
api/physics.js
Changed flockPhysics.onIntersect(meshName, otherMeshName, { trigger, callback } = {}) → adds { applyToGroupOther = false }. When true, derives a group root from otherMeshName, records pending intersection entries in flock.pendingIntersections or immediately expands to matching scene meshes, deduplicates/excludes the source, and recursively registers handlers with applyToGroupOther: false. Queued triggers now include meshName and applyToGroup.
Core scene state
flock.js
Added flock.pendingIntersections (Map) and reset/initialized with scene lifecycle. Refactored announceMeshReady to reapply pending triggers, replay group triggers across siblings (preventing recursion), and process pendingIntersections for a group by registering intersection pairs only for new, non-self, non-duplicate RHS meshes.
Generators
generators/generators-events.js
Updated on-collision event generator to include applyToGroupOther: true in the emitted options only when the Blockly block is top-level (!block.getSurroundParent()); nested blocks omit the option. Adjusted generated callback closure formatting accordingly.
Tests — physics
tests/physics.test.js
Added four tests for onIntersect(..., { applyToGroupOther: true }): multiple RHS group callbacks firing, exclusion of self-pair when source equals group seed, deferred wiring when RHS meshes are created after registration, and normalization/canonicalization of unsanitized RHS group identifiers.
Tests — triggers
tests/events.test.js
Added two onTrigger tests validating replay behavior with applyToGroup: false (single target replay) and applyToGroup: true (group replay across siblings).

Sequence Diagram(s)

sequenceDiagram
    participant Block as Blockly Block
    participant Gen as Event Generator
    participant Physics as flockPhysics
    participant Scene as Scene Graph
    participant Registry as ActionManager

    Block->>Gen: generate onIntersect code (top-level)
    Gen->>Physics: onIntersect(src, otherSeed, {trigger, callback, applyToGroupOther:true})
    Physics->>Physics: compute canonical group root from otherSeed
    alt Scene present
        Physics->>Scene: query meshes matching group root
        Scene-->>Physics: list of mesh names
        Physics->>Physics: filter out src, dedupe names
        loop for each match
            Physics->>Physics: call onIntersect(src, match, {applyToGroupOther:false,...})
            Physics->>Registry: register ExecuteCodeAction for pair
            Registry-->>Physics: action registered
        end
    else Scene absent
        Physics->>Physics: enqueue pending entry in flock.pendingIntersections
    end
    Note over Physics,Scene: When new meshes are announced, announceMeshReady processes pendingIntersections and wires callbacks for new, non-duplicate RHS meshes
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I nibble names and split them neat,
Root seeds bloom into a hopping fleet.
I queue and match when scenes are late,
Wire callbacks tidy, never duplicate —
Rabbity cheers for intersections complete!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: adding an applyToGroupOther option to the onIntersect function with corresponding generator support and test coverage.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/add-on-collision-support-for-matching-meshes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
api/physics.js (1)

614-615: Deduplicate group-root parsing helper.

getGroupRoot is now duplicated in multiple methods. Please move it to a shared module-level helper so grouping behavior can’t drift between trigger/intersection paths.

Proposed refactor
+const getGroupRoot = (name) =>
+  name.includes("__") ? name.split("__")[0] : name.split("_")[0];
+
 export const flockPhysics = {
   onTrigger(
     meshName,
     { trigger, callback, mode = "wait", applyToGroup = false },
   ) {
-    const getGroupRoot = (name) =>
-      name.includes("__") ? name.split("__")[0] : name.split("_")[0];
-
     const groupName = getGroupRoot(meshName);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/physics.js` around lines 614 - 615, Extract the getGroupRoot
implementation into a single module-level helper function (e.g., declare const
getGroupRoot = (name) => ...) and replace all duplicated local copies in trigger
and intersection-related functions with calls to this shared helper; update any
files or scopes that currently reimplement getGroupRoot so they reference the
module-level symbol, remove the duplicated definitions, and ensure behavior
remains identical (splitting on "__" first, then "_" as in the original
implementation).
tests/physics.test.js (1)

220-226: Strengthen self-pair exclusion assertion.

The test currently proves positive behavior for the sibling mesh, but it doesn’t directly assert that self-trigger does not fire. Add a self-trigger check before the sibling trigger.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/physics.test.js` around lines 220 - 226, Before invoking the sibling
trigger, explicitly assert that triggering the source mesh itself does not
increment the counter: call sourceMesh.actionManager.processTrigger with
flock.BABYLON.ActionManager.OnIntersectionEnterTrigger and { mesh: sourceMesh }
and assert count remains 0; then call sourceMesh.actionManager.processTrigger
with { mesh: otherMesh } and assert count becomes 1 (using the existing
expect(count).to.equal(1)), ensuring the self-trigger exclusion is tested
directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/physics.test.js`:
- Around line 161-168: The test is racing because flock.onIntersect is
asynchronous; modify the test to await the async registration by awaiting the
Promise returned by flock.onIntersect wherever it's called (e.g., the
onIntersect calls in the tests), so registration completes before calling
processTrigger; update both occurrences of flock.onIntersect (the one that
currently increments count and the other at the later occurrence) to use await
so callbacks are reliably registered before invoking processTrigger.

---

Nitpick comments:
In `@api/physics.js`:
- Around line 614-615: Extract the getGroupRoot implementation into a single
module-level helper function (e.g., declare const getGroupRoot = (name) => ...)
and replace all duplicated local copies in trigger and intersection-related
functions with calls to this shared helper; update any files or scopes that
currently reimplement getGroupRoot so they reference the module-level symbol,
remove the duplicated definitions, and ensure behavior remains identical
(splitting on "__" first, then "_" as in the original implementation).

In `@tests/physics.test.js`:
- Around line 220-226: Before invoking the sibling trigger, explicitly assert
that triggering the source mesh itself does not increment the counter: call
sourceMesh.actionManager.processTrigger with
flock.BABYLON.ActionManager.OnIntersectionEnterTrigger and { mesh: sourceMesh }
and assert count remains 0; then call sourceMesh.actionManager.processTrigger
with { mesh: otherMesh } and assert count becomes 1 (using the existing
expect(count).to.equal(1)), ensuring the self-trigger exclusion is tested
directly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bf963dbf-b4bf-49f3-bb3f-acf6dabd7efd

📥 Commits

Reviewing files that changed from the base of the PR and between ad200a6 and 332daba.

📒 Files selected for processing (3)
  • api/physics.js
  • generators/generators-events.js
  • tests/physics.test.js

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Apr 11, 2026

Deploying flockdev with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6a60c3f
Status: ✅  Deploy successful!
Preview URL: https://f67a19aa.flockdev.pages.dev
Branch Preview URL: https://codex-add-on-collision-suppo.flockdev.pages.dev

View logs

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@api/physics.js`:
- Around line 614-649: The code derives groupName from the raw otherMeshName and
stores/queries pending entries under that raw key, which can mismatch sanitized
scene mesh names; before computing getGroupRoot(otherMeshName) and before
scanning flock.scene.meshes, canonicalize otherMeshName using the same
exact-or-normalized lookup used by flock.whenModelReady() (i.e., resolve the
alias or normalized name via the flock.whenModelReady lookup logic), then
compute groupName = getGroupRoot(canonicalName) and use that canonicalName when
registering/queueing into flock.pendingIntersections and when comparing against
flock.scene.meshes so announceMeshReady() and the scene scan will find the same
group key.

In `@flock.js`:
- Around line 2504-2531: The pending trigger entries lack the original target
metadata so announceMeshReady's replay logic always treats entries as non-group
and rebinds them to the announcing mesh; update the queuing in
flockPhysics.onTrigger (or wherever flock.pendingTriggers entries are pushed) to
store the original meshName and applyToGroup (e.g. include meshName and
applyToGroup in each queued object) and change the replay logic in
announceMeshReady/wherever you iterate flock.pendingTriggers to use that stored
meshName/applyToGroup: when applyToGroup is true, fan out to all matching meshes
as before, and when false, invoke flock.onTrigger using the stored meshName (not
the announcing meshName) with applyToGroup set to false to avoid recursion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8d8015fe-bfbc-416f-85fb-f39108f7dcc0

📥 Commits

Reviewing files that changed from the base of the PR and between 332daba and 9ec1d9c.

📒 Files selected for processing (3)
  • api/physics.js
  • flock.js
  • tests/physics.test.js
✅ Files skipped from review due to trivial changes (1)
  • tests/physics.test.js

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
flock.js (1)

2518-2532: Register only the newly ready sibling here.

This path rescans the whole group and replays onTrigger() for every sibling on each readiness event. Mirroring pendingIntersections.registeredOthers with a per-pending registeredMeshes set would make this O(1) per new sibling and avoid duplicate registration attempts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@flock.js` around lines 2518 - 2532, The current applyToGroup branch rescans
the entire group and re-invokes flock.onTrigger for every sibling each time,
causing duplicate attempts; change it to track which meshes have already been
registered per pending entry by adding a Set (e.g., pending.registeredMeshes) to
the pending object and, instead of computing matching =
flock.scene.meshes.filter(...), only register the single newly-ready mesh (the
one that triggered this readiness event) if its name is not in
pending.registeredMeshes; add the mesh name to pending.registeredMeshes after
calling flock.onTrigger and keep remaining.push(pending) as before to preserve
future sibling registration semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@flock.js`:
- Around line 2534-2538: The check for a target control uses direct root
children lookup and misses nested GUI controls; update the detection logic in
the block that computes targetExists to use
flock.scene?.UITexture?.getControlByName(targetMeshName) (falling back to
flock.scene?.getMeshByName(targetMeshName)) instead of
_rootContainer._children.some(...), mirroring the recursive approach used by
whenModelReady(), so pending trigger replay correctly finds nested controls such
as those inside flock.stackPanel.

In `@tests/events.test.js`:
- Around line 414-452: The test leaves applyToGroup registrations in
flock.pendingTriggers which causes order-dependent failures; add cleanup in the
test suite's afterEach() to reset flock.pendingTriggers (e.g., clear the
Map/array or assign a fresh empty container) so deferred callbacks registered
with applyToGroup are removed between tests; locate the afterEach() that
currently only disposes meshes and add a line to clear/reset
flock.pendingTriggers to ensure no live callbacks remain.

---

Nitpick comments:
In `@flock.js`:
- Around line 2518-2532: The current applyToGroup branch rescans the entire
group and re-invokes flock.onTrigger for every sibling each time, causing
duplicate attempts; change it to track which meshes have already been registered
per pending entry by adding a Set (e.g., pending.registeredMeshes) to the
pending object and, instead of computing matching =
flock.scene.meshes.filter(...), only register the single newly-ready mesh (the
one that triggered this readiness event) if its name is not in
pending.registeredMeshes; add the mesh name to pending.registeredMeshes after
calling flock.onTrigger and keep remaining.push(pending) as before to preserve
future sibling registration semantics.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fd91ab49-7655-48c1-8358-d793d00f2357

📥 Commits

Reviewing files that changed from the base of the PR and between 9ec1d9c and c2f384d.

📒 Files selected for processing (4)
  • api/physics.js
  • flock.js
  • tests/events.test.js
  • tests/physics.test.js
✅ Files skipped from review due to trivial changes (1)
  • tests/physics.test.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • api/physics.js

Comment on lines +414 to +452
it("replays pending group trigger across siblings when applyToGroup is true", async function () {
const first = "lategroup_1";
const second = "lategroup_2";

let count = 0;
flock.onTrigger(first, {
trigger: "OnPickTrigger",
callback: () => count++,
applyToGroup: true,
});

await flock.createBox(first, {
width: 1,
height: 1,
depth: 1,
position: [0, 0, 0],
});
await flock.createBox(second, {
width: 1,
height: 1,
depth: 1,
position: [2, 0, 0],
});
meshIds.push(first, second);

const mesh1 = flock.scene.getMeshByName(first);
const mesh2 = flock.scene.getMeshByName(second);
expect(mesh1).to.exist;
expect(mesh2).to.exist;

mesh1.actionManager?.processTrigger(
flock.BABYLON.ActionManager.OnPickTrigger,
);
mesh2.actionManager?.processTrigger(
flock.BABYLON.ActionManager.OnPickTrigger,
);

expect(count).to.equal(2);
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Reset deferred trigger state after this replay test.

applyToGroup: true registrations stay in flock.pendingTriggers by design, so this test leaves a live callback behind. afterEach() only disposes meshes, which makes later tests order-dependent if they reuse the same group root or the same shared flock instance.

🧹 Suggested cleanup in afterEach()
     afterEach(function () {
       // Clear any custom events registered during tests
       if (flock.events) {
         Object.keys(flock.events).forEach((key) => delete flock.events[key]);
       }
+      flock.pendingTriggers = new Map();
+      flock.pendingIntersections = new Map();
       // Dispose any meshes created during tests
       meshIds.forEach((id) => {
🧰 Tools
🪛 GitHub Actions: Prettier

[warning] Prettier reports formatting issue (listed under 'warn').

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/events.test.js` around lines 414 - 452, The test leaves applyToGroup
registrations in flock.pendingTriggers which causes order-dependent failures;
add cleanup in the test suite's afterEach() to reset flock.pendingTriggers
(e.g., clear the Map/array or assign a fresh empty container) so deferred
callbacks registered with applyToGroup are removed between tests; locate the
afterEach() that currently only disposes meshes and add a line to clear/reset
flock.pendingTriggers to ensure no live callbacks remain.

@tracygardner tracygardner merged commit b1a4102 into main Apr 11, 2026
10 checks passed
@tracygardner tracygardner deleted the codex/add-on-collision-support-for-matching-meshes branch April 11, 2026 16:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant