mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-18 22:42:37 +00:00
Mate viability: which of the five apply, and why the others do not
snaporca-lukg wants a palette offering all five mate types with the non-viable
ones DIMMED AND EXPLAINED rather than hidden — its reasoning being that a menu
changing shape between invocations destroys the motor memory experts rely on.
That needs an answer this document could not give. This is that answer, and
nothing else: mate_options(cs_a, cs_b) returns five MateOption{kind, viable,
reason}, always five, always in kind order, never filtered.
The geometry test rides on the fingerprint added for snaporca-kqih, which is why
it costs no new serialized field: coordsys_face_kind already records the surface
type. Revolute and Cylindrical need a cylindrical face at both ends because they
need an axis to turn about; Planar needs flat faces; Fastened and Slider
constrain frames rather than surfaces, so no geometry test applies to them.
UNKNOWN IS PERMISSIVE. A fingerprint of -1 means PointWorld or a connector that
has not resolved yet, and it does NOT make a type non-viable. Refusing on missing
information is the false-alarm behaviour that gets a whole feature ignored — the
same reasoning already recorded on kqih for the drift warning, applied again
because it is the same trade.
Reasons name WHICH connector is the problem when only one is. "needs a
cylindrical face at both ends" tells the user what the rule is; "connector A is
on a flat face" tells them where to look, and the second half is the one that
saves the time.
The stability contract has its own test, asserting five entries in kind order
even for a completely invalid pair. That matters more than any individual
verdict: the palette addresses rows by position, so a shorter list would move
every row below it.
Golden fixture unchanged — this is a pure query. Suite 167 -> 171 cases,
2284 -> 2348 assertions, green. snaporca-lukg part A; the palette is part B.
This commit is contained in:
@@ -1315,6 +1315,62 @@ int CadDocument::add_mate(int kind, int cs_a, int cs_b, double offset, double an
|
||||
return int(features.size()) - 1;
|
||||
}
|
||||
|
||||
std::vector<CadDocument::MateOption> CadDocument::mate_options(int cs_a, int cs_b) const
|
||||
{
|
||||
std::vector<MateOption> out(5);
|
||||
for (int k = 0; k < 5; ++k) out[k].kind = k;
|
||||
|
||||
auto is_connector = [&](int idx) -> bool {
|
||||
return idx >= 0 && idx < int(features.size()) &&
|
||||
features[idx].type == CadFeatureType::CoordSys &&
|
||||
features[idx].enabled;
|
||||
};
|
||||
if (!is_connector(cs_a) || !is_connector(cs_b)) {
|
||||
const char side = is_connector(cs_a) ? 'B' : 'A';
|
||||
for (auto& o : out) { o.viable = false; o.reason = std::string("connector ") + side + " is not a coordinate system"; }
|
||||
return out;
|
||||
}
|
||||
if (cs_a == cs_b) {
|
||||
for (auto& o : out) { o.viable = false; o.reason = "a mate needs two different connectors"; }
|
||||
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"; };
|
||||
|
||||
// Planar: needs a flat face at both ends.
|
||||
const bool plan_bad_a = ka >= 0 && ka != GeomAbs_Plane;
|
||||
const bool plan_bad_b = kb >= 0 && kb != GeomAbs_Plane;
|
||||
if (plan_bad_a || plan_bad_b) {
|
||||
out[1].viable = false;
|
||||
if (plan_bad_a && plan_bad_b)
|
||||
out[1].reason = "needs a flat face at both ends — both connectors are on curved faces";
|
||||
else if (plan_bad_a)
|
||||
out[1].reason = "needs a flat face at both ends — connector A is on " + face_desc(ka);
|
||||
else
|
||||
out[1].reason = "needs a flat face at both ends — connector B is on " + face_desc(kb);
|
||||
}
|
||||
|
||||
// Revolute and Cylindrical: need a cylindrical face at both ends.
|
||||
for (int k : {2, 4}) {
|
||||
const bool ax_bad_a = ka >= 0 && ka != GeomAbs_Cylinder;
|
||||
const bool ax_bad_b = kb >= 0 && kb != GeomAbs_Cylinder;
|
||||
if (!ax_bad_a && !ax_bad_b) continue;
|
||||
out[k].viable = false;
|
||||
if (ax_bad_a && ax_bad_b)
|
||||
out[k].reason = (ka == GeomAbs_Plane && kb == GeomAbs_Plane)
|
||||
? "needs a cylindrical face at both ends — both connectors are on flat faces"
|
||||
: "needs a cylindrical face at both ends — both connectors are on non-cylindrical faces";
|
||||
else if (ax_bad_a)
|
||||
out[k].reason = "needs a cylindrical face at both ends — connector A is on " + face_desc(ka);
|
||||
else
|
||||
out[k].reason = "needs a cylindrical face at both ends — connector B is on " + face_desc(kb);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
int CadDocument::add_helix(const SketchPlane& plane, double radius, double pitch, double height,
|
||||
bool left_handed, double taper_deg, const std::string& name)
|
||||
{
|
||||
|
||||
@@ -578,6 +578,18 @@ public:
|
||||
int add_coordsys(CoordSysType type, const Vec3d& point, const std::string& name);
|
||||
int add_mate(int kind, int cs_a, int cs_b, double offset, double angle_deg, bool flip,
|
||||
const std::string& name);
|
||||
|
||||
// Which mate types apply to a connector pair, as reported to the viewport palette.
|
||||
struct MateOption {
|
||||
int kind{0}; // 0..4, the five mate types in CadDocument.hpp:308-314
|
||||
bool viable{true};
|
||||
std::string reason; // empty when viable; why not, when not
|
||||
};
|
||||
// ALWAYS all five entries, ALWAYS in kind order. Never filtered: the caller dims what is
|
||||
// not viable rather than hiding it, so the list must be stable in length and order between
|
||||
// calls. Pure query over existing data — records nothing, mutates nothing.
|
||||
std::vector<MateOption> mate_options(int cs_a, int cs_b) const;
|
||||
|
||||
int add_helix(const SketchPlane& plane, double radius, double pitch, double height,
|
||||
bool left_handed, double taper_deg, const std::string& name);
|
||||
// Build the helix wire from a Helix feature's params (exposed for tests).
|
||||
|
||||
Reference in New Issue
Block a user