diff --git a/src/Mod/Assembly/App/AssemblyObject.cpp b/src/Mod/Assembly/App/AssemblyObject.cpp index ee82a21e4e..b98b56e42c 100644 --- a/src/Mod/Assembly/App/AssemblyObject.cpp +++ b/src/Mod/Assembly/App/AssemblyObject.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -45,6 +46,7 @@ #include #include +#include #include #include @@ -92,6 +94,9 @@ FC_LOG_LEVEL_INIT("Assembly", true, true, true) using namespace Assembly; using namespace MbD; + +namespace PartApp = Part; + /* static void printPlacement(Base::Placement plc, const char* name) { @@ -370,7 +375,8 @@ Base::Placement AssemblyObject::getMbdPlacement(std::shared_ptr mbdPar bool AssemblyObject::validateNewPlacements() { // First we check if a grounded object has moved. It can happen that they flip. - for (auto* obj : getGroundedParts()) { + std::vector groundedParts = getGroundedParts(); + for (auto* obj : groundedParts) { auto* propPlacement = dynamic_cast(obj->getPropertyByName("Placement")); if (propPlacement) { @@ -386,7 +392,8 @@ bool AssemblyObject::validateNewPlacements() if (!oldPlc.isSame(newPlacement)) { Base::Console().Warning( - "Assembly : Ignoring bad solve, a grounded object moved.\n"); + "Assembly : Ignoring bad solve, a grounded object (%s) moved.\n", + obj->getFullLabel()); return false; } } @@ -783,34 +790,54 @@ std::vector AssemblyObject::getGroundedParts() if (propObj) { App::DocumentObject* objToGround = propObj->getValue(); - groundedObjs.push_back(objToGround); + if (objToGround) { + if (std::find(groundedObjs.begin(), groundedObjs.end(), objToGround) + == groundedObjs.end()) { + groundedObjs.push_back(objToGround); + } + } } } + + // We also need to add all the root-level datums objects that are not attached. + std::vector objs = Group.getValues(); + for (auto* obj : objs) { + if (obj->isDerivedFrom() + || obj->isDerivedFrom()) { + auto* pcAttach = obj->getExtensionByType(); + if (pcAttach) { + // If it's a Part datums, we check if it's attached. If yes then we ignore it. + std::string mode = pcAttach->MapMode.getValueAsString(); + if (mode != "Deactivated") { + continue; + } + } + if (std::find(groundedObjs.begin(), groundedObjs.end(), obj) == groundedObjs.end()) { + groundedObjs.push_back(obj); + } + } + } + + // Origin is not in Group so we add it separately + groundedObjs.push_back(Origin.getValue()); + return groundedObjs; } std::vector AssemblyObject::fixGroundedParts() { - std::vector groundedJoints = getGroundedJoints(); + std::vector groundedParts = getGroundedParts(); - std::vector groundedObjs; - for (auto obj : groundedJoints) { + for (auto obj : groundedParts) { if (!obj) { continue; } - auto* propObj = dynamic_cast(obj->getPropertyByName("ObjectToGround")); - - if (propObj) { - App::DocumentObject* objToGround = propObj->getValue(); - - Base::Placement plc = getPlacementFromProp(obj, "Placement"); - std::string str = obj->getFullName(); - fixGroundedPart(objToGround, plc, str); - groundedObjs.push_back(objToGround); - } + Base::Placement plc = getPlacementFromProp(obj, "Placement"); + std::string str = obj->getFullName(); + fixGroundedPart(obj, plc, str); } - return groundedObjs; + return groundedParts; } void AssemblyObject::fixGroundedPart(App::DocumentObject* obj, @@ -1984,29 +2011,6 @@ std::vector AssemblyObject::getSubAssemblies() return subAssemblies; } -void AssemblyObject::updateGroundedJointsPlacements() -{ - std::vector groundedJoints = getGroundedJoints(); - - for (auto gJoint : groundedJoints) { - if (!gJoint) { - continue; - } - - auto* propObj = - dynamic_cast(gJoint->getPropertyByName("ObjectToGround")); - auto* propPlc = - dynamic_cast(gJoint->getPropertyByName("Placement")); - - if (propObj && propPlc) { - App::DocumentObject* obj = propObj->getValue(); - auto* propObjPlc = - dynamic_cast(obj->getPropertyByName("Placement")); - propPlc->setValue(propObjPlc->getValue()); - } - } -} - void AssemblyObject::ensureIdentityPlacements() { std::vector group = Group.getValues(); @@ -2037,36 +2041,3 @@ void AssemblyObject::ensureIdentityPlacements() } } } - -/*void Part::handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property* -prop) -{ - App::Part::handleChangedPropertyType(reader, TypeName, prop); -}*/ - -/* Apparently not necessary as App::Part doesn't have this. -// Python Assembly feature --------------------------------------------------------- - -namespace App -{ - /// @cond DOXERR - PROPERTY_SOURCE_TEMPLATE(Assembly::AssemblyObjectPython, Assembly::AssemblyObject) - template<> - const char* Assembly::AssemblyObjectPython::getViewProviderName() const - { - return "AssemblyGui::ViewProviderAssembly"; - } - template<> - PyObject* Assembly::AssemblyObjectPython::getPyObject() - { - if (PythonObject.is(Py::_None())) { - // ref counter is set to 1 - PythonObject = Py::Object(new FeaturePythonPyT(this), true); - } - return Py::new_reference_to(PythonObject); - } - /// @endcond - - // explicit template instantiation - template class AssemblyExport FeaturePythonT; -}// namespace App*/ diff --git a/src/Mod/Assembly/App/AssemblyObject.h b/src/Mod/Assembly/App/AssemblyObject.h index a5f7df556c..f4c1c2589d 100644 --- a/src/Mod/Assembly/App/AssemblyObject.h +++ b/src/Mod/Assembly/App/AssemblyObject.h @@ -186,7 +186,6 @@ public: void setObjMasses(std::vector> objectMasses); std::vector getSubAssemblies(); - void updateGroundedJointsPlacements(); std::vector getMotionsFromSimulation(App::DocumentObject* sim); diff --git a/src/Mod/Assembly/App/AssemblyUtils.cpp b/src/Mod/Assembly/App/AssemblyUtils.cpp index d406ef5bfd..f48eb4dbd7 100644 --- a/src/Mod/Assembly/App/AssemblyUtils.cpp +++ b/src/Mod/Assembly/App/AssemblyUtils.cpp @@ -33,6 +33,7 @@ #endif #include +#include #include #include #include @@ -536,7 +537,9 @@ App::DocumentObject* getObjFromRef(const App::DocumentObject* obj, const std::st // getViewProviderName instead of isDerivedFrom to avoid dependency on sketcher const auto isDerivedFromVpSketch = strcmp(obj->getViewProviderName(), "SketcherGui::ViewProviderSketch") == 0; - return isDerivedFromVpSketch || obj->isDerivedFrom(); + return isDerivedFromVpSketch || obj->isDerivedFrom() + || obj->isDerivedFrom() + || obj->isDerivedFrom(); }; // Helper function to handle PartDesign::Body objects diff --git a/src/Mod/Assembly/CommandCreateJoint.py b/src/Mod/Assembly/CommandCreateJoint.py index 77e6b3b7f7..3f3dae5512 100644 --- a/src/Mod/Assembly/CommandCreateJoint.py +++ b/src/Mod/Assembly/CommandCreateJoint.py @@ -47,11 +47,7 @@ def noOtherTaskActive(): def isCreateJointActive(): - return ( - UtilsAssembly.isAssemblyGrounded() - and UtilsAssembly.assembly_has_at_least_n_parts(2) - and noOtherTaskActive() - ) + return UtilsAssembly.assembly_has_at_least_n_parts(1) and noOtherTaskActive() def activateJoint(index): diff --git a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp index 1f3016927d..de098764b6 100644 --- a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp +++ b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp @@ -233,12 +233,6 @@ bool ViewProviderAssembly::setEdit(int mode) PARTKEY, this->getObject()->getNameInDocument()); - // When we set edit, we update the grounded joints placements to support : - // - If user transformed the grounded object - // - For nested assemblies where the grounded object moves around. - auto* assembly = getObject(); - assembly->updateGroundedJointsPlacements(); - setDragger(); attachSelection(); diff --git a/src/Mod/Assembly/JointObject.py b/src/Mod/Assembly/JointObject.py index 5c6f8d6559..fdac6ed824 100644 --- a/src/Mod/Assembly/JointObject.py +++ b/src/Mod/Assembly/JointObject.py @@ -956,18 +956,6 @@ class GroundedJoint: joint.ObjectToGround = obj_to_ground - joint.addProperty( - "App::PropertyPlacement", - "Placement", - "Ground", - QT_TRANSLATE_NOOP( - "App::Property", - "This is where the part is grounded.", - ), - ) - - joint.Placement = obj_to_ground.Placement - def dumps(self): return None @@ -1160,24 +1148,33 @@ class MakeJointSelGate: return False ref = [obj, [sub]] - selected_object = UtilsAssembly.getObject(ref) + sel_obj = UtilsAssembly.getObject(ref) - if not ( - selected_object.isDerivedFrom("Part::Feature") - or selected_object.isDerivedFrom("App::Part") + if UtilsAssembly.isLink(sel_obj): + linked = sel_obj.getLinkedObject() + if linked == sel_obj: + return True # We accept empty links + sel_obj = linked + + if sel_obj.isDerivedFrom("Part::Feature") or sel_obj.isDerivedFrom("App::Part"): + return True + + if sel_obj.isDerivedFrom("App::LocalCoordinateSystem") or sel_obj.isDerivedFrom( + "App::DatumElement" ): - if UtilsAssembly.isLink(selected_object): - linked = selected_object.getLinkedObject() - if linked == selected_object: - # We accept empty links - return True + datum = sel_obj + if datum.isDerivedFrom("App::DatumElement"): + parent = datum.getParent() + if parent.isDerivedFrom("App::LocalCoordinateSystem"): + datum = parent - if not (linked.isDerivedFrom("Part::Feature") or linked.isDerivedFrom("App::Part")): - return False - else: - return False + if self.assembly.hasObject(datum) and hasattr(datum, "MapMode"): + # accept only datum that are not attached + return datum.MapMode == "Deactivated" - return True + return True + + return False activeTask = None diff --git a/src/Mod/Assembly/UtilsAssembly.py b/src/Mod/Assembly/UtilsAssembly.py index 9474568aab..5995a21ad1 100644 --- a/src/Mod/Assembly/UtilsAssembly.py +++ b/src/Mod/Assembly/UtilsAssembly.py @@ -161,6 +161,21 @@ def getObject(ref): if obj.TypeId in {"App::Part", "Assembly::AssemblyObject"} or isLinkGroup(obj): continue + elif obj.isDerivedFrom("App::LocalCoordinateSystem"): + # 2 cases possible, either we have the LCS itself: "part.LCS." + # or we have a datum: "part.LCS.X_Axis" + if i + 1 < len(names): + obj2 = None + for obji in obj.OutList: + if obji.Name == names[i + 1]: + obj2 = obji + break + if obj2 and obj2.isDerivedFrom("App::DatumElement"): + return obj2 + + elif obj.isDerivedFrom("App::DatumElement"): + return obj + elif obj.TypeId == "PartDesign::Body": if i + 1 < len(names): obj2 = None @@ -168,7 +183,7 @@ def getObject(ref): if obji.Name == names[i + 1]: obj2 = obji break - if obj2 and isBodySubObject(obj2.TypeId): + if obj2 and isBodySubObject(obj2): return obj2 return obj @@ -185,7 +200,7 @@ def getObject(ref): if obji.Name == names[i + 1]: obj2 = obji break - if obj2 and isBodySubObject(obj2.TypeId): + if obj2 and isBodySubObject(obj2): return obj2 return obj elif linked_obj.isDerivedFrom("Part::Feature"): @@ -197,13 +212,12 @@ def getObject(ref): return None -def isBodySubObject(typeId): +def isBodySubObject(obj): return ( - typeId == "Sketcher::SketchObject" - or typeId == "PartDesign::Point" - or typeId == "PartDesign::Line" - or typeId == "PartDesign::Plane" - or typeId == "PartDesign::CoordinateSystem" + obj.isDerivedFrom("Sketcher::SketchObject") + or obj.isDerivedFrom("PartDesign::Datum") + or obj.isDerivedFrom("App::DatumElement") + or obj.isDerivedFrom("App::LocalCoordinateSystem") ) @@ -348,21 +362,6 @@ def getElementName(full_name): if parts[-1] in {"X", "Y", "Z", "Point", "Line", "Plane"}: return "" - # Case of origin objects - if parts[-1] == "": - if "X_Axis" in parts[-2]: - return "X_Axis" - if "Y_Axis" in parts[-2]: - return "Y_Axis" - if "Z_Axis" in parts[-2]: - return "Z_Axis" - if "XY_Plane" in parts[-2]: - return "XY_Plane" - if "XZ_Plane" in parts[-2]: - return "XZ_Plane" - if "YZ_Plane" in parts[-2]: - return "YZ_Plane" - return parts[-1] @@ -946,16 +945,8 @@ def findPlacement(ref, ignoreVertex=False): elt = getElementName(ref[1][0]) vtx = getElementName(ref[1][1]) - # case of origin objects. - if elt == "X_Axis" or elt == "YZ_Plane": - return App.Placement(App.Vector(), App.Rotation(App.Vector(0, 1, 0), -90)) - if elt == "Y_Axis" or elt == "XZ_Plane": - return App.Placement(App.Vector(), App.Rotation(App.Vector(1, 0, 0), 90)) - if elt == "Z_Axis" or elt == "XY_Plane": - return App.Placement() - if not elt or not vtx: - # case of whole parts such as PartDesign::Body or PartDesign::CordinateSystem/Point/Line/Plane. + # case of whole parts such as PartDesign::Body or App/PartDesign::CordinateSystem/Point/Line/Plane. return App.Placement() plc = App.Placement()