diff --git a/src/App/DocumentObject.h b/src/App/DocumentObject.h index af4867a5bd..8c5ae02155 100644 --- a/src/App/DocumentObject.h +++ b/src/App/DocumentObject.h @@ -442,6 +442,12 @@ public: /* Return true to cause PropertyView to show linked object's property */ virtual bool canLinkProperties() const {return true;} + /* Return whether this object is a link */ + virtual bool isLink() const {return false;}; + + /* Return whether this object is a link group */ + virtual bool isLinkGroup() const {return false;}; + /* Return true to bypass duplicate label checking */ virtual bool allowDuplicateLabel() const {return false;} diff --git a/src/App/GeoFeature.cpp b/src/App/GeoFeature.cpp index 03f3ccfe4f..2a09632199 100644 --- a/src/App/GeoFeature.cpp +++ b/src/App/GeoFeature.cpp @@ -25,6 +25,8 @@ #include +#include + #include "ComplexGeoData.h" #include "Document.h" #include "GeoFeature.h" @@ -270,3 +272,63 @@ GeoFeature::getHigherElements(const char *element, bool silent) const return {}; return prop->getComplexData()->getHigherElements(element, silent); } + +Base::Placement GeoFeature::getPlacementFromProp(App::DocumentObject* obj, const char* propName) +{ + Base::Placement plc = Base::Placement(); + auto* propPlacement = dynamic_cast(obj->getPropertyByName(propName)); + if (propPlacement) { + plc = propPlacement->getValue(); + } + return plc; +} + +Base::Placement GeoFeature::getGlobalPlacement(App::DocumentObject* targetObj, + App::DocumentObject* rootObj, + const std::string& sub) +{ + if (!targetObj || !rootObj || sub.empty()) { + return Base::Placement(); + } + std::vector names = Base::Tools::splitSubName(sub); + + App::Document* doc = rootObj->getDocument(); + Base::Placement plc = getPlacementFromProp(rootObj, "Placement"); + + if (targetObj == rootObj) return plc; + + for (auto& name : names) { + App::DocumentObject* obj = doc->getObject(name.c_str()); + if (!obj) { + return Base::Placement(); + } + + plc = plc * getPlacementFromProp(obj, "Placement"); + + if (obj == targetObj) { + return plc; + } + if (obj->isLink()) { + // Update doc in case its an external link. + doc = obj->getLinkedObject()->getDocument(); + } + } + + // If targetObj has not been found there's a problem + return Base::Placement(); +} + +Base::Placement GeoFeature::getGlobalPlacement(App::DocumentObject* targetObj, + App::PropertyXLinkSub* prop) +{ + if (!targetObj || !prop) { + return Base::Placement(); + } + + std::vector subs = prop->getSubValues(); + if (subs.empty()) { + return Base::Placement(); + } + + return getGlobalPlacement(targetObj, prop->getValue(), subs[0]); +} diff --git a/src/App/GeoFeature.h b/src/App/GeoFeature.h index 9c0443611f..216f741dcd 100644 --- a/src/App/GeoFeature.h +++ b/src/App/GeoFeature.h @@ -179,6 +179,10 @@ public: /// Return the higher level element names of the given element virtual std::vector getHigherElements(const char *name, bool silent=false) const; + static Base::Placement getPlacementFromProp(DocumentObject* obj, const char* propName); + static Base::Placement getGlobalPlacement(DocumentObject* targetObj, DocumentObject* rootObj, const std::string& sub); + static Base::Placement getGlobalPlacement(DocumentObject* targetObj, PropertyXLinkSub* prop); + protected: void onChanged(const Property* prop) override; // void onDocumentRestored() override; diff --git a/src/App/Link.cpp b/src/App/Link.cpp index 12fb793b9f..815787de66 100644 --- a/src/App/Link.cpp +++ b/src/App/Link.cpp @@ -2282,6 +2282,16 @@ bool Link::canLinkProperties() const { return true; } +bool Link::isLink() const +{ + return ElementCount.getValue() == 0; +} + +bool Link::isLinkGroup() const +{ + return ElementCount.getValue() > 0; +} + ////////////////////////////////////////////////////////////////////////////////////////// namespace App { @@ -2309,6 +2319,11 @@ bool LinkElement::canDelete() const { return !owner || !owner->getDocument()->getObjectByID(_LinkOwner.getValue()); } +bool LinkElement::isLink() const +{ + return true; +} + App::Link* LinkElement::getLinkGroup() const { std::vector inList = getInList(); diff --git a/src/App/Link.h b/src/App/Link.h index 4dcb433ee6..7cb879f75a 100644 --- a/src/App/Link.h +++ b/src/App/Link.h @@ -556,6 +556,10 @@ public: } bool canLinkProperties() const override; + + bool isLink() const override; + + bool isLinkGroup() const override; }; using LinkPython = App::FeaturePythonT; @@ -600,6 +604,8 @@ public: _handleChangedPropertyName(reader,TypeName,PropName); } + bool isLink() const override; + App::Link* getLinkGroup() const; }; diff --git a/src/Base/Tools.cpp b/src/Base/Tools.cpp index b164164489..eab37f5860 100644 --- a/src/Base/Tools.cpp +++ b/src/Base/Tools.cpp @@ -372,3 +372,24 @@ std::string Base::Tools::currentDateTimeString() .toString(Qt::ISODate) .toStdString(); } + +std::vector Base::Tools::splitSubName(const std::string& subname) +{ + // Turns 'Part.Part001.Body.Pad.Edge1' + // Into ['Part', 'Part001', 'Body', 'Pad', 'Edge1'] + std::vector subNames; + std::string subName; + std::istringstream subNameStream(subname); + while (std::getline(subNameStream, subName, '.')) { + subNames.push_back(subName); + } + + // Check if the last character of the input string is the delimiter. + // If so, add an empty string to the subNames vector. + // Because the last subname is the element name and can be empty. + if (!subname.empty() && subname.back() == '.') { + subNames.push_back(""); // Append empty string for trailing dot. + } + + return subNames; +} diff --git a/src/Base/Tools.h b/src/Base/Tools.h index b17907bbc4..e2018b9889 100644 --- a/src/Base/Tools.h +++ b/src/Base/Tools.h @@ -325,6 +325,8 @@ struct BaseExport Tools static std::string joinList(const std::vector& vec, const std::string& sep = ", "); static std::string currentDateTimeString(); + + static std::vector splitSubName(const std::string& subname); }; diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index 28a2692382..5e8c5412c2 100644 --- a/src/Gui/View3DInventorViewer.cpp +++ b/src/Gui/View3DInventorViewer.cpp @@ -127,9 +127,9 @@ #include "Utilities.h" -FC_LOG_LEVEL_INIT("3DViewer",true,true) +FC_LOG_LEVEL_INIT("3DViewer", true, true) -//#define FC_LOGGING_CB +// #define FC_LOGGING_CB using namespace Gui; @@ -3358,7 +3358,7 @@ void View3DInventorViewer::alignToSelection() return; } - const auto selection = Selection().getSelection(); + const auto selection = Selection().getSelection(nullptr, ResolveMode::NoResolve); // Empty selection if (selection.empty()) { @@ -3373,13 +3373,18 @@ void View3DInventorViewer::alignToSelection() // Get the geo feature App::GeoFeature* geoFeature = nullptr; App::ElementNamePair elementName; - App::GeoFeature::resolveElement(selection[0].pObject, selection[0].SubName, elementName, false, App::GeoFeature::ElementNameType::Normal, nullptr, nullptr, &geoFeature); + App::GeoFeature::resolveElement(selection[0].pObject, selection[0].SubName, elementName, true, App::GeoFeature::ElementNameType::Normal, nullptr, nullptr, &geoFeature); if (!geoFeature) { return; } + const auto globalPlacement = App::GeoFeature::getGlobalPlacement(selection[0].pResolvedObject, selection[0].pObject, elementName.oldName); + const auto rotation = globalPlacement.getRotation() * geoFeature->Placement.getValue().getRotation().inverse(); + const auto geoFeatureSubName = Base::Tools::splitSubName(elementName.oldName).back(); + Base::Vector3d direction; - if (geoFeature->getCameraAlignmentDirection(direction, selection[0].SubName)) { + if (geoFeature->getCameraAlignmentDirection(direction, geoFeatureSubName.c_str())) { + rotation.multVec(direction, direction); const auto orientation = SbRotation(SbVec3f(0, 0, 1), Base::convertTo(direction)); setCameraOrientation(orientation); } diff --git a/src/Mod/Assembly/App/AssemblyObject.cpp b/src/Mod/Assembly/App/AssemblyObject.cpp index 70c1c3c136..ce1a631a7d 100644 --- a/src/Mod/Assembly/App/AssemblyObject.cpp +++ b/src/Mod/Assembly/App/AssemblyObject.cpp @@ -111,39 +111,6 @@ static void printPlacement(Base::Placement plc, const char* name) angle); }*/ -static bool isLink(App::DocumentObject* obj) -{ - if (!obj) { - return false; - } - - auto* link = dynamic_cast(obj); - if (link) { - return link->ElementCount.getValue() == 0; - } - - auto* linkEl = dynamic_cast(obj); - if (linkEl) { - return true; - } - - return false; -} - -static bool isLinkGroup(App::DocumentObject* obj) -{ - if (!obj) { - return false; - } - - auto* link = dynamic_cast(obj); - if (link) { - return link->ElementCount.getValue() > 0; - } - - return false; -} - // ================================ Assembly Object ============================ PROPERTY_SOURCE(Assembly::AssemblyObject, App::Part) @@ -1733,7 +1700,7 @@ void AssemblyObject::ensureIdentityPlacements() std::vector group = Group.getValues(); for (auto* obj : group) { // When used in assembly, link groups must have identity placements. - if (isLinkGroup(obj)) { + if (obj->isLinkGroup()) { auto* link = dynamic_cast(obj); auto* pPlc = dynamic_cast(obj->getPropertyByName("Placement")); if (!pPlc || !link) { @@ -2078,6 +2045,7 @@ void AssemblyObject::setJointActivated(App::DocumentObject* joint, bool val) propActivated->setValue(val); } } + bool AssemblyObject::getJointActivated(App::DocumentObject* joint) { auto* propActivated = dynamic_cast(joint->getPropertyByName("Activated")); @@ -2087,65 +2055,6 @@ bool AssemblyObject::getJointActivated(App::DocumentObject* joint) return false; } -Base::Placement AssemblyObject::getPlacementFromProp(App::DocumentObject* obj, const char* propName) -{ - Base::Placement plc = Base::Placement(); - auto* propPlacement = dynamic_cast(obj->getPropertyByName(propName)); - if (propPlacement) { - plc = propPlacement->getValue(); - } - return plc; -} - - -Base::Placement AssemblyObject::getGlobalPlacement(App::DocumentObject* targetObj, - App::DocumentObject* rootObj, - const std::string& sub) -{ - if (!targetObj || !rootObj || sub == "") { - return Base::Placement(); - } - std::vector names = splitSubName(sub); - - App::Document* doc = rootObj->getDocument(); - Base::Placement plc = getPlacementFromProp(rootObj, "Placement"); - - for (auto& name : names) { - App::DocumentObject* obj = doc->getObject(name.c_str()); - if (!obj) { - return Base::Placement(); - } - - plc = plc * getPlacementFromProp(obj, "Placement"); - - if (obj == targetObj) { - return plc; - } - if (isLink(obj)) { - // Update doc in case its an external link. - doc = obj->getLinkedObject()->getDocument(); - } - } - - // If targetObj has not been found there's a problem - return Base::Placement(); -} - -Base::Placement AssemblyObject::getGlobalPlacement(App::DocumentObject* targetObj, - App::PropertyXLinkSub* prop) -{ - if (!targetObj || !prop) { - return Base::Placement(); - } - - std::vector subs = prop->getSubValues(); - if (subs.empty()) { - return Base::Placement(); - } - - return getGlobalPlacement(targetObj, prop->getValue(), subs[0]); -} - double AssemblyObject::getJointDistance(App::DocumentObject* joint) { double distance = 0.0; @@ -2193,7 +2102,7 @@ std::vector AssemblyObject::getSubAsList(App::PropertyXLinkSub* pro return {}; } - return splitSubName(subs[0]); + return Base::Tools::splitSubName(subs[0]); } std::vector AssemblyObject::getSubAsList(App::DocumentObject* obj, const char* pName) @@ -2203,27 +2112,6 @@ std::vector AssemblyObject::getSubAsList(App::DocumentObject* obj, return getSubAsList(prop); } -std::vector AssemblyObject::splitSubName(const std::string& sub) -{ - // Turns 'Part.Part001.Body.Pad.Edge1' - // Into ['Part', 'Part001','Body','Pad','Edge1'] - std::vector subNames; - std::string subName; - std::istringstream subNameStream(sub); - while (std::getline(subNameStream, subName, '.')) { - subNames.push_back(subName); - } - - // Check if the last character of the input string is the delimiter. - // If so, add an empty string to the subNames vector. - // Because the last subname is the element name and can be empty. - if (!sub.empty() && sub.back() == '.') { - subNames.push_back(""); // Append empty string for trailing dot. - } - - return subNames; -} - std::string AssemblyObject::getElementFromProp(App::DocumentObject* obj, const char* pName) { std::vector names = getSubAsList(obj, pName); @@ -2264,7 +2152,7 @@ App::DocumentObject* AssemblyObject::getObjFromRef(App::DocumentObject* obj, std App::Document* doc = obj->getDocument(); - std::vector names = splitSubName(sub); + std::vector names = Base::Tools::splitSubName(sub); // Lambda function to check if the typeId is a BodySubObject auto isBodySubObject = [](App::DocumentObject* obj) -> bool { @@ -2306,7 +2194,7 @@ App::DocumentObject* AssemblyObject::getObjFromRef(App::DocumentObject* obj, std return obj; } - if (obj->isDerivedFrom() || isLinkGroup(obj)) { + if (obj->isDerivedFrom() || obj->isLinkGroup()) { continue; } else if (obj->isDerivedFrom()) { @@ -2316,7 +2204,7 @@ App::DocumentObject* AssemblyObject::getObjFromRef(App::DocumentObject* obj, std // Primitive, fastener, gear, etc. return obj; } - else if (isLink(obj)) { + else if (obj->isLink()) { App::DocumentObject* linked_obj = obj->getLinkedObject(); if (linked_obj->isDerivedFrom()) { auto* retObj = handlePartDesignBody(linked_obj, it); @@ -2370,7 +2258,7 @@ App::DocumentObject* AssemblyObject::getMovingPartFromRef(App::DocumentObject* o App::Document* doc = obj->getDocument(); - std::vector names = splitSubName(sub); + std::vector names = Base::Tools::splitSubName(sub); names.insert(names.begin(), obj->getNameInDocument()); bool assemblyPassed = false; @@ -2381,7 +2269,7 @@ App::DocumentObject* AssemblyObject::getMovingPartFromRef(App::DocumentObject* o continue; } - if (isLink(obj)) { // update the document if necessary for next object + if (obj->isLink()) { // update the document if necessary for next object doc = obj->getLinkedObject()->getDocument(); } @@ -2398,7 +2286,7 @@ App::DocumentObject* AssemblyObject::getMovingPartFromRef(App::DocumentObject* o continue; // we ignore groups. } - if (isLinkGroup(obj)) { + if (obj->isLinkGroup()) { continue; } diff --git a/src/Mod/Assembly/App/AssemblyObject.h b/src/Mod/Assembly/App/AssemblyObject.h index 1615ffae31..4fc2185460 100644 --- a/src/Mod/Assembly/App/AssemblyObject.h +++ b/src/Mod/Assembly/App/AssemblyObject.h @@ -280,14 +280,6 @@ public: const char* propName); static std::vector getSubAsList(App::PropertyXLinkSub* prop); static std::vector getSubAsList(App::DocumentObject* joint, const char* propName); - static std::vector splitSubName(const std::string& subName); - static Base::Placement getPlacementFromProp(App::DocumentObject* obj, const char* propName); - - static Base::Placement getGlobalPlacement(App::DocumentObject* targetObj, - App::DocumentObject* rootObj, - const std::string& sub); - static Base::Placement getGlobalPlacement(App::DocumentObject* targetObj, - App::PropertyXLinkSub* prop); }; // using AssemblyObjectPython = App::FeaturePythonT; diff --git a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp index 834006cb84..45abfeffd8 100644 --- a/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp +++ b/src/Mod/Assembly/Gui/ViewProviderAssembly.cpp @@ -46,6 +46,8 @@ #include #include +#include + #include #include #include @@ -618,7 +620,7 @@ bool ViewProviderAssembly::getSelectedObjectsWithinAssembly(bool addPreselection std::vector objsSubNames = selObj.getSubNames(); for (auto& subNamesStr : objsSubNames) { - std::vector subNames = AssemblyObject::splitSubName(subNamesStr); + std::vector subNames = Base::Tools::splitSubName(subNamesStr); if (subNames.empty()) { continue; } @@ -764,7 +766,7 @@ ViewProviderAssembly::DragMode ViewProviderAssembly::findDragMode() const char* plcPropName = (pName == "Reference1") ? "Placement1" : "Placement2"; // jcsPlc is relative to the Object - jcsPlc = AssemblyObject::getPlacementFromProp(movingJoint, plcPropName); + jcsPlc = App::GeoFeature::getPlacementFromProp(movingJoint, plcPropName); // Make jcsGlobalPlc relative to the origin of the doc auto* ref = @@ -773,7 +775,7 @@ ViewProviderAssembly::DragMode ViewProviderAssembly::findDragMode() return DragMode::Translation; } auto* obj = assemblyPart->getObjFromRef(movingJoint, pName.c_str()); - Base::Placement global_plc = AssemblyObject::getGlobalPlacement(obj, ref); + Base::Placement global_plc = App::GeoFeature::getGlobalPlacement(obj, ref); jcsGlobalPlc = global_plc * jcsPlc; // Add downstream parts so that they move together @@ -920,7 +922,7 @@ void ViewProviderAssembly::initMoveDragger() App::DocumentObject* part = docsToMove[0].obj; draggerInitPlc = - AssemblyObject::getGlobalPlacement(part, docsToMove[0].rootObj, docsToMove[0].sub); + App::GeoFeature::getGlobalPlacement(part, docsToMove[0].rootObj, docsToMove[0].sub); std::vector listOfObjs; std::vector listOfRefs; for (auto& movingObj : docsToMove) { @@ -1130,11 +1132,11 @@ ViewProviderAssembly::getCenterOfBoundingBox(const std::vector& mo // bboxCenter does not take into account obj global placement Base::Placement plc(bboxCenter, Base::Rotation()); // Change plc to be relative to the object placement. - Base::Placement objPlc = AssemblyObject::getPlacementFromProp(movingObj.obj, "Placement"); + Base::Placement objPlc = App::GeoFeature::getPlacementFromProp(movingObj.obj, "Placement"); plc = objPlc.inverse() * plc; // Change plc to be relative to the origin of the document. Base::Placement global_plc = - AssemblyObject::getGlobalPlacement(movingObj.obj, movingObj.rootObj, movingObj.sub); + App::GeoFeature::getGlobalPlacement(movingObj.obj, movingObj.rootObj, movingObj.sub); plc = global_plc * plc; bboxCenter = plc.getPosition();