Skip to content

Conversation

max-mrgrsk
Copy link
Contributor

@max-mrgrsk max-mrgrsk commented Oct 3, 2025

Selection Coercion Implementation

Problem

When users select a face or edge and then click a command button that only works with bodies (like Pattern Linear 3D, Pattern Circular 3D, Translate, Rotate, Scale, Clone, and all Boolean operations), the selection would pass initial validation but fail at submission time with an error.

The issue occurred because:

  1. The selectionFilter was set to ['object'] which allows clicking on bodies
  2. But if you selected a face/edge FIRST, then clicked the button, the validation would incorrectly accept it
  3. At submission time, the code mod would fail because it expected body artifacts, not face/edge artifacts

Solution

Implemented a selection coercion mechanism that automatically converts face/edge selections to their parent body selections immediately when the command button is clicked, providing instant visual feedback in the viewport.

Changes Made

1. New Utility Function (src/lang/std/artifactGraph.ts)

Enhanced getSweepArtifactFromSelection() function:

  • Extended to handle edgeCut artifact type in addition to existing types
  • Now handles all common selection types: sweepEdge, segment, wall, cap, and edgeCut

Added coerceSelectionsToBody() function:

  • Takes a Selections object that may contain faces, edges, or bodies
  • For each selection, determines if it's already a body type (path/sweep/compositeSolid)
  • If not, calls getSweepArtifactFromSelection() once to find the parent body
  • Critical fix: Prefers PATH over SWEEP artifacts to match engine behavior when object filter is active
  • Returns a new Selections object containing only body artifacts
  • Deduplicates bodies (if user selected multiple faces from the same body, only one body is returned)

2. Component-Level Coercion (src/components/CommandBar/CommandBarSelectionMixedInput.tsx)

Added coercion logic that runs immediately when the command button is clicked:

First useEffect:

  • Runs once on component mount
  • Checks if the command argument only accepts body types (selectionTypes includes 'path', 'sweep', or 'compositeSolid')
  • If current selections include faces/edges, calls coerceSelectionsToBody()
  • Sends 'Set selection' event with selectionType: 'completeSelection' to update the modeling machine state
  • Uses hasCoercedSelections flag to prevent infinite loops

Second useEffect (only bodies are clickable):

  • Runs after coercion completes (guarded by hasCoercedSelections flag)
  • Calls setSelectionFilter() with the coerced selections
  • This triggers a batched command that sets the filter AND restores the coerced selection
  • Cleanup function restores default filter when component unmounts

3. Selection Filter Batching (src/lang/KclSingleton.ts)

Updated setSelectionFilter() method:

  • Added optional selectionsToRestore parameter to the public API
  • The batching mechanism already existed but was not accessible through the KclManager class

Detailed Flow

Before Fix:

  1. User selects a face on a cylinder (face turns yellow)
  2. User clicks "Pattern Linear 3D" button
  3. Command bar opens with no selection in the viewport and 1 face selected in the UI
  4. User fills in parameters and submits
  5. ❌ Error: code mod expects a body, receives a face

After Fix - Complete Step-by-Step:

  1. User selects a face on a cylinder → face turns yellow (selection filter: default ['face', 'edge', 'curve', 'object'])
  2. User clicks "Pattern Linear 3D" button → Command bar component mounts
  3. First useEffect runs (coercion):
    • Detects command only accepts bodies (selectionTypes: ['path', 'sweep', 'compositeSolid'])
    • Calls coerceSelectionsToBody(selection, artifactGraph)
    • Face selection → Parent body (PATH artifact)
    • Sends 'Set selection' event with selectionType: 'completeSelection' to modeling machine
    • Modeling machine updates React state: selectionRanges.graphSelections = [pathArtifact]
    • No engine commands sent yet (they'll be batched in the next step)
    • Sets hasCoercedSelections = true
  4. Second useEffect runs (filter locking):
    • Detects arg.selectionFilter === ['object'] (body-only filter)
    • Calls setSelectionFilter(['object'], selection)
    • Batched engine commands sent:
      modeling_cmd_batch_req: [
        set_selection_filter(['object']),
        select_clear(),
        select_add(pathArtifactId)
      ]
      
    • ✨ Entire cylinder turns yellow (whole body highlighted)
    • User can now ONLY click other bodies (not faces/edges)
  5. UI updates to show "1 path selected"
  6. User clicks around
    • If user tries to click the viewport, they can only select bodies
  7. User clicks "Continue"
    • Form submits with body selection
    • ✅ Command executes successfully
  8. Component unmounts (cleanup):
    • Cleanup function calls defaultSelectionFilter(selection)
    • Restores default filter ['face', 'edge', 'curve', 'object']
    • Re-adds body selection with different visuals (body + curves/points visible)

Why Two useEffects:

  • First useEffect: Coerces the selection data (face → body)
  • Second useEffect: Locks the selection type (only bodies clickable) and prevents visual glitches via batching
  • Without the second useEffect, users could click faces/edges after coercion and break the body selection!

Benefits

  • Immediate visual feedback: Whole body highlights yellow the moment you click the command button
  • Better UX: Users can select faces/edges naturally and the system handles it transparently
  • Consistent behavior: Whether you select a body or a face first, it works the same way
  • Error prevention: Coercion happens before submission, preventing confusing error messages
  • Deduplication: Automatically handles multiple selections from the same body
  • No flickering: Batched commands ensure smooth visual transition from face to body selection

Commands Affected

The coercion applies to all commands with selectionTypes: ['path', 'sweep', 'compositeSolid']:

  • Pattern Linear 3D
  • Pattern Circular 3D
  • Translate
  • Rotate
  • Scale
  • Clone
  • Boolean Subtract
  • Boolean Union
  • Boolean Intersect

Visual States

The selection content (what's in state) is always the same (just the PATH/SWEEP artifact), but what's visually highlighted differs based on the active selection filter and how the selection was made.

Three Visual States:

  1. After Coercion (filter: ['object']):

    • Selection state: PATH/SWEEP artifact only
    • Visual: Body highlighted (yellow), no extra geometry
    • Method: select_add with body ID, filter = ['object']
    • Why: Restrictive filter prevents engine from displaying curves/edges/points
  2. Clicking Body in Viewport (filter: ['object']):

    • Selection state: PATH/SWEEP artifact only (same as Figuring out MVP #1!)
    • Visual: Body + curves/points/edges highlighted
    • Method: select_with_point, filter = ['object']
    • Why: User click triggers engine's visualization
  3. After Clicking Continue (filter: default):

Added logic to support 'edgeCut' artifact type in getSweepArtifactFromSelection. The function now retrieves the associated sweep artifact by resolving the consumed edge, handling both 'segment' and 'sweepEdge' cases.
Introduces coerceSelectionsToBody to convert selections containing faces or edges to their parent body artifacts. This utility helps commands that require body-level selections by ensuring only body artifacts are returned, handling various artifact types and avoiding duplicates.
Adds logic to automatically coerce selections to body types (path, sweep, compositeSolid) when the argument only accepts bodies. Updates selection filter handling to ensure correct selection state after coercion, improving support for body-only commands in the command bar.
Added an optional selectionsToRestore parameter to setSelectionFilter in KclManager and passed it to setSelectionFilter implementation. This allows restoring selections when setting the filter.
@max-mrgrsk max-mrgrsk linked an issue Oct 3, 2025 that may be closed by this pull request
@max-mrgrsk max-mrgrsk self-assigned this Oct 3, 2025
Copy link

vercel bot commented Oct 3, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
modeling-app Ready Ready Preview Comment Oct 4, 2025 1:57pm

Introduces unit tests for getSweepArtifactFromSelection and coerceSelectionsToBody functions, verifying correct artifact resolution and selection coercion behavior in artifactGraph.
Updates setSelectionFilter to explicitly pass handleSelectionBatchFn as an argument, ensuring the correct function is used for selection batch handling.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto‑coerce face/edge selection to body for Translate/Pattern

1 participant