CAD: a mate needs B to have a body, so stop offering one when it does not

The mate palette called all five kinds viable on connectors created as
Point(world), which belong to no body. Clicking one built the Mate feature and
the RECOMPUTE then failed with "mate: mate_cs_b has no associated body" — the
refusal arrived one step too late, after the feature was already in the tree,
and the user is told about it by an error line rather than by the palette that
offered the thing.

mate_options now checks the same two conditions the apply path throws on: B must
have a body (B is the connector whose body moves; A is the fixed reference and
needs none), and that body must still resolve. Either way all five kinds go
non-viable with a reason, so the offer and the kernel cannot disagree.

Verified on the rig: with two Point(world) connectors, every row is now dimmed
and reads "connector B is not attached to a body — a mate moves B's body". That
also exercises the dimmed-with-a-reason presentation for the first time, which
until now had nothing to show because every kind was always viable.

Tests: a new [mate] case covering no body, a body that no longer resolves, and
the revival once B is given one. One existing case needed its setup widened
rather than its assertion weakened: "an unrecorded fingerprint does not make a
type non-viable" built two body-less connectors, so it was asserting a side
effect of the old permissiveness instead of the property it is named for. Its
connectors now have a body, leaving the missing fingerprint as the only variable.
Full [CadDocument] suite: 2458 assertions in 185 cases.
This commit is contained in:
Tommaso Bianchi
2026-08-14 07:45:32 +02:00
parent 08ef43fad8
commit d8103f794b
2 changed files with 67 additions and 0 deletions
+21
View File
@@ -1335,6 +1335,27 @@ std::vector<CadDocument::MateOption> CadDocument::mate_options(int cs_a, int cs_
return out;
}
// B is the connector on the body that MOVES, so B must belong to one; A is the fixed
// reference and needs no body. Without this every kind was offered on a Point(world)
// connector and the mate only failed at RECOMPUTE, with "mate_cs_b has no associated body" —
// one step too late, after the feature already existed in the tree. The same two conditions
// the apply path throws on are checked here, so the offer and the kernel cannot disagree.
const int body_b = features[cs_b].coordsys_body;
if (body_b < 0) {
for (auto& o : out) {
o.viable = false;
o.reason = "connector B is not attached to a body — a mate moves B's body";
}
return out;
}
if (body_b >= int(bodies.size()) || bodies[body_b].shape.IsNull()) {
for (auto& o : out) {
o.viable = false;
o.reason = "connector B's body no longer exists";
}
return out;
}
const int ka = features[cs_a].coordsys_face_kind;
const int kb = features[cs_b].coordsys_face_kind;
auto face_desc = [](int kind) -> std::string { return kind == GeomAbs_Plane ? "a flat face" : "a curved face"; };