fix(assembly): use instance suffixes for duplicate part labels
All checks were successful
Build and Test / build (pull_request) Successful in 29m11s

When parts with structured part numbers (e.g., P03-0001) are inserted
into an assembly multiple times, UniqueNameManager::decomposeName()
treats the trailing digits as an auto-generated suffix and increments
them (P03-0002, P03-0003), corrupting the part number.

Add a makeInstanceLabel() helper in AssemblyLink.cpp that appends -N
instance suffixes instead (P03-0001-1, P03-0001-2). All instances get
a suffix starting at -1 so the original part number is never modified.

Applied at all three Label.setValue() sites in
synchronizeComponents() (AssemblyLink, link group, and regular link
creation paths).

Also add a UniqueNameManager test documenting the trailing-digit
decomposition behavior for structured part numbers.

Closes #327
This commit is contained in:
2026-02-26 09:00:13 -06:00
parent 82f2422285
commit 7c85b2ad93
2 changed files with 27 additions and 3 deletions

View File

@@ -311,6 +311,19 @@ void AssemblyLink::updateContents()
purgeTouched();
}
// Generate an instance label for assembly components by appending a -N suffix.
// All instances get a suffix (starting at -1) so that structured part numbers
// like "P03-0001" are never mangled by UniqueNameManager's trailing-digit logic.
static std::string makeInstanceLabel(App::Document* doc, const std::string& baseLabel)
{
for (int i = 1;; ++i) {
std::string candidate = baseLabel + "-" + std::to_string(i);
if (!doc->containsLabel(candidate)) {
return candidate;
}
}
}
void AssemblyLink::synchronizeComponents()
{
App::Document* doc = getDocument();
@@ -428,7 +441,7 @@ void AssemblyLink::synchronizeComponents()
auto* subAsmLink = static_cast<AssemblyLink*>(newObj);
subAsmLink->LinkedObject.setValue(obj);
subAsmLink->Rigid.setValue(asmLink->Rigid.getValue());
subAsmLink->Label.setValue(obj->Label.getValue());
subAsmLink->Label.setValue(makeInstanceLabel(doc, obj->Label.getValue()));
addObject(subAsmLink);
link = subAsmLink;
}
@@ -440,7 +453,7 @@ void AssemblyLink::synchronizeComponents()
);
newLink->LinkedObject.setValue(srcLink->getTrueLinkedObject(false));
newLink->Label.setValue(obj->Label.getValue());
newLink->Label.setValue(makeInstanceLabel(doc, obj->Label.getValue()));
addObject(newLink);
newLink->ElementCount.setValue(srcLink->ElementCount.getValue());
@@ -461,7 +474,7 @@ void AssemblyLink::synchronizeComponents()
App::DocumentObject* newObj = doc->addObject("App::Link", obj->getNameInDocument());
auto* newLink = static_cast<App::Link*>(newObj);
newLink->LinkedObject.setValue(obj);
newLink->Label.setValue(obj->Label.getValue());
newLink->Label.setValue(makeInstanceLabel(doc, obj->Label.getValue()));
addObject(newLink);
link = newLink;
}

View File

@@ -122,4 +122,15 @@ TEST(UniqueNameManager, UniqueNameWith9NDigits)
manager.addExactName("Compound123456789");
EXPECT_EQ(manager.makeUniqueName("Compound", 3), "Compound123456790");
}
TEST(UniqueNameManager, StructuredPartNumberDecomposition)
{
// Structured part numbers like P03-0001 have their trailing digits
// treated as the uniquifying suffix by UniqueNameManager. This is
// correct for default FreeCAD objects (Body -> Body001) but wrong
// for structured identifiers. Assembly module handles this separately
// via makeInstanceLabel which appends -N instance suffixes instead.
Base::UniqueNameManager manager;
manager.addExactName("P03-0001");
EXPECT_EQ(manager.makeUniqueName("P03-0001", 3), "P03-0002");
}
// NOLINTEND(cppcoreguidelines-*,readability-*)