diff --git a/src/App/ExtensionContainer.h b/src/App/ExtensionContainer.h index 9ef6999b12..9fb5d6284f 100644 --- a/src/App/ExtensionContainer.h +++ b/src/App/ExtensionContainer.h @@ -173,6 +173,11 @@ public: //@{ /// find a property by its name Property* getPropertyByName(const char* name) const override; + /// find a property by its name, dynamic cased to specified type + template + T* getPropertyByName(const char* name) const { + return dynamic_cast(this->getPropertyByName(name)); + } /// get the name of a property const char* getPropertyName(const Property* prop) const override; /// get all properties of the class (including properties of the parent) diff --git a/src/Mod/Assembly/App/AssemblyLink.cpp b/src/Mod/Assembly/App/AssemblyLink.cpp index f8df4f771e..52607d3180 100644 --- a/src/Mod/Assembly/App/AssemblyLink.cpp +++ b/src/Mod/Assembly/App/AssemblyLink.cpp @@ -45,6 +45,7 @@ #include #include "AssemblyObject.h" +#include "AssemblyUtils.h" #include "JointGroup.h" #include "AssemblyLink.h" @@ -468,7 +469,7 @@ void AssemblyLink::handleJointReference(App::DocumentObject* joint, } // Lastly we need to replace the object name by its link name. - auto* obj = AssemblyObject::getObjFromRef(prop1); + auto* obj = getObjFromRef(prop1); auto* link = objLinkMap[obj]; if (!obj || !link) { return; @@ -496,7 +497,7 @@ void AssemblyLink::handleJointReference(App::DocumentObject* joint, void AssemblyLink::ensureNoJointGroup() { // Make sure there is no joint group - JointGroup* jGroup = AssemblyObject::getJointGroup(this); + JointGroup* jGroup = getJointGroup(this); if (jGroup) { // If there is a joint group, we delete it and its content. jGroup->removeObjectsFromDocument(); @@ -506,7 +507,7 @@ void AssemblyLink::ensureNoJointGroup() JointGroup* AssemblyLink::ensureJointGroup() { // Make sure there is a jointGroup - JointGroup* jGroup = AssemblyObject::getJointGroup(this); + JointGroup* jGroup = getJointGroup(this); if (!jGroup) { jGroup = new JointGroup(); getDocument()->addObject(jGroup, tr("Joints").toStdString().c_str()); @@ -572,7 +573,7 @@ bool AssemblyLink::isRigid() std::vector AssemblyLink::getJoints() { - JointGroup* jointGroup = AssemblyObject::getJointGroup(this); + JointGroup* jointGroup = getJointGroup(this); if (!jointGroup) { return {}; diff --git a/src/Mod/Assembly/App/AssemblyObject.cpp b/src/Mod/Assembly/App/AssemblyObject.cpp index 510b34e31f..cd5c454f92 100644 --- a/src/Mod/Assembly/App/AssemblyObject.cpp +++ b/src/Mod/Assembly/App/AssemblyObject.cpp @@ -24,13 +24,6 @@ #include "PreCompiled.h" #ifndef _PreComp_ #include -#include -#include -#include -#include -#include -#include -#include #include #include #include @@ -48,10 +41,7 @@ #include #include -#include #include -#include -#include #include #include @@ -85,13 +75,12 @@ #include "AssemblyLink.h" #include "AssemblyObject.h" #include "AssemblyObjectPy.h" +#include "AssemblyUtils.h" #include "JointGroup.h" #include "ViewGroup.h" FC_LOG_LEVEL_INIT("Assembly", true, true, true) -namespace PartApp = Part; - using namespace Assembly; using namespace MbD; /* @@ -513,8 +502,8 @@ App::DocumentObject* AssemblyObject::getJointOfPartConnectingToGround(App::Docum if (!joint) { continue; } - App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1"); - App::DocumentObject* part2 = getMovingPartFromRef(joint, "Reference2"); + App::DocumentObject* part1 = getMovingPartFromRef(this, joint, "Reference1"); + App::DocumentObject* part2 = getMovingPartFromRef(this, joint, "Reference2"); if (!part1 || !part2) { continue; } @@ -532,30 +521,9 @@ App::DocumentObject* AssemblyObject::getJointOfPartConnectingToGround(App::Docum return nullptr; } -JointGroup* AssemblyObject::getJointGroup(const App::Part* part) -{ - if (!part) { - return nullptr; - } - - App::Document* doc = part->getDocument(); - - std::vector jointGroups = - doc->getObjectsOfType(Assembly::JointGroup::getClassTypeId()); - if (jointGroups.empty()) { - return nullptr; - } - for (auto jointGroup : jointGroups) { - if (part->hasObject(jointGroup)) { - return dynamic_cast(jointGroup); - } - } - return nullptr; -} - JointGroup* AssemblyObject::getJointGroup() const { - return getJointGroup(this); + return Assembly::getJointGroup(this); } ViewGroup* AssemblyObject::getExplodedViewGroup() const @@ -596,8 +564,8 @@ AssemblyObject::getJoints(bool updateJCS, bool delBadJoints, bool subJoints) continue; } - auto* part1 = getMovingPartFromRef(joint, "Reference1"); - auto* part2 = getMovingPartFromRef(joint, "Reference2"); + auto* part1 = getMovingPartFromRef(this, joint, "Reference1"); + auto* part2 = getMovingPartFromRef(this, joint, "Reference2"); if (!part1 || !part2 || part1->getFullName() == part2->getFullName()) { // Remove incomplete joints. Left-over when the user deletes a part. // Remove incoherent joints (self-pointing joints) @@ -686,8 +654,8 @@ std::vector AssemblyObject::getJointsOfPart(App::DocumentO std::vector jointsOf; for (auto joint : joints) { - App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1"); - App::DocumentObject* part2 = getMovingPartFromRef(joint, "Reference2"); + App::DocumentObject* part1 = getMovingPartFromRef(this, joint, "Reference1"); + App::DocumentObject* part2 = getMovingPartFromRef(this, joint, "Reference2"); if (part == part1 || part == part2) { jointsOf.push_back(joint); } @@ -777,7 +745,7 @@ bool AssemblyObject::isJointConnectingPartToGround(App::DocumentObject* joint, c return false; } - App::DocumentObject* part = getMovingPartFromRef(joint, propname); + App::DocumentObject* part = getMovingPartFromRef(this, joint, propname); if (!part) { return false; } @@ -869,8 +837,8 @@ void AssemblyObject::removeUnconnectedJoints(std::vector& joints.begin(), joints.end(), [&](App::DocumentObject* joint) { - App::DocumentObject* obj1 = getMovingPartFromRef(joint, "Reference1"); - App::DocumentObject* obj2 = getMovingPartFromRef(joint, "Reference2"); + App::DocumentObject* obj1 = getMovingPartFromRef(this, joint, "Reference1"); + App::DocumentObject* obj2 = getMovingPartFromRef(this, joint, "Reference2"); if (!isObjInSetOfObjRefs(obj1, connectedParts) || !isObjInSetOfObjRefs(obj2, connectedParts)) { Base::Console().Warning( @@ -913,8 +881,8 @@ AssemblyObject::getConnectedParts(App::DocumentObject* part, continue; } - App::DocumentObject* obj1 = getMovingPartFromRef(joint, "Reference1"); - App::DocumentObject* obj2 = getMovingPartFromRef(joint, "Reference2"); + App::DocumentObject* obj1 = getMovingPartFromRef(this, joint, "Reference1"); + App::DocumentObject* obj2 = getMovingPartFromRef(this, joint, "Reference2"); if (obj1 == part) { auto* ref = dynamic_cast(joint->getPropertyByName("Reference2")); @@ -1401,7 +1369,7 @@ std::string AssemblyObject::handleOneSideOfJoint(App::DocumentObject* joint, const char* propRefName, const char* propPlcName) { - App::DocumentObject* part = getMovingPartFromRef(joint, propRefName); + App::DocumentObject* part = getMovingPartFromRef(this, joint, propRefName); App::DocumentObject* obj = getObjFromRef(joint, propRefName); if (!part || !obj) { @@ -1463,7 +1431,7 @@ void AssemblyObject::getRackPinionMarkers(App::DocumentObject* joint, swapJCS(joint); // make sure that rack is first. } - App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1"); + App::DocumentObject* part1 = getMovingPartFromRef(this, joint, "Reference1"); App::DocumentObject* obj1 = getObjFromRef(joint, "Reference1"); Base::Placement plc1 = getPlacementFromProp(joint, "Placement1"); @@ -1537,12 +1505,12 @@ void AssemblyObject::getRackPinionMarkers(App::DocumentObject* joint, int AssemblyObject::slidingPartIndex(App::DocumentObject* joint) { - App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1"); + App::DocumentObject* part1 = getMovingPartFromRef(this, joint, "Reference1"); App::DocumentObject* obj1 = getObjFromRef(joint, "Reference1"); boost::ignore_unused(obj1); Base::Placement plc1 = getPlacementFromProp(joint, "Placement1"); - App::DocumentObject* part2 = getMovingPartFromRef(joint, "Reference2"); + App::DocumentObject* part2 = getMovingPartFromRef(this, joint, "Reference2"); App::DocumentObject* obj2 = getObjFromRef(joint, "Reference2"); boost::ignore_unused(obj2); Base::Placement plc2 = getPlacementFromProp(joint, "Placement2"); @@ -1550,8 +1518,8 @@ int AssemblyObject::slidingPartIndex(App::DocumentObject* joint) int slidingFound = 0; for (auto* jt : getJoints(false, false)) { if (getJointType(jt) == JointType::Slider) { - App::DocumentObject* jpart1 = getMovingPartFromRef(jt, "Reference1"); - App::DocumentObject* jpart2 = getMovingPartFromRef(jt, "Reference2"); + App::DocumentObject* jpart1 = getMovingPartFromRef(this, jt, "Reference1"); + App::DocumentObject* jpart2 = getMovingPartFromRef(this, jt, "Reference2"); int found = 0; Base::Placement plcjt, plci; if (jpart1 == part1 || jpart1 == part2) { @@ -1604,8 +1572,8 @@ AssemblyObject::MbDPartData AssemblyObject::getMbDData(App::DocumentObject* part for (auto* joint : joints) { JointType jointType = getJointType(joint); if (jointType == JointType::Fixed) { - App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1"); - App::DocumentObject* part2 = getMovingPartFromRef(joint, "Reference2"); + App::DocumentObject* part1 = getMovingPartFromRef(this, joint, "Reference1"); + App::DocumentObject* part2 = getMovingPartFromRef(this, joint, "Reference2"); App::DocumentObject* partToAdd = currentPart == part1 ? part2 : part1; if (objectPartMap.find(partToAdd) != objectPartMap.end()) { @@ -1718,7 +1686,7 @@ std::vector AssemblyObject::getDownstreamParts(App::DocumentObject* part } if (joint) { - AssemblyObject::setJointActivated(joint, state); + setJointActivated(joint, state); } return downstreamParts; @@ -1743,7 +1711,9 @@ std::vector AssemblyObject::getUpstreamParts(App::Document std::string name; App::DocumentObject* connectingJoint = getJointOfPartConnectingToGround(part, name); App::DocumentObject* upPart = - getMovingPartFromRef(connectingJoint, name == "Reference1" ? "Reference2" : "Reference1"); + getMovingPartFromRef(this, + connectingJoint, + name == "Reference1" ? "Reference2" : "Reference1"); std::vector upstreamParts = getUpstreamParts(upPart, limit); upstreamParts.push_back(part); @@ -1764,7 +1734,7 @@ App::DocumentObject* AssemblyObject::getUpstreamMovingPart(App::DocumentObject* return part; } - part = getMovingPartFromRef(joint, name == "Reference1" ? "Reference2" : "Reference1"); + part = getMovingPartFromRef(this, joint, name == "Reference1" ? "Reference2" : "Reference1"); return getUpstreamMovingPart(part, joint, name); } @@ -1859,687 +1829,6 @@ void AssemblyObject::ensureIdentityPlacements() } } -// ======================================= Utils ====================================== - -void AssemblyObject::swapJCS(App::DocumentObject* joint) -{ - if (!joint) { - return; - } - - auto pPlc1 = dynamic_cast(joint->getPropertyByName("Placement1")); - auto pPlc2 = dynamic_cast(joint->getPropertyByName("Placement2")); - if (pPlc1 && pPlc2) { - auto temp = pPlc1->getValue(); - pPlc1->setValue(pPlc2->getValue()); - pPlc2->setValue(temp); - } - auto pRef1 = dynamic_cast(joint->getPropertyByName("Reference1")); - auto pRef2 = dynamic_cast(joint->getPropertyByName("Reference2")); - if (pRef1 && pRef2) { - auto temp = pRef1->getValue(); - auto subs1 = pRef1->getSubValues(); - auto subs2 = pRef2->getSubValues(); - pRef1->setValue(pRef2->getValue()); - pRef1->setSubValues(std::move(subs2)); - pRef2->setValue(temp); - pRef2->setSubValues(std::move(subs1)); - } -} - -bool AssemblyObject::isEdgeType(App::DocumentObject* obj, - std::string& elName, - GeomAbs_CurveType type) -{ - auto* base = dynamic_cast(obj); - if (!base) { - return false; - } - - const PartApp::TopoShape& TopShape = base->Shape.getShape(); - - // Check for valid face types - TopoDS_Edge edge = TopoDS::Edge(TopShape.getSubShape(elName.c_str())); - BRepAdaptor_Curve sf(edge); - - if (sf.GetType() == type) { - return true; - } - - return false; -} - -bool AssemblyObject::isFaceType(App::DocumentObject* obj, - std::string& elName, - GeomAbs_SurfaceType type) -{ - auto* base = dynamic_cast(obj); - if (!base) { - return false; - } - - const PartApp::TopoShape TopShape = base->Shape.getShape(); - - // Check for valid face types - TopoDS_Face face = TopoDS::Face(TopShape.getSubShape(elName.c_str())); - BRepAdaptor_Surface sf(face); - - if (sf.GetType() == type) { - return true; - } - - return false; -} - -double AssemblyObject::getFaceRadius(App::DocumentObject* obj, std::string& elt) -{ - auto* base = dynamic_cast(obj); - if (!base) { - return 0.0; - } - - const PartApp::TopoShape& TopShape = base->Shape.getShape(); - - // Check for valid face types - TopoDS_Face face = TopoDS::Face(TopShape.getSubShape(elt.c_str())); - BRepAdaptor_Surface sf(face); - - if (sf.GetType() == GeomAbs_Cylinder) { - return sf.Cylinder().Radius(); - } - else if (sf.GetType() == GeomAbs_Sphere) { - return sf.Sphere().Radius(); - } - - return 0.0; -} - -double AssemblyObject::getEdgeRadius(App::DocumentObject* obj, std::string& elt) -{ - auto* base = dynamic_cast(obj); - if (!base) { - return 0.0; - } - - const PartApp::TopoShape& TopShape = base->Shape.getShape(); - - // Check for valid face types - TopoDS_Edge edge = TopoDS::Edge(TopShape.getSubShape(elt.c_str())); - BRepAdaptor_Curve sf(edge); - - if (sf.GetType() == GeomAbs_Circle) { - return sf.Circle().Radius(); - } - - return 0.0; -} - -DistanceType AssemblyObject::getDistanceType(App::DocumentObject* joint) -{ - if (!joint) { - return DistanceType::Other; - } - - std::string type1 = getElementTypeFromProp(joint, "Reference1"); - std::string type2 = getElementTypeFromProp(joint, "Reference2"); - std::string elt1 = getElementFromProp(joint, "Reference1"); - std::string elt2 = getElementFromProp(joint, "Reference2"); - auto* obj1 = getLinkedObjFromRef(joint, "Reference1"); - auto* obj2 = getLinkedObjFromRef(joint, "Reference2"); - - if (type1 == "Vertex" && type2 == "Vertex") { - return DistanceType::PointPoint; - } - else if (type1 == "Edge" && type2 == "Edge") { - if (isEdgeType(obj1, elt1, GeomAbs_Line) || isEdgeType(obj2, elt2, GeomAbs_Line)) { - if (!isEdgeType(obj1, elt1, GeomAbs_Line)) { - swapJCS(joint); // make sure that line is first if not 2 lines. - std::swap(elt1, elt2); - std::swap(obj1, obj2); - } - - if (isEdgeType(obj2, elt2, GeomAbs_Line)) { - return DistanceType::LineLine; - } - else if (isEdgeType(obj2, elt2, GeomAbs_Circle)) { - return DistanceType::LineCircle; - } - // TODO : other cases Ellipse, parabola, hyperbola... - } - - else if (isEdgeType(obj1, elt1, GeomAbs_Circle) || isEdgeType(obj2, elt2, GeomAbs_Circle)) { - if (!isEdgeType(obj1, elt1, GeomAbs_Circle)) { - swapJCS(joint); // make sure that circle is first if not 2 lines. - std::swap(elt1, elt2); - std::swap(obj1, obj2); - } - - if (isEdgeType(obj2, elt2, GeomAbs_Circle)) { - return DistanceType::CircleCircle; - } - // TODO : other cases Ellipse, parabola, hyperbola... - } - } - else if (type1 == "Face" && type2 == "Face") { - if (isFaceType(obj1, elt1, GeomAbs_Plane) || isFaceType(obj2, elt2, GeomAbs_Plane)) { - if (!isFaceType(obj1, elt1, GeomAbs_Plane)) { - swapJCS(joint); // make sure plane is first if its not 2 planes. - std::swap(elt1, elt2); - std::swap(obj1, obj2); - } - - if (isFaceType(obj2, elt2, GeomAbs_Plane)) { - return DistanceType::PlanePlane; - } - else if (isFaceType(obj2, elt2, GeomAbs_Cylinder)) { - return DistanceType::PlaneCylinder; - } - else if (isFaceType(obj2, elt2, GeomAbs_Sphere)) { - return DistanceType::PlaneSphere; - } - else if (isFaceType(obj2, elt2, GeomAbs_Cone)) { - return DistanceType::PlaneCone; - } - else if (isFaceType(obj2, elt2, GeomAbs_Torus)) { - return DistanceType::PlaneTorus; - } - } - - else if (isFaceType(obj1, elt1, GeomAbs_Cylinder) - || isFaceType(obj2, elt2, GeomAbs_Cylinder)) { - if (!isFaceType(obj1, elt1, GeomAbs_Cylinder)) { - swapJCS(joint); // make sure cylinder is first if its not 2 cylinders. - std::swap(elt1, elt2); - std::swap(obj1, obj2); - } - - if (isFaceType(obj2, elt2, GeomAbs_Cylinder)) { - return DistanceType::CylinderCylinder; - } - else if (isFaceType(obj2, elt2, GeomAbs_Sphere)) { - return DistanceType::CylinderSphere; - } - else if (isFaceType(obj2, elt2, GeomAbs_Cone)) { - return DistanceType::CylinderCone; - } - else if (isFaceType(obj2, elt2, GeomAbs_Torus)) { - return DistanceType::CylinderTorus; - } - } - - else if (isFaceType(obj1, elt1, GeomAbs_Cone) || isFaceType(obj2, elt2, GeomAbs_Cone)) { - if (!isFaceType(obj1, elt1, GeomAbs_Cone)) { - swapJCS(joint); // make sure cone is first if its not 2 cones. - std::swap(elt1, elt2); - std::swap(obj1, obj2); - } - - if (isFaceType(obj2, elt2, GeomAbs_Cone)) { - return DistanceType::ConeCone; - } - else if (isFaceType(obj2, elt2, GeomAbs_Torus)) { - return DistanceType::ConeTorus; - } - else if (isFaceType(obj2, elt2, GeomAbs_Sphere)) { - return DistanceType::ConeSphere; - } - } - - else if (isFaceType(obj1, elt1, GeomAbs_Torus) || isFaceType(obj2, elt2, GeomAbs_Torus)) { - if (!isFaceType(obj1, elt1, GeomAbs_Torus)) { - swapJCS(joint); // make sure torus is first if its not 2 torus. - std::swap(elt1, elt2); - std::swap(obj1, obj2); - } - - if (isFaceType(obj2, elt2, GeomAbs_Torus)) { - return DistanceType::TorusTorus; - } - else if (isFaceType(obj2, elt2, GeomAbs_Sphere)) { - return DistanceType::TorusSphere; - } - } - - else if (isFaceType(obj1, elt1, GeomAbs_Sphere) || isFaceType(obj2, elt2, GeomAbs_Sphere)) { - if (!isFaceType(obj1, elt1, GeomAbs_Sphere)) { - swapJCS(joint); // make sure sphere is first if its not 2 spheres. - std::swap(elt1, elt2); - std::swap(obj1, obj2); - } - - if (isFaceType(obj2, elt2, GeomAbs_Sphere)) { - return DistanceType::SphereSphere; - } - } - } - else if ((type1 == "Vertex" && type2 == "Face") || (type1 == "Face" && type2 == "Vertex")) { - if (type1 == "Vertex") { // Make sure face is the first. - swapJCS(joint); - std::swap(elt1, elt2); - std::swap(obj1, obj2); - } - if (isFaceType(obj1, elt1, GeomAbs_Plane)) { - return DistanceType::PointPlane; - } - else if (isFaceType(obj1, elt1, GeomAbs_Cylinder)) { - return DistanceType::PointCylinder; - } - else if (isFaceType(obj1, elt1, GeomAbs_Sphere)) { - return DistanceType::PointSphere; - } - else if (isFaceType(obj1, elt1, GeomAbs_Cone)) { - return DistanceType::PointCone; - } - else if (isFaceType(obj1, elt1, GeomAbs_Torus)) { - return DistanceType::PointTorus; - } - } - else if ((type1 == "Edge" && type2 == "Face") || (type1 == "Face" && type2 == "Edge")) { - if (type1 == "Edge") { // Make sure face is the first. - swapJCS(joint); - std::swap(elt1, elt2); - std::swap(obj1, obj2); - } - if (isEdgeType(obj2, elt2, GeomAbs_Line)) { - if (isFaceType(obj1, elt1, GeomAbs_Plane)) { - return DistanceType::LinePlane; - } - else if (isFaceType(obj1, elt1, GeomAbs_Cylinder)) { - return DistanceType::LineCylinder; - } - else if (isFaceType(obj1, elt1, GeomAbs_Sphere)) { - return DistanceType::LineSphere; - } - else if (isFaceType(obj1, elt1, GeomAbs_Cone)) { - return DistanceType::LineCone; - } - else if (isFaceType(obj1, elt1, GeomAbs_Torus)) { - return DistanceType::LineTorus; - } - } - else { - // For other curves we consider them as planes for now. Can be refined later. - if (isFaceType(obj1, elt1, GeomAbs_Plane)) { - return DistanceType::CurvePlane; - } - else if (isFaceType(obj1, elt1, GeomAbs_Cylinder)) { - return DistanceType::CurveCylinder; - } - else if (isFaceType(obj1, elt1, GeomAbs_Sphere)) { - return DistanceType::CurveSphere; - } - else if (isFaceType(obj1, elt1, GeomAbs_Cone)) { - return DistanceType::CurveCone; - } - else if (isFaceType(obj1, elt1, GeomAbs_Torus)) { - return DistanceType::CurveTorus; - } - } - } - else if ((type1 == "Vertex" && type2 == "Edge") || (type1 == "Edge" && type2 == "Vertex")) { - if (type1 == "Vertex") { // Make sure edge is the first. - swapJCS(joint); - std::swap(elt1, elt2); - std::swap(obj1, obj2); - } - if (isEdgeType(obj1, elt1, GeomAbs_Line)) { // Point on line joint. - return DistanceType::PointLine; - } - else { - // For other curves we do a point in plane-of-the-curve. - // Maybe it would be best tangent / distance to the conic? For arcs and - // circles we could use ASMTRevSphJoint. But is it better than pointInPlane? - return DistanceType::PointCurve; - } - } - return DistanceType::Other; -} - -void AssemblyObject::setJointActivated(App::DocumentObject* joint, bool val) -{ - if (!joint) { - return; - } - - auto* propActivated = dynamic_cast(joint->getPropertyByName("Activated")); - if (propActivated) { - propActivated->setValue(val); - } -} - -bool AssemblyObject::getJointActivated(App::DocumentObject* joint) -{ - if (!joint) { - return false; - } - - auto* propActivated = dynamic_cast(joint->getPropertyByName("Activated")); - if (propActivated) { - return propActivated->getValue(); - } - return false; -} - -double AssemblyObject::getJointDistance(App::DocumentObject* joint) -{ - double distance = 0.0; - if (!joint) { - return distance; - } - - auto* prop = dynamic_cast(joint->getPropertyByName("Distance")); - if (prop) { - distance = prop->getValue(); - } - - return distance; -} - -double AssemblyObject::getJointDistance2(App::DocumentObject* joint) -{ - double distance = 0.0; - if (!joint) { - return distance; - } - - auto* prop = dynamic_cast(joint->getPropertyByName("Distance2")); - if (prop) { - distance = prop->getValue(); - } - - return distance; -} - -JointType AssemblyObject::getJointType(App::DocumentObject* joint) -{ - JointType jointType = JointType::Fixed; - if (!joint) { - return jointType; - } - - auto* prop = dynamic_cast(joint->getPropertyByName("JointType")); - if (prop) { - jointType = static_cast(prop->getValue()); - } - - return jointType; -} - -std::vector AssemblyObject::getSubAsList(App::PropertyXLinkSub* prop) -{ - if (!prop) { - return {}; - } - - std::vector subs = prop->getSubValues(); - if (subs.empty()) { - return {}; - } - - return Base::Tools::splitSubName(subs[0]); -} - -std::vector AssemblyObject::getSubAsList(App::DocumentObject* obj, const char* pName) -{ - if (!obj) { - return {}; - } - - auto* prop = dynamic_cast(obj->getPropertyByName(pName)); - - return getSubAsList(prop); -} - -std::string AssemblyObject::getElementFromProp(App::DocumentObject* obj, const char* pName) -{ - if (!obj) { - return ""; - } - - std::vector names = getSubAsList(obj, pName); - - if (names.empty()) { - return ""; - } - - return names.back(); -} - -std::string AssemblyObject::getElementTypeFromProp(App::DocumentObject* obj, const char* propName) -{ - // The prop is going to be something like 'Edge14' or 'Face7'. We need 'Edge' or 'Face' - std::string elementType; - for (char ch : getElementFromProp(obj, propName)) { - if (std::isalpha(ch)) { - elementType += ch; - } - } - return elementType; -} - -App::DocumentObject* AssemblyObject::getObjFromProp(App::DocumentObject* joint, const char* pName) -{ - if (!joint) { - return nullptr; - } - - auto* propObj = dynamic_cast(joint->getPropertyByName(pName)); - if (!propObj) { - return nullptr; - } - return propObj->getValue(); -} - -App::DocumentObject* AssemblyObject::getObjFromRef(App::DocumentObject* obj, std::string& sub) -{ - if (!obj) { - return nullptr; - } - - App::Document* doc = obj->getDocument(); - - std::vector names = Base::Tools::splitSubName(sub); - - // Lambda function to check if the typeId is a BodySubObject - auto isBodySubObject = [](App::DocumentObject* obj) -> bool { - // PartDesign::Point + Line + Plane + CoordinateSystem - // getViewProviderName instead of isDerivedFrom to avoid dependency on sketcher - return (strcmp(obj->getViewProviderName(), "SketcherGui::ViewProviderSketch") == 0 - || obj->isDerivedFrom()); - }; - - // Helper function to handle PartDesign::Body objects - auto handlePartDesignBody = [&](App::DocumentObject* obj, - std::vector::iterator it) -> App::DocumentObject* { - auto nextIt = std::next(it); - if (nextIt != names.end()) { - for (auto* obji : obj->getOutList()) { - if (*nextIt == obji->getNameInDocument()) { - if (isBodySubObject(obji)) { - return obji; - } - } - } - } - return obj; - }; - - - for (auto it = names.begin(); it != names.end(); ++it) { - App::DocumentObject* obj = doc->getObject(it->c_str()); - if (!obj) { - return nullptr; - } - - if (obj->isDerivedFrom()) { - continue; - } - - // The last but one name should be the selected - if (std::next(it) == std::prev(names.end())) { - return obj; - } - - if (obj->isDerivedFrom() || obj->isLinkGroup()) { - continue; - } - else if (obj->isDerivedFrom()) { - return handlePartDesignBody(obj, it); - } - else if (obj->isDerivedFrom()) { - // Primitive, fastener, gear, etc. - return obj; - } - else if (obj->isLink()) { - App::DocumentObject* linked_obj = obj->getLinkedObject(); - if (linked_obj->isDerivedFrom()) { - auto* retObj = handlePartDesignBody(linked_obj, it); - return retObj == linked_obj ? obj : retObj; - } - else if (linked_obj->isDerivedFrom()) { - return obj; - } - else { - doc = linked_obj->getDocument(); - continue; - } - } - } - - return nullptr; -} - -App::DocumentObject* AssemblyObject::getObjFromRef(App::PropertyXLinkSub* prop) -{ - if (!prop) { - return nullptr; - } - - App::DocumentObject* obj = prop->getValue(); - if (!obj) { - return nullptr; - } - - std::vector subs = prop->getSubValues(); - if (subs.empty()) { - return nullptr; - } - - return getObjFromRef(obj, subs[0]); -} - -App::DocumentObject* AssemblyObject::getObjFromRef(App::DocumentObject* joint, const char* pName) -{ - if (!joint) { - return nullptr; - } - - auto* prop = dynamic_cast(joint->getPropertyByName(pName)); - - return getObjFromRef(prop); -} - -App::DocumentObject* AssemblyObject::getMovingPartFromRef(App::DocumentObject* obj, - std::string& sub) -{ - if (!obj) { - return nullptr; - } - - App::Document* doc = obj->getDocument(); - - std::vector names = Base::Tools::splitSubName(sub); - names.insert(names.begin(), obj->getNameInDocument()); - - bool assemblyPassed = false; - - for (const auto& objName : names) { - obj = doc->getObject(objName.c_str()); - if (!obj) { - continue; - } - - if (obj->isLink()) { // update the document if necessary for next object - doc = obj->getLinkedObject()->getDocument(); - } - - if (obj == this) { - // We make sure we pass the assembly for cases like part.assembly.part.body - assemblyPassed = true; - continue; - } - if (!assemblyPassed) { - continue; - } - - if (obj->isDerivedFrom()) { - continue; // we ignore groups. - } - - if (obj->isLinkGroup()) { - continue; - } - - // We ignore dynamic sub-assemblies. - if (obj->isDerivedFrom()) { - auto* pRigid = dynamic_cast(obj->getPropertyByName("Rigid")); - if (pRigid && !pRigid->getValue()) { - continue; - } - } - - return obj; - } - - return nullptr; -} - -App::DocumentObject* AssemblyObject::getMovingPartFromRef(App::PropertyXLinkSub* prop) -{ - if (!prop) { - return nullptr; - } - - App::DocumentObject* obj = prop->getValue(); - if (!obj) { - return nullptr; - } - - std::vector subs = prop->getSubValues(); - if (subs.empty()) { - return nullptr; - } - - return getMovingPartFromRef(obj, subs[0]); -} - -App::DocumentObject* AssemblyObject::getMovingPartFromRef(App::DocumentObject* joint, - const char* pName) -{ - if (!joint) { - return nullptr; - } - - auto* prop = dynamic_cast(joint->getPropertyByName(pName)); - - return getMovingPartFromRef(prop); -} - -App::DocumentObject* AssemblyObject::getLinkedObjFromRef(App::DocumentObject* joint, - const char* pObj) -{ - if (!joint) { - return nullptr; - } - - auto* obj = getObjFromRef(joint, pObj); - if (obj) { - return obj->getLinkedObject(true); - } - return nullptr; -} - - /*void Part::handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property* prop) { diff --git a/src/Mod/Assembly/App/AssemblyObject.h b/src/Mod/Assembly/App/AssemblyObject.h index a51968058e..c310ab6da6 100644 --- a/src/Mod/Assembly/App/AssemblyObject.h +++ b/src/Mod/Assembly/App/AssemblyObject.h @@ -26,9 +26,6 @@ #define ASSEMBLY_AssemblyObject_H -#include -#include - #include #include @@ -62,6 +59,8 @@ namespace Assembly class AssemblyLink; class JointGroup; class ViewGroup; +enum class JointType; + struct ObjRef { @@ -69,72 +68,6 @@ struct ObjRef App::PropertyXLinkSub* ref; }; -// This enum has to be the same as the one in JointObject.py -enum class JointType -{ - Fixed, - Revolute, - Cylindrical, - Slider, - Ball, - Distance, - Parallel, - Perpendicular, - Angle, - RackPinion, - Screw, - Gears, - Belt, -}; - -enum class DistanceType -{ - PointPoint, - - LineLine, - LineCircle, - CircleCircle, - - PlanePlane, - PlaneCylinder, - PlaneSphere, - PlaneCone, - PlaneTorus, - CylinderCylinder, - CylinderSphere, - CylinderCone, - CylinderTorus, - ConeCone, - ConeTorus, - ConeSphere, - TorusTorus, - TorusSphere, - SphereSphere, - - PointPlane, - PointCylinder, - PointSphere, - PointCone, - PointTorus, - - LinePlane, - LineCylinder, - LineSphere, - LineCone, - LineTorus, - - CurvePlane, - CurveCylinder, - CurveSphere, - CurveCone, - CurveTorus, - - PointLine, - PointCurve, - - Other, -}; - class AssemblyExport AssemblyObject: public App::Part { PROPERTY_HEADER_WITH_OVERRIDE(Assembly::AssemblyObject); @@ -257,42 +190,6 @@ private: bool bundleFixed; // void handleChangedPropertyType(Base::XMLReader &reader, const char *TypeName, App::Property // *prop) override; - -public: - // ---------------- Utils ------------------- - // Can't put the functions by themselves in AssemblyUtils.cpp : - // see https://forum.freecad.org/viewtopic.php?p=729577#p729577 - - static void swapJCS(App::DocumentObject* joint); - - static bool isEdgeType(App::DocumentObject* obj, std::string& elName, GeomAbs_CurveType type); - static bool isFaceType(App::DocumentObject* obj, std::string& elName, GeomAbs_SurfaceType type); - static double getFaceRadius(App::DocumentObject* obj, std::string& elName); - static double getEdgeRadius(App::DocumentObject* obj, std::string& elName); - - static DistanceType getDistanceType(App::DocumentObject* joint); - - static JointGroup* getJointGroup(const App::Part* part); - - // getters to get from properties - static void setJointActivated(App::DocumentObject* joint, bool val); - static bool getJointActivated(App::DocumentObject* joint); - static double getJointDistance(App::DocumentObject* joint); - static double getJointDistance2(App::DocumentObject* joint); - static JointType getJointType(App::DocumentObject* joint); - static std::string getElementFromProp(App::DocumentObject* obj, const char* propName); - static std::string getElementTypeFromProp(App::DocumentObject* obj, const char* propName); - static App::DocumentObject* getObjFromProp(App::DocumentObject* joint, const char* propName); - static App::DocumentObject* getObjFromRef(App::DocumentObject* obj, std::string& sub); - static App::DocumentObject* getObjFromRef(App::PropertyXLinkSub* prop); - static App::DocumentObject* getObjFromRef(App::DocumentObject* joint, const char* propName); - App::DocumentObject* getMovingPartFromRef(App::DocumentObject* obj, std::string& sub); - App::DocumentObject* getMovingPartFromRef(App::PropertyXLinkSub* prop); - App::DocumentObject* getMovingPartFromRef(App::DocumentObject* joint, const char* propName); - static App::DocumentObject* getLinkedObjFromRef(App::DocumentObject* joint, - const char* propName); - static std::vector getSubAsList(App::PropertyXLinkSub* prop); - static std::vector getSubAsList(App::DocumentObject* joint, const char* propName); }; // using AssemblyObjectPython = App::FeaturePythonT; diff --git a/src/Mod/Assembly/App/AssemblyUtils.cpp b/src/Mod/Assembly/App/AssemblyUtils.cpp index 4c495628ac..3d99602b54 100644 --- a/src/Mod/Assembly/App/AssemblyUtils.cpp +++ b/src/Mod/Assembly/App/AssemblyUtils.cpp @@ -23,6 +23,13 @@ #include "PreCompiled.h" #ifndef _PreComp_ +#include +#include +#include +#include +#include +#include +#include #endif #include @@ -36,15 +43,692 @@ #include #include +#include #include +#include #include "AssemblyUtils.h" +#include "AssemblyObject.h" +#include "AssemblyLink.h" + +#include "JointGroup.h" + + +namespace PartApp = Part; // ======================================= Utils ====================================== -/* namespace Assembly { +void swapJCS(const App::DocumentObject* joint) +{ + if (!joint) { + return; + } + + auto pPlc1 = joint->getPropertyByName("Placement1"); + auto pPlc2 = joint->getPropertyByName("Placement2"); + if (pPlc1 && pPlc2) { + const auto temp = pPlc1->getValue(); + pPlc1->setValue(pPlc2->getValue()); + pPlc2->setValue(temp); + } + auto pRef1 = joint->getPropertyByName("Reference1"); + auto pRef2 = joint->getPropertyByName("Reference2"); + if (pRef1 && pRef2) { + auto temp = pRef1->getValue(); + auto subs1 = pRef1->getSubValues(); + auto subs2 = pRef2->getSubValues(); + pRef1->setValue(pRef2->getValue()); + pRef1->setSubValues(std::move(subs2)); + pRef2->setValue(temp); + pRef2->setSubValues(std::move(subs1)); + } +} + +bool isEdgeType(const App::DocumentObject* obj, + const std::string& elName, + const GeomAbs_CurveType type) +{ + auto* base = dynamic_cast(obj); + if (!base) { + return false; + } + + const auto& TopShape = base->Shape.getShape(); + + // Check for valid face types + const auto edge = TopoDS::Edge(TopShape.getSubShape(elName.c_str())); + BRepAdaptor_Curve sf(edge); + + return sf.GetType() == type; +} + +bool isFaceType(const App::DocumentObject* obj, + const std::string& elName, + const GeomAbs_SurfaceType type) +{ + auto* base = dynamic_cast(obj); + if (!base) { + return false; + } + + const auto TopShape = base->Shape.getShape(); + + // Check for valid face types + const auto face = TopoDS::Face(TopShape.getSubShape(elName.c_str())); + BRepAdaptor_Surface sf(face); + + return sf.GetType() == type; +} + +double getFaceRadius(const App::DocumentObject* obj, const std::string& elt) +{ + auto* base = dynamic_cast(obj); + if (!base) { + return 0.0; + } + + const PartApp::TopoShape& TopShape = base->Shape.getShape(); + + // Check for valid face types + TopoDS_Face face = TopoDS::Face(TopShape.getSubShape(elt.c_str())); + BRepAdaptor_Surface sf(face); + + const auto type = sf.GetType(); + return type == GeomAbs_Cylinder ? sf.Cylinder().Radius() + : type == GeomAbs_Sphere ? sf.Sphere().Radius() + : 0.0; +} + +double getEdgeRadius(const App::DocumentObject* obj, const std::string& elt) +{ + auto* base = dynamic_cast(obj); + if (!base) { + return 0.0; + } + + const auto& TopShape = base->Shape.getShape(); + + // Check for valid face types + const auto edge = TopoDS::Edge(TopShape.getSubShape(elt.c_str())); + BRepAdaptor_Curve sf(edge); + + return sf.GetType() == GeomAbs_Circle ? sf.Circle().Radius() : 0.0; +} + +DistanceType getDistanceType(App::DocumentObject* joint) +{ + if (!joint) { + return DistanceType::Other; + } + + const auto type1 = getElementTypeFromProp(joint, "Reference1"); + const auto type2 = getElementTypeFromProp(joint, "Reference2"); + auto elt1 = getElementFromProp(joint, "Reference1"); + auto elt2 = getElementFromProp(joint, "Reference2"); + auto* obj1 = getLinkedObjFromRef(joint, "Reference1"); + auto* obj2 = getLinkedObjFromRef(joint, "Reference2"); + + if (type1 == "Vertex" && type2 == "Vertex") { + return DistanceType::PointPoint; + } + else if (type1 == "Edge" && type2 == "Edge") { + if (isEdgeType(obj1, elt1, GeomAbs_Line) || isEdgeType(obj2, elt2, GeomAbs_Line)) { + if (!isEdgeType(obj1, elt1, GeomAbs_Line)) { + swapJCS(joint); // make sure that line is first if not 2 lines. + std::swap(elt1, elt2); + std::swap(obj1, obj2); + } + + if (isEdgeType(obj2, elt2, GeomAbs_Line)) { + return DistanceType::LineLine; + } + else if (isEdgeType(obj2, elt2, GeomAbs_Circle)) { + return DistanceType::LineCircle; + } + // TODO : other cases Ellipse, parabola, hyperbola... + } + + else if (isEdgeType(obj1, elt1, GeomAbs_Circle) || isEdgeType(obj2, elt2, GeomAbs_Circle)) { + if (!isEdgeType(obj1, elt1, GeomAbs_Circle)) { + swapJCS(joint); // make sure that circle is first if not 2 lines. + std::swap(elt1, elt2); + std::swap(obj1, obj2); + } + + if (isEdgeType(obj2, elt2, GeomAbs_Circle)) { + return DistanceType::CircleCircle; + } + // TODO : other cases Ellipse, parabola, hyperbola... + } + } + else if (type1 == "Face" && type2 == "Face") { + if (isFaceType(obj1, elt1, GeomAbs_Plane) || isFaceType(obj2, elt2, GeomAbs_Plane)) { + if (!isFaceType(obj1, elt1, GeomAbs_Plane)) { + swapJCS(joint); // make sure plane is first if its not 2 planes. + std::swap(elt1, elt2); + std::swap(obj1, obj2); + } + + if (isFaceType(obj2, elt2, GeomAbs_Plane)) { + return DistanceType::PlanePlane; + } + else if (isFaceType(obj2, elt2, GeomAbs_Cylinder)) { + return DistanceType::PlaneCylinder; + } + else if (isFaceType(obj2, elt2, GeomAbs_Sphere)) { + return DistanceType::PlaneSphere; + } + else if (isFaceType(obj2, elt2, GeomAbs_Cone)) { + return DistanceType::PlaneCone; + } + else if (isFaceType(obj2, elt2, GeomAbs_Torus)) { + return DistanceType::PlaneTorus; + } + } + + else if (isFaceType(obj1, elt1, GeomAbs_Cylinder) + || isFaceType(obj2, elt2, GeomAbs_Cylinder)) { + if (!isFaceType(obj1, elt1, GeomAbs_Cylinder)) { + swapJCS(joint); // make sure cylinder is first if its not 2 cylinders. + std::swap(elt1, elt2); + std::swap(obj1, obj2); + } + + if (isFaceType(obj2, elt2, GeomAbs_Cylinder)) { + return DistanceType::CylinderCylinder; + } + else if (isFaceType(obj2, elt2, GeomAbs_Sphere)) { + return DistanceType::CylinderSphere; + } + else if (isFaceType(obj2, elt2, GeomAbs_Cone)) { + return DistanceType::CylinderCone; + } + else if (isFaceType(obj2, elt2, GeomAbs_Torus)) { + return DistanceType::CylinderTorus; + } + } + + else if (isFaceType(obj1, elt1, GeomAbs_Cone) || isFaceType(obj2, elt2, GeomAbs_Cone)) { + if (!isFaceType(obj1, elt1, GeomAbs_Cone)) { + swapJCS(joint); // make sure cone is first if its not 2 cones. + std::swap(elt1, elt2); + std::swap(obj1, obj2); + } + + if (isFaceType(obj2, elt2, GeomAbs_Cone)) { + return DistanceType::ConeCone; + } + else if (isFaceType(obj2, elt2, GeomAbs_Torus)) { + return DistanceType::ConeTorus; + } + else if (isFaceType(obj2, elt2, GeomAbs_Sphere)) { + return DistanceType::ConeSphere; + } + } + + else if (isFaceType(obj1, elt1, GeomAbs_Torus) || isFaceType(obj2, elt2, GeomAbs_Torus)) { + if (!isFaceType(obj1, elt1, GeomAbs_Torus)) { + swapJCS(joint); // make sure torus is first if its not 2 torus. + std::swap(elt1, elt2); + std::swap(obj1, obj2); + } + + if (isFaceType(obj2, elt2, GeomAbs_Torus)) { + return DistanceType::TorusTorus; + } + else if (isFaceType(obj2, elt2, GeomAbs_Sphere)) { + return DistanceType::TorusSphere; + } + } + + else if (isFaceType(obj1, elt1, GeomAbs_Sphere) || isFaceType(obj2, elt2, GeomAbs_Sphere)) { + if (!isFaceType(obj1, elt1, GeomAbs_Sphere)) { + swapJCS(joint); // make sure sphere is first if its not 2 spheres. + std::swap(elt1, elt2); + std::swap(obj1, obj2); + } + + if (isFaceType(obj2, elt2, GeomAbs_Sphere)) { + return DistanceType::SphereSphere; + } + } + } + else if ((type1 == "Vertex" && type2 == "Face") || (type1 == "Face" && type2 == "Vertex")) { + if (type1 == "Vertex") { // Make sure face is the first. + swapJCS(joint); + std::swap(elt1, elt2); + std::swap(obj1, obj2); + } + if (isFaceType(obj1, elt1, GeomAbs_Plane)) { + return DistanceType::PointPlane; + } + else if (isFaceType(obj1, elt1, GeomAbs_Cylinder)) { + return DistanceType::PointCylinder; + } + else if (isFaceType(obj1, elt1, GeomAbs_Sphere)) { + return DistanceType::PointSphere; + } + else if (isFaceType(obj1, elt1, GeomAbs_Cone)) { + return DistanceType::PointCone; + } + else if (isFaceType(obj1, elt1, GeomAbs_Torus)) { + return DistanceType::PointTorus; + } + } + else if ((type1 == "Edge" && type2 == "Face") || (type1 == "Face" && type2 == "Edge")) { + if (type1 == "Edge") { // Make sure face is the first. + swapJCS(joint); + std::swap(elt1, elt2); + std::swap(obj1, obj2); + } + if (isEdgeType(obj2, elt2, GeomAbs_Line)) { + if (isFaceType(obj1, elt1, GeomAbs_Plane)) { + return DistanceType::LinePlane; + } + else if (isFaceType(obj1, elt1, GeomAbs_Cylinder)) { + return DistanceType::LineCylinder; + } + else if (isFaceType(obj1, elt1, GeomAbs_Sphere)) { + return DistanceType::LineSphere; + } + else if (isFaceType(obj1, elt1, GeomAbs_Cone)) { + return DistanceType::LineCone; + } + else if (isFaceType(obj1, elt1, GeomAbs_Torus)) { + return DistanceType::LineTorus; + } + } + else { + // For other curves we consider them as planes for now. Can be refined later. + if (isFaceType(obj1, elt1, GeomAbs_Plane)) { + return DistanceType::CurvePlane; + } + else if (isFaceType(obj1, elt1, GeomAbs_Cylinder)) { + return DistanceType::CurveCylinder; + } + else if (isFaceType(obj1, elt1, GeomAbs_Sphere)) { + return DistanceType::CurveSphere; + } + else if (isFaceType(obj1, elt1, GeomAbs_Cone)) { + return DistanceType::CurveCone; + } + else if (isFaceType(obj1, elt1, GeomAbs_Torus)) { + return DistanceType::CurveTorus; + } + } + } + else if ((type1 == "Vertex" && type2 == "Edge") || (type1 == "Edge" && type2 == "Vertex")) { + if (type1 == "Vertex") { // Make sure edge is the first. + swapJCS(joint); + std::swap(elt1, elt2); + std::swap(obj1, obj2); + } + if (isEdgeType(obj1, elt1, GeomAbs_Line)) { // Point on line joint. + return DistanceType::PointLine; + } + else { + // For other curves we do a point in plane-of-the-curve. + // Maybe it would be best tangent / distance to the conic? For arcs and + // circles we could use ASMTRevSphJoint. But is it better than pointInPlane? + return DistanceType::PointCurve; + } + } + return DistanceType::Other; +} + +JointGroup* getJointGroup(const App::Part* part) +{ + if (!part) { + return nullptr; + } + + const auto* doc = part->getDocument(); + + const auto jointGroups = doc->getObjectsOfType(JointGroup::getClassTypeId()); + if (jointGroups.empty()) { + return nullptr; + } + for (auto jointGroup : jointGroups) { + if (part->hasObject(jointGroup)) { + return dynamic_cast(jointGroup); + } + } + return nullptr; +} + +void setJointActivated(const App::DocumentObject* joint, bool val) +{ + if (!joint) { + return; + } + + if (auto propActivated = joint->getPropertyByName("Activated")) { + propActivated->setValue(val); + } +} + +bool getJointActivated(const App::DocumentObject* joint) +{ + if (!joint) { + return false; + } + + if (const auto propActivated = joint->getPropertyByName("Activated")) { + return propActivated->getValue(); + } + return false; +} + +double getJointDistance(const App::DocumentObject* joint, const char* propertyName) +{ + if (!joint) { + return 0.0; + } + + const auto* prop = joint->getPropertyByName(propertyName); + if (!prop) { + return 0.0; + } + + return prop->getValue(); +} + +double getJointDistance(const App::DocumentObject* joint) +{ + return getJointDistance(joint, "Distance"); +} + +double getJointDistance2(const App::DocumentObject* joint) +{ + return getJointDistance(joint, "Distance2"); +} + +JointType getJointType(const App::DocumentObject* joint) +{ + if (!joint) { + return JointType::Fixed; + } + + const auto* prop = joint->getPropertyByName("JointType"); + if (!prop) { + return JointType::Fixed; + } + + return static_cast(prop->getValue()); +} + +std::vector getSubAsList(const App::PropertyXLinkSub* prop) +{ + if (!prop) { + return {}; + } + + const auto subs = prop->getSubValues(); + if (subs.empty()) { + return {}; + } + + return Base::Tools::splitSubName(subs[0]); +} + +std::vector getSubAsList(const App::DocumentObject* obj, const char* pName) +{ + if (!obj) { + return {}; + } + return getSubAsList(obj->getPropertyByName(pName)); +} + +std::string getElementFromProp(const App::DocumentObject* obj, const char* pName) +{ + if (!obj) { + return ""; + } + + const auto names = getSubAsList(obj, pName); + if (names.empty()) { + return ""; + } + + return names.back(); +} + +std::string getElementTypeFromProp(const App::DocumentObject* obj, const char* propName) +{ + // The prop is going to be something like 'Edge14' or 'Face7'. We need 'Edge' or 'Face' + std::string elementType; + for (const char ch : getElementFromProp(obj, propName)) { + if (std::isalpha(ch)) { + elementType += ch; + } + } + return elementType; +} + +App::DocumentObject* getObjFromProp(const App::DocumentObject* joint, const char* pName) +{ + if (!joint) { + return {}; + } + + const auto* propObj = joint->getPropertyByName(pName); + if (!propObj) { + return {}; + } + + return propObj->getValue(); +} + +App::DocumentObject* getObjFromRef(const App::DocumentObject* obj, const std::string& sub) +{ + if (!obj) { + return nullptr; + } + + const auto* doc = obj->getDocument(); + const auto names = Base::Tools::splitSubName(sub); + + // Lambda function to check if the typeId is a BodySubObject + const auto isBodySubObject = [](App::DocumentObject* obj) -> bool { + // PartDesign::Point + Line + Plane + CoordinateSystem + // getViewProviderName instead of isDerivedFrom to avoid dependency on sketcher + const auto isDerivedFromVpSketch = + strcmp(obj->getViewProviderName(), "SketcherGui::ViewProviderSketch") == 0; + return isDerivedFromVpSketch || obj->isDerivedFrom(); + }; + + // Helper function to handle PartDesign::Body objects + const auto handlePartDesignBody = + [&](App::DocumentObject* obj, + std::vector::const_iterator it) -> App::DocumentObject* { + const auto nextIt = std::next(it); + if (nextIt != names.end()) { + for (auto* obji : obj->getOutList()) { + if (*nextIt == obji->getNameInDocument() && isBodySubObject(obji)) { + return obji; + } + } + } + return obj; + }; + + + for (auto it = names.begin(); it != names.end(); ++it) { + App::DocumentObject* obj = doc->getObject(it->c_str()); + if (!obj) { + return nullptr; + } + + if (obj->isDerivedFrom()) { + continue; + } + + // The last but one name should be the selected + if (std::next(it) == std::prev(names.end())) { + return obj; + } + + if (obj->isDerivedFrom() || obj->isLinkGroup()) { + continue; + } + else if (obj->isDerivedFrom()) { + return handlePartDesignBody(obj, it); + } + else if (obj->isDerivedFrom()) { + // Primitive, fastener, gear, etc. + return obj; + } + else if (obj->isLink()) { + App::DocumentObject* linked_obj = obj->getLinkedObject(); + if (linked_obj->isDerivedFrom()) { + auto* retObj = handlePartDesignBody(linked_obj, it); + return retObj == linked_obj ? obj : retObj; + } + else if (linked_obj->isDerivedFrom()) { + return obj; + } + else { + doc = linked_obj->getDocument(); + continue; + } + } + } + + return nullptr; +} + +App::DocumentObject* getObjFromRef(const App::PropertyXLinkSub* prop) +{ + if (!prop) { + return nullptr; + } + + const App::DocumentObject* obj = prop->getValue(); + if (!obj) { + return nullptr; + } + + const std::vector subs = prop->getSubValues(); + if (subs.empty()) { + return nullptr; + } + + return getObjFromRef(obj, subs[0]); +} + +App::DocumentObject* getObjFromRef(const App::DocumentObject* joint, const char* pName) +{ + if (!joint) { + return nullptr; + } + + const auto* prop = joint->getPropertyByName(pName); + return getObjFromRef(prop); +} + +App::DocumentObject* getLinkedObjFromRef(const App::DocumentObject* joint, const char* pObj) +{ + if (!joint) { + return nullptr; + } + + if (const auto* obj = getObjFromRef(joint, pObj)) { + return obj->getLinkedObject(true); + } + return nullptr; +} + +App::DocumentObject* getMovingPartFromRef(const AssemblyObject* assemblyObject, + App::DocumentObject* obj, + const std::string& sub) +{ + if (!obj) { + return nullptr; + } + + auto* doc = obj->getDocument(); + + auto names = Base::Tools::splitSubName(sub); + names.insert(names.begin(), obj->getNameInDocument()); + + bool assemblyPassed = false; + + for (const auto& objName : names) { + obj = doc->getObject(objName.c_str()); + if (!obj) { + continue; + } + + if (obj->isLink()) { // update the document if necessary for next object + doc = obj->getLinkedObject()->getDocument(); + } + + if (obj == assemblyObject) { + // We make sure we pass the assembly for cases like part.assembly.part.body + assemblyPassed = true; + continue; + } + if (!assemblyPassed) { + continue; + } + + if (obj->isDerivedFrom()) { + continue; // we ignore groups. + } + + if (obj->isLinkGroup()) { + continue; + } + + // We ignore dynamic sub-assemblies. + if (obj->isDerivedFrom()) { + const auto* pRigid = obj->getPropertyByName("Rigid"); + if (pRigid && !pRigid->getValue()) { + continue; + } + } + + return obj; + } + + return nullptr; +} + +App::DocumentObject* getMovingPartFromRef(const AssemblyObject* assemblyObject, + App::PropertyXLinkSub* prop) +{ + if (!prop) { + return nullptr; + } + + App::DocumentObject* obj = prop->getValue(); + if (!obj) { + return nullptr; + } + + const std::vector subs = prop->getSubValues(); + if (subs.empty()) { + return nullptr; + } + + return getMovingPartFromRef(assemblyObject, obj, subs[0]); +} + +App::DocumentObject* getMovingPartFromRef(const AssemblyObject* assemblyObject, + App::DocumentObject* joint, + const char* pName) +{ + if (!joint) { + return nullptr; + } + + auto* prop = joint->getPropertyByName(pName); + return getMovingPartFromRef(assemblyObject, prop); +} + +/* Base::Placement getPlacementFromProp(App::DocumentObject* obj, const char* propName) { Base::Placement plc = Base::Placement(); @@ -140,110 +824,6 @@ foundPlacement = getTargetPlacementRelativeTo(targetObj, part, container, inCont return Base::Placement(); } */ -/* -double getJointDistance(App::DocumentObject* joint) -{ - double distance = 0.0; - auto* prop = dynamic_cast(joint->getPropertyByName("Distance")); - if (prop) { - distance = prop->getValue(); - } - return distance; -} - -JointType getJointType(App::DocumentObject* joint) -{ - JointType jointType = JointType::Fixed; - - auto* prop = dynamic_cast(joint->getPropertyByName("JointType")); - if (prop) { - jointType = static_cast(prop->getValue()); - } - - return jointType; -} - -const char* getElementFromProp(App::DocumentObject* obj, const char* propName) -{ - auto* prop = dynamic_cast(obj->getPropertyByName(propName)); - if (!prop) { - return ""; - } - - return prop->getValue(); -} - -std::string getElementTypeFromProp(App::DocumentObject* obj, const char* propName) -{ - // The prop is going to be something like 'Edge14' or 'Face7'. We need 'Edge' or 'Face' - std::string elementType; - for (char ch : std::string(getElementFromProp(obj, propName))) { - if (std::isalpha(ch)) { - elementType += ch; - } - } - return elementType; -} - -App::DocumentObject* getLinkObjFromProp(App::DocumentObject* joint, - const char* propLinkName) -{ - auto* propObj = dynamic_cast(joint->getPropertyByName(propLinkName)); - if (!propObj) { - return nullptr; - } - return propObj->getValue(); -} - -App::DocumentObject* getObjFromNameProp(App::DocumentObject* joint, - const char* pObjName, - const char* pPart) -{ - auto* propObjName = dynamic_cast(joint->getPropertyByName(pObjName)); - if (!propObjName) { - return nullptr; - } - std::string objName = std::string(propObjName->getValue()); - - App::DocumentObject* containingPart = getLinkObjFromProp(joint, pPart); - if (!containingPart) { - return nullptr; - } - - if (objName == containingPart->getNameInDocument()) { - return containingPart; - } - - if (containingPart->getTypeId().isDerivedFrom(App::Link::getClassTypeId())) { - App::Link* link = dynamic_cast(containingPart); - - containingPart = link->getLinkedObject(); - if (!containingPart) { - return nullptr; - } - } - - for (auto obj : containingPart->getOutList()) { - if (objName == obj->getNameInDocument()) { - return obj; - } - } - - return nullptr; -} - -App::DocumentObject* getLinkedObjFromNameProp(App::DocumentObject* joint, - const char* pObjName, - const char* pPart) - { - auto* obj = getObjFromNameProp(joint, pObjName, pPart); - if (obj) { - return obj->getLinkedObject(true); - } - return nullptr; - } - -} // namespace Assembly -*/ +} // namespace Assembly diff --git a/src/Mod/Assembly/App/AssemblyUtils.h b/src/Mod/Assembly/App/AssemblyUtils.h index c5851fe30a..b04145d26b 100644 --- a/src/Mod/Assembly/App/AssemblyUtils.h +++ b/src/Mod/Assembly/App/AssemblyUtils.h @@ -26,6 +26,9 @@ #define ASSEMBLY_AssemblyUtils_H +#include +#include + #include #include @@ -43,7 +46,7 @@ class Placement; namespace Assembly { -/* + // This enum has to be the same as the one in JointObject.py enum class JointType { @@ -52,19 +55,126 @@ enum class JointType Cylindrical, Slider, Ball, - Distance + Distance, + Parallel, + Perpendicular, + Angle, + RackPinion, + Screw, + Gears, + Belt, }; +enum class GeometryType +{ + Point = 0, + + // Edges + Line = 1, + Curve = 2, + Circle = 3, + + // Faces + Place = 4, + Cylinder = 5, + Sphere = 6, + Cone = 7, + Torus = 8, +}; + +enum class DistanceType +{ + PointPoint, + + LineLine, + LineCircle, + CircleCircle, + + PlanePlane, + PlaneCylinder, + PlaneSphere, + PlaneCone, + PlaneTorus, + CylinderCylinder, + CylinderSphere, + CylinderCone, + CylinderTorus, + ConeCone, + ConeTorus, + ConeSphere, + TorusTorus, + TorusSphere, + SphereSphere, + + PointPlane, + PointCylinder, + PointSphere, + PointCone, + PointTorus, + + LinePlane, + LineCylinder, + LineSphere, + LineCone, + LineTorus, + + CurvePlane, + CurveCylinder, + CurveSphere, + CurveCone, + CurveTorus, + + PointLine, + PointCurve, + + Other, +}; + +class AssemblyObject; +class JointGroup; + +AssemblyExport void swapJCS(const App::DocumentObject* joint); + +AssemblyExport bool +isEdgeType(const App::DocumentObject* obj, const std::string& elName, const GeomAbs_CurveType type); +AssemblyExport bool isFaceType(const App::DocumentObject* obj, + const std::string& elName, + const GeomAbs_SurfaceType type); +AssemblyExport double getFaceRadius(const App::DocumentObject* obj, const std::string& elName); +AssemblyExport double getEdgeRadius(const App::DocumentObject* obj, const std::string& elName); + +AssemblyExport DistanceType getDistanceType(App::DocumentObject* joint); +AssemblyExport JointGroup* getJointGroup(const App::Part* part); + // getters to get from properties -double getJointDistance(App::DocumentObject* joint); -JointType getJointType(App::DocumentObject* joint); -const char* getElementFromProp(App::DocumentObject* obj, const char* propName); -std::string getElementTypeFromProp(App::DocumentObject* obj, const char* propName); -App::DocumentObject* getLinkObjFromProp(App::DocumentObject* joint, const char* propName); -App::DocumentObject* getObjFromNameProp(App::DocumentObject* joint, const char* pObjName, const -char* pPart); App::DocumentObject* getLinkedObjFromNameProp(App::DocumentObject* joint, const char* -pObjName, const char* pPart); Base::Placement getPlacementFromProp(App::DocumentObject* obj, const -char* propName);*/ +AssemblyExport void setJointActivated(const App::DocumentObject* joint, bool val); +AssemblyExport bool getJointActivated(const App::DocumentObject* joint); +AssemblyExport double getJointDistance(const App::DocumentObject* joint); +AssemblyExport double getJointDistance2(const App::DocumentObject* joint); +AssemblyExport JointType getJointType(const App::DocumentObject* joint); +AssemblyExport std::string getElementFromProp(const App::DocumentObject* obj, const char* propName); +AssemblyExport std::string getElementTypeFromProp(const App::DocumentObject* obj, + const char* propName); +AssemblyExport App::DocumentObject* getObjFromProp(const App::DocumentObject* joint, + const char* propName); +AssemblyExport App::DocumentObject* getObjFromRef(const App::DocumentObject* obj, + const std::string& sub); +AssemblyExport App::DocumentObject* getObjFromRef(const App::PropertyXLinkSub* prop); +AssemblyExport App::DocumentObject* getObjFromRef(const App::DocumentObject* joint, + const char* propName); +AssemblyExport App::DocumentObject* getLinkedObjFromRef(const App::DocumentObject* joint, + const char* propName); +AssemblyExport App::DocumentObject* getMovingPartFromRef(const AssemblyObject* assemblyObject, + App::DocumentObject* obj, + const std::string& sub); +AssemblyExport App::DocumentObject* getMovingPartFromRef(const AssemblyObject* assemblyObject, + const App::PropertyXLinkSub* prop); +AssemblyExport App::DocumentObject* getMovingPartFromRef(const AssemblyObject* assemblyObject, + App::DocumentObject* joint, + const char* pName); +AssemblyExport std::vector getSubAsList(const App::PropertyXLinkSub* prop); +AssemblyExport std::vector getSubAsList(const App::DocumentObject* joint, + const char* propName); } // namespace Assembly diff --git a/src/Mod/Assembly/App/CMakeLists.txt b/src/Mod/Assembly/App/CMakeLists.txt index 70063733c0..7638935a80 100644 --- a/src/Mod/Assembly/App/CMakeLists.txt +++ b/src/Mod/Assembly/App/CMakeLists.txt @@ -54,6 +54,8 @@ SET(Assembly_SRCS AssemblyObject.h AssemblyLink.cpp AssemblyLink.h + AssemblyUtils.cpp + AssemblyUtils.h BomObject.cpp BomObject.h BomGroup.cpp diff --git a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp index 67de217b8c..7ac1941fe1 100644 --- a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp +++ b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp @@ -189,11 +189,11 @@ bool ViewProviderAssembly::canDragObjectToTarget(App::DocumentObject* obj, for (auto joint : allJoints) { // getLinkObjFromProp returns nullptr if the property doesn't exist. - App::DocumentObject* obj1 = AssemblyObject::getObjFromRef(joint, "Reference1"); - App::DocumentObject* obj2 = AssemblyObject::getObjFromRef(joint, "Reference2"); - App::DocumentObject* part1 = assemblyPart->getMovingPartFromRef(joint, "Reference1"); - App::DocumentObject* part2 = assemblyPart->getMovingPartFromRef(joint, "Reference2"); - App::DocumentObject* obj3 = AssemblyObject::getObjFromProp(joint, "ObjectToGround"); + App::DocumentObject* part1 = getMovingPartFromRef(assemblyPart, joint, "Reference1"); + App::DocumentObject* part2 = getMovingPartFromRef(assemblyPart, joint, "Reference2"); + App::DocumentObject* obj1 = getObjFromRef(joint, "Reference1"); + App::DocumentObject* obj2 = getObjFromRef(joint, "Reference2"); + App::DocumentObject* obj3 = getObjFromProp(joint, "ObjectToGround"); if (obj == obj1 || obj == obj2 || obj == part1 || obj == part2 || obj == obj3) { if (!prompted) { prompted = true; @@ -662,13 +662,13 @@ bool ViewProviderAssembly::getSelectedObjectsWithinAssembly(bool addPreselection } App::DocumentObject* selRoot = selObj.getObject(); - App::DocumentObject* obj = assemblyPart->getObjFromRef(selRoot, subNamesStr); + App::DocumentObject* obj = getObjFromRef(selRoot, subNamesStr); if (!obj) { // In case of sub-assembly, the jointgroup would trigger the dragger. continue; } App::DocumentObject* part = - assemblyPart->getMovingPartFromRef(selRoot, subNamesStr); + getMovingPartFromRef(assemblyPart, selRoot, subNamesStr); if (!canDragObjectIn3d(part)) { continue; @@ -694,7 +694,7 @@ bool ViewProviderAssembly::getSelectedObjectsWithinAssembly(bool addPreselection App::DocumentObject* selRoot = Gui::Selection().getPreselection().Object.getObject(); std::string sub = Gui::Selection().getPreselection().pSubName; - App::DocumentObject* obj = assemblyPart->getMovingPartFromRef(selRoot, sub); + App::DocumentObject* obj = getMovingPartFromRef(assemblyPart, selRoot, sub); if (canDragObjectIn3d(obj)) { bool alreadyIn = false; @@ -760,7 +760,7 @@ ViewProviderAssembly::DragMode ViewProviderAssembly::findDragMode() return DragMode::TranslationNoSolve; } - JointType jointType = AssemblyObject::getJointType(movingJoint); + JointType jointType = getJointType(movingJoint); if (jointType == JointType::Fixed) { // If fixed joint we need to find the upstream joint to find move mode. // For example : Gnd -(revolute)- A -(fixed)- B : if user try to move B, then we should @@ -793,7 +793,7 @@ ViewProviderAssembly::DragMode ViewProviderAssembly::findDragMode() docsToMove.emplace_back(upPart, pPlc->getValue(), selRoot, subs[0]); } - jointType = AssemblyObject::getJointType(movingJoint); + jointType = getJointType(movingJoint); } const char* plcPropName = (pName == "Reference1") ? "Placement1" : "Placement2"; @@ -807,7 +807,7 @@ ViewProviderAssembly::DragMode ViewProviderAssembly::findDragMode() if (!ref) { return DragMode::Translation; } - auto* obj = assemblyPart->getObjFromRef(movingJoint, pName.c_str()); + auto* obj = getObjFromRef(movingJoint, pName.c_str()); Base::Placement global_plc = App::GeoFeature::getGlobalPlacement(obj, ref); jcsGlobalPlc = global_plc * jcsPlc; @@ -830,7 +830,7 @@ ViewProviderAssembly::DragMode ViewProviderAssembly::findDragMode() } else if (jointType == JointType::Distance) { // depends on the type of distance. For example plane-plane: - DistanceType distanceType = AssemblyObject::getDistanceType(movingJoint); + DistanceType distanceType = getDistanceType(movingJoint); if (distanceType == DistanceType::PlanePlane || distanceType == DistanceType::Other) { return DragMode::TranslationOnPlane; }