Skip to content

Fix reload-from-disk: matching, positions, deleted bodies, STEP names#10380

Open
jomixlaf wants to merge 11 commits intobambulab:masterfrom
jomixlaf:fix/reload-from-disk
Open

Fix reload-from-disk: matching, positions, deleted bodies, STEP names#10380
jomixlaf wants to merge 11 commits intobambulab:masterfrom
jomixlaf:fix/reload-from-disk

Conversation

@jomixlaf
Copy link
Copy Markdown
Contributor

@jomixlaf jomixlaf commented Apr 25, 2026

Summary

This PR fixes a number of long-standing issues with the Reload from disk action when the source file is a STEP exported from CAD (Fusion 360 in particular). It also tightens up the STEP loader's body name extraction so that bodies don't lose their identity on reload.

I believe it would fix most, if not all, of these issues listed here, some I experienced myself.

https://github.com/bambulab/BambuStudio/issues/9478
https://github.com/bambulab/BambuStudio/issues/8982
https://github.com/bambulab/BambuStudio/issues/1600

All changes live behind the existing ENABLE_RELOAD_FROM_DISK_REWORK compile flag for the reload-path edits, plus targeted fixes in the STEP loader.

STEP loader

  • Resolve MANIFOLD_SOLID_BREP body names via the entity transfer map. Bodies that previously came in as Body1/Body4/etc. now carry their authored names from the STEP product hierarchy.
  • Use the component name for bodies in root-level assembly components, fixing cases where a body in a sibling component lost its name and inherited the parent assembly's name instead.
  • Normalize non-ASCII codepoints in body names. OCCT's TCollection_AsciiString(extString) preserved raw high bytes, which then failed StepPreProcessor::isUtf8 (a lone 0xA0/0xE9 is not a valid UTF-8 start). The body would silently fall back to the parent component name. Walking the extended string codepoint-by-codepoint and emitting a benign space for anything outside printable ASCII keeps the user's name intact (e.g. cylinder<NBSP>[f4] is no longer rejected).

Reload-from-disk

  • Reload now picks up new components and name changes instead of dropping bodies that don't match the original source-index.
  • Prevent source-index mismatch when the STEP file gains or reorders bodies. Source indices are updated on each reload so the next reload's matching is stable.
  • Refresh the object list sidebar for each modified object so names, types, and filament columns reflect the reloaded volumes.
  • Three-pass matching for old → new volume reconciliation:
    1. Source-index + name (unchanged bodies)
    2. Name search (body moved to a different index)
    3. Source-index only (renamed body, same slot)
  • Ungate Pass 2 so the name search runs whenever the volume has a name. The previous gate (old_volume->name == path.filename()) was never true for multi-body STEP, so reorders fell through to Pass 3 and matched by index alone, swapping transforms between bodies.
  • Handle deleted bodies on reload silently (no error dialog) and move sort_volumes out of the matching loop so it doesn't corrupt subsequent vol_idx values.
  • Correct position of new STEP bodies on reload via a coord_shift captured from the first matched volume — the difference between the initial bb-center and the new bb-center. Applied to unmatched new bodies so they land in the existing scene's coordinate frame.
  • Matched volumes follow Fusion repositioning. Replaced the unconditional set_transformation(old_volume->get_transformation()) with applying coord_shift to the new volume's offset. For the first matched volume this yields exactly the old offset (preserving the object's plate anchor); for subsequent volumes it tracks any repositioning the user did in CAD between exports while keeping the object anchored.

Test plan

  • Fresh import of a multi-body STEP, then reload the same file unchanged → no movement, all bodies matched.
  • Add a new body in Fusion, reload → new body lands at correct position relative to the existing layout.
  • Delete a body in Fusion, reload → body silently removed.
  • Reorder bodies in Fusion (so name and source-index disagree), reload → all bodies stay correctly matched and positioned.
  • Rename bodies between exports → matched by source-index (Pass 3) with offsets preserved.
  • Reposition / resize bodies in Fusion (e.g. enlarge a base plate, move a sub-body to a new corner), reload → matched bodies follow new positions, plate anchor stable.
  • Body name with non-ASCII characters (é, NBSP) → name imports without falling back to the parent assembly name.
  • Iterative add → rename → retag → reposition cycles across multiple consecutive reloads stay stable.

jomixlaf added 11 commits April 25, 2026 16:13
…ansfer map

OCCT's XCAF layer only preserves names from PRODUCT entities, so bodies that
live directly inside a root Fusion 360 component (stored as MANIFOLD_SOLID_BREP
in STEP) were always imported as "SOLID" regardless of their actual name.

After the XCAF transfer, iterate all STEP entities via the transfer process to
build a TShape-pointer → MANIFOLD_SOLID_BREP-name map. getNamedSolids now
consults this map when naming solid shapes, so tags like [f3] embedded in a
body name are correctly preserved and subsequently applied by the smart-import
naming rules.
…nents

When a Fusion 360 file has a body directly inside the root component, OCCT's
XCAF layer creates a virtual label for that geometry with a shape-type default
name ("SOLID") rather than the component/product name.  The previous approach
tried to recover the MANIFOLD_SOLID_BREP entity name (the body name), but that
gives the wrong result — users should tag the *component* name, not the body
name, consistent with how sub-components already work.

Fix: treat OCCT shape-type default strings (SOLID, COMPOUND, SHELL, etc.) as
missing names and fall back to the parent component name (prefix).  Bodies that
live directly in an assembly component now inherit the component name so tags
like [f3] or [neg] placed on the component are correctly applied on import.
Two issues with reload from disk for STEP files:

1. New bodies added to the STEP file were never imported — the reload loop
   only iterated over existing volumes looking for matches, so anything new
   in the file was silently dropped.

2. Component name changes didn't trigger naming rule re-evaluation because
   the old config/type were restored after loading without re-running rules.

Fix: track which new_model volumes were matched during the update loop, then
add all unmatched volumes as new volumes to the target object. Naming rules
are applied to both updated and newly added volumes so filament/type tags
are always current with what's in the file on disk.
…odies

When a STEP file is updated with new bodies, existing volumes can shift
to different indices in the newly-loaded model. The previous source-index
match only checked input_file, so adding a body could silently match the
wrong volume and copy its type/config onto a different volume (e.g. giving
the wrong part a NEGATIVE_VOLUME type). Now the candidate must also have
the same name as the old volume before the source-index match is accepted.
After reloading volumes from disk, the sidebar object list was not
updated, leaving stale volume names, types, and filament columns from
before the reload. Now add_volumes_to_object_in_list() is called for
every modified object after the reload loop completes, so the sidebar
immediately reflects new component names, updated tags, and newly-added
volumes.
Previously, renaming a component (e.g. polo[f2] → polo[f5]) caused
reload to fail with an error because source-index+name and name-search
both required the old name. Now matching uses three passes:

  Pass 1: source index + exact name match (unchanged bodies)
  Pass 2: name search (body moved to different index, name kept)
  Pass 3: source index only (body renamed in place)

Already-claimed new_model slots are skipped at every pass to prevent
two old volumes from mapping to the same new geometry. Source indices
on the resulting volume are updated to the new model positions so the
next reload also finds the correct slot.
Three related fixes:

1. Deleted bodies: when a body no longer exists in the reloaded STEP
   file (deleted from Fusion), it now gets silently removed from the
   model instead of showing an error and leaving a stale volume behind.

2. sort_volumes moved out of per-volume loop: calling sort_volumes
   after each swap/delete was reordering the volumes array and making
   later vol_idx values point to the wrong volumes, causing duplicates
   and missed updates. sort_volumes is now called once after the loop
   for all modified objects.

3. Deleted-volume cleanup: volumes marked as deleted are removed in
   reverse index order after the loop so earlier indices stay valid.
When a body is added to a Fusion 360 STEP file and the model is
reloaded, the new body appeared at the center of the plate instead
of its correct position.

Root cause: center_around_origin() shifts all volumes by -bb_center.
On initial import the bb_center is computed from the original set of
bodies; on reload new_model has an extra body that shifts the
bounding-box center. Matched volumes are repositioned via
old_volume->get_transformation(), so they land correctly. New
(unmatched) volumes were copied directly from new_model, which uses
the new shifted center — wrong coordinate frame.

Fix: capture coord_shift = old_offset - new_model_offset from the
first matched volume. This equals new_bb_center - initial_bb_center.
Apply the shift to every newly added volume so it lands in the same
coordinate frame as the existing volumes.
Pass 2 (name-based fallback) was gated by has_name, which only fires
when old_volume->name equals the reloaded file's filename — false for
multi-body STEP files where each body has its own name. The result:
Pass 2 never ran, and when Fusion reordered bodies in the new STEP,
Pass 1 missed and Pass 3 fell through, matching by source.volume_idx
alone and routing the wrong body's transform.

Run Pass 2 whenever the volume has a name; the outer has_source ||
has_name gate already restricts processing to volumes from the file
being reloaded.
Previously, set_transformation(old_volume->get_transformation()) preserved
the matched volume's old position regardless of whether the body moved
in CAD. This broke when users repositioned bodies between exports —
matched bodies stayed at stale offsets while only their meshes updated.

Use the new STEP position translated by coord_shift instead. For the
first matched volume (which defines coord_shift), this yields its old
offset exactly, keeping the object's plate anchor stable. For all other
volumes, it tracks their new STEP positions in the existing scene's
coordinate frame.
The previous attempt of passing a replacement char to TCollection_AsciiString
did not engage as expected (OCCT's behavior with that constructor preserved
raw high bytes). A lone byte like 0xE9 (é) or 0xA0 (NBSP) then failed
StepPreProcessor::isUtf8 and the body fell back to the parent component
name, losing both the name and any [tag] inside it.

Walk the OCCT extended (UTF-16) string codepoint by codepoint and emit a
benign space for anything outside printable ASCII. Pure ASCII output
always passes isUtf8, so the user's tags are preserved no matter what
character they paste into a body name in Fusion.
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.

1 participant