Skip to content

Clean up green task bounding boxes #8535

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 5, 2025
Merged

Clean up green task bounding boxes #8535

merged 7 commits into from
May 5, 2025

Conversation

philippotto
Copy link
Member

@philippotto philippotto commented Apr 15, 2025

Only show task bounding boxes (in green) if a task exists. Also, show one for each layer (relevant for hybrid tasks).

URL of deployed dev instance (used for testing):

  • https://___.webknossos.xyz

Steps to test:

  • create a task with a specific bounding box
  • check that the bounding box is rendered in green
  • check that a volume-only non-task annotation hasn't a green bounding box

Issues:


(Please delete unneeded items, merge only when none are left open)

Copy link
Contributor

coderabbitai bot commented Apr 15, 2025

📝 Walkthrough

"""

Walkthrough

The changes refactor the management of task bounding boxes in the frontend scene controller to support multiple bounding boxes, each associated with a tracing ID, instead of a single bounding box. The code introduces a new accessor function to retrieve all relevant bounding boxes from an annotation and updates the scene controller to build, update, and manage these bounding boxes collectively. The new approach iterates over all bounding boxes, updating their visibility and scene presence based on the current annotation state, and removes outdated cubes as needed. The exported API and internal logic are adjusted to accommodate this multi-bounding box handling.

Changes

Files / Paths Change Summary
frontend/javascripts/oxalis/controller/scene_controller.ts Refactored from single to multiple bounding box management; introduced forEachTaskCube method; updated bounding box handling logic; adjusted method signatures and scene updates.
frontend/javascripts/oxalis/model/accessors/tracing_accessor.ts Added getTaskBoundingBoxes accessor to collect bounding boxes by tracing ID; uses memoization for instance reuse.
CHANGELOG.unreleased.md Added fix note for incorrect green coloring of layer bounding boxes, clarifying color usage for tasks only.

Assessment against linked issues

Objective Addressed Explanation
Unify/clarify bounding box color and handling for volume-only annotations (e.g., ensure correct color/logic) (#7831)

Suggested labels

frontend, enhancement

Poem

A hop and a skip through bounding box land,
Now each tracing gets its own—oh, isn't that grand?
No more confusion, no green where there's grey,
The scene's unified, in a rabbit-approved way.
With cubes for each task, and colors just right,
This code leaps ahead—what a bounding delight! 🐇✨
"""


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c6cda9a and 1dc8cd3.

📒 Files selected for processing (2)
  • CHANGELOG.unreleased.md (1 hunks)
  • frontend/javascripts/oxalis/controller/scene_controller.ts (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.unreleased.md
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build-smoketest-push
  • GitHub Check: backend-tests
🔇 Additional comments (6)
frontend/javascripts/oxalis/controller/scene_controller.ts (6)

49-49: Properly importing new accessor for task bounding boxes

Adding this import enables the scene controller to retrieve multiple bounding boxes by tracing ID, supporting the multi-layer bounding box functionality.


87-87: Good data structure choice for multi-layer support

Replacing the single task bounding box with a record that maps tracing IDs to cubes supports the requirement to show one bounding box per layer, which is particularly valuable for hybrid tasks.


366-372: Well-designed helper method improves code clarity

This helper method centralizes the iteration over task cubes, making the code more maintainable and reducing duplication. It's properly used throughout the file for various operations on task cubes.


395-395: Good use of the new helper method for updating camera views

Properly updates all task bounding boxes for the specified camera view, ensuring consistent behavior across multiple bounding boxes.


649-649: Consistent handling of task bounding boxes throughout the lifecycle

The helper method is appropriately used to manage visibility and cleanup of all task bounding boxes in various controller operations (stop/start plane mode, destroy).

Also applies to: 664-664, 695-695


737-739: Store listener properly updated for new multi-bounding box functionality

The listener now uses the new getTaskBoundingBoxes accessor and correctly passes the mapping to the updated method, ensuring the view stays in sync with the model.

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@philippotto philippotto requested a review from hotzenklotz April 15, 2025 14:31
@philippotto philippotto self-assigned this Apr 15, 2025
Copy link
Contributor

@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: 0

🧹 Nitpick comments (2)
frontend/javascripts/oxalis/model/accessors/tracing_accessor.ts (1)

94-101: Consider clarifying the function’s doc comment.

The _getTaskBoundingBoxes function is self-explanatory, but adding a short doc comment about what layers it processes and how it handles null bounding boxes can help future maintainers understand its behavior more quickly.

frontend/javascripts/oxalis/controller/scene_controller.ts (1)

272-301: Method name might more accurately reflect multiple bounding boxes.

Since this method handles multiple tracing IDs, renaming buildTaskBoundingBox to buildTaskBoundingBoxes could clarify that it processes a set of bounding boxes rather than a single one.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 920807a and 64d17a1.

📒 Files selected for processing (2)
  • frontend/javascripts/oxalis/controller/scene_controller.ts (8 hunks)
  • frontend/javascripts/oxalis/model/accessors/tracing_accessor.ts (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
frontend/javascripts/oxalis/model/accessors/tracing_accessor.ts (1)
frontend/javascripts/oxalis/store.ts (1)
  • StoreAnnotation (285-290)
frontend/javascripts/oxalis/controller/scene_controller.ts (2)
frontend/javascripts/oxalis/constants.ts (1)
  • BoundingBoxType (41-44)
frontend/javascripts/oxalis/model/accessors/tracing_accessor.ts (1)
  • getTaskBoundingBoxes (100-100)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: frontend-tests
  • GitHub Check: backend-tests
  • GitHub Check: build-smoketest-push
🔇 Additional comments (4)
frontend/javascripts/oxalis/controller/scene_controller.ts (4)

39-39: Import usage is clear.

No concerns regarding the new import statement.


66-66: Good initialization of the bounding box record.

The taskCubeByTracingId record is well-structured and clearly named. No issues here.


303-309: Helper function usage is well-organized.

forEachTaskCube nicely abstracts iteration over the bounding box cubes. The code is succinct and improves maintainability.


610-613: Validate the single-task assumption.

By requiring Store.getState().task != null before creating bounding boxes, the code presumes a single active task. If multiple tasks may exist in the future, consider extending or revisiting this logic.

@philippotto
Copy link
Member Author

@hotzenklotz ping :) but feel free to delegate

@hotzenklotz
Copy link
Member

Sorry, overlooked this. Will review.

Copy link
Member

@hotzenklotz hotzenklotz left a comment

Choose a reason for hiding this comment

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

LGTM.

Tested with a new task and the BB was green.

@philippotto philippotto enabled auto-merge (squash) May 5, 2025 06:56
Copy link
Contributor

@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: 0

🧹 Nitpick comments (1)
CHANGELOG.unreleased.md (1)

20-20: Improve clarity of the changelog entry by adding a comma.

To enhance readability and align with standard punctuation, add a comma after "green":

- Fixed that layer bounding boxes were sometimes colored green even though this should only happen for tasks. [#8535](https://github.com/scalableminds/webknossos/pull/8535)
+ Fixed that layer bounding boxes were sometimes colored green, even though this should only happen for tasks. [#8535](https://github.com/scalableminds/webknossos/pull/8535)
🧰 Tools
🪛 LanguageTool

[uncategorized] ~20-~20: Possible missing comma found.
Context: ...r bounding boxes were sometimes colored green even though this should only happen for...

(AI_HYDRA_LEO_MISSING_COMMA)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7cd883b and 5274a2e.

📒 Files selected for processing (3)
  • CHANGELOG.unreleased.md (1 hunks)
  • frontend/javascripts/oxalis/controller/scene_controller.ts (10 hunks)
  • frontend/javascripts/oxalis/model/accessors/tracing_accessor.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/javascripts/oxalis/model/accessors/tracing_accessor.ts
  • frontend/javascripts/oxalis/controller/scene_controller.ts
🧰 Additional context used
🪛 LanguageTool
CHANGELOG.unreleased.md

[uncategorized] ~20-~20: Possible missing comma found.
Context: ...r bounding boxes were sometimes colored green even though this should only happen for...

(AI_HYDRA_LEO_MISSING_COMMA)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: backend-tests
  • GitHub Check: build-smoketest-push

Copy link
Contributor

@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

🔭 Outside diff range comments (1)
frontend/javascripts/oxalis/controller/scene_controller.ts (1)

226-236: ⚠️ Potential issue

this.rootNode is used before it is initialised – causes runtime crash

this.rootNode is still undefined at this point in createMeshes().
Calling .add() on it will throw, preventing the scene from being created.

-    this.datasetBoundingBox.getMeshes().forEach((mesh) => this.rootNode.add(mesh));

The dataset cube is already added a few lines later via the spread operator when this.rootNode
is created, so this early call is both unsafe and redundant.
Please remove the highlighted line (or move it after this.rootNode is assigned) to avoid the
crash.

🧹 Nitpick comments (2)
frontend/javascripts/oxalis/controller/scene_controller.ts (2)

649-665: Visibility toggling works, but duplicates logic

stopPlaneMode() and startPlaneMode() now correctly reuse forEachTaskCube, nice!
Consider extracting identical visibility toggling for userBoundingBoxGroup and task cubes into a
helper to DRY these two methods, but this is purely optional.


695-696: Clear reference map on destroy

After destroying each task cube you might also want to reset the map to free references:

   this.forEachTaskCube((cube) => cube.destroy());
+  this.taskCubeByTracingId = {};

Not critical, but helps GC.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5274a2e and c6cda9a.

📒 Files selected for processing (2)
  • CHANGELOG.unreleased.md (1 hunks)
  • frontend/javascripts/oxalis/controller/scene_controller.ts (10 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.unreleased.md
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: backend-tests
  • GitHub Check: build-smoketest-push

Comment on lines +328 to +364
updateTaskBoundingBoxes(
taskCubeByTracingId: Record<string, BoundingBoxType | null | undefined>,
): void {
/*
Ensures that a green task bounding box is rendered in the scene for
each layer.
The update is implemented by simply removing the old geometry and
adding a new one. Since this function is executed very rarely,
this is not a performance problem.
*/
for (const [tracingId, boundingBox] of Object.entries(taskCubeByTracingId)) {
let taskCube = this.taskCubeByTracingId[tracingId];
// Remove the old box if it exists
if (taskCube != null) {
taskCube.getMeshes().forEach((mesh) => this.rootNode.remove(mesh));
}
this.taskCubeByTracingId[tracingId] = null;
if (boundingBox == null || Store.getState().task == null) {
continue;
}

const { viewMode } = Store.getState().temporaryConfiguration;
this.taskBoundingBox = new Cube({
min: taskBoundingBox.min,
max: taskBoundingBox.max,
taskCube = new Cube({
min: boundingBox.min,
max: boundingBox.max,
color: 0x00ff00,
showCrossSections: true,
isHighlighted: false,
});
this.taskBoundingBox.getMeshes().forEach((mesh) => this.rootNode.add(mesh));
taskCube.getMeshes().forEach((mesh) => this.rootNode.add(mesh));

if (constants.MODES_ARBITRARY.includes(viewMode)) {
this.taskBoundingBox?.setVisibility(false);
taskCube?.setVisibility(false);
}

this.taskCubeByTracingId[tracingId] = taskCube;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Leaking geometry & leaving stale cubes in updateTaskBoundingBoxes

  1. Old cubes are detached from the scene but never .destroy()-ed → GPU memory leak.
  2. Cubes belonging to tracing IDs that disappear from the new map are never removed at all, so
    obsolete green boxes stay around.
  3. Store.getState() is called in every iteration although the value is constant within the
    function.

A minimal, safe fix:

   updateTaskBoundingBoxes(
     taskCubeByTracingId: Record<string, BoundingBoxType | null | undefined>,
   ): void {
-    for (const [tracingId, boundingBox] of Object.entries(taskCubeByTracingId)) {
+    const previousIds = new Set(Object.keys(this.taskCubeByTracingId));
+    const state      = Store.getState();   // single lookup
+
+    for (const [tracingId, boundingBox] of Object.entries(taskCubeByTracingId)) {
+      previousIds.delete(tracingId);       // mark as still in use
       let taskCube = this.taskCubeByTracingId[tracingId];
       // Remove the old box if it exists
       if (taskCube != null) {
-        taskCube.getMeshes().forEach((mesh) => this.rootNode.remove(mesh));
+        taskCube.getMeshes().forEach((mesh) => this.rootNode.remove(mesh));
+        taskCube.destroy();
       }
       this.taskCubeByTracingId[tracingId] = null;
-      if (boundingBox == null || Store.getState().task == null) {
+      if (boundingBox == null || state.task == null) {
         continue;
       }
+      // … create & add new Cube as before …
     }
+
+    // Remove cubes that are no longer present
+    for (const staleId of previousIds) {
+      const staleCube = this.taskCubeByTracingId[staleId];
+      if (staleCube) {
+        staleCube.getMeshes().forEach((m) => this.rootNode.remove(m));
+        staleCube.destroy();
+      }
+      delete this.taskCubeByTracingId[staleId];
+    }
   }

This prevents memory leaks and guarantees that the scene exactly mirrors the current set of
task bounding boxes.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
updateTaskBoundingBoxes(
taskCubeByTracingId: Record<string, BoundingBoxType | null | undefined>,
): void {
/*
Ensures that a green task bounding box is rendered in the scene for
each layer.
The update is implemented by simply removing the old geometry and
adding a new one. Since this function is executed very rarely,
this is not a performance problem.
*/
for (const [tracingId, boundingBox] of Object.entries(taskCubeByTracingId)) {
let taskCube = this.taskCubeByTracingId[tracingId];
// Remove the old box if it exists
if (taskCube != null) {
taskCube.getMeshes().forEach((mesh) => this.rootNode.remove(mesh));
}
this.taskCubeByTracingId[tracingId] = null;
if (boundingBox == null || Store.getState().task == null) {
continue;
}
const { viewMode } = Store.getState().temporaryConfiguration;
this.taskBoundingBox = new Cube({
min: taskBoundingBox.min,
max: taskBoundingBox.max,
taskCube = new Cube({
min: boundingBox.min,
max: boundingBox.max,
color: 0x00ff00,
showCrossSections: true,
isHighlighted: false,
});
this.taskBoundingBox.getMeshes().forEach((mesh) => this.rootNode.add(mesh));
taskCube.getMeshes().forEach((mesh) => this.rootNode.add(mesh));
if (constants.MODES_ARBITRARY.includes(viewMode)) {
this.taskBoundingBox?.setVisibility(false);
taskCube?.setVisibility(false);
}
this.taskCubeByTracingId[tracingId] = taskCube;
}
}
updateTaskBoundingBoxes(
taskCubeByTracingId: Record<string, BoundingBoxType | null | undefined>,
): void {
/*
Ensures that a green task bounding box is rendered in the scene for
each layer.
The update is implemented by simply removing the old geometry and
adding a new one. Since this function is executed very rarely,
this is not a performance problem.
*/
const previousIds = new Set(Object.keys(this.taskCubeByTracingId));
const state = Store.getState(); // single lookup
for (const [tracingId, boundingBox] of Object.entries(taskCubeByTracingId)) {
previousIds.delete(tracingId); // mark as still in use
let taskCube = this.taskCubeByTracingId[tracingId];
// Remove the old box if it exists
if (taskCube != null) {
taskCube.getMeshes().forEach((mesh) => this.rootNode.remove(mesh));
taskCube.destroy();
}
this.taskCubeByTracingId[tracingId] = null;
if (boundingBox == null || state.task == null) {
continue;
}
const { viewMode } = Store.getState().temporaryConfiguration;
taskCube = new Cube({
min: boundingBox.min,
max: boundingBox.max,
color: 0x00ff00,
showCrossSections: true,
isHighlighted: false,
});
taskCube.getMeshes().forEach((mesh) => this.rootNode.add(mesh));
if (constants.MODES_ARBITRARY.includes(viewMode)) {
taskCube?.setVisibility(false);
}
this.taskCubeByTracingId[tracingId] = taskCube;
}
// Remove cubes that are no longer present
for (const staleId of previousIds) {
const staleCube = this.taskCubeByTracingId[staleId];
if (staleCube) {
staleCube.getMeshes().forEach((m) => this.rootNode.remove(m));
staleCube.destroy();
}
delete this.taskCubeByTracingId[staleId];
}
}

@philippotto philippotto merged commit be42307 into master May 5, 2025
5 checks passed
@philippotto philippotto deleted the green-task-bboxes branch May 5, 2025 14:39
philippotto added a commit that referenced this pull request May 5, 2025
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.

Volume-Only annotations show green bounding box. Should be unified/clarified
2 participants