diff --git a/src/Mod/Assembly/App/AssemblyUtils.cpp b/src/Mod/Assembly/App/AssemblyUtils.cpp index 8569af29f4..8ab3c064fd 100644 --- a/src/Mod/Assembly/App/AssemblyUtils.cpp +++ b/src/Mod/Assembly/App/AssemblyUtils.cpp @@ -164,6 +164,49 @@ DistanceType getDistanceType(App::DocumentObject* joint) auto* obj1 = getLinkedObjFromRef(joint, "Reference1"); auto* obj2 = getLinkedObjFromRef(joint, "Reference2"); + // Datum planes (App::Plane) have empty element types because their + // sub-name ends with "." and yields no Face/Edge/Vertex element. + // Detect them here and classify before the main geometry chain, + // which cannot handle the empty element type. + const bool datum1 = type1.empty() && obj1 && obj1->isDerivedFrom(); + const bool datum2 = type2.empty() && obj2 && obj2->isDerivedFrom(); + + if (datum1 || datum2) { + if (datum1 && datum2) { + return DistanceType::PlanePlane; + } + + // One side is a datum plane, the other has a real element type. + // For PointPlane/LinePlane, the solver's PointInPlaneConstraint + // reads the plane normal from marker_j (Reference2). Unlike + // real Face+Vertex joints (where both Placements carry the + // face normal from findPlacement), datum planes only carry + // their normal through computeMarkerTransform. So the datum + // must end up on Reference2 for the normal to reach marker_j. + // + // For PlanePlane the convention matches the existing Face+Face + // path (plane on Reference1). + const auto& otherType = datum1 ? type2 : type1; + + if (otherType == "Vertex" || otherType == "Edge") { + // Datum must be on Reference2 (j side). + if (datum1) { + swapJCS(joint); // move datum from Ref1 → Ref2 + } + if (otherType == "Vertex") { + return DistanceType::PointPlane; + } + return DistanceType::LinePlane; + } + + // Face + datum or unknown + datum → PlanePlane + // Datum on Reference1 for consistency with Face+Face path. + if (!datum1) { + swapJCS(joint); // move datum from Ref2 → Ref1 + } + return DistanceType::PlanePlane; + } + if (type1 == "Vertex" && type2 == "Vertex") { return DistanceType::PointPoint; }