mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 02:41:17 +00:00
A body carries its own name, because a body is not its first feature
User report 2026-08-23, and it is right: "you have renamed the feature extrusion,
not the body. i clicked rename on the body feature tree and the feature extrude
changed name. this means that you consider the extrusion = the body. this is very
far from truth as a body can contain several extrusions."
That is exactly what the previous commit did. It resolved a selected body to
CadBody::source_feature and renamed THAT feature, on the reasoning that a body has
no name of its own. The reasoning described an implementation detail — CadBody::
name is derived and restamped on every recompute — and mistook it for the user's
model. An Extrude, a Cut and a Fillet all land on the same body: the maker is one
operation in its history, and renaming it renames the wrong object.
A body now has a name of its own. CadBody::user_name, set only by a rename, is:
- carried across recompute() by body index, next to the per-body colour override
and under the same index contract the GUI already relies on for visibility and
Move — without which a name would survive exactly until the next feature;
- written into the recipe, because bodies are recomputed and never serialised, so
a name has nowhere else to live and would otherwise vanish on reopen;
- shown on the Bodies row ahead of the derived maker name, as "Body N — name",
with the number still leading because every status line, the interference
report and the mate errors identify a body that way.
The recipe block is APPENDED after the variables block rather than given a version
bump. A build that predates it reads features and variables, returns, and never
looks at the trailing bytes — so yesterday's projects open here and today's
projects still open there. A bump would have cost every project written today its
readability by the previous build, for one optional field.
Renaming a FEATURE is unchanged. The feature tree renames features; the Bodies
list renames bodies; neither reaches into the other.
WHAT THIS COST, and why it is written down. Getting here took two wrong turns
inside one fix, both mine:
1. UnselectAll() -> Unselect(). Both trees are wxTR_SINGLE, where UnselectAll()
— the MULTI-selection call — does nothing. That was the one-word reason the
rename had been vetoed everywhere (BEGIN_LABEL_EDIT refuses while
tree_body_selection() >= 0). Fixing it turned a pair of harmless no-ops into
a real loop: the two lists clear each other so "the target" is unambiguous,
so clicking a body row ran apply_body_row -> m_tree->Unselect() -> the feature
tree's SEL_CHANGED -> m_parts->Unselect(), which cleared the row just clicked.
The handler now clears the other list only when it actually holds a selection.
2. Trusting a screenshot taken after a polluted run. A leftover Rib card had
shifted the whole panel, so a click measured against it landed nowhere near
the row. Relaunch, then measure.
VERIFIED, on the rig and in the kernel:
body renamed ('Extrude', user_name=False) -> ('Bracket', user_name=True)
features untouched ['Sketch', 'Extrude'] before and after
survives a recompute add a Hole to the same body: features become
['Sketch', 'Extrude', 'Hole'], body stays 'Bracket'
survives the recipe serialize -> deserialize -> 'Bracket'
New kernel test "a body carries its own name, through recompute and the recipe"
pins all three properties; the suite is 189 cases / 2547 assertions.
Gate green: ALL LADDERS HELD — offer table matches the atlas, kernel suite,
engine rungs 1-8, 977-sheet corpus + the heaviest sheets, gesture ladder 98/98,
offer ladder 108/108.
This commit is contained in:
@@ -3592,6 +3592,12 @@ bool CadDocument::recompute()
|
||||
built[i].has_color = true;
|
||||
built[i].color = bodies[i].color;
|
||||
}
|
||||
// ...and the name the user gave the body, for the same reason and by the same index
|
||||
// contract. Without this a rename would live exactly until the next feature was added.
|
||||
if (bodies[i].has_user_name) {
|
||||
built[i].has_user_name = true;
|
||||
built[i].user_name = bodies[i].user_name;
|
||||
}
|
||||
}
|
||||
bodies = std::move(built);
|
||||
// The face and edge maps have just been rebuilt, so every global id handed out before this
|
||||
@@ -3729,6 +3735,31 @@ std::string CadDocument::serialize_recipe() const
|
||||
uint32_t vlen = static_cast<uint32_t>(vb.size());
|
||||
ar(vlen);
|
||||
ar(cereal::binary_data(vb.data(), vb.size()));
|
||||
// BODY NAMES, appended after the variables block. Bodies are not serialised — they are
|
||||
// recomputed — so a name the user gave one has nowhere else to live, and without this it
|
||||
// would survive a recompute (see the carry-over in recompute()) but not a save.
|
||||
//
|
||||
// APPENDED RATHER THAN VERSION-BUMPED, on purpose: a build that predates this block
|
||||
// reads features and variables, returns, and never looks at the trailing bytes, so its
|
||||
// projects still open here AND this build's projects still open there. A version bump
|
||||
// would have made every project written today unreadable by yesterday's build for the
|
||||
// sake of one optional field. Written as (index, name) pairs so an unnamed body costs
|
||||
// nothing.
|
||||
// A map, not a vector of pairs: cereal's map support is already included here and its
|
||||
// pair support is not, and one more include for one more field is not worth it.
|
||||
std::map<uint32_t, std::string> named;
|
||||
for (uint32_t i = 0; i < bodies.size(); ++i)
|
||||
if (bodies[i].has_user_name && !bodies[i].user_name.empty())
|
||||
named[i] = bodies[i].user_name;
|
||||
std::ostringstream bos;
|
||||
{
|
||||
cereal::BinaryOutputArchive ba(bos);
|
||||
ba(named);
|
||||
}
|
||||
std::string bb = bos.str();
|
||||
uint32_t blen = static_cast<uint32_t>(bb.size());
|
||||
ar(blen);
|
||||
ar(cereal::binary_data(bb.data(), bb.size()));
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
@@ -3789,7 +3820,29 @@ bool CadDocument::deserialize_recipe(const std::string& blob)
|
||||
} catch (...) {
|
||||
// Variables blob predates this build: keep what was read, default the rest.
|
||||
}
|
||||
return recompute();
|
||||
// Body names, if this project carries them. A project written before the block
|
||||
// simply ends here, so the read throws and there are no names — not an error.
|
||||
std::map<uint32_t, std::string> named;
|
||||
try {
|
||||
uint32_t blen;
|
||||
ar(blen);
|
||||
std::string bbuf(blen, '\0');
|
||||
if (blen > 0)
|
||||
ar(cereal::binary_data(&bbuf[0], blen));
|
||||
std::istringstream bs(bbuf);
|
||||
cereal::BinaryInputArchive ba(bs);
|
||||
ba(named);
|
||||
} catch (...) {
|
||||
named.clear();
|
||||
}
|
||||
// AFTER the rebuild, never before: recompute() replaces the bodies vector wholesale.
|
||||
const bool ok = recompute();
|
||||
for (const auto& kv : named)
|
||||
if (kv.first < bodies.size()) {
|
||||
bodies[kv.first].has_user_name = true;
|
||||
bodies[kv.first].user_name = kv.second;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
if (v == 4) {
|
||||
// Pre-framing flat path, unchanged: v4 projects keep opening exactly as before.
|
||||
|
||||
Reference in New Issue
Block a user