| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include <boost/core/ignore_unused.hpp> |
| | #include <cmath> |
| | #include <vector> |
| | #include <unordered_map> |
| |
|
| |
|
| | #include <App/Application.h> |
| | #include <App/Datums.h> |
| | #include <App/Document.h> |
| | #include <App/DocumentObjectGroup.h> |
| | #include <App/FeaturePythonPyImp.h> |
| | #include <App/Link.h> |
| | #include <App/PropertyPythonObject.h> |
| | #include <Base/Console.h> |
| | #include <Base/Placement.h> |
| | #include <Base/Rotation.h> |
| | #include <Base/Tools.h> |
| | #include <Base/Interpreter.h> |
| |
|
| | #include <Mod/Part/App/TopoShape.h> |
| | #include <Mod/Part/App/AttachExtension.h> |
| |
|
| | #include <OndselSolver/CREATE.h> |
| | #include <OndselSolver/ASMTSimulationParameters.h> |
| | #include <OndselSolver/ASMTAssembly.h> |
| | #include <OndselSolver/ASMTMarker.h> |
| | #include <OndselSolver/ASMTPart.h> |
| | #include <OndselSolver/ASMTJoint.h> |
| | #include <OndselSolver/ASMTAngleJoint.h> |
| | #include <OndselSolver/ASMTFixedJoint.h> |
| | #include <OndselSolver/ASMTGearJoint.h> |
| | #include <OndselSolver/ASMTRevoluteJoint.h> |
| | #include <OndselSolver/ASMTCylindricalJoint.h> |
| | #include <OndselSolver/ASMTTranslationalJoint.h> |
| | #include <OndselSolver/ASMTSphericalJoint.h> |
| | #include <OndselSolver/ASMTParallelAxesJoint.h> |
| | #include <OndselSolver/ASMTPerpendicularJoint.h> |
| | #include <OndselSolver/ASMTPointInPlaneJoint.h> |
| | #include <OndselSolver/ASMTPointInLineJoint.h> |
| | #include <OndselSolver/ASMTLineInPlaneJoint.h> |
| | #include <OndselSolver/ASMTPlanarJoint.h> |
| | #include <OndselSolver/ASMTRevCylJoint.h> |
| | #include <OndselSolver/ASMTCylSphJoint.h> |
| | #include <OndselSolver/ASMTRackPinionJoint.h> |
| | #include <OndselSolver/ASMTRotationLimit.h> |
| | #include <OndselSolver/ASMTTranslationLimit.h> |
| | #include <OndselSolver/ASMTRotationalMotion.h> |
| | #include <OndselSolver/ASMTTranslationalMotion.h> |
| | #include <OndselSolver/ASMTGeneralMotion.h> |
| | #include <OndselSolver/ASMTScrewJoint.h> |
| | #include <OndselSolver/ASMTSphSphJoint.h> |
| | #include <OndselSolver/ASMTTime.h> |
| | #include <OndselSolver/ASMTConstantGravity.h> |
| | #include <OndselSolver/ExternalSystem.h> |
| | #include <OndselSolver/enum.h> |
| |
|
| | #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) |
| |
|
| | using namespace Assembly; |
| | using namespace MbD; |
| |
|
| |
|
| | namespace PartApp = Part; |
| |
|
| |
|
| | |
| |
|
| | PROPERTY_SOURCE(Assembly::AssemblyObject, App::Part) |
| |
|
| | AssemblyObject::AssemblyObject() |
| | : mbdAssembly(std::make_shared<ASMTAssembly>()) |
| | , bundleFixed(false) |
| | , lastDoF(0) |
| | , lastHasConflict(false) |
| | , lastHasRedundancies(false) |
| | , lastHasPartialRedundancies(false) |
| | , lastHasMalformedConstraints(false) |
| | , lastSolverStatus(0) |
| | { |
| | mbdAssembly->externalSystem->freecadAssemblyObject = this; |
| |
|
| | lastDoF = numberOfComponents() * 6; |
| | signalSolverUpdate(); |
| | } |
| |
|
| | AssemblyObject::~AssemblyObject() = default; |
| |
|
| | PyObject* AssemblyObject::getPyObject() |
| | { |
| | if (PythonObject.is(Py::_None())) { |
| | |
| | PythonObject = Py::Object(new AssemblyObjectPy(this), true); |
| | } |
| | return Py::new_reference_to(PythonObject); |
| | } |
| |
|
| | App::DocumentObjectExecReturn* AssemblyObject::execute() |
| | { |
| | App::DocumentObjectExecReturn* ret = App::Part::execute(); |
| |
|
| | ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath( |
| | "User parameter:BaseApp/Preferences/Mod/Assembly" |
| | ); |
| | if (hGrp->GetBool("SolveOnRecompute", true)) { |
| | solve(); |
| | } |
| | return ret; |
| | } |
| |
|
| | int AssemblyObject::solve(bool enableRedo, bool updateJCS) |
| | { |
| | lastDoF = numberOfComponents() * 6; |
| |
|
| | ensureIdentityPlacements(); |
| |
|
| | mbdAssembly = makeMbdAssembly(); |
| | objectPartMap.clear(); |
| | motions.clear(); |
| |
|
| | auto groundedObjs = fixGroundedParts(); |
| | if (groundedObjs.empty()) { |
| | |
| | return -6; |
| | } |
| |
|
| | std::vector<App::DocumentObject*> joints = getJoints(updateJCS); |
| |
|
| | removeUnconnectedJoints(joints, groundedObjs); |
| |
|
| | jointParts(joints); |
| |
|
| | if (enableRedo) { |
| | savePlacementsForUndo(); |
| | } |
| |
|
| | try { |
| | mbdAssembly->runPreDrag(); |
| | } |
| | catch (const std::exception& e) { |
| | FC_ERR("Solve failed: " << e.what()); |
| | return -1; |
| | } |
| | catch (...) { |
| | FC_ERR("Solve failed: unhandled exception"); |
| | return -1; |
| | } |
| |
|
| | setNewPlacements(); |
| |
|
| | redrawJointPlacements(joints); |
| |
|
| | signalSolverUpdate(); |
| |
|
| | return 0; |
| | } |
| |
|
| | int AssemblyObject::generateSimulation(App::DocumentObject* sim) |
| | { |
| | mbdAssembly = makeMbdAssembly(); |
| | objectPartMap.clear(); |
| |
|
| | motions = getMotionsFromSimulation(sim); |
| |
|
| | auto groundedObjs = fixGroundedParts(); |
| | if (groundedObjs.empty()) { |
| | |
| | return -6; |
| | } |
| |
|
| | std::vector<App::DocumentObject*> joints = getJoints(); |
| |
|
| | removeUnconnectedJoints(joints, groundedObjs); |
| |
|
| | jointParts(joints); |
| |
|
| | create_mbdSimulationParameters(sim); |
| |
|
| | try { |
| | mbdAssembly->runKINEMATIC(); |
| | } |
| | catch (...) { |
| | Base::Console().error("Generation of simulation failed\n"); |
| | motions.clear(); |
| | return -1; |
| | } |
| |
|
| | motions.clear(); |
| |
|
| | return 0; |
| | } |
| |
|
| | std::vector<App::DocumentObject*> AssemblyObject::getMotionsFromSimulation(App::DocumentObject* sim) |
| | { |
| | if (!sim) { |
| | return {}; |
| | } |
| |
|
| | auto* prop = dynamic_cast<App::PropertyLinkList*>(sim->getPropertyByName("Group")); |
| | if (!prop) { |
| | return {}; |
| | } |
| |
|
| | return prop->getValue(); |
| | } |
| |
|
| | int Assembly::AssemblyObject::updateForFrame(size_t index, bool updateJCS) |
| | { |
| | if (!mbdAssembly) { |
| | return -1; |
| | } |
| |
|
| | auto nfrms = mbdAssembly->numberOfFrames(); |
| | if (index >= nfrms) { |
| | return -1; |
| | } |
| |
|
| | mbdAssembly->updateForFrame(index); |
| | setNewPlacements(); |
| | auto jointDocs = getJoints(updateJCS); |
| | redrawJointPlacements(jointDocs); |
| | return 0; |
| | } |
| |
|
| | size_t Assembly::AssemblyObject::numberOfFrames() |
| | { |
| | return mbdAssembly->numberOfFrames(); |
| | } |
| |
|
| | void AssemblyObject::preDrag(std::vector<App::DocumentObject*> dragParts) |
| | { |
| | bundleFixed = true; |
| | solve(); |
| | bundleFixed = false; |
| |
|
| | draggedParts.clear(); |
| | for (auto part : dragParts) { |
| | |
| | if (std::ranges::find(draggedParts, part) != draggedParts.end()) { |
| | continue; |
| | } |
| |
|
| | |
| | if (!isPartConnected(part)) { |
| | continue; |
| | } |
| |
|
| | |
| | Base::Placement plc; |
| | for (auto& pair : objectPartMap) { |
| | App::DocumentObject* parti = pair.first; |
| | if (parti != part) { |
| | continue; |
| | } |
| | plc = pair.second.offsetPlc; |
| | } |
| | if (!plc.isIdentity()) { |
| | |
| | |
| | |
| | continue; |
| | } |
| |
|
| | draggedParts.push_back(part); |
| | } |
| | } |
| |
|
| | void AssemblyObject::doDragStep() |
| | { |
| | try { |
| | std::vector<std::shared_ptr<MbD::ASMTPart>> dragMbdParts; |
| |
|
| | for (auto& part : draggedParts) { |
| | if (!part) { |
| | continue; |
| | } |
| |
|
| | auto mbdPart = getMbDPart(part); |
| | dragMbdParts.push_back(mbdPart); |
| |
|
| | |
| | Base::Placement plc = getPlacementFromProp(part, "Placement"); |
| | Base::Vector3d pos = plc.getPosition(); |
| | mbdPart->updateMbDFromPosition3D( |
| | std::make_shared<FullColumn<double>>(ListD {pos.x, pos.y, pos.z}) |
| | ); |
| |
|
| | |
| | Base::Rotation rot = plc.getRotation(); |
| | Base::Matrix4D mat; |
| | rot.getValue(mat); |
| | Base::Vector3d r0 = mat.getRow(0); |
| | Base::Vector3d r1 = mat.getRow(1); |
| | Base::Vector3d r2 = mat.getRow(2); |
| | mbdPart->updateMbDFromRotationMatrix(r0.x, r0.y, r0.z, r1.x, r1.y, r1.z, r2.x, r2.y, r2.z); |
| | } |
| |
|
| | |
| | auto dragPartsVec = std::make_shared<std::vector<std::shared_ptr<ASMTPart>>>(dragMbdParts); |
| | mbdAssembly->runDragStep(dragPartsVec); |
| |
|
| | |
| | if (validateNewPlacements()) { |
| | setNewPlacements(); |
| |
|
| | auto joints = getJoints(false); |
| | for (auto* joint : joints) { |
| | if (joint->Visibility.getValue()) { |
| | |
| | redrawJointPlacement(joint); |
| | } |
| | } |
| | } |
| | } |
| | catch (...) { |
| | |
| | } |
| | } |
| |
|
| | Base::Placement AssemblyObject::getMbdPlacement(std::shared_ptr<ASMTPart> mbdPart) |
| | { |
| | if (!mbdPart) { |
| | return Base::Placement(); |
| | } |
| |
|
| | double x, y, z; |
| | mbdPart->getPosition3D(x, y, z); |
| | Base::Vector3d pos = Base::Vector3d(x, y, z); |
| |
|
| | double q0, q1, q2, q3; |
| | mbdPart->getQuarternions(q3, q0, q1, q2); |
| | Base::Rotation rot = Base::Rotation(q0, q1, q2, q3); |
| |
|
| | return Base::Placement(pos, rot); |
| | } |
| |
|
| | bool AssemblyObject::validateNewPlacements() |
| | { |
| | |
| | auto groundedParts = getGroundedParts(); |
| | for (auto* obj : groundedParts) { |
| | auto* propPlacement = dynamic_cast<App::PropertyPlacement*>( |
| | obj->getPropertyByName("Placement") |
| | ); |
| | if (propPlacement) { |
| | Base::Placement oldPlc = propPlacement->getValue(); |
| |
|
| | auto it = objectPartMap.find(obj); |
| | if (it != objectPartMap.end()) { |
| | std::shared_ptr<MbD::ASMTPart> mbdPart = it->second.part; |
| | Base::Placement newPlacement = getMbdPlacement(mbdPart); |
| | if (!it->second.offsetPlc.isIdentity()) { |
| | newPlacement = newPlacement * it->second.offsetPlc; |
| | } |
| |
|
| | if (!oldPlc.isSame(newPlacement, Precision::Confusion())) { |
| | Base::Console().warning( |
| | "Assembly : Ignoring bad solve, a grounded object (%s) moved.\n", |
| | obj->getFullLabel() |
| | ); |
| | return false; |
| | } |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | return true; |
| | } |
| |
|
| | void AssemblyObject::postDrag() |
| | { |
| | mbdAssembly->runPostDrag(); |
| | } |
| |
|
| | void AssemblyObject::savePlacementsForUndo() |
| | { |
| | previousPositions.clear(); |
| |
|
| | for (auto& pair : objectPartMap) { |
| | App::DocumentObject* obj = pair.first; |
| | if (!obj) { |
| | continue; |
| | } |
| |
|
| | std::pair<App::DocumentObject*, Base::Placement> savePair; |
| | savePair.first = obj; |
| |
|
| | |
| | auto* propPlc = dynamic_cast<App::PropertyPlacement*>(obj->getPropertyByName("Placement")); |
| | if (!propPlc) { |
| | continue; |
| | } |
| | savePair.second = propPlc->getValue(); |
| |
|
| | previousPositions.push_back(savePair); |
| | } |
| | } |
| |
|
| | void AssemblyObject::undoSolve() |
| | { |
| | if (previousPositions.size() == 0) { |
| | return; |
| | } |
| |
|
| | for (auto& pair : previousPositions) { |
| | App::DocumentObject* obj = pair.first; |
| | if (!obj) { |
| | continue; |
| | } |
| |
|
| | |
| | auto* propPlacement = dynamic_cast<App::PropertyPlacement*>( |
| | obj->getPropertyByName("Placement") |
| | ); |
| | if (!propPlacement) { |
| | continue; |
| | } |
| |
|
| | propPlacement->setValue(pair.second); |
| | } |
| | previousPositions.clear(); |
| |
|
| | |
| | getJoints( true, false); |
| | } |
| |
|
| | void AssemblyObject::clearUndo() |
| | { |
| | previousPositions.clear(); |
| | } |
| |
|
| | void AssemblyObject::exportAsASMT(std::string fileName) |
| | { |
| | mbdAssembly = makeMbdAssembly(); |
| | objectPartMap.clear(); |
| | fixGroundedParts(); |
| |
|
| | std::vector<App::DocumentObject*> joints = getJoints(); |
| |
|
| | jointParts(joints); |
| |
|
| | mbdAssembly->outputFile(fileName); |
| | } |
| |
|
| | void AssemblyObject::setNewPlacements() |
| | { |
| | for (auto& pair : objectPartMap) { |
| | App::DocumentObject* obj = pair.first; |
| | std::shared_ptr<ASMTPart> mbdPart = pair.second.part; |
| |
|
| | if (!obj || !mbdPart) { |
| | continue; |
| | } |
| |
|
| | |
| | auto* propPlacement = dynamic_cast<App::PropertyPlacement*>( |
| | obj->getPropertyByName("Placement") |
| | ); |
| | if (!propPlacement) { |
| | continue; |
| | } |
| |
|
| |
|
| | Base::Placement newPlacement = getMbdPlacement(mbdPart); |
| | if (!pair.second.offsetPlc.isIdentity()) { |
| | newPlacement = newPlacement * pair.second.offsetPlc; |
| | } |
| | if (!propPlacement->getValue().isSame(newPlacement)) { |
| | propPlacement->setValue(newPlacement); |
| | obj->purgeTouched(); |
| | } |
| | } |
| | } |
| |
|
| | void AssemblyObject::redrawJointPlacements(std::vector<App::DocumentObject*> joints) |
| | { |
| | |
| | for (auto* joint : joints) { |
| | if (!joint) { |
| | continue; |
| | } |
| | redrawJointPlacement(joint); |
| | } |
| | } |
| |
|
| | void AssemblyObject::redrawJointPlacement(App::DocumentObject* joint) |
| | { |
| | if (!joint) { |
| | return; |
| | } |
| |
|
| | |
| | auto* pPlc = dynamic_cast<App::PropertyPlacement*>(joint->getPropertyByName("Placement1")); |
| | if (pPlc) { |
| | pPlc->setValue(pPlc->getValue()); |
| | } |
| | pPlc = dynamic_cast<App::PropertyPlacement*>(joint->getPropertyByName("Placement2")); |
| | if (pPlc) { |
| | pPlc->setValue(pPlc->getValue()); |
| | } |
| | joint->purgeTouched(); |
| | } |
| |
|
| | void AssemblyObject::recomputeJointPlacements(std::vector<App::DocumentObject*> joints) |
| | { |
| | |
| | Base::PyGILStateLocker lock; |
| | for (auto* joint : joints) { |
| | if (!joint) { |
| | continue; |
| | } |
| |
|
| | App::PropertyPythonObject* proxy = joint |
| | ? dynamic_cast<App::PropertyPythonObject*>(joint->getPropertyByName("Proxy")) |
| | : nullptr; |
| |
|
| | if (!proxy) { |
| | continue; |
| | } |
| |
|
| | Py::Object jointPy = proxy->getValue(); |
| |
|
| | if (!jointPy.hasAttr("updateJCSPlacements")) { |
| | continue; |
| | } |
| |
|
| | Py::Object attr = jointPy.getAttr("updateJCSPlacements"); |
| | if (attr.ptr() && attr.isCallable()) { |
| | Py::Tuple args(1); |
| | args.setItem(0, Py::asObject(joint->getPyObject())); |
| | Py::Callable(attr).apply(args); |
| | joint->purgeTouched(); |
| | } |
| | } |
| | } |
| |
|
| | std::shared_ptr<ASMTAssembly> AssemblyObject::makeMbdAssembly() |
| | { |
| | auto assembly = CREATE<ASMTAssembly>::With(); |
| | assembly->externalSystem->freecadAssemblyObject = this; |
| | assembly->setName("OndselAssembly"); |
| |
|
| | ParameterGrp::handle hPgr = App::GetApplication().GetParameterGroupByPath( |
| | "User parameter:BaseApp/Preferences/Mod/Assembly" |
| | ); |
| |
|
| | assembly->setDebug(hPgr->GetBool("LogSolverDebug", false)); |
| | return assembly; |
| | } |
| |
|
| | App::DocumentObject* AssemblyObject::getJointOfPartConnectingToGround( |
| | App::DocumentObject* part, |
| | std::string& name, |
| | const std::vector<App::DocumentObject*>& excludeJoints |
| | ) |
| | { |
| | if (!part) { |
| | return nullptr; |
| | } |
| |
|
| | std::vector<App::DocumentObject*> joints = getJointsOfPart(part); |
| |
|
| | for (auto joint : joints) { |
| | if (!joint) { |
| | continue; |
| | } |
| |
|
| | if (std::ranges::find(excludeJoints, joint) != excludeJoints.end()) { |
| | continue; |
| | } |
| |
|
| | App::DocumentObject* part1 = getMovingPartFromRef(this, joint, "Reference1"); |
| | App::DocumentObject* part2 = getMovingPartFromRef(this, joint, "Reference2"); |
| | if (!part1 || !part2) { |
| | continue; |
| | } |
| |
|
| | if (part == part1 && isJointConnectingPartToGround(joint, "Reference1")) { |
| | name = "Reference1"; |
| | return joint; |
| | } |
| | if (part == part2 && isJointConnectingPartToGround(joint, "Reference2")) { |
| | name = "Reference2"; |
| | return joint; |
| | } |
| | } |
| | return nullptr; |
| | } |
| |
|
| | template<typename T> |
| | T* AssemblyObject::getGroup() |
| | { |
| | App::Document* doc = getDocument(); |
| |
|
| | std::vector<DocumentObject*> groups = doc->getObjectsOfType(T::getClassTypeId()); |
| | if (groups.empty()) { |
| | return nullptr; |
| | } |
| | for (auto group : groups) { |
| | if (hasObject(group)) { |
| | return freecad_cast<T*>(group); |
| | } |
| | } |
| | return nullptr; |
| | } |
| |
|
| | JointGroup* AssemblyObject::getJointGroup() const |
| | { |
| | return Assembly::getJointGroup(this); |
| | } |
| |
|
| | ViewGroup* AssemblyObject::getExplodedViewGroup() const |
| | { |
| | App::Document* doc = getDocument(); |
| |
|
| | std::vector<DocumentObject*> viewGroups = doc->getObjectsOfType(ViewGroup::getClassTypeId()); |
| | if (viewGroups.empty()) { |
| | return nullptr; |
| | } |
| | for (auto viewGroup : viewGroups) { |
| | if (hasObject(viewGroup)) { |
| | return freecad_cast<ViewGroup*>(viewGroup); |
| | } |
| | } |
| | return nullptr; |
| | } |
| |
|
| | std::vector<App::DocumentObject*> AssemblyObject::getJoints(bool updateJCS, bool delBadJoints, bool subJoints) |
| | { |
| | std::vector<App::DocumentObject*> joints = {}; |
| |
|
| | JointGroup* jointGroup = getJointGroup(); |
| | if (!jointGroup) { |
| | return {}; |
| | } |
| |
|
| | Base::PyGILStateLocker lock; |
| | for (auto joint : jointGroup->getObjects()) { |
| | if (!joint) { |
| | continue; |
| | } |
| |
|
| | auto* prop = dynamic_cast<App::PropertyBool*>(joint->getPropertyByName("Suppressed")); |
| | if (joint->isError() || !prop || prop->getValue()) { |
| | |
| | continue; |
| | } |
| |
|
| | auto* part1 = getMovingPartFromRef(this, joint, "Reference1"); |
| | auto* part2 = getMovingPartFromRef(this, joint, "Reference2"); |
| | if (!part1 || !part2 || part1->getFullName() == part2->getFullName()) { |
| | |
| | |
| | if (delBadJoints) { |
| | getDocument()->removeObject(joint->getNameInDocument()); |
| | } |
| | continue; |
| | } |
| |
|
| | auto proxy = dynamic_cast<App::PropertyPythonObject*>(joint->getPropertyByName("Proxy")); |
| | if (proxy) { |
| | if (proxy->getValue().hasAttr("setJointConnectors")) { |
| | joints.push_back(joint); |
| | } |
| | } |
| | } |
| |
|
| | |
| | if (subJoints) { |
| | for (auto& assembly : getSubAssemblies()) { |
| | auto subJoints = assembly->getJoints(); |
| | joints.insert(joints.end(), subJoints.begin(), subJoints.end()); |
| | } |
| | } |
| |
|
| | |
| | if (updateJCS) { |
| | recomputeJointPlacements(joints); |
| | } |
| |
|
| | return joints; |
| | } |
| |
|
| | std::vector<App::DocumentObject*> AssemblyObject::getGroundedJoints() |
| | { |
| | std::vector<App::DocumentObject*> joints = {}; |
| |
|
| | JointGroup* jointGroup = getJointGroup(); |
| | if (!jointGroup) { |
| | return {}; |
| | } |
| |
|
| | Base::PyGILStateLocker lock; |
| | for (auto obj : jointGroup->getObjects()) { |
| | if (!obj) { |
| | continue; |
| | } |
| |
|
| | auto* propObj = dynamic_cast<App::PropertyLink*>(obj->getPropertyByName("ObjectToGround")); |
| |
|
| | if (propObj) { |
| | joints.push_back(obj); |
| | } |
| | } |
| |
|
| | return joints; |
| | } |
| |
|
| | std::vector<App::DocumentObject*> AssemblyObject::getJointsOfObj(App::DocumentObject* obj) |
| | { |
| | if (!obj) { |
| | return {}; |
| | } |
| |
|
| | std::vector<App::DocumentObject*> joints = getJoints(false); |
| | std::vector<App::DocumentObject*> jointsOf; |
| |
|
| | for (auto joint : joints) { |
| | App::DocumentObject* obj1 = getObjFromRef(joint, "Reference1"); |
| | App::DocumentObject* obj2 = getObjFromRef(joint, "Reference2"); |
| | if (obj == obj1 || obj == obj2) { |
| | jointsOf.push_back(joint); |
| | } |
| | } |
| |
|
| | return jointsOf; |
| | } |
| |
|
| | std::vector<App::DocumentObject*> AssemblyObject::getJointsOfPart(App::DocumentObject* part) |
| | { |
| | if (!part) { |
| | return {}; |
| | } |
| |
|
| | std::vector<App::DocumentObject*> joints = getJoints(false); |
| | std::vector<App::DocumentObject*> jointsOf; |
| |
|
| | for (auto joint : joints) { |
| | App::DocumentObject* part1 = getMovingPartFromRef(this, joint, "Reference1"); |
| | App::DocumentObject* part2 = getMovingPartFromRef(this, joint, "Reference2"); |
| | if (part == part1 || part == part2) { |
| | jointsOf.push_back(joint); |
| | } |
| | } |
| | return jointsOf; |
| | } |
| |
|
| | std::unordered_set<App::DocumentObject*> AssemblyObject::getGroundedParts() |
| | { |
| | std::vector<App::DocumentObject*> groundedJoints = getGroundedJoints(); |
| |
|
| | std::unordered_set<App::DocumentObject*> groundedSet; |
| | for (auto gJoint : groundedJoints) { |
| | if (!gJoint) { |
| | continue; |
| | } |
| |
|
| | auto* propObj = dynamic_cast<App::PropertyLink*>(gJoint->getPropertyByName("ObjectToGround")); |
| |
|
| | if (propObj) { |
| | App::DocumentObject* objToGround = propObj->getValue(); |
| | if (objToGround) { |
| | if (auto* asmLink = dynamic_cast<AssemblyLink*>(objToGround)) { |
| | if (!asmLink->isRigid()) { |
| | continue; |
| | } |
| | } |
| | groundedSet.insert(objToGround); |
| | } |
| | } |
| | } |
| |
|
| | |
| | std::vector<App::DocumentObject*> objs = Group.getValues(); |
| | for (auto* obj : objs) { |
| | if (obj->isDerivedFrom<App::LocalCoordinateSystem>() |
| | || obj->isDerivedFrom<App::DatumElement>()) { |
| | auto* pcAttach = obj->getExtensionByType<PartApp::AttachExtension>(); |
| | if (pcAttach) { |
| | |
| | std::string mode = pcAttach->MapMode.getValueAsString(); |
| | if (mode != "Deactivated") { |
| | continue; |
| | } |
| | } |
| | groundedSet.insert(obj); |
| | } |
| | } |
| |
|
| | |
| | groundedSet.insert(Origin.getValue()); |
| |
|
| | return groundedSet; |
| | } |
| |
|
| | std::unordered_set<App::DocumentObject*> AssemblyObject::fixGroundedParts() |
| | { |
| | auto groundedParts = getGroundedParts(); |
| |
|
| | for (auto obj : groundedParts) { |
| | if (!obj) { |
| | continue; |
| | } |
| |
|
| | Base::Placement plc = getPlacementFromProp(obj, "Placement"); |
| | std::string str = obj->getFullName(); |
| | fixGroundedPart(obj, plc, str); |
| | } |
| | return groundedParts; |
| | } |
| |
|
| | void AssemblyObject::fixGroundedPart(App::DocumentObject* obj, Base::Placement& plc, std::string& name) |
| | { |
| | if (!obj) { |
| | return; |
| | } |
| |
|
| | std::string markerName1 = "marker-" + obj->getFullName(); |
| | auto mbdMarker1 = makeMbdMarker(markerName1, plc); |
| | mbdAssembly->addMarker(mbdMarker1); |
| |
|
| | std::shared_ptr<ASMTPart> mbdPart = getMbDPart(obj); |
| |
|
| | std::string markerName2 = "FixingMarker"; |
| | Base::Placement basePlc = Base::Placement(); |
| | auto mbdMarker2 = makeMbdMarker(markerName2, basePlc); |
| | mbdPart->addMarker(mbdMarker2); |
| |
|
| | markerName1 = "/OndselAssembly/" + mbdMarker1->name; |
| | markerName2 = "/OndselAssembly/" + mbdPart->name + "/" + mbdMarker2->name; |
| |
|
| | auto mbdJoint = CREATE<ASMTFixedJoint>::With(); |
| | mbdJoint->setName(name); |
| | mbdJoint->setMarkerI(markerName1); |
| | mbdJoint->setMarkerJ(markerName2); |
| |
|
| | mbdAssembly->addJoint(mbdJoint); |
| | } |
| |
|
| | bool AssemblyObject::isJointConnectingPartToGround(App::DocumentObject* joint, const char* propname) |
| | { |
| | if (!joint || !isJointTypeConnecting(joint)) { |
| | return false; |
| | } |
| |
|
| | App::DocumentObject* part = getMovingPartFromRef(this, joint, propname); |
| | if (!part) { |
| | return false; |
| | } |
| |
|
| | |
| | bool isGrounded = isPartGrounded(part); |
| | if (isGrounded) { |
| | return false; |
| | } |
| |
|
| | |
| | bool isConnected = isPartConnected(part); |
| | if (!isConnected) { |
| | return false; |
| | } |
| |
|
| | |
| | std::vector<App::DocumentObject*> jointsOfPart = getJointsOfPart(part); |
| | std::vector<bool> activatedStates; |
| |
|
| | for (auto jointi : jointsOfPart) { |
| | if (jointi->getFullName() == joint->getFullName()) { |
| | continue; |
| | } |
| |
|
| | activatedStates.push_back(getJointActivated(jointi)); |
| | setJointActivated(jointi, false); |
| | } |
| |
|
| | isConnected = isPartConnected(part); |
| |
|
| | |
| | for (auto jointi : jointsOfPart) { |
| | if (jointi->getFullName() == joint->getFullName() || activatedStates.empty()) { |
| | continue; |
| | } |
| |
|
| | setJointActivated(jointi, activatedStates[0]); |
| | activatedStates.erase(activatedStates.begin()); |
| | } |
| |
|
| | return isConnected; |
| | } |
| |
|
| | bool AssemblyObject::isJointTypeConnecting(App::DocumentObject* joint) |
| | { |
| | if (!joint) { |
| | return false; |
| | } |
| |
|
| | JointType jointType = getJointType(joint); |
| | return jointType != JointType::RackPinion && jointType != JointType::Screw |
| | && jointType != JointType::Gears && jointType != JointType::Belt; |
| | } |
| |
|
| |
|
| | bool AssemblyObject::isObjInSetOfObjRefs(App::DocumentObject* obj, const std::vector<ObjRef>& set) |
| | { |
| | if (!obj) { |
| | return false; |
| | } |
| |
|
| | for (const auto& pair : set) { |
| | if (pair.obj == obj) { |
| | return true; |
| | } |
| | } |
| | return false; |
| | } |
| |
|
| | void AssemblyObject::removeUnconnectedJoints( |
| | std::vector<App::DocumentObject*>& joints, |
| | std::unordered_set<App::DocumentObject*> groundedObjs |
| | ) |
| | { |
| | std::vector<ObjRef> connectedParts; |
| |
|
| | |
| | for (auto* groundedObj : groundedObjs) { |
| | connectedParts.push_back({groundedObj, nullptr}); |
| | } |
| |
|
| | |
| | for (auto* groundedObj : groundedObjs) { |
| | traverseAndMarkConnectedParts(groundedObj, connectedParts, joints); |
| | } |
| |
|
| | |
| | joints.erase( |
| | std::remove_if( |
| | joints.begin(), |
| | joints.end(), |
| | [&](App::DocumentObject* joint) { |
| | App::DocumentObject* obj1 = getMovingPartFromRef(this, joint, "Reference1"); |
| | App::DocumentObject* obj2 = getMovingPartFromRef(this, joint, "Reference2"); |
| | return ( |
| | !isObjInSetOfObjRefs(obj1, connectedParts) |
| | || !isObjInSetOfObjRefs(obj2, connectedParts) |
| | ); |
| | } |
| | ), |
| | joints.end() |
| | ); |
| | } |
| |
|
| | void AssemblyObject::traverseAndMarkConnectedParts( |
| | App::DocumentObject* currentObj, |
| | std::vector<ObjRef>& connectedParts, |
| | const std::vector<App::DocumentObject*>& joints |
| | ) |
| | { |
| | |
| | auto connectedObjs = getConnectedParts(currentObj, joints); |
| | for (auto& nextObjRef : connectedObjs) { |
| | if (!isObjInSetOfObjRefs(nextObjRef.obj, connectedParts)) { |
| | |
| | connectedParts.push_back(nextObjRef); |
| | traverseAndMarkConnectedParts(nextObjRef.obj, connectedParts, joints); |
| | } |
| | } |
| | } |
| |
|
| | std::vector<ObjRef> AssemblyObject::getConnectedParts( |
| | App::DocumentObject* part, |
| | const std::vector<App::DocumentObject*>& joints |
| | ) |
| | { |
| | if (!part) { |
| | return {}; |
| | } |
| |
|
| | std::vector<ObjRef> connectedParts; |
| |
|
| | for (auto joint : joints) { |
| | if (!isJointTypeConnecting(joint)) { |
| | continue; |
| | } |
| |
|
| | App::DocumentObject* obj1 = getMovingPartFromRef(this, joint, "Reference1"); |
| | App::DocumentObject* obj2 = getMovingPartFromRef(this, joint, "Reference2"); |
| |
|
| | if (!obj1 || !obj2) { |
| | continue; |
| | } |
| |
|
| | if (obj1 == part) { |
| | auto* ref = dynamic_cast<App::PropertyXLinkSub*>(joint->getPropertyByName("Reference2")); |
| | if (!ref) { |
| | continue; |
| | } |
| | connectedParts.push_back({obj2, ref}); |
| | } |
| | else if (obj2 == part) { |
| | auto* ref = dynamic_cast<App::PropertyXLinkSub*>(joint->getPropertyByName("Reference1")); |
| | if (!ref) { |
| | continue; |
| | } |
| | connectedParts.push_back({obj1, ref}); |
| | } |
| | } |
| | return connectedParts; |
| | } |
| |
|
| | bool AssemblyObject::isPartGrounded(App::DocumentObject* obj) |
| | { |
| | if (!obj) { |
| | return false; |
| | } |
| |
|
| | auto groundedObjs = getGroundedParts(); |
| |
|
| | for (auto* groundedObj : groundedObjs) { |
| | if (groundedObj->getFullName() == obj->getFullName()) { |
| | return true; |
| | } |
| | } |
| |
|
| | return false; |
| | } |
| |
|
| | bool AssemblyObject::isPartConnected(App::DocumentObject* obj) |
| | { |
| | if (!obj) { |
| | return false; |
| | } |
| |
|
| | auto groundedObjs = getGroundedParts(); |
| | std::vector<App::DocumentObject*> joints = getJoints(false); |
| |
|
| | std::vector<ObjRef> connectedParts; |
| |
|
| | |
| | for (auto* groundedObj : groundedObjs) { |
| | connectedParts.push_back({groundedObj, nullptr}); |
| | } |
| |
|
| | |
| | for (auto* groundedObj : groundedObjs) { |
| | traverseAndMarkConnectedParts(groundedObj, connectedParts, joints); |
| | } |
| |
|
| | for (auto& objRef : connectedParts) { |
| | if (obj == objRef.obj) { |
| | return true; |
| | } |
| | } |
| |
|
| | return false; |
| | } |
| |
|
| | void AssemblyObject::jointParts(std::vector<App::DocumentObject*> joints) |
| | { |
| | for (auto* joint : joints) { |
| | if (!joint) { |
| | continue; |
| | } |
| |
|
| | std::vector<std::shared_ptr<MbD::ASMTJoint>> mbdJoints = makeMbdJoint(joint); |
| | for (auto& mbdJoint : mbdJoints) { |
| | mbdAssembly->addJoint(mbdJoint); |
| | } |
| | } |
| | } |
| |
|
| | void Assembly::AssemblyObject::create_mbdSimulationParameters(App::DocumentObject* sim) |
| | { |
| | auto mbdSim = mbdAssembly->simulationParameters; |
| | if (!sim) { |
| | return; |
| | } |
| | auto valueOf = [](DocumentObject* docObj, const char* propName) { |
| | auto* prop = dynamic_cast<App::PropertyFloat*>(docObj->getPropertyByName(propName)); |
| | if (!prop) { |
| | return 0.0; |
| | } |
| | return prop->getValue(); |
| | }; |
| | mbdSim->settstart(valueOf(sim, "aTimeStart")); |
| | mbdSim->settend(valueOf(sim, "bTimeEnd")); |
| | mbdSim->sethout(valueOf(sim, "cTimeStepOutput")); |
| | mbdSim->sethmin(1.0e-9); |
| | mbdSim->sethmax(1.0); |
| | mbdSim->seterrorTol(valueOf(sim, "fGlobalErrorTolerance")); |
| | } |
| |
|
| | std::shared_ptr<ASMTJoint> AssemblyObject::makeMbdJointOfType(App::DocumentObject* joint, JointType type) |
| | { |
| | switch (type) { |
| | case JointType::Fixed: |
| | if (bundleFixed) { |
| | return nullptr; |
| | } |
| | return CREATE<ASMTFixedJoint>::With(); |
| |
|
| | case JointType::Revolute: |
| | return CREATE<ASMTRevoluteJoint>::With(); |
| |
|
| | case JointType::Cylindrical: |
| | return CREATE<ASMTCylindricalJoint>::With(); |
| |
|
| | case JointType::Slider: |
| | return CREATE<ASMTTranslationalJoint>::With(); |
| |
|
| | case JointType::Ball: |
| | return CREATE<ASMTSphericalJoint>::With(); |
| |
|
| | case JointType::Distance: |
| | return makeMbdJointDistance(joint); |
| |
|
| | case JointType::Parallel: |
| | return CREATE<ASMTParallelAxesJoint>::With(); |
| |
|
| | case JointType::Perpendicular: |
| | return CREATE<ASMTPerpendicularJoint>::With(); |
| |
|
| | case JointType::Angle: { |
| | double angle = fabs(Base::toRadians(getJointAngle(joint))); |
| | if (fmod(angle, 2 * std::numbers::pi) < Precision::Confusion()) { |
| | return CREATE<ASMTParallelAxesJoint>::With(); |
| | } |
| | auto mbdJoint = CREATE<ASMTAngleJoint>::With(); |
| | mbdJoint->theIzJz = angle; |
| | return mbdJoint; |
| | } |
| |
|
| | case JointType::RackPinion: { |
| | auto mbdJoint = CREATE<ASMTRackPinionJoint>::With(); |
| | mbdJoint->pitchRadius = getJointDistance(joint); |
| | return mbdJoint; |
| | } |
| |
|
| | case JointType::Screw: { |
| | int slidingIndex = slidingPartIndex(joint); |
| | if (slidingIndex == 0) { |
| | return nullptr; |
| | } |
| |
|
| | if (slidingIndex != 1) { |
| | swapJCS(joint); |
| | } |
| |
|
| | auto mbdJoint = CREATE<ASMTScrewJoint>::With(); |
| | mbdJoint->pitch = getJointDistance(joint); |
| | return mbdJoint; |
| | } |
| |
|
| | case JointType::Gears: { |
| | auto mbdJoint = CREATE<ASMTGearJoint>::With(); |
| | mbdJoint->radiusI = getJointDistance(joint); |
| | mbdJoint->radiusJ = getJointDistance2(joint); |
| | return mbdJoint; |
| | } |
| |
|
| | case JointType::Belt: { |
| | auto mbdJoint = CREATE<ASMTGearJoint>::With(); |
| | mbdJoint->radiusI = getJointDistance(joint); |
| | mbdJoint->radiusJ = -getJointDistance2(joint); |
| | return mbdJoint; |
| | } |
| |
|
| | default: |
| | return nullptr; |
| | } |
| | } |
| |
|
| | std::shared_ptr<ASMTJoint> AssemblyObject::makeMbdJointDistance(App::DocumentObject* joint) |
| | { |
| | DistanceType type = getDistanceType(joint); |
| |
|
| | std::string elt1 = getElementFromProp(joint, "Reference1"); |
| | std::string elt2 = getElementFromProp(joint, "Reference2"); |
| | auto* obj1 = getLinkedObjFromRef(joint, "Reference1"); |
| | auto* obj2 = getLinkedObjFromRef(joint, "Reference2"); |
| |
|
| | switch (type) { |
| | case DistanceType::PointPoint: { |
| | |
| | double distance = getJointDistance(joint); |
| | if (distance < Precision::Confusion()) { |
| | return CREATE<ASMTSphericalJoint>::With(); |
| | } |
| | auto mbdJoint = CREATE<ASMTSphSphJoint>::With(); |
| | mbdJoint->distanceIJ = distance; |
| | return mbdJoint; |
| | } |
| |
|
| | |
| | case DistanceType::LineLine: { |
| | auto mbdJoint = CREATE<ASMTRevCylJoint>::With(); |
| | mbdJoint->distanceIJ = getJointDistance(joint); |
| | return mbdJoint; |
| | } |
| |
|
| | case DistanceType::LineCircle: { |
| | auto mbdJoint = CREATE<ASMTRevCylJoint>::With(); |
| | mbdJoint->distanceIJ = getJointDistance(joint) + getEdgeRadius(obj2, elt2); |
| | return mbdJoint; |
| | } |
| |
|
| | case DistanceType::CircleCircle: { |
| | auto mbdJoint = CREATE<ASMTRevCylJoint>::With(); |
| | mbdJoint->distanceIJ = getJointDistance(joint) + getEdgeRadius(obj1, elt1) |
| | + getEdgeRadius(obj2, elt2); |
| | return mbdJoint; |
| | } |
| |
|
| | |
| | case DistanceType::PlanePlane: { |
| | auto mbdJoint = CREATE<ASMTPlanarJoint>::With(); |
| | mbdJoint->offset = getJointDistance(joint); |
| | return mbdJoint; |
| | } |
| |
|
| | case DistanceType::PlaneCylinder: { |
| | auto mbdJoint = CREATE<ASMTLineInPlaneJoint>::With(); |
| | mbdJoint->offset = getJointDistance(joint) + getFaceRadius(obj2, elt2); |
| | return mbdJoint; |
| | } |
| |
|
| | case DistanceType::PlaneSphere: { |
| | auto mbdJoint = CREATE<ASMTPointInPlaneJoint>::With(); |
| | mbdJoint->offset = getJointDistance(joint) + getFaceRadius(obj2, elt2); |
| | return mbdJoint; |
| | } |
| |
|
| | case DistanceType::PlaneTorus: { |
| | auto mbdJoint = CREATE<ASMTPlanarJoint>::With(); |
| | mbdJoint->offset = getJointDistance(joint); |
| | return mbdJoint; |
| | } |
| |
|
| | case DistanceType::CylinderCylinder: { |
| | auto mbdJoint = CREATE<ASMTRevCylJoint>::With(); |
| | mbdJoint->distanceIJ = getJointDistance(joint) + getFaceRadius(obj1, elt1) |
| | + getFaceRadius(obj2, elt2); |
| | return mbdJoint; |
| | } |
| |
|
| | case DistanceType::CylinderSphere: { |
| | auto mbdJoint = CREATE<ASMTCylSphJoint>::With(); |
| | mbdJoint->distanceIJ = getJointDistance(joint) + getFaceRadius(obj1, elt1) |
| | + getFaceRadius(obj2, elt2); |
| | return mbdJoint; |
| | } |
| |
|
| | case DistanceType::CylinderTorus: { |
| | auto mbdJoint = CREATE<ASMTRevCylJoint>::With(); |
| | mbdJoint->distanceIJ = getJointDistance(joint) + getFaceRadius(obj1, elt1) |
| | + getFaceRadius(obj2, elt2); |
| | return mbdJoint; |
| | } |
| |
|
| | case DistanceType::TorusTorus: { |
| | auto mbdJoint = CREATE<ASMTPlanarJoint>::With(); |
| | mbdJoint->offset = getJointDistance(joint); |
| | return mbdJoint; |
| | } |
| |
|
| | case DistanceType::TorusSphere: { |
| | auto mbdJoint = CREATE<ASMTCylSphJoint>::With(); |
| | mbdJoint->distanceIJ = getJointDistance(joint) + getFaceRadius(obj1, elt1) |
| | + getFaceRadius(obj2, elt2); |
| | return mbdJoint; |
| | } |
| |
|
| | case DistanceType::SphereSphere: { |
| | auto mbdJoint = CREATE<ASMTSphSphJoint>::With(); |
| | mbdJoint->distanceIJ = getJointDistance(joint) + getFaceRadius(obj1, elt1) |
| | + getFaceRadius(obj2, elt2); |
| | return mbdJoint; |
| | } |
| |
|
| | |
| | case DistanceType::PointPlane: { |
| | auto mbdJoint = CREATE<ASMTPointInPlaneJoint>::With(); |
| | mbdJoint->offset = getJointDistance(joint); |
| | return mbdJoint; |
| | } |
| |
|
| | case DistanceType::PointCylinder: { |
| | auto mbdJoint = CREATE<ASMTCylSphJoint>::With(); |
| | mbdJoint->distanceIJ = getJointDistance(joint) + getFaceRadius(obj1, elt1); |
| | return mbdJoint; |
| | } |
| |
|
| | case DistanceType::PointSphere: { |
| | auto mbdJoint = CREATE<ASMTSphSphJoint>::With(); |
| | mbdJoint->distanceIJ = getJointDistance(joint) + getFaceRadius(obj1, elt1); |
| | return mbdJoint; |
| | } |
| |
|
| | |
| | case DistanceType::LinePlane: { |
| | auto mbdJoint = CREATE<ASMTLineInPlaneJoint>::With(); |
| | mbdJoint->offset = getJointDistance(joint); |
| | return mbdJoint; |
| | } |
| |
|
| | |
| | case DistanceType::PointLine: { |
| | auto mbdJoint = CREATE<ASMTCylSphJoint>::With(); |
| | mbdJoint->distanceIJ = getJointDistance(joint); |
| | return mbdJoint; |
| | } |
| |
|
| | case DistanceType::PointCurve: { |
| | |
| | |
| | |
| | |
| | auto mbdJoint = CREATE<ASMTPointInPlaneJoint>::With(); |
| | mbdJoint->offset = getJointDistance(joint); |
| | return mbdJoint; |
| | } |
| |
|
| | default: { |
| | |
| | auto mbdJoint = CREATE<ASMTPlanarJoint>::With(); |
| | mbdJoint->offset = getJointDistance(joint); |
| | return mbdJoint; |
| | } |
| | } |
| | } |
| |
|
| | std::vector<std::shared_ptr<MbD::ASMTJoint>> AssemblyObject::makeMbdJoint(App::DocumentObject* joint) |
| | { |
| | if (!joint) { |
| | return {}; |
| | } |
| |
|
| | JointType jointType = getJointType(joint); |
| |
|
| | std::shared_ptr<ASMTJoint> mbdJoint = makeMbdJointOfType(joint, jointType); |
| | if (!mbdJoint || !isMbDJointValid(joint)) { |
| | return {}; |
| | } |
| |
|
| | std::string fullMarkerNameI, fullMarkerNameJ; |
| | if (jointType == JointType::RackPinion) { |
| | getRackPinionMarkers(joint, fullMarkerNameI, fullMarkerNameJ); |
| | } |
| | else { |
| | fullMarkerNameI = handleOneSideOfJoint(joint, "Reference1", "Placement1"); |
| | fullMarkerNameJ = handleOneSideOfJoint(joint, "Reference2", "Placement2"); |
| | } |
| | if (fullMarkerNameI == "" || fullMarkerNameJ == "") { |
| | return {}; |
| | } |
| |
|
| | mbdJoint->setName(joint->getFullName()); |
| | mbdJoint->setMarkerI(fullMarkerNameI); |
| | mbdJoint->setMarkerJ(fullMarkerNameJ); |
| |
|
| | |
| | if (motions.empty()) { |
| | if (jointType == JointType::Slider || jointType == JointType::Cylindrical) { |
| | auto* pLenMin = dynamic_cast<App::PropertyFloat*>(joint->getPropertyByName("LengthMin")); |
| | auto* pLenMax = dynamic_cast<App::PropertyFloat*>(joint->getPropertyByName("LengthMax")); |
| | auto* pMinEnabled = dynamic_cast<App::PropertyBool*>( |
| | joint->getPropertyByName("EnableLengthMin") |
| | ); |
| | auto* pMaxEnabled = dynamic_cast<App::PropertyBool*>( |
| | joint->getPropertyByName("EnableLengthMax") |
| | ); |
| |
|
| | if (pLenMin && pLenMax && pMinEnabled && pMaxEnabled) { |
| | |
| | bool minEnabled = pMinEnabled->getValue(); |
| | bool maxEnabled = pMaxEnabled->getValue(); |
| | double minLength = pLenMin->getValue(); |
| | double maxLength = pLenMax->getValue(); |
| |
|
| | if ((minLength > maxLength) && minEnabled && maxEnabled) { |
| | pLenMin->setValue(maxLength); |
| | pLenMax->setValue(minLength); |
| | minLength = maxLength; |
| | maxLength = pLenMax->getValue(); |
| |
|
| | pMinEnabled->setValue(maxEnabled); |
| | pMaxEnabled->setValue(minEnabled); |
| | minEnabled = maxEnabled; |
| | maxEnabled = pMaxEnabled->getValue(); |
| | } |
| |
|
| | if (minEnabled) { |
| | auto limit = ASMTTranslationLimit::With(); |
| | limit->setName(joint->getFullName() + "-LimitLenMin"); |
| | limit->setMarkerI(fullMarkerNameI); |
| | limit->setMarkerJ(fullMarkerNameJ); |
| | limit->settype("=>"); |
| | limit->setlimit(std::to_string(minLength)); |
| | limit->settol("1.0e-9"); |
| | mbdAssembly->addLimit(limit); |
| | } |
| |
|
| | if (maxEnabled) { |
| | auto limit2 = ASMTTranslationLimit::With(); |
| | limit2->setName(joint->getFullName() + "-LimitLenMax"); |
| | limit2->setMarkerI(fullMarkerNameI); |
| | limit2->setMarkerJ(fullMarkerNameJ); |
| | limit2->settype("=<"); |
| | limit2->setlimit(std::to_string(maxLength)); |
| | limit2->settol("1.0e-9"); |
| | mbdAssembly->addLimit(limit2); |
| | } |
| | } |
| | } |
| | if (jointType == JointType::Revolute || jointType == JointType::Cylindrical) { |
| | auto* pRotMin = dynamic_cast<App::PropertyFloat*>(joint->getPropertyByName("AngleMin")); |
| | auto* pRotMax = dynamic_cast<App::PropertyFloat*>(joint->getPropertyByName("AngleMax")); |
| | auto* pMinEnabled = dynamic_cast<App::PropertyBool*>( |
| | joint->getPropertyByName("EnableAngleMin") |
| | ); |
| | auto* pMaxEnabled = dynamic_cast<App::PropertyBool*>( |
| | joint->getPropertyByName("EnableAngleMax") |
| | ); |
| |
|
| | if (pRotMin && pRotMax && pMinEnabled && pMaxEnabled) { |
| | |
| | bool minEnabled = pMinEnabled->getValue(); |
| | bool maxEnabled = pMaxEnabled->getValue(); |
| | double minAngle = pRotMin->getValue(); |
| | double maxAngle = pRotMax->getValue(); |
| | if ((minAngle > maxAngle) && minEnabled && maxEnabled) { |
| | pRotMin->setValue(maxAngle); |
| | pRotMax->setValue(minAngle); |
| | minAngle = maxAngle; |
| | maxAngle = pRotMax->getValue(); |
| |
|
| | pMinEnabled->setValue(maxEnabled); |
| | pMaxEnabled->setValue(minEnabled); |
| | minEnabled = maxEnabled; |
| | maxEnabled = pMaxEnabled->getValue(); |
| | } |
| |
|
| | if (minEnabled) { |
| | auto limit = ASMTRotationLimit::With(); |
| | limit->setName(joint->getFullName() + "-LimitRotMin"); |
| | limit->setMarkerI(fullMarkerNameI); |
| | limit->setMarkerJ(fullMarkerNameJ); |
| | limit->settype("=>"); |
| | limit->setlimit(std::to_string(minAngle) + "*pi/180.0"); |
| | limit->settol("1.0e-9"); |
| | mbdAssembly->addLimit(limit); |
| | } |
| |
|
| | if (maxEnabled) { |
| | auto limit2 = ASMTRotationLimit::With(); |
| | limit2->setName(joint->getFullName() + "-LimitRotMax"); |
| | limit2->setMarkerI(fullMarkerNameI); |
| | limit2->setMarkerJ(fullMarkerNameJ); |
| | limit2->settype("=<"); |
| | limit2->setlimit(std::to_string(maxAngle) + "*pi/180.0"); |
| | limit2->settol("1.0e-9"); |
| | mbdAssembly->addLimit(limit2); |
| | } |
| | } |
| | } |
| | } |
| | std::vector<App::DocumentObject*> done; |
| | |
| | for (auto* motion : motions) { |
| | if (std::ranges::find(done, motion) != done.end()) { |
| | continue; |
| | } |
| |
|
| | auto* pJoint = dynamic_cast<App::PropertyXLinkSub*>(motion->getPropertyByName("Joint")); |
| | if (!pJoint) { |
| | continue; |
| | } |
| | App::DocumentObject* motionJoint = pJoint->getValue(); |
| | if (joint != motionJoint) { |
| | continue; |
| | } |
| |
|
| | auto* pType = dynamic_cast<App::PropertyEnumeration*>(motion->getPropertyByName("MotionType")); |
| | auto* pFormula = dynamic_cast<App::PropertyString*>(motion->getPropertyByName("Formula")); |
| | if (!pType || !pFormula) { |
| | continue; |
| | } |
| | std::string formula = pFormula->getValue(); |
| | if (formula == "") { |
| | continue; |
| | } |
| | std::string motionType = pType->getValueAsString(); |
| |
|
| | |
| | |
| | for (auto* motion2 : motions) { |
| | pJoint = dynamic_cast<App::PropertyXLinkSub*>(motion2->getPropertyByName("Joint")); |
| | if (!pJoint) { |
| | continue; |
| | } |
| | motionJoint = pJoint->getValue(); |
| | if (joint != motionJoint || motion2 == motion) { |
| | continue; |
| | } |
| |
|
| | auto* pType2 = dynamic_cast<App::PropertyEnumeration*>( |
| | motion2->getPropertyByName("MotionType") |
| | ); |
| | auto* pFormula2 = dynamic_cast<App::PropertyString*>(motion2->getPropertyByName("Formula")); |
| | if (!pType2 || !pFormula2) { |
| | continue; |
| | } |
| | std::string formula2 = pFormula2->getValue(); |
| | if (formula2 == "") { |
| | continue; |
| | } |
| | std::string motionType2 = pType2->getValueAsString(); |
| | if (motionType2 == motionType) { |
| | continue; |
| | } |
| |
|
| | auto ASMTmotion = CREATE<ASMTGeneralMotion>::With(); |
| | ASMTmotion->setName(joint->getFullName() + "-ScrewMotion"); |
| | ASMTmotion->setMarkerI(fullMarkerNameI); |
| | ASMTmotion->setMarkerJ(fullMarkerNameJ); |
| | ASMTmotion->rIJI->atiput(2, motionType == "Angular" ? formula2 : formula); |
| | ASMTmotion->angIJJ->atiput(2, motionType == "Angular" ? formula : formula2); |
| | mbdAssembly->addMotion(ASMTmotion); |
| |
|
| | done.push_back(motion2); |
| | } |
| |
|
| | if (motionType == "Angular") { |
| | auto ASMTmotion = CREATE<ASMTRotationalMotion>::With(); |
| | ASMTmotion->setName(joint->getFullName() + "-AngularMotion"); |
| | ASMTmotion->setMarkerI(fullMarkerNameI); |
| | ASMTmotion->setMarkerJ(fullMarkerNameJ); |
| | ASMTmotion->setRotationZ(formula); |
| | mbdAssembly->addMotion(ASMTmotion); |
| | } |
| | else if (motionType == "Linear") { |
| | auto ASMTmotion = CREATE<ASMTTranslationalMotion>::With(); |
| | ASMTmotion->setName(joint->getFullName() + "-LinearMotion"); |
| | ASMTmotion->setMarkerI(fullMarkerNameI); |
| | ASMTmotion->setMarkerJ(fullMarkerNameJ); |
| | ASMTmotion->setTranslationZ(formula); |
| | mbdAssembly->addMotion(ASMTmotion); |
| | } |
| | } |
| |
|
| | return {mbdJoint}; |
| | } |
| |
|
| | std::string AssemblyObject::handleOneSideOfJoint( |
| | App::DocumentObject* joint, |
| | const char* propRefName, |
| | const char* propPlcName |
| | ) |
| | { |
| | App::DocumentObject* part = getMovingPartFromRef(this, joint, propRefName); |
| | App::DocumentObject* obj = getObjFromRef(joint, propRefName); |
| |
|
| | if (!part || !obj) { |
| | Base::Console() |
| | .warning("The property %s of Joint %s is bad.", propRefName, joint->getFullName()); |
| | return ""; |
| | } |
| |
|
| | MbDPartData data = getMbDData(part); |
| | std::shared_ptr<ASMTPart> mbdPart = data.part; |
| | Base::Placement plc = getPlacementFromProp(joint, propPlcName); |
| | |
| | |
| |
|
| | if (obj->getNameInDocument() != part->getNameInDocument()) { |
| |
|
| | auto* ref = dynamic_cast<App::PropertyXLinkSub*>(joint->getPropertyByName(propRefName)); |
| | if (!ref) { |
| | return ""; |
| | } |
| |
|
| | Base::Placement obj_global_plc = getGlobalPlacement(obj, ref); |
| | plc = obj_global_plc * plc; |
| |
|
| | Base::Placement part_global_plc = getGlobalPlacement(part, ref); |
| | plc = part_global_plc.inverse() * plc; |
| | } |
| | |
| | if (!data.offsetPlc.isIdentity()) { |
| | plc = data.offsetPlc * plc; |
| | } |
| |
|
| | std::string markerName = joint->getFullName(); |
| | auto mbdMarker = makeMbdMarker(markerName, plc); |
| | mbdPart->addMarker(mbdMarker); |
| |
|
| | return "/OndselAssembly/" + mbdPart->name + "/" + markerName; |
| | } |
| |
|
| | void AssemblyObject::getRackPinionMarkers( |
| | App::DocumentObject* joint, |
| | std::string& markerNameI, |
| | std::string& markerNameJ |
| | ) |
| | { |
| | |
| | |
| | |
| | |
| | |
| |
|
| | int slidingIndex = slidingPartIndex(joint); |
| | if (slidingIndex == 0) { |
| | return; |
| | } |
| |
|
| | if (slidingIndex != 1) { |
| | swapJCS(joint); |
| | } |
| |
|
| | App::DocumentObject* part1 = getMovingPartFromRef(this, joint, "Reference1"); |
| | App::DocumentObject* obj1 = getObjFromRef(joint, "Reference1"); |
| | Base::Placement plc1 = getPlacementFromProp(joint, "Placement1"); |
| |
|
| | App::DocumentObject* obj2 = getObjFromRef(joint, "Reference2"); |
| | Base::Placement plc2 = getPlacementFromProp(joint, "Placement2"); |
| |
|
| | if (!part1 || !obj1) { |
| | Base::Console().warning("Reference1 of Joint %s is bad.", joint->getFullName()); |
| | return; |
| | } |
| |
|
| | |
| | markerNameJ = handleOneSideOfJoint(joint, "Reference2", "Placement2"); |
| |
|
| | |
| | |
| | auto* ref1 = dynamic_cast<App::PropertyXLinkSub*>(joint->getPropertyByName("Reference1")); |
| | auto* ref2 = dynamic_cast<App::PropertyXLinkSub*>(joint->getPropertyByName("Reference2")); |
| | if (!ref1 || !ref2) { |
| | return; |
| | } |
| | Base::Placement pinion_global_plc = getGlobalPlacement(obj2, ref2); |
| | plc2 = pinion_global_plc * plc2; |
| | Base::Placement rack_global_plc = getGlobalPlacement(obj1, ref1); |
| | plc2 = rack_global_plc.inverse() * plc2; |
| |
|
| | |
| | |
| | Base::Rotation rot = plc2.getRotation(); |
| | |
| | Base::Vector3d currentZAxis = rot.multVec(Base::Vector3d(0, 0, 1)); |
| | Base::Vector3d currentXAxis = rot.multVec(Base::Vector3d(1, 0, 0)); |
| | Base::Vector3d targetXAxis = plc1.getRotation().multVec(Base::Vector3d(0, 0, 1)); |
| |
|
| | |
| | double yawAdjustment = currentXAxis.GetAngle(targetXAxis); |
| |
|
| | |
| | Base::Vector3d crossProd = currentXAxis.Cross(targetXAxis); |
| | if (currentZAxis * crossProd < 0) { |
| | yawAdjustment = -yawAdjustment; |
| | } |
| |
|
| | |
| | Base::Rotation yawRotation(currentZAxis, yawAdjustment); |
| |
|
| | |
| | Base::Rotation adjustedRotation = rot * yawRotation; |
| | plc1.setRotation(adjustedRotation); |
| |
|
| | |
| | MbDPartData data1 = getMbDData(part1); |
| | std::shared_ptr<ASMTPart> mbdPart = data1.part; |
| | if (obj1->getNameInDocument() != part1->getNameInDocument()) { |
| | plc1 = rack_global_plc * plc1; |
| |
|
| | Base::Placement part_global_plc = getGlobalPlacement(part1, ref1); |
| | plc1 = part_global_plc.inverse() * plc1; |
| | } |
| | |
| | if (!data1.offsetPlc.isIdentity()) { |
| | plc1 = data1.offsetPlc * plc1; |
| | } |
| |
|
| | std::string markerName = joint->getFullName(); |
| | auto mbdMarker = makeMbdMarker(markerName, plc1); |
| | mbdPart->addMarker(mbdMarker); |
| |
|
| | markerNameI = "/OndselAssembly/" + mbdPart->name + "/" + markerName; |
| | } |
| |
|
| | int AssemblyObject::slidingPartIndex(App::DocumentObject* joint) |
| | { |
| | 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(this, joint, "Reference2"); |
| | App::DocumentObject* obj2 = getObjFromRef(joint, "Reference2"); |
| | boost::ignore_unused(obj2); |
| | Base::Placement plc2 = getPlacementFromProp(joint, "Placement2"); |
| |
|
| | int slidingFound = 0; |
| | for (auto* jt : getJoints(false, false)) { |
| | if (getJointType(jt) == JointType::Slider) { |
| | 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) { |
| | found = (jpart1 == part1) ? 1 : 2; |
| | plci = (jpart1 == part1) ? plc1 : plc2; |
| | plcjt = getPlacementFromProp(jt, "Placement1"); |
| | } |
| | else if (jpart2 == part1 || jpart2 == part2) { |
| | found = (jpart2 == part1) ? 1 : 2; |
| | plci = (jpart2 == part1) ? plc1 : plc2; |
| | plcjt = getPlacementFromProp(jt, "Placement2"); |
| | } |
| |
|
| | if (found != 0) { |
| | |
| | |
| | double y1, p1, r1, y2, p2, r2; |
| | plcjt.getRotation().getYawPitchRoll(y1, p1, r1); |
| | plci.getRotation().getYawPitchRoll(y2, p2, r2); |
| | if (fabs(p1 - p2) < Precision::Confusion() && fabs(r1 - r2) < Precision::Confusion()) { |
| | slidingFound = found; |
| | } |
| | } |
| | } |
| | } |
| | return slidingFound; |
| | } |
| |
|
| | bool AssemblyObject::isMbDJointValid(App::DocumentObject* joint) |
| | { |
| | |
| | |
| | |
| | App::DocumentObject* part1 = getMovingPartFromRef(this, joint, "Reference1"); |
| | App::DocumentObject* part2 = getMovingPartFromRef(this, joint, "Reference2"); |
| | if (!part1 || !part2) { |
| | return false; |
| | } |
| |
|
| | |
| | if (getMbDPart(part1) == getMbDPart(part2)) { |
| | Base::Console().warning( |
| | "Assembly: Ignoring joint (%s) because its parts are connected by a fixed " |
| | "joint bundle. This joint is a conflicting or redundant constraint.\n", |
| | joint->getFullLabel() |
| | ); |
| | return false; |
| | } |
| | return true; |
| | } |
| |
|
| | AssemblyObject::MbDPartData AssemblyObject::getMbDData(App::DocumentObject* part) |
| | { |
| | auto it = objectPartMap.find(part); |
| | if (it != objectPartMap.end()) { |
| | |
| | return it->second; |
| | } |
| |
|
| | |
| | std::string str = part->getFullName(); |
| | Base::Placement plc = getPlacementFromProp(part, "Placement"); |
| | std::shared_ptr<ASMTPart> mbdPart = makeMbdPart(str, plc); |
| | mbdAssembly->addPart(mbdPart); |
| | MbDPartData data = {mbdPart, Base::Placement()}; |
| | objectPartMap[part] = data; |
| |
|
| | |
| | if (bundleFixed) { |
| | auto addConnectedFixedParts = [&](App::DocumentObject* currentPart, auto& self) -> void { |
| | std::vector<App::DocumentObject*> joints = getJointsOfPart(currentPart); |
| | for (auto* joint : joints) { |
| | JointType jointType = getJointType(joint); |
| | if (jointType == JointType::Fixed) { |
| | 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()) { |
| | |
| | continue; |
| | } |
| |
|
| | Base::Placement plci = getPlacementFromProp(partToAdd, "Placement"); |
| | MbDPartData partData = {mbdPart, plc.inverse() * plci}; |
| | objectPartMap[partToAdd] = partData; |
| |
|
| | |
| | self(partToAdd, self); |
| | } |
| | } |
| | }; |
| |
|
| | addConnectedFixedParts(part, addConnectedFixedParts); |
| | } |
| | return data; |
| | } |
| |
|
| | std::shared_ptr<ASMTPart> AssemblyObject::getMbDPart(App::DocumentObject* part) |
| | { |
| | if (!part) { |
| | return nullptr; |
| | } |
| | return getMbDData(part).part; |
| | } |
| |
|
| | std::shared_ptr<ASMTPart> AssemblyObject::makeMbdPart(std::string& name, Base::Placement plc, double mass) |
| | { |
| | auto mbdPart = CREATE<ASMTPart>::With(); |
| | mbdPart->setName(name); |
| |
|
| | auto massMarker = CREATE<ASMTPrincipalMassMarker>::With(); |
| | massMarker->setMass(mass); |
| | massMarker->setDensity(1.0); |
| | massMarker->setMomentOfInertias(1.0, 1.0, 1.0); |
| | mbdPart->setPrincipalMassMarker(massMarker); |
| |
|
| | Base::Vector3d pos = plc.getPosition(); |
| | mbdPart->setPosition3D(pos.x, pos.y, pos.z); |
| |
|
| | |
| | Base::Rotation rot = plc.getRotation(); |
| | Base::Matrix4D mat; |
| | rot.getValue(mat); |
| | Base::Vector3d r0 = mat.getRow(0); |
| | Base::Vector3d r1 = mat.getRow(1); |
| | Base::Vector3d r2 = mat.getRow(2); |
| | mbdPart->setRotationMatrix(r0.x, r0.y, r0.z, r1.x, r1.y, r1.z, r2.x, r2.y, r2.z); |
| |
|
| | return mbdPart; |
| | } |
| |
|
| | std::shared_ptr<ASMTMarker> AssemblyObject::makeMbdMarker(std::string& name, Base::Placement& plc) |
| | { |
| | auto mbdMarker = CREATE<ASMTMarker>::With(); |
| | mbdMarker->setName(name); |
| |
|
| | Base::Vector3d pos = plc.getPosition(); |
| | mbdMarker->setPosition3D(pos.x, pos.y, pos.z); |
| |
|
| | |
| | Base::Rotation rot = plc.getRotation(); |
| | Base::Matrix4D mat; |
| | rot.getValue(mat); |
| | Base::Vector3d r0 = mat.getRow(0); |
| | Base::Vector3d r1 = mat.getRow(1); |
| | Base::Vector3d r2 = mat.getRow(2); |
| | mbdMarker->setRotationMatrix(r0.x, r0.y, r0.z, r1.x, r1.y, r1.z, r2.x, r2.y, r2.z); |
| |
|
| | return mbdMarker; |
| | } |
| |
|
| | std::vector<ObjRef> AssemblyObject::getDownstreamParts( |
| | App::DocumentObject* part, |
| | App::DocumentObject* joint |
| | ) |
| | { |
| | if (!part) { |
| | return {}; |
| | } |
| |
|
| | |
| | bool state = false; |
| | if (joint) { |
| | state = getJointActivated(joint); |
| | setJointActivated(joint, false); |
| | } |
| |
|
| | std::vector<App::DocumentObject*> joints = getJoints(false); |
| |
|
| | std::vector<ObjRef> connectedParts = {{part, nullptr}}; |
| | traverseAndMarkConnectedParts(part, connectedParts, joints); |
| |
|
| | std::vector<ObjRef> downstreamParts; |
| | for (auto& parti : connectedParts) { |
| | if (!isPartConnected(parti.obj) && (parti.obj != part)) { |
| | downstreamParts.push_back(parti); |
| | } |
| | } |
| |
|
| | if (joint) { |
| | setJointActivated(joint, state); |
| | } |
| |
|
| | return downstreamParts; |
| | } |
| |
|
| | App::DocumentObject* AssemblyObject::getUpstreamMovingPart( |
| | App::DocumentObject* part, |
| | App::DocumentObject*& joint, |
| | std::string& name, |
| | std::vector<App::DocumentObject*> excludeJoints |
| | ) |
| | { |
| | if (!part || isPartGrounded(part)) { |
| | return nullptr; |
| | } |
| |
|
| | excludeJoints.push_back(joint); |
| |
|
| | joint = getJointOfPartConnectingToGround(part, name, excludeJoints); |
| | JointType jointType = getJointType(joint); |
| | if (jointType != JointType::Fixed) { |
| | return part; |
| | } |
| |
|
| | part = getMovingPartFromRef(this, joint, name == "Reference1" ? "Reference2" : "Reference1"); |
| |
|
| | return getUpstreamMovingPart(part, joint, name); |
| | } |
| |
|
| | double AssemblyObject::getObjMass(App::DocumentObject* obj) |
| | { |
| | if (!obj) { |
| | return 0.0; |
| | } |
| |
|
| | for (auto& pair : objMasses) { |
| | if (pair.first == obj) { |
| | return pair.second; |
| | } |
| | } |
| | return 1.0; |
| | } |
| |
|
| | void AssemblyObject::setObjMasses(std::vector<std::pair<App::DocumentObject*, double>> objectMasses) |
| | { |
| | objMasses = objectMasses; |
| | } |
| |
|
| | std::vector<AssemblyLink*> AssemblyObject::getSubAssemblies() |
| | { |
| | std::vector<AssemblyLink*> subAssemblies = {}; |
| |
|
| | App::Document* doc = getDocument(); |
| |
|
| | std::vector<DocumentObject*> assemblies = doc->getObjectsOfType( |
| | Assembly::AssemblyLink::getClassTypeId() |
| | ); |
| | for (auto assembly : assemblies) { |
| | if (hasObject(assembly)) { |
| | subAssemblies.push_back(freecad_cast<AssemblyLink*>(assembly)); |
| | } |
| | } |
| |
|
| | return subAssemblies; |
| | } |
| |
|
| | void AssemblyObject::ensureIdentityPlacements() |
| | { |
| | std::vector<App::DocumentObject*> group = Group.getValues(); |
| | for (auto* obj : group) { |
| | |
| | if (obj->isLinkGroup()) { |
| | auto* link = dynamic_cast<App::Link*>(obj); |
| | auto* pPlc = dynamic_cast<App::PropertyPlacement*>(obj->getPropertyByName("Placement")); |
| | if (!pPlc || !link) { |
| | continue; |
| | } |
| |
|
| | Base::Placement plc = pPlc->getValue(); |
| | if (plc.isIdentity()) { |
| | continue; |
| | } |
| |
|
| | pPlc->setValue(Base::Placement()); |
| | obj->purgeTouched(); |
| |
|
| | |
| | std::vector<App::DocumentObject*> elts = link->ElementList.getValues(); |
| | for (auto* elt : elts) { |
| | pPlc = dynamic_cast<App::PropertyPlacement*>(elt->getPropertyByName("Placement")); |
| | pPlc->setValue(plc * pPlc->getValue()); |
| | elt->purgeTouched(); |
| | } |
| | } |
| | } |
| | } |
| |
|
| | int AssemblyObject::numberOfComponents() const |
| | { |
| | int count = 0; |
| | const std::vector<App::DocumentObject*> objects = Group.getValues(); |
| |
|
| | for (auto* obj : objects) { |
| | if (!obj) { |
| | continue; |
| | } |
| |
|
| | if (obj->isLinkGroup()) { |
| | auto* link = static_cast<const App::Link*>(obj); |
| | count += link->ElementCount.getValue(); |
| | continue; |
| | } |
| |
|
| | if (obj->isDerivedFrom(Assembly::AssemblyLink::getClassTypeId())) { |
| | auto* subAssembly = static_cast<const AssemblyLink*>(obj); |
| | count += subAssembly->numberOfComponents(); |
| | continue; |
| | } |
| |
|
| | |
| | if (obj->isDerivedFrom(App::Link::getClassTypeId())) { |
| | obj = static_cast<const App::Link*>(obj)->getLinkedObject(); |
| | if (!obj) { |
| | continue; |
| | } |
| | } |
| |
|
| | if (!obj->isDerivedFrom(App::GeoFeature::getClassTypeId())) { |
| | continue; |
| | } |
| |
|
| | if (obj->isDerivedFrom(App::LocalCoordinateSystem::getClassTypeId())) { |
| | continue; |
| | } |
| |
|
| | count++; |
| | } |
| |
|
| | return count; |
| | } |
| |
|
| | bool AssemblyObject::isEmpty() const |
| | { |
| | return numberOfComponents() == 0; |
| | } |
| |
|