Assembly: Property change from Object/Part to Reference

This commit is contained in:
PaddleStroke
2024-07-12 08:36:14 +02:00
committed by Yorik van Havre
parent 336a581942
commit 4f79c675b7
9 changed files with 1217 additions and 1057 deletions

View File

@@ -51,6 +51,7 @@
#include <Mod/Part/App/PartFeature.h>
#include <Mod/Part/App/TopoShape.h>
#include <Mod/PartDesign/App/Body.h>
#include <Mod/Part/App/DatumFeature.h>
#include <OndselSolver/CREATE.h>
#include <OndselSolver/ASMTSimulationParameters.h>
@@ -434,18 +435,18 @@ App::DocumentObject* AssemblyObject::getJointOfPartConnectingToGround(App::Docum
if (!joint) {
continue;
}
App::DocumentObject* part1 = getObjFromProp(joint, "Part1");
App::DocumentObject* part2 = getObjFromProp(joint, "Part2");
App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1");
App::DocumentObject* part2 = getMovingPartFromRef(joint, "Reference2");
if (!part1 || !part2) {
continue;
}
if (part == part1 && isJointConnectingPartToGround(joint, "Part1")) {
name = "Part1";
if (part == part1 && isJointConnectingPartToGround(joint, "Reference1")) {
name = "Reference1";
return joint;
}
if (part == part2 && isJointConnectingPartToGround(joint, "Part2")) {
name = "Part2";
if (part == part2 && isJointConnectingPartToGround(joint, "Reference2")) {
name = "Reference2";
return joint;
}
}
@@ -507,8 +508,8 @@ std::vector<App::DocumentObject*> AssemblyObject::getJoints(bool updateJCS, bool
continue;
}
auto* part1 = getObjFromProp(joint, "Part1");
auto* part2 = getObjFromProp(joint, "Part2");
auto* part1 = getMovingPartFromRef(joint, "Reference1");
auto* part2 = getMovingPartFromRef(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)
@@ -571,8 +572,8 @@ std::vector<App::DocumentObject*> AssemblyObject::getJointsOfObj(App::DocumentOb
std::vector<App::DocumentObject*> jointsOf;
for (auto joint : joints) {
App::DocumentObject* obj1 = getObjFromProp(joint, "object1");
App::DocumentObject* obj2 = getObjFromProp(joint, "Object2");
App::DocumentObject* obj1 = getObjFromRef(joint, "Reference1");
App::DocumentObject* obj2 = getObjFromRef(joint, "Reference2");
if (obj == obj1 || obj == obj2) {
jointsOf.push_back(obj);
}
@@ -587,8 +588,8 @@ std::vector<App::DocumentObject*> AssemblyObject::getJointsOfPart(App::DocumentO
std::vector<App::DocumentObject*> jointsOf;
for (auto joint : joints) {
App::DocumentObject* part1 = getObjFromProp(joint, "Part1");
App::DocumentObject* part2 = getObjFromProp(joint, "Part2");
App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1");
App::DocumentObject* part2 = getMovingPartFromRef(joint, "Reference2");
if (part == part1 || part == part2) {
jointsOf.push_back(joint);
}
@@ -674,11 +675,7 @@ bool AssemblyObject::isJointConnectingPartToGround(App::DocumentObject* joint, c
return false;
}
auto* propPart = dynamic_cast<App::PropertyLink*>(joint->getPropertyByName(propname));
if (!propPart) {
return false;
}
App::DocumentObject* part = propPart->getValue();
App::DocumentObject* part = getMovingPartFromRef(joint, propname);
// Check if the part is grounded.
bool isGrounded = isPartGrounded(part);
@@ -727,14 +724,26 @@ bool AssemblyObject::isJointTypeConnecting(App::DocumentObject* joint)
&& jointType != JointType::Gears && jointType != JointType::Belt;
}
bool AssemblyObject::isObjInSetOfObjRefPairs(App::DocumentObject* obj,
const std::set<ObjRefPair>& set)
{
for (const auto& pair : set) {
if (pair.first == obj) {
return true;
}
}
return false;
}
void AssemblyObject::removeUnconnectedJoints(std::vector<App::DocumentObject*>& joints,
std::vector<App::DocumentObject*> groundedObjs)
{
std::set<App::DocumentObject*> connectedParts;
std::set<ObjRefPair> connectedParts;
// Initialize connectedParts with groundedObjs
for (auto* groundedObj : groundedObjs) {
connectedParts.insert(groundedObj);
connectedParts.insert({groundedObj, nullptr});
}
// Perform a traversal from each grounded object
@@ -747,11 +756,11 @@ void AssemblyObject::removeUnconnectedJoints(std::vector<App::DocumentObject*>&
std::remove_if(
joints.begin(),
joints.end(),
[&connectedParts](App::DocumentObject* joint) {
App::DocumentObject* obj1 = getObjFromProp(joint, "Part1");
App::DocumentObject* obj2 = getObjFromProp(joint, "Part2");
if ((connectedParts.find(obj1) == connectedParts.end())
|| (connectedParts.find(obj2) == connectedParts.end())) {
[&](App::DocumentObject* joint) {
App::DocumentObject* obj1 = getMovingPartFromRef(joint, "Reference1");
App::DocumentObject* obj2 = getMovingPartFromRef(joint, "Reference2");
if (!isObjInSetOfObjRefPairs(obj1, connectedParts)
|| !isObjInSetOfObjRefPairs(obj2, connectedParts)) {
Base::Console().Warning(
"%s is unconnected to a grounded part so it is ignored.\n",
joint->getFullName());
@@ -763,36 +772,47 @@ void AssemblyObject::removeUnconnectedJoints(std::vector<App::DocumentObject*>&
}
void AssemblyObject::traverseAndMarkConnectedParts(App::DocumentObject* currentObj,
std::set<App::DocumentObject*>& connectedParts,
std::set<ObjRefPair>& connectedParts,
const std::vector<App::DocumentObject*>& joints)
{
// getConnectedParts returns the objs connected to the currentObj by any joint
auto connectedObjs = getConnectedParts(currentObj, joints);
for (auto* nextObj : connectedObjs) {
if (connectedParts.find(nextObj) == connectedParts.end()) {
connectedParts.insert(nextObj);
traverseAndMarkConnectedParts(nextObj, connectedParts, joints);
for (auto nextObjRef : connectedObjs) {
if (!isObjInSetOfObjRefPairs(nextObjRef.first, connectedParts)) {
// Create a new ObjRefPair with the nextObj and a nullptr for PropertyXLinkSub*
connectedParts.insert(nextObjRef);
traverseAndMarkConnectedParts(nextObjRef.first, connectedParts, joints);
}
}
}
std::vector<App::DocumentObject*>
std::vector<ObjRefPair>
AssemblyObject::getConnectedParts(App::DocumentObject* part,
const std::vector<App::DocumentObject*>& joints)
{
std::vector<App::DocumentObject*> connectedParts;
std::vector<ObjRefPair> connectedParts;
for (auto joint : joints) {
if (!isJointTypeConnecting(joint)) {
continue;
}
App::DocumentObject* obj1 = getObjFromProp(joint, "Part1");
App::DocumentObject* obj2 = getObjFromProp(joint, "Part2");
App::DocumentObject* obj1 = getMovingPartFromRef(joint, "Reference1");
App::DocumentObject* obj2 = getMovingPartFromRef(joint, "Reference2");
if (obj1 == part) {
connectedParts.push_back(obj2);
auto* ref =
dynamic_cast<App::PropertyXLinkSub*>(joint->getPropertyByName("Reference2"));
if (!ref) {
continue;
}
connectedParts.push_back({obj2, ref});
}
else if (obj2 == part) {
connectedParts.push_back(obj1);
auto* ref =
dynamic_cast<App::PropertyXLinkSub*>(joint->getPropertyByName("Reference1"));
if (!ref) {
continue;
}
connectedParts.push_back({obj1, ref});
}
}
return connectedParts;
@@ -816,11 +836,11 @@ bool AssemblyObject::isPartConnected(App::DocumentObject* obj)
std::vector<App::DocumentObject*> groundedObjs = getGroundedParts();
std::vector<App::DocumentObject*> joints = getJoints(false);
std::set<App::DocumentObject*> connectedParts;
std::set<ObjRefPair> connectedParts;
// Initialize connectedParts with groundedObjs
for (auto* groundedObj : groundedObjs) {
connectedParts.insert(groundedObj);
connectedParts.insert({groundedObj, nullptr});
}
// Perform a traversal from each grounded object
@@ -828,8 +848,8 @@ bool AssemblyObject::isPartConnected(App::DocumentObject* obj)
traverseAndMarkConnectedParts(groundedObj, connectedParts, joints);
}
for (auto part : connectedParts) {
if (obj == part) {
for (auto objRef : connectedParts) {
if (obj == objRef.first) {
return true;
}
}
@@ -928,10 +948,10 @@ std::shared_ptr<ASMTJoint> AssemblyObject::makeMbdJointDistance(App::DocumentObj
{
DistanceType type = getDistanceType(joint);
const char* elt1 = getElementFromProp(joint, "Object1");
const char* elt2 = getElementFromProp(joint, "Object2");
auto* obj1 = getLinkedObjFromProp(joint, "Object1");
auto* obj2 = getLinkedObjFromProp(joint, "Object2");
const char* elt1 = getElementFromProp(joint, "Reference1");
const char* elt2 = getElementFromProp(joint, "Reference2");
auto* obj1 = getLinkedObjFromRef(joint, "Reference1");
auto* obj2 = getLinkedObjFromRef(joint, "Reference2");
if (type == DistanceType::PointPoint) {
// Point to point distance, or ball joint if distance=0.
@@ -1130,8 +1150,8 @@ AssemblyObject::makeMbdJoint(App::DocumentObject* joint)
getRackPinionMarkers(joint, fullMarkerNameI, fullMarkerNameJ);
}
else {
fullMarkerNameI = handleOneSideOfJoint(joint, "Object1", "Part1", "Placement1");
fullMarkerNameJ = handleOneSideOfJoint(joint, "Object2", "Part2", "Placement2");
fullMarkerNameI = handleOneSideOfJoint(joint, "Reference1", "Placement1");
fullMarkerNameJ = handleOneSideOfJoint(joint, "Reference2", "Placement2");
}
if (fullMarkerNameI == "" || fullMarkerNameJ == "") {
return {};
@@ -1246,16 +1266,15 @@ AssemblyObject::makeMbdJoint(App::DocumentObject* joint)
}
std::string AssemblyObject::handleOneSideOfJoint(App::DocumentObject* joint,
const char* propObjName,
const char* propPartName,
const char* propRefName,
const char* propPlcName)
{
App::DocumentObject* part = getObjFromProp(joint, propPartName);
App::DocumentObject* obj = getObjFromProp(joint, propObjName);
App::DocumentObject* part = getMovingPartFromRef(joint, propRefName);
App::DocumentObject* obj = getObjFromRef(joint, propRefName);
if (!part || !obj) {
Base::Console().Warning("The property %s of Joint %s is empty.",
obj ? propPartName : propObjName,
Base::Console().Warning("The property %s of Joint %s is bad.",
propRefName,
joint->getFullName());
return "";
}
@@ -1269,10 +1288,15 @@ std::string AssemblyObject::handleOneSideOfJoint(App::DocumentObject* joint,
// Make plc relative to the containing part
// plc = objPlc * plc; // this would not work for nested parts.
Base::Placement obj_global_plc = getGlobalPlacement(obj, part);
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);
Base::Placement part_global_plc = getGlobalPlacement(part, ref);
plc = part_global_plc.inverse() * plc;
}
@@ -1302,29 +1326,31 @@ void AssemblyObject::getRackPinionMarkers(App::DocumentObject* joint,
swapJCS(joint); // make sure that rack is first.
}
App::DocumentObject* part1 = getObjFromProp(joint, "Part1");
App::DocumentObject* obj1 = getObjFromProp(joint, "Object1");
App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1");
App::DocumentObject* obj1 = getObjFromRef(joint, "Reference1");
Base::Placement plc1 = getPlacementFromProp(joint, "Placement1");
App::DocumentObject* part2 = getObjFromProp(joint, "Part2");
App::DocumentObject* obj2 = getObjFromProp(joint, "Object2");
App::DocumentObject* obj2 = getObjFromRef(joint, "Reference2");
Base::Placement plc2 = getPlacementFromProp(joint, "Placement2");
if (!part1 || !obj1) {
Base::Console().Warning("The property %s of Joint %s is empty.",
obj1 ? "Part1" : "Object1",
joint->getFullName());
Base::Console().Warning("Reference1 of Joint %s is bad.", joint->getFullName());
return;
}
// For the pinion nothing special needed :
markerNameJ = handleOneSideOfJoint(joint, "Object2", "Part2", "Placement2");
markerNameJ = handleOneSideOfJoint(joint, "Reference2", "Placement2");
// For the rack we need to change the placement :
// make the pinion plc relative to the rack placement.
Base::Placement pinion_global_plc = getGlobalPlacement(obj2, part2);
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, part1);
Base::Placement rack_global_plc = getGlobalPlacement(obj1, ref1);
plc2 = rack_global_plc.inverse() * plc2;
// The rot of the rack placement should be the same as the pinion, but with X axis along the
@@ -1356,7 +1382,7 @@ void AssemblyObject::getRackPinionMarkers(App::DocumentObject* joint,
if (obj1->getNameInDocument() != part1->getNameInDocument()) {
plc1 = rack_global_plc * plc1;
Base::Placement part_global_plc = getGlobalPlacement(part1);
Base::Placement part_global_plc = getGlobalPlacement(part1, ref1);
plc1 = part_global_plc.inverse() * plc1;
}
@@ -1370,21 +1396,21 @@ void AssemblyObject::getRackPinionMarkers(App::DocumentObject* joint,
int AssemblyObject::slidingPartIndex(App::DocumentObject* joint)
{
App::DocumentObject* part1 = getObjFromProp(joint, "Part1");
App::DocumentObject* obj1 = getObjFromProp(joint, "Object1");
App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1");
App::DocumentObject* obj1 = getObjFromRef(joint, "Reference1");
boost::ignore_unused(obj1);
Base::Placement plc1 = getPlacementFromProp(joint, "Placement1");
App::DocumentObject* part2 = getObjFromProp(joint, "Part2");
App::DocumentObject* obj2 = getObjFromProp(joint, "Object2");
App::DocumentObject* part2 = getMovingPartFromRef(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 = getObjFromProp(jt, "Part1");
App::DocumentObject* jpart2 = getObjFromProp(jt, "Part2");
App::DocumentObject* jpart1 = getMovingPartFromRef(jt, "Reference1");
App::DocumentObject* jpart2 = getMovingPartFromRef(jt, "Reference2");
int found = 0;
Base::Placement plcjt, plci;
if (jpart1 == part1 || jpart1 == part2) {
@@ -1489,8 +1515,8 @@ std::shared_ptr<ASMTMarker> AssemblyObject::makeMbdMarker(std::string& name, Bas
return mbdMarker;
}
std::vector<App::DocumentObject*> AssemblyObject::getDownstreamParts(App::DocumentObject* part,
App::DocumentObject* joint)
std::vector<std::pair<App::DocumentObject*, App::PropertyXLinkSub*>>
AssemblyObject::getDownstreamParts(App::DocumentObject* part, App::DocumentObject* joint)
{
// First we deactivate the joint
bool state = getJointActivated(joint);
@@ -1498,12 +1524,13 @@ std::vector<App::DocumentObject*> AssemblyObject::getDownstreamParts(App::Docume
std::vector<App::DocumentObject*> joints = getJoints(false);
std::set<App::DocumentObject*> connectedParts = {part};
std::set<std::pair<App::DocumentObject*, App::PropertyXLinkSub*>> connectedParts = {
{part, nullptr}};
traverseAndMarkConnectedParts(part, connectedParts, joints);
std::vector<App::DocumentObject*> downstreamParts;
std::vector<std::pair<App::DocumentObject*, App::PropertyXLinkSub*>> downstreamParts;
for (auto parti : connectedParts) {
if (!isPartConnected(parti) && (parti != part)) {
if (!isPartConnected(parti.first) && (parti.first != part)) {
downstreamParts.push_back(parti);
}
}
@@ -1527,8 +1554,8 @@ std::vector<App::DocumentObject*> AssemblyObject::getDownstreamParts(App::Docume
auto it = std::remove(jointsOfPart.begin(), jointsOfPart.end(), connectingJoint);
jointsOfPart.erase(it, jointsOfPart.end());
for (auto joint : jointsOfPart) {
App::DocumentObject* part1 = getObjFromProp(joint, "Part1");
App::DocumentObject* part2 = getObjFromProp(joint, "Part2");
App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1");
App::DocumentObject* part2 = getMovingPartFromRef(joint, "Reference2");
bool firstIsDown = part->getFullName() == part2->getFullName();
App::DocumentObject* downstreamPart = firstIsDown ? part1 : part2;
@@ -1574,30 +1601,30 @@ std::vector<App::DocumentObject*> AssemblyObject::getUpstreamParts(App::Document
std::string name;
App::DocumentObject* connectingJoint = getJointOfPartConnectingToGround(part, name);
App::DocumentObject* upPart =
getObjFromProp(connectingJoint, name == "Part1" ? "Part2" : "Part1");
getMovingPartFromRef(connectingJoint, name == "Reference1" ? "Reference2" : "Reference1");
std::vector<App::DocumentObject*> upstreamParts = getUpstreamParts(upPart, limit);
upstreamParts.push_back(part);
return upstreamParts;
}
App::DocumentObject* AssemblyObject::getUpstreamMovingPart(App::DocumentObject* part)
App::DocumentObject* AssemblyObject::getUpstreamMovingPart(App::DocumentObject* part,
App::DocumentObject*& joint,
std::string& name)
{
if (isPartGrounded(part)) {
return nullptr;
}
std::string name;
App::DocumentObject* connectingJoint = getJointOfPartConnectingToGround(part, name);
JointType jointType = getJointType(connectingJoint);
joint = getJointOfPartConnectingToGround(part, name);
JointType jointType = getJointType(joint);
if (jointType != JointType::Fixed) {
return part;
}
App::DocumentObject* upPart =
getObjFromProp(connectingJoint, name == "Part1" ? "Part2" : "Part1");
part = getMovingPartFromRef(joint, name == "Reference1" ? "Reference2" : "Reference1");
return getUpstreamMovingPart(upPart);
return getUpstreamMovingPart(part, joint, name);
}
double AssemblyObject::getObjMass(App::DocumentObject* obj)
@@ -1659,32 +1686,23 @@ void AssemblyObject::updateGroundedJointsPlacements()
void AssemblyObject::swapJCS(App::DocumentObject* joint)
{
auto propPlacement1 =
dynamic_cast<App::PropertyPlacement*>(joint->getPropertyByName("Placement1"));
auto propPlacement2 =
dynamic_cast<App::PropertyPlacement*>(joint->getPropertyByName("Placement2"));
if (propPlacement1 && propPlacement2) {
auto temp = propPlacement1->getValue();
propPlacement1->setValue(propPlacement2->getValue());
propPlacement2->setValue(temp);
auto pPlc1 = dynamic_cast<App::PropertyPlacement*>(joint->getPropertyByName("Placement1"));
auto pPlc2 = dynamic_cast<App::PropertyPlacement*>(joint->getPropertyByName("Placement2"));
if (pPlc1 && pPlc2) {
auto temp = pPlc1->getValue();
pPlc1->setValue(pPlc2->getValue());
pPlc2->setValue(temp);
}
auto propObject1 = dynamic_cast<App::PropertyXLinkSub*>(joint->getPropertyByName("Object1"));
auto propObject2 = dynamic_cast<App::PropertyXLinkSub*>(joint->getPropertyByName("Object2"));
if (propObject1 && propObject2) {
auto temp = propObject1->getValue();
auto subs1 = propObject1->getSubValues();
auto subs2 = propObject2->getSubValues();
propObject1->setValue(propObject2->getValue());
propObject1->setSubValues(std::move(subs2));
propObject2->setValue(temp);
propObject2->setSubValues(std::move(subs1));
}
auto propPart1 = dynamic_cast<App::PropertyLink*>(joint->getPropertyByName("Part1"));
auto propPart2 = dynamic_cast<App::PropertyLink*>(joint->getPropertyByName("Part2"));
if (propPart1 && propPart2) {
auto temp = propPart1->getValue();
propPart1->setValue(propPart2->getValue());
propPart2->setValue(temp);
auto pRef1 = dynamic_cast<App::PropertyXLinkSub*>(joint->getPropertyByName("Reference1"));
auto pRef2 = dynamic_cast<App::PropertyXLinkSub*>(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));
}
}
@@ -1761,12 +1779,12 @@ double AssemblyObject::getEdgeRadius(App::DocumentObject* obj, const char* elt)
DistanceType AssemblyObject::getDistanceType(App::DocumentObject* joint)
{
std::string type1 = getElementTypeFromProp(joint, "Object1");
std::string type2 = getElementTypeFromProp(joint, "Object2");
std::string elt1 = getElementFromProp(joint, "Object1");
std::string elt2 = getElementFromProp(joint, "Object2");
auto* obj1 = getLinkedObjFromProp(joint, "Object1");
auto* obj2 = getLinkedObjFromProp(joint, "Object2");
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;
@@ -2002,115 +2020,53 @@ Base::Placement AssemblyObject::getPlacementFromProp(App::DocumentObject* obj, c
return plc;
}
bool AssemblyObject::getTargetPlacementRelativeTo(Base::Placement& foundPlc,
App::DocumentObject* targetObj,
App::DocumentObject* part,
App::DocumentObject* container,
bool inContainerBranch,
bool ignorePlacement)
{
inContainerBranch = inContainerBranch || (!ignorePlacement && part == container);
if (targetObj == part && inContainerBranch && !ignorePlacement) {
foundPlc = getPlacementFromProp(targetObj, "Placement");
return true;
}
if (part->isDerivedFrom(App::DocumentObjectGroup::getClassTypeId())) {
for (auto& obj : part->getOutList()) {
bool found = getTargetPlacementRelativeTo(foundPlc,
targetObj,
obj,
container,
inContainerBranch,
ignorePlacement);
if (found) {
return true;
}
}
}
else if (part->isDerivedFrom(Assembly::AssemblyObject::getClassTypeId())
|| part->isDerivedFrom(App::Part::getClassTypeId())
|| part->isDerivedFrom(PartDesign::Body::getClassTypeId())) {
for (auto& obj : part->getOutList()) {
bool found = getTargetPlacementRelativeTo(foundPlc,
targetObj,
obj,
container,
inContainerBranch);
if (!found) {
continue;
}
if (!ignorePlacement) {
foundPlc = getPlacementFromProp(part, "Placement") * foundPlc;
}
return true;
}
}
else if (auto link = dynamic_cast<App::Link*>(part)) {
auto linked_obj = link->getLinkedObject();
if (dynamic_cast<App::Part*>(linked_obj) || dynamic_cast<AssemblyObject*>(linked_obj)) {
for (auto& obj : linked_obj->getOutList()) {
bool found = getTargetPlacementRelativeTo(foundPlc,
targetObj,
obj,
container,
inContainerBranch);
if (!found) {
continue;
}
foundPlc = getPlacementFromProp(link, "Placement") * foundPlc;
return true;
}
}
bool found = getTargetPlacementRelativeTo(foundPlc,
targetObj,
linked_obj,
container,
inContainerBranch,
true);
if (found) {
if (!ignorePlacement) {
foundPlc = getPlacementFromProp(link, "Placement") * foundPlc;
}
return true;
}
}
return false;
}
Base::Placement AssemblyObject::getGlobalPlacement(App::DocumentObject* targetObj,
App::DocumentObject* container)
App::DocumentObject* rootObj,
const std::string& sub)
{
bool inContainerBranch = (container == nullptr);
auto rootObjects = App::GetApplication().getActiveDocument()->getRootObjectsIgnoreLinks();
for (auto& part : rootObjects) {
Base::Placement foundPlc;
bool found =
getTargetPlacementRelativeTo(foundPlc, targetObj, part, container, inContainerBranch);
if (found) {
return foundPlc;
if (!targetObj || !rootObj || sub == "") {
return Base::Placement();
}
std::vector<std::string> 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 (obj->isDerivedFrom<App::Link>()) {
// 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* joint,
const char* targetObj,
const char* container)
Base::Placement AssemblyObject::getGlobalPlacement(App::DocumentObject* targetObj,
App::PropertyXLinkSub* prop)
{
App::DocumentObject* obj = getObjFromProp(joint, targetObj);
App::DocumentObject* part = getObjFromProp(joint, container);
return getGlobalPlacement(obj, part);
if (!targetObj || !prop) {
return Base::Placement();
}
std::vector<std::string> subs = prop->getSubValues();
if (subs.empty()) {
return Base::Placement();
}
return getGlobalPlacement(targetObj, prop->getValue(), subs[0]);
}
double AssemblyObject::getJointDistance(App::DocumentObject* joint)
@@ -2149,19 +2105,57 @@ JointType AssemblyObject::getJointType(App::DocumentObject* joint)
return jointType;
}
const char* AssemblyObject::getElementFromProp(App::DocumentObject* obj, const char* propName)
std::vector<std::string> AssemblyObject::getSubAsList(App::PropertyXLinkSub* prop)
{
auto* prop = dynamic_cast<App::PropertyXLinkSub*>(obj->getPropertyByName(propName));
if (!prop) {
return "";
return {};
}
auto subs = prop->getSubValues();
std::vector<std::string> subs = prop->getSubValues();
if (subs.empty()) {
return {};
}
return splitSubName(subs[0]);
}
std::vector<std::string> AssemblyObject::getSubAsList(App::DocumentObject* obj, const char* pName)
{
auto* prop = dynamic_cast<App::PropertyXLinkSub*>(obj->getPropertyByName(pName));
return getSubAsList(prop);
}
std::vector<std::string> AssemblyObject::splitSubName(const std::string& sub)
{
// Turns 'Part.Part001.Body.Pad.Edge1'
// Into ['Part', 'Part001','Body','Pad','Edge1']
std::vector<std::string> 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;
}
const char* AssemblyObject::getElementFromProp(App::DocumentObject* obj, const char* pName)
{
std::vector<std::string> names = getSubAsList(obj, pName);
if (names.empty()) {
return "";
}
return subs[0].c_str();
return names.back().c_str();
}
std::string AssemblyObject::getElementTypeFromProp(App::DocumentObject* obj, const char* propName)
@@ -2185,10 +2179,157 @@ App::DocumentObject* AssemblyObject::getObjFromProp(App::DocumentObject* joint,
return propObj->getValue();
}
App::DocumentObject* AssemblyObject::getLinkedObjFromProp(App::DocumentObject* joint,
const char* pObj)
App::DocumentObject* AssemblyObject::getObjFromRef(App::PropertyXLinkSub* prop)
{
auto* obj = getObjFromProp(joint, pObj);
if (!prop) {
return nullptr;
}
App::Document* doc = prop->getValue()->getDocument();
std::vector<std::string> names = getSubAsList(prop);
// 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<PartApp::Datum>());
};
// Helper function to handle PartDesign::Body objects
auto handlePartDesignBody = [&](App::DocumentObject* obj,
std::vector<std::string>::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;
}
// The last but one name should be the selected
if (std::next(it) == std::prev(names.end())) {
return obj;
}
if (obj->isDerivedFrom<App::Part>()) {
continue;
}
else if (obj->isDerivedFrom<PartDesign::Body>()) {
return handlePartDesignBody(obj, it);
}
else if (obj->isDerivedFrom<PartApp::Feature>()) {
// Primitive, fastener, gear, etc.
return obj;
}
else if (obj->isDerivedFrom<App::Link>()) {
App::DocumentObject* linked_obj = obj->getLinkedObject();
if (linked_obj->isDerivedFrom<PartDesign::Body>()) {
auto* retObj = handlePartDesignBody(linked_obj, it);
return retObj == linked_obj ? obj : retObj;
}
else if (linked_obj->isDerivedFrom<PartApp::Feature>()) {
return obj;
}
else {
doc = linked_obj->getDocument();
continue;
}
}
}
return nullptr;
}
App::DocumentObject* AssemblyObject::getObjFromRef(App::DocumentObject* joint, const char* pName)
{
auto* prop = dynamic_cast<App::PropertyXLinkSub*>(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<std::string> names = 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->isDerivedFrom<App::Link>()) { // 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;
}
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<std::string> subs = prop->getSubValues();
if (subs.empty()) {
return nullptr;
}
return getMovingPartFromRef(obj, subs[0]);
}
App::DocumentObject* AssemblyObject::getMovingPartFromRef(App::DocumentObject* joint,
const char* pName)
{
auto* prop = dynamic_cast<App::PropertyXLinkSub*>(joint->getPropertyByName(pName));
return getMovingPartFromRef(prop);
}
App::DocumentObject* AssemblyObject::getLinkedObjFromRef(App::DocumentObject* joint,
const char* pObj)
{
auto* obj = getObjFromRef(joint, pObj);
if (obj) {
return obj->getLinkedObject(true);
}

View File

@@ -44,6 +44,11 @@ class ASMTMarker;
class ASMTPart;
} // namespace MbD
namespace App
{
class PropertyXLinkSub;
} // namespace App
namespace Base
{
class Placement;
@@ -57,6 +62,8 @@ namespace Assembly
class JointGroup;
class ViewGroup;
using ObjRefPair = std::pair<App::DocumentObject*, App::PropertyXLinkSub*>;
// This enum has to be the same as the one in JointObject.py
enum class JointType
{
@@ -123,7 +130,6 @@ enum class DistanceType
Other,
};
class AssemblyExport AssemblyObject: public App::Part
{
PROPERTY_HEADER_WITH_OVERRIDE(Assembly::AssemblyObject);
@@ -173,8 +179,7 @@ public:
JointType jointType);
std::shared_ptr<MbD::ASMTJoint> makeMbdJointDistance(App::DocumentObject* joint);
std::string handleOneSideOfJoint(App::DocumentObject* joint,
const char* propObjLinkName,
const char* propPartName,
const char* propRefName,
const char* propPlcName);
void getRackPinionMarkers(App::DocumentObject* joint,
std::string& markerNameI,
@@ -197,20 +202,23 @@ public:
bool isJointConnectingPartToGround(App::DocumentObject* joint, const char* partPropName);
bool isJointTypeConnecting(App::DocumentObject* joint);
bool isObjInSetOfObjRefPairs(App::DocumentObject* obj, const std::set<ObjRefPair>& pairs);
void removeUnconnectedJoints(std::vector<App::DocumentObject*>& joints,
std::vector<App::DocumentObject*> groundedObjs);
void traverseAndMarkConnectedParts(App::DocumentObject* currentPart,
std::set<App::DocumentObject*>& connectedParts,
std::set<ObjRefPair>& connectedParts,
const std::vector<App::DocumentObject*>& joints);
std::vector<App::DocumentObject*>
getConnectedParts(App::DocumentObject* part, const std::vector<App::DocumentObject*>& joints);
std::vector<ObjRefPair> getConnectedParts(App::DocumentObject* part,
const std::vector<App::DocumentObject*>& joints);
bool isPartGrounded(App::DocumentObject* part);
bool isPartConnected(App::DocumentObject* part);
std::vector<App::DocumentObject*> getDownstreamParts(App::DocumentObject* part,
App::DocumentObject* joint);
std::vector<ObjRefPair> getDownstreamParts(App::DocumentObject* part,
App::DocumentObject* joint);
std::vector<App::DocumentObject*> getUpstreamParts(App::DocumentObject* part, int limit = 0);
App::DocumentObject* getUpstreamMovingPart(App::DocumentObject* part);
App::DocumentObject* getUpstreamMovingPart(App::DocumentObject* part,
App::DocumentObject*& joint,
std::string& name);
double getObjMass(App::DocumentObject* obj);
void setObjMasses(std::vector<std::pair<App::DocumentObject*, double>> objectMasses);
@@ -253,19 +261,23 @@ public:
static const char* 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* getLinkedObjFromProp(App::DocumentObject* joint, const char* pObj);
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<std::string> getSubAsList(App::PropertyXLinkSub* prop);
static std::vector<std::string> getSubAsList(App::DocumentObject* joint, const char* propName);
static std::vector<std::string> splitSubName(const std::string& subName);
static Base::Placement getPlacementFromProp(App::DocumentObject* obj, const char* propName);
static bool getTargetPlacementRelativeTo(Base::Placement& foundPlc,
App::DocumentObject* targetObj,
App::DocumentObject* part,
App::DocumentObject* container,
bool inContainerBranch,
bool ignorePlacement = false);
static Base::Placement getGlobalPlacement(App::DocumentObject* targetObj,
App::DocumentObject* container = nullptr);
static Base::Placement getGlobalPlacement(App::DocumentObject* joint,
const char* targetObj,
const char* container = "");
App::DocumentObject* rootObj,
const std::string& sub);
static Base::Placement getGlobalPlacement(App::DocumentObject* targetObj,
App::PropertyXLinkSub* prop);
};
// using AssemblyObjectPython = App::FeaturePythonT<AssemblyObject>;

View File

@@ -149,29 +149,34 @@ class TestCore(unittest.TestCase):
box.Placement = App.Placement(App.Vector(10, 20, 30), App.Rotation(15, 25, 35))
# Step 0 : box with placement. No element selected
plc = joint.Proxy.findPlacement(joint, box, box, "", "")
ref = [self.assembly, [box.Name + ".", box.Name + "."]]
plc = joint.Proxy.findPlacement(joint, ref)
targetPlc = App.Placement(App.Vector(), App.Rotation())
self.assertTrue(plc.isSame(targetPlc, 1e-6), "'{}' failed - Step 0".format(operation))
# Step 1 : box with placement. Face + Vertex
plc = joint.Proxy.findPlacement(joint, box, box, "Face6", "Vertex7")
ref = [self.assembly, [box.Name + ".Face6", box.Name + ".Vertex7"]]
plc = joint.Proxy.findPlacement(joint, ref)
targetPlc = App.Placement(App.Vector(L, W, H), App.Rotation())
self.assertTrue(plc.isSame(targetPlc, 1e-6), "'{}' failed - Step 1".format(operation))
# Step 2 : box with placement. Edge + Vertex
plc = joint.Proxy.findPlacement(joint, box, box, "Edge8", "Vertex8")
ref = [self.assembly, [box.Name + ".Edge8", box.Name + ".Vertex8"]]
plc = joint.Proxy.findPlacement(joint, ref)
targetPlc = App.Placement(App.Vector(L, W, 0), App.Rotation(0, -90, 270))
self.assertTrue(plc.isSame(targetPlc, 1e-6), "'{}' failed - Step 2".format(operation))
# Step 3 : box with placement. Vertex
plc = joint.Proxy.findPlacement(joint, box, box, "Vertex3", "Vertex3")
ref = [self.assembly, [box.Name + ".Vertex3", box.Name + ".Vertex3"]]
plc = joint.Proxy.findPlacement(joint, ref)
targetPlc = App.Placement(App.Vector(0, W, H), App.Rotation())
_msg(" plc '{}'".format(plc))
_msg(" targetPlc '{}'".format(targetPlc))
self.assertTrue(plc.isSame(targetPlc, 1e-6), "'{}' failed - Step 3".format(operation))
# Step 4 : box with placement. Face
plc = joint.Proxy.findPlacement(joint, box, box, "Face2", "Face2")
ref = [self.assembly, [box.Name + ".Face2", box.Name + ".Face2"]]
plc = joint.Proxy.findPlacement(joint, ref)
targetPlc = App.Placement(App.Vector(L, W / 2, H / 2), App.Rotation(0, -90, 180))
_msg(" plc '{}'".format(plc))
_msg(" targetPlc '{}'".format(targetPlc))
@@ -200,24 +205,11 @@ class TestCore(unittest.TestCase):
joint = self.jointgroup.newObject("App::FeaturePython", "testJoint")
JointObject.Joint(joint, 0)
current_selection = []
current_selection.append(
{
"object": box2,
"part": box2,
"element_name": "Face6",
"vertex_name": "Vertex7",
}
)
current_selection.append(
{
"object": box,
"part": box,
"element_name": "Face6",
"vertex_name": "Vertex7",
}
)
refs = [
[self.assembly, [box2.Name + ".Face6", box2.Name + ".Vertex7"]],
[self.assembly, [box.Name + ".Face6", box.Name + ".Vertex7"]],
]
joint.Proxy.setJointConnectors(joint, current_selection)
joint.Proxy.setJointConnectors(joint, refs)
self.assertTrue(box.Placement.isSame(box2.Placement, 1e-6), "'{}'".format(operation))

View File

@@ -529,22 +529,17 @@ class CommandToggleGrounded:
# If you select 2 solids (bodies for example) within an assembly.
# There'll be a single sel but 2 SubElementNames.
for sub in sel.SubElementNames:
# Only objects within the assembly.
objs_names, element_name = UtilsAssembly.getObjsNamesAndElement(sel.ObjectName, sub)
if assembly.Name not in objs_names:
continue
ref = [sel.Object, [sub, sub]]
moving_part = UtilsAssembly.getMovingPart(assembly, ref)
full_element_name = UtilsAssembly.getFullElementName(sel.ObjectName, sub)
obj = UtilsAssembly.getObject(full_element_name)
part_containing_obj = UtilsAssembly.getContainingPart(full_element_name, obj)
# Only objects within the assembly.
if moving_part is None:
continue
# Check if part is grounded and if so delete the joint.
ungrounded = False
for joint in joint_group.Group:
if (
hasattr(joint, "ObjectToGround")
and joint.ObjectToGround == part_containing_obj
):
if hasattr(joint, "ObjectToGround") and joint.ObjectToGround == moving_part:
doc = App.ActiveDocument
doc.removeObject(joint.Name)
doc.recompute()
@@ -554,7 +549,7 @@ class CommandToggleGrounded:
continue
# Create groundedJoint.
createGroundedJoint(part_containing_obj)
createGroundedJoint(moving_part)
App.closeActiveTransaction()

View File

@@ -227,47 +227,80 @@ class ExplodedViewStep:
def __init__(self, evStep, type_index=0):
evStep.Proxy = self
# we cannot use "App::PropertyLinkList" for objs because they can be external
evStep.addProperty(
"App::PropertyStringList",
"ObjNames",
"Exploded Move",
QT_TRANSLATE_NOOP("App::Property", "The object moved by the move"),
)
self.createProperties(evStep)
evStep.addProperty(
"App::PropertyLinkList",
"Parts",
"Exploded Move",
QT_TRANSLATE_NOOP("App::Property", "The containing parts of objects moved by the move"),
)
evStep.addProperty(
"App::PropertyPlacement",
"MovementTransform",
"Exploded Move",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the movement of the move. The end placement is the result of the start placement * this placement.",
),
)
evStep.addProperty(
"App::PropertyEnumeration",
"MoveType",
"Exploded Move",
QT_TRANSLATE_NOOP("App::Property", "The type of the move"),
)
evStep.MoveType = ExplodedViewStepTypes # sets the list
evStep.MoveType = ExplodedViewStepTypes[type_index] # set the initial value
def onDocumentRestored(self, evStep):
self.createProperties(evStep)
def createProperties(self, evStep):
self.migrationScript(evStep)
if not hasattr(evStep, "References"):
evStep.addProperty(
"App::PropertyXLinkSubHidden",
"References",
"Exploded Move",
QT_TRANSLATE_NOOP("App::Property", "The objects moved by the move"),
)
if not hasattr(evStep, "MovementTransform"):
evStep.addProperty(
"App::PropertyPlacement",
"MovementTransform",
"Exploded Move",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the movement of the move. The end placement is the result of the start placement * this placement.",
),
)
if not hasattr(evStep, "MoveType"):
evStep.addProperty(
"App::PropertyEnumeration",
"MoveType",
"Exploded Move",
QT_TRANSLATE_NOOP("App::Property", "The type of the move"),
)
def migrationScript(self, evStep):
if hasattr(evStep, "Parts"):
objNames = evStep.ObjNames
parts = evStep.Parts
evStep.removeProperty("ObjNames")
evStep.removeProperty("Parts")
evStep.addProperty(
"App::PropertyXLinkSubHidden",
"References",
"Exploded Move",
QT_TRANSLATE_NOOP("App::Property", "The objects moved by the move"),
)
rootObj = None
paths = []
for objName, part in zip(objNames, parts):
# now we need to get the 'selection-root-obj' and the global path
obj = UtilsAssembly.getObjectInPart(objName, part)
rootObj, path = UtilsAssembly.getRootPath(obj, part)
if rootObj is None:
continue
paths.append(path)
# Note: all the parts should have the same rootObj.
evStep.References = [rootObj, paths]
def dumps(self):
return None
def loads(self, state):
return None
def onChanged(self, joint, prop):
def onChanged(self, evStep, prop):
"""Do something when a property has changed"""
pass
@@ -277,20 +310,23 @@ class ExplodedViewStep:
pass
def applyStep(self, move, com=App.Vector(), size=100):
if not UtilsAssembly.isRefValid(move.References, 1):
return
positions = []
if move.MoveType == "Radial":
distance = move.MovementTransform.Base.Length
factor = 4 * distance / size
for objName, part in zip(move.ObjNames, move.Parts):
if not objName:
continue
obj = UtilsAssembly.getObjectInPart(objName, part)
subs = move.References[1]
for sub in subs:
ref = [move.References[0], [sub]]
obj = UtilsAssembly.getObject(ref)
if not obj:
continue
if move.ViewObject:
startPos = UtilsAssembly.getCenterOfBoundingBox([obj], [part])
startPos = UtilsAssembly.getCenterOfBoundingBox([obj], [ref])
if move.MoveType == "Radial":
objCom, objSize = UtilsAssembly.getComAndSize(obj)
@@ -300,7 +336,7 @@ class ExplodedViewStep:
obj.Placement = move.MovementTransform * obj.Placement
if move.ViewObject:
endPos = UtilsAssembly.getCenterOfBoundingBox([obj], [part])
endPos = UtilsAssembly.getCenterOfBoundingBox([obj], [ref])
positions.append([startPos, endPos])
if move.ViewObject:
@@ -308,14 +344,6 @@ class ExplodedViewStep:
return positions
def getMovingobjects(self, move):
movingObjs = []
for objName, part in zip(move.ObjNames, move.Parts):
obj = UtilsAssembly.getObjectInPart(objName, part)
if obj is not None:
movingObjs.append(obj)
return movingObjs
class ViewProviderExplodedViewStep:
def __init__(self, vobj):
@@ -485,7 +513,7 @@ class TaskAssemblyCreateView(QtCore.QObject):
self.selectingFeature = False
self.form.LabelAlignDragger.setVisible(False)
self.preselection_dict = None
self.presel_ref = None
self.blockSetDragger = False
self.blockDraggerMove = True
@@ -532,8 +560,8 @@ class TaskAssemblyCreateView(QtCore.QObject):
return
self.dismissCurrentStep()
self.selectedRefs = []
self.selectedObjs = []
self.selectedParts = [] # containing parts
self.selectedObjsInitPlc = []
selection = Gui.Selection.getSelectionEx("*", 0)
if not selection:
@@ -549,39 +577,33 @@ class TaskAssemblyCreateView(QtCore.QObject):
continue
for sub_name in sel.SubElementNames:
# Only objects within the assembly.
objs_names, element_name = UtilsAssembly.getObjsNamesAndElement(
sel.ObjectName, sub_name
)
if self.assembly.Name not in objs_names:
ref = [sel.Object, [sub_name]]
obj = UtilsAssembly.getObject(ref)
moving_part = UtilsAssembly.getMovingPart(self.assembly, ref)
element_name = UtilsAssembly.getElementName(sub_name)
# Only objects within the assembly, not the assembly and not elements.
if obj is None or moving_part is None or obj == self.assembly or element_name != "":
Gui.Selection.removeSelection(sel.Object, sub_name)
continue
obj_name = sel.ObjectName
full_obj_name = UtilsAssembly.getFullObjName(obj_name, sub_name)
full_element_name = UtilsAssembly.getFullElementName(obj_name, sub_name)
selected_object = UtilsAssembly.getObject(full_element_name)
if selected_object is None:
continue
element_name = UtilsAssembly.getElementName(full_element_name)
part = UtilsAssembly.getContainingPart(
full_element_name, selected_object, self.assembly
)
partAsSolid = self.form.CheckBox_PartsAsSingleSolid.isChecked()
if partAsSolid:
obj = moving_part
if selected_object == self.assembly or element_name != "":
# do not accept selection of assembly itself or elements
Gui.Selection.removeSelection(sel.Object, sub_name)
continue
# truncate the sub name at obj.Name
if partAsSolid:
# We handle both cases separately because with external files there
# can be several times the same name. For containing part we are sure it's
# the first instance, for the object we are sure it's the last.
ref[1][0] = UtilsAssembly.truncateSubAtLast(ref[1][0], obj.Name)
else:
ref[1][0] = UtilsAssembly.truncateSubAtFirst(ref[1][0], obj.Name)
if self.form.CheckBox_PartsAsSingleSolid.isChecked():
selected_object = part
if not selected_object in self.selectedObjs and hasattr(
selected_object, "Placement"
):
self.selectedObjs.append(selected_object)
self.selectedParts.append(part)
self.selectedObjsInitPlc.append(App.Placement(selected_object.Placement))
if not obj in self.selectedObjs and hasattr(obj, "Placement"):
self.selectedRefs.append(ref)
self.selectedObjs.append(obj)
self.selectedObjsInitPlc.append(App.Placement(obj.Placement))
if len(self.selectedObjs) != 0:
self.enableDragger(True)
@@ -658,11 +680,11 @@ class TaskAssemblyCreateView(QtCore.QObject):
if self.alignMode == "Custom":
self.initialDraggerPlc = App.Placement(self.assembly.ViewObject.DraggerPlacement)
else:
plc = UtilsAssembly.getGlobalPlacement(self.selectedObjs[0], self.selectedParts[0])
plc = UtilsAssembly.getGlobalPlacement(self.selectedRefs[0], self.selectedObjs[0])
self.initialDraggerPlc = App.Placement(plc)
if self.alignMode == "Center":
self.initialDraggerPlc.Base = UtilsAssembly.getCenterOfBoundingBox(
self.selectedObjs, self.selectedParts
self.selectedObjs, self.selectedRefs
)
def setDraggerObjectPlc(self):
@@ -684,19 +706,20 @@ class TaskAssemblyCreateView(QtCore.QObject):
ExplodedViewStep(self.currentStep, moveType_index)
ViewProviderExplodedViewStep(self.currentStep.ViewObject)
self.currentStep.MovementTransform = App.Placement()
# Note: the rootObj of all our refs must be the same since all the
# objects are within assembly. So we put all the sub in a single ref.
listOfSubs = []
for ref in self.selectedRefs:
listOfSubs.append(ref[1][0])
self.currentStep.References = [self.selectedRefs[0][0], listOfSubs]
# Note: self.viewObj.Moves.append(self.currentStep) does not work
listOfMoves = self.viewObj.Moves
listOfMoves.append(self.currentStep)
self.viewObj.Moves = listOfMoves
objNames = []
for obj in self.selectedObjs:
objNames.append(obj.Name)
self.currentStep.MovementTransform = App.Placement()
self.currentStep.ObjNames = objNames
self.currentStep.Parts = self.selectedParts
def dismissCurrentStep(self):
if self.currentStep is None:
return
@@ -728,13 +751,13 @@ class TaskAssemblyCreateView(QtCore.QObject):
self.currentStep.Proxy.applyStep(self.currentStep, self.com, self.size)
def draggerFinished(self, event):
if self.currentStep.MoveType == "Radial":
self.currentStep = None
isRadial = self.currentStep.MoveType == "Radial"
self.currentStep = None
if isRadial:
Gui.Selection.clearSelection()
return
self.currentStep = None
# Reset the initial placements
self.findDraggerInitialPlc()
@@ -748,29 +771,23 @@ class TaskAssemblyCreateView(QtCore.QObject):
view = Gui.activeDocument().activeView()
cursor_info = view.getObjectInfo(view.getCursorPos())
if not cursor_info or not self.preselection_dict:
if not cursor_info or not self.presel_ref:
self.assembly.ViewObject.DraggerVisibility = False
return
newPos = App.Vector(cursor_info["x"], cursor_info["y"], cursor_info["z"])
self.preselection_dict["mouse_pos"] = newPos
ref = self.presel_ref
element_name = UtilsAssembly.getElementName(ref[1][0])
if self.preselection_dict["element_name"] == "":
self.preselection_dict["vertex_name"] = ""
if element_name == "":
vertex_name = ""
else:
self.preselection_dict["vertex_name"] = UtilsAssembly.findElementClosestVertex(
self.preselection_dict
)
newPos = App.Vector(cursor_info["x"], cursor_info["y"], cursor_info["z"])
vertex_name = UtilsAssembly.findElementClosestVertex(self.assembly, ref, newPos)
obj = self.preselection_dict["object"]
part = self.preselection_dict["part"]
plc = UtilsAssembly.findPlacement(
obj,
part,
self.preselection_dict["element_name"],
self.preselection_dict["vertex_name"],
)
global_plc = UtilsAssembly.getGlobalPlacement(obj, part)
ref = UtilsAssembly.addVertexToReference(ref, vertex_name)
plc = UtilsAssembly.findPlacement(ref)
global_plc = UtilsAssembly.getGlobalPlacement(ref)
plc = global_plc * plc
self.blockDraggerMove = True
@@ -827,22 +844,23 @@ class TaskAssemblyCreateView(QtCore.QObject):
return
else:
full_element_name = UtilsAssembly.getFullElementName(obj_name, sub_name)
selected_object = UtilsAssembly.getObject(full_element_name)
if selected_object is None:
ref = [App.getDocument(doc_name).getObject(obj_name), [sub_name]]
obj = UtilsAssembly.getObject(ref)
moving_part = UtilsAssembly.getMovingPart(self.assembly, ref)
if obj is None or moving_part is None:
return
element_name = UtilsAssembly.getElementName(full_element_name)
part = UtilsAssembly.getContainingPart(
full_element_name, selected_object, self.assembly
)
if self.form.CheckBox_PartsAsSingleSolid.isChecked():
part = moving_part
else:
part = obj
if not self.form.CheckBox_PartsAsSingleSolid.isChecked():
part = selected_object
element_name = UtilsAssembly.getElementName(sub_name)
if element_name != "":
# When selecting, we do not want to select an element, but only the containing part.
Gui.Selection.removeSelection(selected_object, element_name)
Gui.Selection.removeSelection(doc_name, obj_name, sub_name)
if Gui.Selection.isSelected(part, ""):
Gui.Selection.removeSelection(part, "")
else:
@@ -857,31 +875,17 @@ class TaskAssemblyCreateView(QtCore.QObject):
self.findDraggerInitialPlc()
return
full_element_name = UtilsAssembly.getFullElementName(obj_name, sub_name)
element_name = UtilsAssembly.getElementName(full_element_name)
element_name = UtilsAssembly.getElementName(sub_name)
if element_name == "":
self.setDragger()
pass
def setPreselection(self, doc_name, obj_name, sub_name):
if not self.selectingFeature or not sub_name:
self.preselection_dict = None
self.presel_ref = None
return
full_obj_name = UtilsAssembly.getFullObjName(obj_name, sub_name)
full_element_name = UtilsAssembly.getFullElementName(obj_name, sub_name)
selected_object = UtilsAssembly.getObject(full_element_name)
element_name = UtilsAssembly.getElementName(full_element_name)
part = UtilsAssembly.getContainingPart(full_element_name, selected_object, self.assembly)
self.preselection_dict = {
"object": selected_object,
"part": part,
"sub_name": sub_name,
"element_name": element_name,
"full_element_name": full_element_name,
"full_obj_name": full_obj_name,
}
self.presel_ref = [App.getDocument(doc_name).getObject(obj_name), [sub_name]]
def clearSelection(self, doc_name):
self.form.stepList.clearSelection()

View File

@@ -164,10 +164,10 @@ bool ViewProviderAssembly::canDragObjectToTarget(App::DocumentObject* obj,
for (auto joint : allJoints) {
// getLinkObjFromProp returns nullptr if the property doesn't exist.
App::DocumentObject* obj1 = AssemblyObject::getObjFromProp(joint, "Object1");
App::DocumentObject* obj2 = AssemblyObject::getObjFromProp(joint, "Object2");
App::DocumentObject* part1 = AssemblyObject::getObjFromProp(joint, "Part1");
App::DocumentObject* part2 = AssemblyObject::getObjFromProp(joint, "Part2");
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");
if (obj == obj1 || obj == obj2 || obj == part1 || obj == part2 || obj == obj3) {
if (!prompted) {
@@ -376,12 +376,12 @@ bool ViewProviderAssembly::mouseMove(const SbVec2s& cursorPos, Gui::View3DInvent
}
for (auto& pair : docsToMove) {
App::DocumentObject* obj = pair.first;
for (auto& objToMove : docsToMove) {
App::DocumentObject* obj = objToMove.obj;
auto* propPlacement =
dynamic_cast<App::PropertyPlacement*>(obj->getPropertyByName("Placement"));
if (propPlacement) {
Base::Placement plc = pair.second;
Base::Placement plc = objToMove.plc;
// Base::Console().Warning("newPos %f %f %f\n", newPos.x, newPos.y, newPos.z);
if (dragMode == DragMode::RotationOnPlane) {
@@ -585,7 +585,7 @@ bool ViewProviderAssembly::getSelectedObjectsWithinAssembly(bool addPreselection
std::vector<std::string> objsSubNames = selObj.getSubNames();
for (auto& subNamesStr : objsSubNames) {
std::vector<std::string> subNames = parseSubNames(subNamesStr);
std::vector<std::string> subNames = AssemblyObject::splitSubName(subNamesStr);
if (subNames.empty()) {
continue;
}
@@ -593,7 +593,8 @@ bool ViewProviderAssembly::getSelectedObjectsWithinAssembly(bool addPreselection
continue;
}
App::DocumentObject* obj = getObjectFromSubNames(subNames);
App::DocumentObject* selRoot = selObj.getObject();
App::DocumentObject* obj = assemblyPart->getMovingPartFromRef(selRoot, subNamesStr);
if (!canDragObjectIn3d(obj)) {
continue;
@@ -601,7 +602,10 @@ bool ViewProviderAssembly::getSelectedObjectsWithinAssembly(bool addPreselection
auto* pPlc =
dynamic_cast<App::PropertyPlacement*>(obj->getPropertyByName("Placement"));
docsToMove.emplace_back(obj, pPlc->getValue());
MovingObject movingObj(obj, pPlc->getValue(), selRoot, subNamesStr);
docsToMove.emplace_back(movingObj);
}
}
}
@@ -613,15 +617,15 @@ bool ViewProviderAssembly::getSelectedObjectsWithinAssembly(bool addPreselection
// Base::Console().Warning("Gui::Selection().getPreselection().pSubName %s\n",
// Gui::Selection().getPreselection().pSubName);
std::string subNamesStr = Gui::Selection().getPreselection().pSubName;
std::vector<std::string> subNames = parseSubNames(subNamesStr);
App::DocumentObject* selRoot = Gui::Selection().getPreselection().Object.getObject();
std::string sub = Gui::Selection().getPreselection().pSubName;
App::DocumentObject* obj = getObjectFromSubNames(subNames);
App::DocumentObject* obj = assemblyPart->getMovingPartFromRef(selRoot, sub);
if (canDragObjectIn3d(obj)) {
bool alreadyIn = false;
for (auto& pair : docsToMove) {
App::DocumentObject* obji = pair.first;
for (auto& movingObj : docsToMove) {
App::DocumentObject* obji = movingObj.obj;
if (obji == obj) {
alreadyIn = true;
break;
@@ -635,7 +639,9 @@ bool ViewProviderAssembly::getSelectedObjectsWithinAssembly(bool addPreselection
Gui::Selection().clearSelection();
docsToMove.clear();
}
docsToMove.emplace_back(obj, pPlc->getValue());
MovingObject movingObj(obj, pPlc->getValue(), selRoot, sub);
docsToMove.emplace_back(movingObj);
}
}
}
@@ -643,88 +649,12 @@ bool ViewProviderAssembly::getSelectedObjectsWithinAssembly(bool addPreselection
return !docsToMove.empty();
}
std::vector<std::string> ViewProviderAssembly::parseSubNames(std::string& subNamesStr)
{
std::vector<std::string> subNames;
std::string subName;
std::istringstream subNameStream(subNamesStr);
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 (!subNamesStr.empty() && subNamesStr.back() == '.') {
subNames.push_back(""); // Append empty string for trailing dot.
}
return subNames;
}
App::DocumentObject* ViewProviderAssembly::getObjectFromSubNames(std::vector<std::string>& subNames)
{
App::Document* appDoc = getObject()->getDocument();
std::string objName;
if (subNames.size() < 2) {
return nullptr;
}
else if (subNames.size() == 2) {
// If two subnames then it can't be a body and the object we want is the first one
// For example we want box in "box.face1"
// "assembly.part.box.face1"
// "p.fcstd.assembly.LinkToPart.box.face1"
// "p2.fcstd.Part.box."
return appDoc->getObject(subNames[0].c_str());
}
// From here subnames is at least 3 and can be more. There are several cases to consider :
// bodyOrLink.pad.face1 -> bodyOrLink should be the moving entity
// partOrLink.bodyOrLink.pad.face1 -> partOrLink should be the moving entity
// partOrLink.box.face1 -> partOrLink should be the moving entity
// partOrLink1...ParOrLinkn.bodyOrLink.pad.face1 -> partOrLink1 should be the moving entity
// assembly1.partOrLink1...ParOrLinkn.bodyOrLink.pad.face1 -> partOrLink1 should be the moving
// entity assembly1.boxOrLink1.face1 -> boxOrLink1 should be the moving entity
for (auto objName : subNames) {
App::DocumentObject* obj = appDoc->getObject(objName.c_str());
if (!obj) {
continue;
}
if (obj->getTypeId().isDerivedFrom(AssemblyObject::getClassTypeId())) {
continue;
}
else if (obj->getTypeId().isDerivedFrom(App::Part::getClassTypeId())
|| obj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) {
return obj;
}
else if (obj->getTypeId().isDerivedFrom(App::Link::getClassTypeId())) {
App::Link* link = dynamic_cast<App::Link*>(obj);
App::DocumentObject* linkedObj = link->getLinkedObject(true);
if (!linkedObj) {
continue;
}
if (linkedObj->getTypeId().isDerivedFrom(App::Part::getClassTypeId())
|| linkedObj->getTypeId().isDerivedFrom(Part::Feature::getClassTypeId())) {
return obj;
}
}
}
return nullptr;
}
ViewProviderAssembly::DragMode ViewProviderAssembly::findDragMode()
{
if (docsToMove.size() == 1) {
auto* assemblyPart = static_cast<AssemblyObject*>(getObject());
std::string partPropName;
movingJoint =
assemblyPart->getJointOfPartConnectingToGround(docsToMove[0].first, partPropName);
std::string pName;
movingJoint = assemblyPart->getJointOfPartConnectingToGround(docsToMove[0].obj, pName);
if (!movingJoint) {
return DragMode::Translation;
@@ -735,45 +665,69 @@ ViewProviderAssembly::DragMode ViewProviderAssembly::findDragMode()
// 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
// actually move A
App::DocumentObject* upstreamPart =
assemblyPart->getUpstreamMovingPart(docsToMove[0].first);
docsToMove.clear();
if (!upstreamPart) {
return DragMode::None;
}
auto* propPlacement =
dynamic_cast<App::PropertyPlacement*>(upstreamPart->getPropertyByName("Placement"));
if (propPlacement) {
docsToMove.emplace_back(upstreamPart, propPlacement->getValue());
}
movingJoint =
assemblyPart->getJointOfPartConnectingToGround(docsToMove[0].first, partPropName);
auto* upPart =
assemblyPart->getUpstreamMovingPart(docsToMove[0].obj, movingJoint, pName);
if (!movingJoint) {
return DragMode::Translation;
}
docsToMove.clear();
if (!upPart) {
return DragMode::None;
}
auto* pPlc =
dynamic_cast<App::PropertyPlacement*>(upPart->getPropertyByName("Placement"));
if (pPlc) {
auto* ref = dynamic_cast<App::PropertyXLinkSub*>(
movingJoint->getPropertyByName(pName.c_str()));
App::DocumentObject* selRoot = ref->getValue();
if (!selRoot) {
return DragMode::None;
}
std::vector<std::string> subs = ref->getSubValues();
if (subs.empty()) {
return DragMode::None;
}
docsToMove.emplace_back(upPart, pPlc->getValue(), selRoot, subs[0]);
}
jointType = AssemblyObject::getJointType(movingJoint);
}
const char* plcPropName = (partPropName == "Part1") ? "Placement1" : "Placement2";
const char* objPropName = (partPropName == "Part1") ? "Object1" : "Object2";
const char* plcPropName = (pName == "Reference1") ? "Placement1" : "Placement2";
// jcsPlc is relative to the Object
jcsPlc = AssemblyObject::getPlacementFromProp(movingJoint, plcPropName);
// Make jcsGlobalPlc relative to the origin of the doc
Base::Placement global_plc =
AssemblyObject::getGlobalPlacement(movingJoint, objPropName, partPropName.c_str());
auto* ref =
dynamic_cast<App::PropertyXLinkSub*>(movingJoint->getPropertyByName(pName.c_str()));
if (!ref) {
return DragMode::Translation;
}
auto* obj = assemblyPart->getObjFromRef(movingJoint, pName.c_str());
Base::Placement global_plc = AssemblyObject::getGlobalPlacement(obj, ref);
jcsGlobalPlc = global_plc * jcsPlc;
// Add downstream parts so that they move together
auto downstreamParts = assemblyPart->getDownstreamParts(docsToMove[0].first, movingJoint);
for (auto part : downstreamParts) {
auto* propPlacement =
dynamic_cast<App::PropertyPlacement*>(part->getPropertyByName("Placement"));
if (propPlacement) {
docsToMove.emplace_back(part, propPlacement->getValue());
auto downstreamParts = assemblyPart->getDownstreamParts(docsToMove[0].obj, movingJoint);
for (auto partRef : downstreamParts) {
auto* pPlc = dynamic_cast<App::PropertyPlacement*>(
partRef.first->getPropertyByName("Placement"));
if (pPlc) {
App::DocumentObject* selRoot = partRef.second->getValue();
if (!selRoot) {
return DragMode::None;
}
std::vector<std::string> subs = partRef.second->getSubValues();
if (subs.empty()) {
return DragMode::None;
}
docsToMove.emplace_back(partRef.first, pPlc->getValue(), selRoot, subs[0]);
}
}
@@ -859,15 +813,15 @@ void ViewProviderAssembly::initMove(const SbVec2s& cursorPos, Gui::View3DInvento
bool solveOnMove = hGrp->GetBool("SolveOnMove", true);
if (solveOnMove) {
objectMasses.clear();
for (auto& pair : docsToMove) {
objectMasses.push_back({pair.first, 10.0});
for (auto& movingObj : docsToMove) {
objectMasses.push_back({movingObj.obj, 10.0});
}
auto* assemblyPart = static_cast<AssemblyObject*>(getObject());
assemblyPart->setObjMasses(objectMasses);
std::vector<App::DocumentObject*> dragParts;
for (auto& pair : docsToMove) {
dragParts.push_back(pair.first);
for (auto& movingObj : docsToMove) {
dragParts.push_back(movingObj.obj);
}
assemblyPart->preDrag(dragParts);
}
@@ -910,13 +864,17 @@ void ViewProviderAssembly::initMoveDragger()
setDraggerVisibility(true);
// find the placement for the dragger.
App::DocumentObject* obj = docsToMove[0].first;
draggerInitPlc = AssemblyObject::getGlobalPlacement(obj, obj);
App::DocumentObject* part = docsToMove[0].obj;
draggerInitPlc =
AssemblyObject::getGlobalPlacement(part, docsToMove[0].rootObj, docsToMove[0].sub);
std::vector<App::DocumentObject*> listOfObjs;
for (auto& pair : docsToMove) {
listOfObjs.push_back(pair.first);
std::vector<App::PropertyXLinkSub*> listOfRefs;
for (auto& movingObj : docsToMove) {
listOfObjs.push_back(movingObj.obj);
listOfRefs.push_back(movingObj.ref);
}
Base::Vector3d pos = getCenterOfBoundingBox(listOfObjs, listOfObjs);
Base::Vector3d pos = getCenterOfBoundingBox(docsToMove);
draggerInitPlc.setPosition(pos);
setDraggerPlacement(draggerInitPlc);
@@ -939,12 +897,12 @@ void ViewProviderAssembly::draggerMotionCallback(void* data, SoDragger* d)
Base::Placement draggerPlc = sudoThis->getDraggerPlacement();
Base::Placement movePlc = draggerPlc * sudoThis->draggerInitPlc.inverse();
for (auto& pair : sudoThis->docsToMove) {
App::DocumentObject* obj = pair.first;
for (auto& movingObj : sudoThis->docsToMove) {
App::DocumentObject* obj = movingObj.obj;
auto* propPlc = dynamic_cast<App::PropertyPlacement*>(obj->getPropertyByName("Placement"));
if (propPlc) {
propPlc->setValue(movePlc * pair.second);
auto* pPlc = dynamic_cast<App::PropertyPlacement*>(obj->getPropertyByName("Placement"));
if (pPlc) {
pPlc->setValue(movePlc * movingObj.plc);
}
}
}
@@ -1078,14 +1036,14 @@ PyObject* ViewProviderAssembly::getPyObject()
// UTILS
Base::Vector3d
ViewProviderAssembly::getCenterOfBoundingBox(const std::vector<App::DocumentObject*>& objs,
const std::vector<App::DocumentObject*>& parts)
ViewProviderAssembly::getCenterOfBoundingBox(const std::vector<MovingObject>& movingObjs)
{
int count = 0;
Base::Vector3d center;
Base::Vector3d center; // feujhzef
for (size_t i = 0; i < objs.size(); ++i) {
Gui::ViewProvider* viewProvider = Gui::Application::Instance->getViewProvider(objs[i]);
for (auto& movingObj : movingObjs) {
Gui::ViewProvider* viewProvider =
Gui::Application::Instance->getViewProvider(movingObj.obj);
if (!viewProvider) {
continue;
}
@@ -1097,17 +1055,16 @@ ViewProviderAssembly::getCenterOfBoundingBox(const std::vector<App::DocumentObje
Base::Vector3d bboxCenter = boundingBox.GetCenter();
if (parts[i] != objs[i]) {
// 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(objs[i], "Placement");
plc = objPlc.inverse() * plc;
// Change plc to be relative to the origin of the document.
Base::Placement global_plc = AssemblyObject::getGlobalPlacement(objs[i], parts[i]);
plc = global_plc * plc;
bboxCenter = plc.getPosition();
}
// 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");
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);
plc = global_plc * plc;
bboxCenter = plc.getPosition();
center += bboxCenter;
++count;

View File

@@ -45,6 +45,37 @@ class View3DInventorViewer;
namespace AssemblyGui
{
struct MovingObject
{
App::DocumentObject* obj; // moving part
Base::Placement plc;
App::PropertyXLinkSub* ref;
App::DocumentObject* rootObj; // object of the selection object
std::string sub; // sub name given by the selection.
// Constructor
MovingObject(App::DocumentObject* o,
const Base::Placement& p,
App::DocumentObject* ro,
std::string& s)
: obj(o)
, plc(p)
, rootObj(ro)
, sub(s)
{}
// Default constructor
MovingObject()
: obj(nullptr)
, plc(Base::Placement())
, rootObj(nullptr)
, sub("")
{}
~MovingObject()
{}
};
class AssemblyGuiExport ViewProviderAssembly: public Gui::ViewProviderPart,
public Gui::SelectionObserver
{
@@ -139,8 +170,6 @@ public:
bool canDragObjectIn3d(App::DocumentObject* obj) const;
bool getSelectedObjectsWithinAssembly(bool addPreselection = true, bool onlySolids = false);
App::DocumentObject* getObjectFromSubNames(std::vector<std::string>& subNames);
std::vector<std::string> parseSubNames(std::string& subNamesStr);
/// Get the python wrapper for that ViewProvider
PyObject* getPyObject() override;
@@ -164,8 +193,7 @@ public:
Base::Placement getDraggerPlacement();
Gui::SoFCCSysDragger* getDragger();
static Base::Vector3d getCenterOfBoundingBox(const std::vector<App::DocumentObject*>& objs,
const std::vector<App::DocumentObject*>& parts);
static Base::Vector3d getCenterOfBoundingBox(const std::vector<MovingObject>& movingObjs);
DragMode dragMode;
bool canStartDragging;
@@ -189,7 +217,7 @@ public:
App::DocumentObject* movingJoint;
std::vector<std::pair<App::DocumentObject*, double>> objectMasses;
std::vector<std::pair<App::DocumentObject*, Base::Placement>> docsToMove;
std::vector<MovingObject> docsToMove;
Gui::SoFCCSysDragger* asmDragger = nullptr;
SoSwitch* asmDraggerSwitch = nullptr;

View File

@@ -171,15 +171,11 @@ def get_active_view(gui_doc):
# The joint object consists of 2 JCS (joint coordinate systems) and a Joint Type.
# A JCS is a placement that is computed (unless it is detached) from :
# - An Object: this can be any Part::Feature solid. Or a PartDesign Body. Or a App::Link to those.
# - A Part DocumentObject : This is the lowest level containing part. It can be either the Object itself if it
# stands alone. Or a App::Part. Or a App::Link to a App::Part.
# For example :
# Assembly.Assembly1.Part1.Part2.Box : Object is Box, part is 'Part1'
# Assembly.Assembly1.LinkToPart1.Part2.Box : Object is Box, part is 'LinkToPart1'
# A JCS is a placement that is computed (unless it is detached) from references (PropertyXLinkSubHidden) that links to :
# - An object: this can be any Part::Feature solid. Or a PartDesign Body. Or a App::Link to those.
# - An element name: This can be either a face, an edge, a vertex or empty. Empty means that the Object placement will be used
# - A vertex name: For faces and edges, we need to specify which vertex of said face/edge to use
# Both element names hold the full path to the object.
# From these a placement is computed. It is relative to the Object.
class Joint:
def __init__(self, joint, type_index):
@@ -203,22 +199,15 @@ class Joint:
def createProperties(self, joint):
self.migrationScript(joint)
self.migrationScript2(joint)
# First Joint Connector
if not hasattr(joint, "Object1"):
if not hasattr(joint, "Reference1"):
joint.addProperty(
"App::PropertyXLinkSub",
"Object1",
"App::PropertyXLinkSubHidden",
"Reference1",
"Joint Connector 1",
QT_TRANSLATE_NOOP("App::Property", "The first object of the joint"),
)
if not hasattr(joint, "Part1"):
joint.addProperty(
"App::PropertyLink",
"Part1",
"Joint Connector 1",
QT_TRANSLATE_NOOP("App::Property", "The first part of the joint"),
QT_TRANSLATE_NOOP("App::Property", "The first reference of the joint"),
)
if not hasattr(joint, "Placement1"):
@@ -228,7 +217,7 @@ class Joint:
"Joint Connector 1",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the local coordinate system within object1 that will be used for the joint.",
"This is the local coordinate system within Reference1's object that will be used for the joint.",
),
)
@@ -244,20 +233,12 @@ class Joint:
)
# Second Joint Connector
if not hasattr(joint, "Object2"):
if not hasattr(joint, "Reference2"):
joint.addProperty(
"App::PropertyXLinkSub",
"Object2",
"App::PropertyXLinkSubHidden",
"Reference2",
"Joint Connector 2",
QT_TRANSLATE_NOOP("App::Property", "The second object of the joint"),
)
if not hasattr(joint, "Part2"):
joint.addProperty(
"App::PropertyLink",
"Part2",
"Joint Connector 2",
QT_TRANSLATE_NOOP("App::Property", "The second part of the joint"),
QT_TRANSLATE_NOOP("App::Property", "The second reference of the joint"),
)
if not hasattr(joint, "Placement2"):
@@ -267,7 +248,7 @@ class Joint:
"Joint Connector 2",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the local coordinate system within object2 that will be used for the joint.",
"This is the local coordinate system within Reference2's object that will be used for the joint.",
),
)
@@ -470,6 +451,97 @@ class Joint:
joint.Object2 = [obj2, [el2, vtx2]]
def migrationScript2(self, joint):
if hasattr(joint, "Object1"):
obj = joint.Object1[0]
part = joint.Part1
elt = joint.Object1[1][0]
vtx = joint.Object1[1][1]
joint.removeProperty("Object1")
joint.removeProperty("Part1")
# now we need to get the 'selection-root-obj' and the global path
rootObj, path = UtilsAssembly.getRootPath(obj, part)
obj = rootObj
elt = path + elt
vtx = path + vtx
joint.addProperty(
"App::PropertyXLinkSubHidden",
"Reference1",
"Joint Connector 1",
QT_TRANSLATE_NOOP("App::Property", "The first reference of the joint"),
)
joint.Reference1 = [obj, [elt, vtx]]
if hasattr(joint, "Object2"):
obj = joint.Object2[0]
part = joint.Part2
elt = joint.Object2[1][0]
vtx = joint.Object2[1][1]
joint.removeProperty("Object2")
joint.removeProperty("Part2")
rootObj, path = UtilsAssembly.getRootPath(obj, part)
obj = rootObj
elt = path + elt
vtx = path + vtx
joint.addProperty(
"App::PropertyXLinkSubHidden",
"Reference2",
"Joint Connector 2",
QT_TRANSLATE_NOOP("App::Property", "The second reference of the joint"),
)
joint.Reference2 = [obj, [elt, vtx]]
def getSubnameForSelection(self, obj, part, elName):
# We need the subname starting from the part.
# Example for : Assembly.Part1.LinkToPart2.Part3.Body.Tip.Face1
# part is Part1 and obj is Body
# we should get : LinkToPart2.Part3.Body.Tip.Face1
if obj is None or part is None:
return elName
if obj.TypeId == "PartDesign::Body":
elName = obj.Tip.Name + "." + elName
elif obj.TypeId == "App::Link":
linked_obj = obj.getLinkedObject()
if linked_obj.TypeId == "PartDesign::Body":
elName = linked_obj.Tip.Name + "." + elName
if obj != part and obj in part.OutListRecursive:
bSub = ""
currentObj = part
limit = 0
while limit < 1000:
limit = limit + 1
if currentObj != part:
if bSub != "":
bSub = bSub + "."
bSub = bSub + currentObj.Name
if currentObj == obj:
break
if currentObj.TypeId == "App::Link":
currentObj = currentObj.getLinkedObject()
for obji in currentObj.OutList:
if obji == obj or obj in obji.OutListRecursive:
currentObj = obji
break
elName = bSub + "." + elName
return elName
def dumps(self):
return None
@@ -495,10 +567,11 @@ class Joint:
return
if prop == "Rotation" or prop == "Offset":
self.updateJCSPlacements(joint)
if joint.Object1 is None or joint.Object2 is None:
if joint.Reference1 is None or joint.Reference2 is None:
return
self.updateJCSPlacements(joint)
presolved = self.preSolve(joint, False)
isAssembly = self.getAssembly(joint).Type == "Assembly"
@@ -508,45 +581,34 @@ class Joint:
self.updateJCSPlacements(joint)
if prop == "Distance" and (joint.JointType == "Distance" or joint.JointType == "Angle"):
if joint.Part1 and joint.Part2:
if joint.JointType == "Angle" and joint.Distance != 0.0:
self.preventParallel(joint)
solveIfAllowed(self.getAssembly(joint))
if joint.Reference1 is None or joint.Reference2 is None:
return
if joint.JointType == "Angle" and joint.Distance != 0.0:
self.preventParallel(joint)
solveIfAllowed(self.getAssembly(joint))
def execute(self, fp):
"""Do something when doing a recomputation, this method is mandatory"""
# App.Console.PrintMessage("Recompute Python Box feature\n")
pass
def setJointConnectors(self, joint, current_selection):
def setJointConnectors(self, joint, refs):
# current selection is a vector of strings like "Assembly.Assembly1.Assembly2.Body.Pad.Edge16" including both what selection return as obj_name and obj_sub
assembly = self.getAssembly(joint)
isAssembly = assembly.Type == "Assembly"
if len(current_selection) >= 1:
joint.Object1 = [
current_selection[0]["object"],
[current_selection[0]["element_name"], current_selection[0]["vertex_name"]],
]
joint.Part1 = current_selection[0]["part"]
joint.Placement1 = self.findPlacement(
joint, joint.Object1[0], joint.Part1, joint.Object1[1][0], joint.Object1[1][1]
)
if len(refs) >= 1:
joint.Reference1 = refs[0]
joint.Placement1 = self.findPlacement(joint, joint.Reference1, 0)
else:
joint.Object1 = None
joint.Part1 = None
joint.Reference1 = None
joint.Placement1 = App.Placement()
self.partMovedByPresolved = None
if len(current_selection) >= 2:
joint.Object2 = [
current_selection[1]["object"],
[current_selection[1]["element_name"], current_selection[1]["vertex_name"]],
]
joint.Part2 = current_selection[1]["part"]
joint.Placement2 = self.findPlacement(
joint, joint.Object2[0], joint.Part2, joint.Object2[1][0], joint.Object2[1][1], True
)
if len(refs) >= 2:
joint.Reference2 = refs[1]
joint.Placement2 = self.findPlacement(joint, joint.Reference2, 1)
if joint.JointType in JointUsingPreSolve:
self.preSolve(joint)
elif joint.JointType in JointParallelForbidden:
@@ -558,8 +620,7 @@ class Joint:
self.updateJCSPlacements(joint)
else:
joint.Object2 = None
joint.Part2 = None
joint.Reference2 = None
joint.Placement2 = App.Placement()
if isAssembly:
assembly.undoSolve()
@@ -567,14 +628,10 @@ class Joint:
def updateJCSPlacements(self, joint):
if not joint.Detach1:
joint.Placement1 = self.findPlacement(
joint, joint.Object1[0], joint.Part1, joint.Object1[1][0], joint.Object1[1][1]
)
joint.Placement1 = self.findPlacement(joint, joint.Reference1, 0)
if not joint.Detach2:
joint.Placement2 = self.findPlacement(
joint, joint.Object2[0], joint.Part2, joint.Object2[1][0], joint.Object2[1][1], True
)
joint.Placement2 = self.findPlacement(joint, joint.Reference2, 1)
"""
So here we want to find a placement that corresponds to a local coordinate system that would be placed at the selected vertex.
@@ -586,15 +643,12 @@ class Joint:
- if elt is a cylindrical face, vtx can also be the center of the arcs of the cylindrical face.
"""
def findPlacement(self, joint, obj, part, elt, vtx, isSecond=False):
if not obj or not part:
return App.Placement()
def findPlacement(self, joint, ref, index=0):
ignoreVertex = joint.JointType == "Distance"
plc = UtilsAssembly.findPlacement(obj, part, elt, vtx, ignoreVertex)
plc = UtilsAssembly.findPlacement(ref, ignoreVertex)
# We apply rotation / reverse / offset it necessary, but only to the second JCS.
if isSecond:
if index == 1:
if joint.Offset.Length != 0.0:
plc = UtilsAssembly.applyOffsetToPlacement(plc, joint.Offset)
if joint.Rotation != 0.0:
@@ -604,28 +658,28 @@ class Joint:
def flipOnePart(self, joint):
assembly = self.getAssembly(joint)
part2ConnectedByJoint = assembly.isJointConnectingPartToGround(joint, "Part2")
part1Grounded = assembly.isPartGrounded(joint.Part1)
part2Grounded = assembly.isPartGrounded(joint.Part2)
part2ConnectedByJoint = assembly.isJointConnectingPartToGround(joint, "Reference2")
part1 = UtilsAssembly.getMovingPart(assembly, joint.Reference1)
part2 = UtilsAssembly.getMovingPart(assembly, joint.Reference2)
part1Grounded = assembly.isPartGrounded(part1)
part2Grounded = assembly.isPartGrounded(part2)
if part2ConnectedByJoint and not part2Grounded:
jcsPlc = UtilsAssembly.getJcsPlcRelativeToPart(
joint.Placement2, joint.Object2[0], joint.Part2
)
globalJcsPlc = UtilsAssembly.getJcsGlobalPlc(
joint.Placement2, joint.Object2[0], joint.Part2
assembly, joint.Placement2, joint.Reference2
)
globalJcsPlc = UtilsAssembly.getJcsGlobalPlc(joint.Placement2, joint.Reference2)
jcsPlc = UtilsAssembly.flipPlacement(jcsPlc)
joint.Part2.Placement = globalJcsPlc * jcsPlc.inverse()
part2.Placement = globalJcsPlc * jcsPlc.inverse()
elif not part1Grounded:
jcsPlc = UtilsAssembly.getJcsPlcRelativeToPart(
joint.Placement1, joint.Object1[0], joint.Part1
)
globalJcsPlc = UtilsAssembly.getJcsGlobalPlc(
joint.Placement1, joint.Object1[0], joint.Part1
assembly, joint.Placement1, joint.Reference1
)
globalJcsPlc = UtilsAssembly.getJcsGlobalPlc(joint.Placement1, joint.Reference1)
jcsPlc = UtilsAssembly.flipPlacement(jcsPlc)
joint.Part1.Placement = globalJcsPlc * jcsPlc.inverse()
part1.Placement = globalJcsPlc * jcsPlc.inverse()
solveIfAllowed(self.getAssembly(joint))
@@ -634,13 +688,18 @@ class Joint:
# we actually don't want to match perfectly the JCS, it is best to match them
# in the current closest direction, ie either matched or flipped.
sameDir = self.areJcsSameDir(joint)
assembly = self.getAssembly(joint)
part1 = UtilsAssembly.getMovingPart(assembly, joint.Reference1)
part2 = UtilsAssembly.getMovingPart(assembly, joint.Reference2)
isAssembly = assembly.Type == "Assembly"
if isAssembly:
joint.Activated = False
part1Connected = assembly.isPartConnected(joint.Part1)
part2Connected = assembly.isPartConnected(joint.Part2)
part1Connected = assembly.isPartConnected(part1)
part2Connected = assembly.isPartConnected(part2)
joint.Activated = True
else:
part1Connected = False
@@ -648,34 +707,30 @@ class Joint:
if not part2Connected:
if savePlc:
self.partMovedByPresolved = joint.Part2
self.presolveBackupPlc = joint.Part2.Placement
self.partMovedByPresolved = part2
self.presolveBackupPlc = part2.Placement
globalJcsPlc1 = UtilsAssembly.getJcsGlobalPlc(
joint.Placement1, joint.Object1[0], joint.Part1
)
globalJcsPlc1 = UtilsAssembly.getJcsGlobalPlc(joint.Placement1, joint.Reference1)
jcsPlc2 = UtilsAssembly.getJcsPlcRelativeToPart(
joint.Placement2, joint.Object2[0], joint.Part2
assembly, joint.Placement2, joint.Reference2
)
if not sameDir:
jcsPlc2 = UtilsAssembly.flipPlacement(jcsPlc2)
joint.Part2.Placement = globalJcsPlc1 * jcsPlc2.inverse()
part2.Placement = globalJcsPlc1 * jcsPlc2.inverse()
return True
elif not part1Connected:
if savePlc:
self.partMovedByPresolved = joint.Part1
self.presolveBackupPlc = joint.Part1.Placement
self.partMovedByPresolved = part1
self.presolveBackupPlc = part1.Placement
globalJcsPlc2 = UtilsAssembly.getJcsGlobalPlc(
joint.Placement2, joint.Object2[0], joint.Part2
)
globalJcsPlc2 = UtilsAssembly.getJcsGlobalPlc(joint.Placement2, joint.Reference2)
jcsPlc1 = UtilsAssembly.getJcsPlcRelativeToPart(
joint.Placement1, joint.Object1[0], joint.Part1
assembly, joint.Placement1, joint.Reference1
)
if not sameDir:
jcsPlc1 = UtilsAssembly.flipPlacement(jcsPlc1)
joint.Part1.Placement = globalJcsPlc2 * jcsPlc1.inverse()
part1.Placement = globalJcsPlc2 * jcsPlc1.inverse()
return True
return False
@@ -693,47 +748,43 @@ class Joint:
return
assembly = self.getAssembly(joint)
part1 = UtilsAssembly.getMovingPart(assembly, joint.Reference1)
part2 = UtilsAssembly.getMovingPart(assembly, joint.Reference2)
isAssembly = assembly.Type == "Assembly"
if isAssembly:
part1ConnectedByJoint = assembly.isJointConnectingPartToGround(joint, "Part1")
part2ConnectedByJoint = assembly.isJointConnectingPartToGround(joint, "Part2")
part1ConnectedByJoint = assembly.isJointConnectingPartToGround(joint, "Reference1")
part2ConnectedByJoint = assembly.isJointConnectingPartToGround(joint, "Reference2")
else:
part1ConnectedByJoint = False
part2ConnectedByJoint = True
if part2ConnectedByJoint:
self.partMovedByPresolved = joint.Part2
self.presolveBackupPlc = joint.Part2.Placement
self.partMovedByPresolved = part2
self.presolveBackupPlc = part2.Placement
joint.Part2.Placement = UtilsAssembly.applyRotationToPlacementAlongAxis(
joint.Part2.Placement, 10, App.Vector(1, 0, 0)
part2.Placement = UtilsAssembly.applyRotationToPlacementAlongAxis(
part2.Placement, 10, App.Vector(1, 0, 0)
)
elif part1ConnectedByJoint:
self.partMovedByPresolved = joint.Part1
self.presolveBackupPlc = joint.Part1.Placement
self.partMovedByPresolved = part1
self.presolveBackupPlc = part1.Placement
joint.Part1.Placement = UtilsAssembly.applyRotationToPlacementAlongAxis(
joint.Part1.Placement, 10, App.Vector(1, 0, 0)
part1.Placement = UtilsAssembly.applyRotationToPlacementAlongAxis(
part1.Placement, 10, App.Vector(1, 0, 0)
)
def areJcsSameDir(self, joint):
globalJcsPlc1 = UtilsAssembly.getJcsGlobalPlc(
joint.Placement1, joint.Object1[0], joint.Part1
)
globalJcsPlc2 = UtilsAssembly.getJcsGlobalPlc(
joint.Placement2, joint.Object2[0], joint.Part2
)
globalJcsPlc1 = UtilsAssembly.getJcsGlobalPlc(joint.Placement1, joint.Reference1)
globalJcsPlc2 = UtilsAssembly.getJcsGlobalPlc(joint.Placement2, joint.Reference2)
return UtilsAssembly.arePlacementSameDir(globalJcsPlc1, globalJcsPlc2)
def areJcsZParallel(self, joint):
globalJcsPlc1 = UtilsAssembly.getJcsGlobalPlc(
joint.Placement1, joint.Object1[0], joint.Part1
)
globalJcsPlc2 = UtilsAssembly.getJcsGlobalPlc(
joint.Placement2, joint.Object2[0], joint.Part2
)
globalJcsPlc1 = UtilsAssembly.getJcsGlobalPlc(joint.Placement1, joint.Reference1)
globalJcsPlc2 = UtilsAssembly.getJcsGlobalPlc(joint.Placement2, joint.Reference2)
return UtilsAssembly.arePlacementZParallel(globalJcsPlc1, globalJcsPlc2)
@@ -872,9 +923,9 @@ class ViewProviderJoint:
def get_JCS_size(self):
return get_camera_height(self.gui_doc) / 20
def set_JCS_placement(self, soTransform, placement, obj, part):
def set_JCS_placement(self, soTransform, placement, ref):
# change plc to be relative to the origin of the document.
global_plc = UtilsAssembly.getGlobalPlacement(obj, part)
global_plc = UtilsAssembly.getGlobalPlacement(ref)
placement = global_plc * placement
t = placement.Base
@@ -887,29 +938,27 @@ class ViewProviderJoint:
"""If a property of the handled feature has changed we have the chance to handle this here"""
# joint is the handled feature, prop is the name of the property that has changed
if prop == "Placement1":
if joint.Object1:
if hasattr(joint, "Reference1") and joint.Reference1:
plc = joint.Placement1
self.switch_JCS1.whichChild = coin.SO_SWITCH_ALL
if joint.Part1:
self.set_JCS_placement(self.transform1, plc, joint.Object1[0], joint.Part1)
self.set_JCS_placement(self.transform1, plc, joint.Reference1)
else:
self.switch_JCS1.whichChild = coin.SO_SWITCH_NONE
if prop == "Placement2":
if joint.Object2:
if hasattr(joint, "Reference2") and joint.Reference2:
plc = joint.Placement2
self.switch_JCS2.whichChild = coin.SO_SWITCH_ALL
if joint.Part2:
self.set_JCS_placement(self.transform2, plc, joint.Object2[0], joint.Part2)
self.set_JCS_placement(self.transform2, plc, joint.Reference2)
else:
self.switch_JCS2.whichChild = coin.SO_SWITCH_NONE
def showPreviewJCS(self, visible, placement=None, obj=None, part=None):
def showPreviewJCS(self, visible, placement=None, ref=None):
if visible:
self.switch_JCS_preview.whichChild = coin.SO_SWITCH_ALL
self.set_JCS_placement(self.transform3, placement, obj, part)
self.set_JCS_placement(self.transform3, placement, ref)
else:
self.switch_JCS_preview.whichChild = coin.SO_SWITCH_NONE
@@ -1232,9 +1281,8 @@ class MakeJointSelGate:
# Only objects within the assembly.
return False
full_obj_name = ".".join(objs_names)
full_element_name = full_obj_name + "." + element_name
selected_object = UtilsAssembly.getObject(full_element_name)
ref = [obj, [sub]]
selected_object = UtilsAssembly.getObject(ref)
if not (
selected_object.isDerivedFrom("Part::Feature")
@@ -1329,8 +1377,8 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
else:
App.setActiveTransaction("Create " + self.jointName + " Joint")
self.current_selection = []
self.preselection_dict = None
self.refs = []
self.presel_ref = None
self.createJointObject()
self.visibilityBackup = False
@@ -1359,7 +1407,7 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
self.addition_rejected = False
def accept(self):
if len(self.current_selection) != 2:
if len(self.refs) != 2:
App.Console.PrintWarning(
translate("Assembly", "You need to select 2 elements from 2 separate parts.")
)
@@ -1419,52 +1467,27 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
continue
for sub_name in sel.SubElementNames:
# We add sub_name twice because the joints references have element name + vertex name
# and in the case of initial selection, both are the same.
ref = [sel.Object, [sub_name, sub_name]]
moving_part = self.getMovingPart(ref)
# Only objects within the assembly.
objs_names, element_name = UtilsAssembly.getObjsNamesAndElement(
sel.ObjectName, sub_name
)
if self.assembly.Name not in objs_names:
if moving_part is None:
Gui.Selection.removeSelection(sel.Object, sub_name)
continue
obj_name = sel.ObjectName
full_obj_name = UtilsAssembly.getFullObjName(obj_name, sub_name)
full_element_name = UtilsAssembly.getFullElementName(obj_name, sub_name)
selected_object = UtilsAssembly.getObject(full_element_name)
element_name = UtilsAssembly.getElementName(full_element_name)
part_containing_selected_object = self.getContainingPart(
full_element_name, selected_object
)
if selected_object == self.assembly:
# do not accept selection of assembly itself
Gui.Selection.removeSelection(sel.Object, sub_name)
continue
if (
len(self.current_selection) == 1
and selected_object == self.current_selection[0]["object"]
):
if len(self.refs) == 1 and moving_part == self.getMovingPart(self.refs[0]):
# do not select several feature of the same object.
self.current_selection.clear()
self.refs.clear()
Gui.Selection.clearSelection()
return
selection_dict = {
"object": selected_object,
"part": part_containing_selected_object,
"element_name": element_name,
"full_element_name": full_element_name,
"full_obj_name": full_obj_name,
"vertex_name": element_name,
}
self.current_selection.append(selection_dict)
self.refs.append(ref)
# do not accept initial selection if we don't have 2 selected features
if len(self.current_selection) != 2:
self.current_selection.clear()
if len(self.refs) != 2:
self.refs.clear()
Gui.Selection.clearSelection()
else:
self.updateJoint()
@@ -1635,46 +1658,17 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
self.form.groupBox_limits.hide()
def updateTaskboxFromJoint(self):
self.current_selection = []
self.preselection_dict = None
self.refs = []
self.presel_ref = None
obj1 = self.joint.Object1[0]
part1 = self.joint.Part1
el1 = self.joint.Object1[1][0]
vtx1 = self.joint.Object1[1][1]
ref1 = self.joint.Reference1
ref2 = self.joint.Reference2
obj2 = self.joint.Object2[0]
part2 = self.joint.Part2
el2 = self.joint.Object2[1][0]
vtx2 = self.joint.Object2[1][1]
self.refs.append(ref1)
self.refs.append(ref2)
selection_dict1 = {
"object": obj1,
"part": part1,
"element_name": el1,
"vertex_name": vtx1,
}
selection_dict2 = {
"object": obj2,
"part": part2,
"element_name": el2,
"vertex_name": vtx2,
}
self.current_selection.append(selection_dict1)
self.current_selection.append(selection_dict2)
# Add the elements to the selection. Note we cannot do :
# Gui.Selection.addSelection(self.doc.Name, obj1.Name, elName)
# Because obj1 can be external in which case addSelection will fail. And
# Gui.Selection.addSelection(obj1.Document.Name, obj1.Name, elName)
# will not select in the assembly doc.
elName = self.getSubnameForSelection(obj1, part1, el1)
Gui.Selection.addSelection(self.doc.Name, part1.Name, elName)
elName = self.getSubnameForSelection(obj2, part2, el2)
Gui.Selection.addSelection(self.doc.Name, part2.Name, elName)
Gui.Selection.addSelection(ref1[0].Document.Name, ref1[0].Name, ref1[1][0])
Gui.Selection.addSelection(ref2[0].Document.Name, ref2[0].Name, ref2[1][0])
self.form.distanceSpinbox.setProperty("rawValue", self.joint.Distance)
self.form.distanceSpinbox2.setProperty("rawValue", self.joint.Distance2)
@@ -1693,63 +1687,23 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
self.form.jointType.setCurrentIndex(JointTypes.index(self.joint.JointType))
self.updateJointList()
def getSubnameForSelection(self, obj, part, elName):
# We need the subname starting from the part.
# Example for : Assembly.Part1.LinkToPart2.Part3.Body.Tip.Face1
# part is Part1 and obj is Body
# we should get : LinkToPart2.Part3.Body.Tip.Face1
if obj is None or part is None:
return elName
if obj.TypeId == "PartDesign::Body":
elName = obj.Tip.Name + "." + elName
elif obj.TypeId == "App::Link":
linked_obj = obj.getLinkedObject()
if linked_obj.TypeId == "PartDesign::Body":
elName = linked_obj.Tip.Name + "." + elName
if obj != part and obj in part.OutListRecursive:
bSub = ""
currentObj = part
limit = 0
while limit < 1000:
limit = limit + 1
if currentObj != part:
if bSub != "":
bSub = bSub + "."
bSub = bSub + currentObj.Name
if currentObj == obj:
break
if currentObj.TypeId == "App::Link":
currentObj = currentObj.getLinkedObject()
for obji in currentObj.OutList:
if obji == obj or obj in obji.OutListRecursive:
currentObj = obji
break
elName = bSub + "." + elName
return elName
def updateJoint(self):
# First we build the listwidget
self.updateJointList()
# Then we pass the new list to the joint object
self.joint.Proxy.setJointConnectors(self.joint, self.current_selection)
self.joint.Proxy.setJointConnectors(self.joint, self.refs)
def updateJointList(self):
self.form.featureList.clear()
simplified_names = []
for sel in self.current_selection:
sname = sel["object"].Label
if sel["element_name"] != "":
sname = sname + "." + sel["element_name"]
for ref in self.refs:
sname = UtilsAssembly.getObject(ref).Label
element_name = UtilsAssembly.getElementName(ref[1][0])
if element_name != "":
sname = sname + "." + element_name
simplified_names.append(sname)
self.form.featureList.addItems(simplified_names)
@@ -1771,15 +1725,15 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
self.form.limitRotMaxSpinbox.setProperty("rawValue", angle)
def moveMouse(self, info):
if len(self.current_selection) >= 2 or (
len(self.current_selection) == 1
if len(self.refs) >= 2 or (
len(self.refs) == 1
and (
not self.preselection_dict
or self.current_selection[0]["part"] == self.preselection_dict["part"]
not self.presel_ref
or self.getMovingPart(self.refs[0]) == self.getMovingPart(self.presel_ref)
)
):
self.joint.ViewObject.Proxy.showPreviewJCS(False)
if len(self.current_selection) >= 2:
if len(self.refs) >= 2:
self.updateLimits()
return
@@ -1789,38 +1743,24 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
if (
not cursor_info
or not self.preselection_dict
# or cursor_info["SubName"] != self.preselection_dict["sub_name"]
or not self.presel_ref
# or cursor_info["SubName"] != self.presel_ref["sub_name"]
# Removed because they are not equal when hovering a line endpoints.
# But we don't actually need to test because if there's no preselection then not cursor is None
):
self.joint.ViewObject.Proxy.showPreviewJCS(False)
return
# newPos = self.view.getPoint(*info["Position"]) # This is not what we want, it's not pos on the object but on the focal plane
ref = self.presel_ref
# newPos = self.view.getPoint(*info["Position"]) is not OK: it's not pos on the object but on the focal plane
newPos = App.Vector(cursor_info["x"], cursor_info["y"], cursor_info["z"])
self.preselection_dict["mouse_pos"] = newPos
vertex_name = UtilsAssembly.findElementClosestVertex(self.assembly, ref, newPos)
if self.preselection_dict["element_name"] == "":
self.preselection_dict["vertex_name"] = ""
else:
self.preselection_dict["vertex_name"] = UtilsAssembly.findElementClosestVertex(
self.preselection_dict
)
ref = UtilsAssembly.addVertexToReference(ref, vertex_name)
isSecond = len(self.current_selection) == 1
obj = self.preselection_dict["object"]
part = self.preselection_dict["part"]
placement = self.joint.Proxy.findPlacement(
self.joint,
obj,
part,
self.preselection_dict["element_name"],
self.preselection_dict["vertex_name"],
isSecond,
)
self.joint.ViewObject.Proxy.showPreviewJCS(True, placement, obj, part)
placement = self.joint.Proxy.findPlacement(self.joint, ref, 0)
self.joint.ViewObject.Proxy.showPreviewJCS(True, placement, ref)
self.previewJCSVisible = True
# 3D view keyboard handler
@@ -1845,38 +1785,46 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
for index in selected_indexes:
row = index.row()
if row < len(self.current_selection):
selection_dict = self.current_selection[row]
elName = self.getSubnameForSelection(
selection_dict["object"],
selection_dict["part"],
selection_dict["element_name"],
)
Gui.Selection.removeSelection(selection_dict["object"], elName)
if row < len(self.refs):
ref = self.refs[row]
Gui.Selection.removeSelection(ref[0], ref[1][0])
return True # Consume the event
return super().eventFilter(watched, event)
def getContainingPart(self, full_element_name, obj):
return UtilsAssembly.getContainingPart(full_element_name, obj, self.assembly)
def getMovingPart(self, ref):
return UtilsAssembly.getMovingPart(self.assembly, ref)
# selectionObserver stuff
def addSelection(self, doc_name, obj_name, sub_name, mousePos):
full_obj_name = UtilsAssembly.getFullObjName(obj_name, sub_name)
full_element_name = UtilsAssembly.getFullElementName(obj_name, sub_name)
selected_object = UtilsAssembly.getObject(full_element_name)
element_name = UtilsAssembly.getElementName(full_element_name)
part_containing_selected_object = self.getContainingPart(full_element_name, selected_object)
rootObj = App.getDocument(doc_name).getObject(obj_name)
resolved = rootObj.resolveSubElement(sub_name)
element_name_TNP = resolved[1]
element_name = resolved[2]
# Preprocess the sub_name to remove the TNP string
# We do this because after we need to add the vertex_name as well.
# And the names will be resolved anyway after.
if len(element_name_TNP.split(".")) == 2:
names = sub_name.split(".")
names.pop(-2) # remove the TNP string
sub_name = ".".join(names)
ref = [rootObj, [sub_name]]
moving_part = self.getMovingPart(ref)
# Check if the addition is acceptable (we are not doing this in selection gate to let user move objects)
acceptable = True
if len(self.current_selection) >= 2:
if len(self.refs) >= 2:
# No more than 2 elements can be selected for basic joints.
acceptable = False
for selection_dict in self.current_selection:
if selection_dict["part"] == part_containing_selected_object:
for reference in self.refs:
sel_moving_part = self.getMovingPart(reference)
if sel_moving_part == moving_part:
# Can't join a solid to itself. So the user need to select 2 different parts.
acceptable = False
@@ -1886,20 +1834,14 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
return
# Selection is acceptable so add it
selection_dict = {
"object": selected_object,
"part": part_containing_selected_object,
"element_name": element_name,
"full_element_name": full_element_name,
"full_obj_name": full_obj_name,
"mouse_pos": App.Vector(mousePos[0], mousePos[1], mousePos[2]),
}
if element_name == "":
selection_dict["vertex_name"] = ""
else:
selection_dict["vertex_name"] = UtilsAssembly.findElementClosestVertex(selection_dict)
self.current_selection.append(selection_dict)
mousePos = App.Vector(mousePos[0], mousePos[1], mousePos[2])
vertex_name = UtilsAssembly.findElementClosestVertex(self.assembly, ref, mousePos)
# add the vertex name to the reference
ref = UtilsAssembly.addVertexToReference(ref, vertex_name)
self.refs.append(ref)
self.updateJoint()
# We hide the preview JCS if we just added to the selection
@@ -1910,41 +1852,27 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
self.addition_rejected = False
return
full_element_name = UtilsAssembly.getFullElementName(obj_name, sub_name)
selected_object = UtilsAssembly.getObject(full_element_name)
element_name = UtilsAssembly.getElementName(full_element_name)
part_containing_selected_object = self.getContainingPart(full_element_name, selected_object)
ref = [App.getDocument(doc_name).getObject(obj_name), [sub_name]]
moving_part = self.getMovingPart(ref)
# Find and remove the corresponding dictionary from the combined list
for selection_dict in self.current_selection:
if selection_dict["part"] == part_containing_selected_object:
self.current_selection.remove(selection_dict)
for reference in self.refs:
sel_moving_part = self.getMovingPart(reference)
if sel_moving_part == moving_part:
self.refs.remove(reference)
break
self.updateJoint()
def setPreselection(self, doc_name, obj_name, sub_name):
if not sub_name:
self.preselection_dict = None
self.presel_ref = None
return
full_obj_name = UtilsAssembly.getFullObjName(obj_name, sub_name)
full_element_name = UtilsAssembly.getFullElementName(obj_name, sub_name)
selected_object = UtilsAssembly.getObject(full_element_name)
element_name = UtilsAssembly.getElementName(full_element_name)
part_containing_selected_object = self.getContainingPart(full_element_name, selected_object)
self.preselection_dict = {
"object": selected_object,
"part": part_containing_selected_object,
"sub_name": sub_name,
"element_name": element_name,
"full_element_name": full_element_name,
"full_obj_name": full_obj_name,
}
self.presel_ref = [App.getDocument(doc_name).getObject(obj_name), [sub_name]]
def clearSelection(self, doc_name):
self.current_selection.clear()
self.refs.clear()
self.updateJoint()
def setJointsPickableState(self, state: bool):

View File

@@ -95,32 +95,27 @@ def assembly_has_at_least_n_parts(n):
return False
def getObject(full_name):
# full_name is "Assembly.LinkOrAssembly1.LinkOrPart1.LinkOrBox.Edge16"
# or "Assembly.LinkOrAssembly1.LinkOrPart1.LinkOrBody.pad.Edge16"
def getObject(ref):
if len(ref) != 2:
return None
subs = ref[1]
if len(subs) < 1:
return None
sub_name = subs[0]
# sub_name is "LinkOrAssembly1.LinkOrPart1.LinkOrBox.Edge16"
# or "LinkOrAssembly1.LinkOrPart1.LinkOrBody.pad.Edge16"
# or "Assembly.LinkOrAssembly1.LinkOrPart1.LinkOrBody.Local_CS.X"
# We want either LinkOrBody or LinkOrBox or Local_CS.
names = full_name.split(".")
doc = App.ActiveDocument
names = sub_name.split(".")
if len(names) < 3:
if len(names) < 2:
return None
prevObj = None
doc = ref[0].Document
for i, objName in enumerate(names):
if i == 0:
prevObj = doc.getObject(objName)
if prevObj.TypeId == "App::Link":
prevObj = prevObj.getLinkedObject()
continue
obj = None
if prevObj.TypeId in {"App::Part", "Assembly::AssemblyObject", "App::DocumentObjectGroup"}:
for obji in prevObj.OutList:
if obji.Name == objName:
obj = obji
break
for i, obj_name in enumerate(names):
obj = doc.getObject(obj_name)
if obj is None:
return None
@@ -129,26 +124,7 @@ def getObject(full_name):
if i == len(names) - 2:
return obj
if obj.TypeId == "App::Link":
linked_obj = obj.getLinkedObject()
if linked_obj.TypeId == "PartDesign::Body":
if i + 1 < len(names):
obj2 = None
for obji in linked_obj.OutList:
if obji.Name == names[i + 1]:
obj2 = obji
break
if obj2 and isBodySubObject(obj2.TypeId):
return obj2
return obj
elif linked_obj.isDerivedFrom("Part::Feature"):
return obj
else:
prevObj = linked_obj
continue
elif obj.TypeId in {"App::Part", "Assembly::AssemblyObject", "App::DocumentObjectGroup"}:
prevObj = obj
if obj.TypeId in {"App::Part", "Assembly::AssemblyObject"}:
continue
elif obj.TypeId == "PartDesign::Body":
@@ -166,6 +142,24 @@ def getObject(full_name):
# primitive, fastener, gear ...
return obj
elif obj.TypeId == "App::Link":
linked_obj = obj.getLinkedObject()
if linked_obj.TypeId == "PartDesign::Body":
if i + 1 < len(names):
obj2 = None
for obji in linked_obj.OutList:
if obji.Name == names[i + 1]:
obj2 = obji
break
if obj2 and isBodySubObject(obj2.TypeId):
return obj2
return obj
elif linked_obj.isDerivedFrom("Part::Feature"):
return obj
else:
doc = linked_obj.Document
continue
return None
@@ -179,6 +173,7 @@ def isBodySubObject(typeId):
)
# To be deprecated. CommandCreateView needs to stop using it.
def getContainingPart(full_name, selected_object, activeAssemblyOrPart=None):
# full_name is "Assembly.Assembly1.LinkOrPart1.LinkOrBox.Edge16" -> LinkOrPart1
# or "Assembly.Assembly1.LinkOrPart1.LinkOrBody.pad.Edge16" -> LinkOrPart1
@@ -243,6 +238,7 @@ def getContainingPart(full_name, selected_object, activeAssemblyOrPart=None):
return selected_object
# To be deprecated. Kept for migrationScript.
def getObjectInPart(objName, part):
if part is None:
return None
@@ -266,43 +262,85 @@ def getObjectInPart(objName, part):
return None
# get the placement of Obj relative to its containing Part
# Used by migrationScript.
def getRootPath(obj, part):
sels = obj.Parents
for sel in sels:
rootObj = sel[0]
# The part and the rootObj should be in the same doc
if rootObj.Document.Name != part.Document.Name:
continue
path = sel[1]
# we need to check that the part name is in the list.
names = path.split(".")
if part.Name not in names:
continue
# for bodies we need to add the tip to the path.
if obj.TypeId == "PartDesign::Body":
path.append(obj.Tip.Name + ".")
return rootObj, path
return None, ""
# get the placement of Obj relative to its moving Part
# Example : assembly.part1.part2.partn.body1 : placement of Obj relative to part1
def getObjPlcRelativeToPart(obj, part):
# we need plc to be relative to the containing part
obj_global_plc = getGlobalPlacement(obj, part)
part_global_plc = getGlobalPlacement(part)
def getObjPlcRelativeToPart(assembly, ref):
# we need plc to be relative to the moving part
moving_part = getMovingPart(assembly, ref)
obj_global_plc = getGlobalPlacement(ref)
part_global_plc = getGlobalPlacement(ref, moving_part)
return part_global_plc.inverse() * obj_global_plc
# Example : assembly.part1.part2.partn.body1 : jcsPlc is relative to body1
# This function returns jcsPlc relative to part1
def getJcsPlcRelativeToPart(jcsPlc, obj, part):
obj_relative_plc = getObjPlcRelativeToPart(obj, part)
def getJcsPlcRelativeToPart(assembly, jcsPlc, ref):
obj_relative_plc = getObjPlcRelativeToPart(assembly, ref)
return obj_relative_plc * jcsPlc
# Return the jcs global placement
def getJcsGlobalPlc(jcsPlc, obj, part):
obj_global_plc = getGlobalPlacement(obj, part)
def getJcsGlobalPlc(jcsPlc, ref):
obj_global_plc = getGlobalPlacement(ref)
return obj_global_plc * jcsPlc
# The container is used to support cases where the same object appears at several places
# which happens when you have a link to a part.
def getGlobalPlacement(targetObj, container=None):
def getGlobalPlacement(ref, targetObj=None):
if not isRefValid(ref, 1):
return App.Placement()
if targetObj is None: # If no targetObj is given, we consider it's the getObject(ref)
targetObj = getObject(ref)
if targetObj is None:
return App.Placement()
inContainerBranch = container is None
for rootObj in App.activeDocument().RootObjectsIgnoreLinks:
foundPlacement = getTargetPlacementRelativeTo(
targetObj, rootObj, container, inContainerBranch
)
if foundPlacement is not None:
return foundPlacement
rootObj = ref[0]
names = ref[1][0].split(".")
doc = rootObj.Document
plc = rootObj.Placement
for objName in names:
obj = doc.getObject(objName)
if not obj:
continue
plc = plc * obj.Placement
if obj == targetObj:
return plc
if obj.TypeId == "App::Link":
linked_obj = obj.getLinkedObject()
doc = linked_obj.Document # in case its an external link.
# If targetObj has not been found there's a problem
return App.Placement()
@@ -313,71 +351,13 @@ def isThereOneRootAssembly():
return False
def getTargetPlacementRelativeTo(
targetObj, part, container, inContainerBranch, ignorePlacement=False
):
inContainerBranch = inContainerBranch or (not ignorePlacement and part == container)
if targetObj == part and inContainerBranch and not ignorePlacement:
return targetObj.Placement
if part.TypeId == "App::DocumentObjectGroup":
for obj in part.OutList:
foundPlacement = getTargetPlacementRelativeTo(
targetObj, obj, container, inContainerBranch, ignorePlacement
)
if foundPlacement is not None:
return foundPlacement
elif part.TypeId in {"App::Part", "Assembly::AssemblyObject", "PartDesign::Body"}:
for obj in part.OutList:
foundPlacement = getTargetPlacementRelativeTo(
targetObj, obj, container, inContainerBranch
)
if foundPlacement is None:
continue
# If we were called from a link then we need to ignore this placement as we use the link placement instead.
if not ignorePlacement:
foundPlacement = part.Placement * foundPlacement
return foundPlacement
elif part.TypeId == "App::Link":
linked_obj = part.getLinkedObject()
if part == linked_obj or linked_obj is None:
return None # upon loading this can happen for external links.
if linked_obj.TypeId in {"App::Part", "Assembly::AssemblyObject", "PartDesign::Body"}:
for obj in linked_obj.OutList:
foundPlacement = getTargetPlacementRelativeTo(
targetObj, obj, container, inContainerBranch
)
if foundPlacement is None:
continue
foundPlacement = part.Placement * foundPlacement
return foundPlacement
foundPlacement = getTargetPlacementRelativeTo(
targetObj, linked_obj, container, inContainerBranch, True
)
if foundPlacement is not None and not ignorePlacement:
foundPlacement = part.Placement * foundPlacement
return foundPlacement
return None
def getElementName(full_name):
# full_name is "Assembly.Assembly1.Assembly2.Assembly3.Box.Edge16"
# We want either Edge16.
parts = full_name.split(".")
if len(parts) < 3:
# At minimum "Assembly.Box.edge16". It shouldn't be shorter
if len(parts) < 2:
# At minimum "Box.edge16". It shouldn't be shorter
return ""
# case of PartDesign datums : CoordinateSystem, point, line, plane
@@ -452,23 +432,27 @@ def extract_type_and_number(element_name):
return None, None
def findElementClosestVertex(selection_dict):
obj = selection_dict["object"]
def findElementClosestVertex(assembly, ref, mousePos):
element_name = getElementName(ref[1][0])
if element_name == "":
return ""
mousePos = selection_dict["mouse_pos"]
moving_part = getMovingPart(assembly, ref)
obj = getObject(ref)
# We need mousePos to be relative to the part containing obj global placement
if selection_dict["object"] != selection_dict["part"]:
if obj != moving_part:
plc = App.Placement()
plc.Base = mousePos
global_plc = getGlobalPlacement(selection_dict["part"])
plc = global_plc.inverse() * plc
global_plc = getGlobalPlacement(ref)
plc = global_plc.inverse() * plc # We make it relative to obj Origin
plc = obj.Placement * plc # Make plc in the same lcs as obj
mousePos = plc.Base
elt_type, elt_index = extract_type_and_number(selection_dict["element_name"])
elt_type, elt_index = extract_type_and_number(element_name)
if elt_type == "Vertex":
return selection_dict["element_name"]
return element_name
elif elt_type == "Edge":
edge = obj.Shape.Edges[elt_index - 1]
@@ -476,7 +460,7 @@ def findElementClosestVertex(selection_dict):
if curve.TypeId == "Part::GeomCircle":
# For centers, as they are not shape vertexes, we return the element name.
# For now we only allow selecting the center of arcs / circles.
return selection_dict["element_name"]
return element_name
edge_points = getPointsFromVertexes(edge.Vertexes)
@@ -489,7 +473,7 @@ def findElementClosestVertex(selection_dict):
if curve.TypeId == "Part::GeomLine" and closest_vertex_index == 2:
# If line center is closest then we have no vertex name to set so we put element name
return selection_dict["element_name"]
return element_name
vertex_name = findVertexNameInObject(edge.Vertexes[closest_vertex_index], obj)
@@ -500,7 +484,7 @@ def findElementClosestVertex(selection_dict):
surface = face.Surface
_type = surface.TypeId
if _type == "Part::GeomSphere" or _type == "Part::GeomTorus":
return selection_dict["element_name"]
return element_name
# Handle the circle/arc edges for their centers
center_points = []
@@ -558,11 +542,11 @@ def findElementClosestVertex(selection_dict):
return "Edge" + str(index)
if _type == "Part::GeomCylinder" or _type == "Part::GeomCone":
return selection_dict["element_name"]
return element_name
if closest_vertex_index == len(face.Vertexes):
# If center of gravity then we have no vertex name to set so we put element name
return selection_dict["element_name"]
return element_name
vertex_name = findVertexNameInObject(face.Vertexes[closest_vertex_index], obj)
@@ -785,10 +769,10 @@ def getObjMassAndCom(obj, containingPart=None):
return 0, App.Vector(0, 0, 0)
def getCenterOfBoundingBox(objs, parts):
def getCenterOfBoundingBox(objs, refs):
i = 0
center = App.Vector()
for obj, part in zip(objs, parts):
for obj, ref in zip(objs, refs):
viewObject = obj.ViewObject
if viewObject is None:
continue
@@ -796,15 +780,15 @@ def getCenterOfBoundingBox(objs, parts):
if boundingBox is None:
continue
bboxCenter = boundingBox.Center
if part != obj:
# bboxCenter does not take into account obj global placement
plc = App.Placement(bboxCenter, App.Rotation())
# change plc to be relative to the object placement.
plc = obj.Placement.inverse() * plc
# change plc to be relative to the origin of the document.
global_plc = getGlobalPlacement(obj, part)
plc = global_plc * plc
bboxCenter = plc.Base
# bboxCenter does not take into account obj global placement
plc = App.Placement(bboxCenter, App.Rotation())
# change plc to be relative to the object placement.
plc = obj.Placement.inverse() * plc
# change plc to be relative to the origin of the document.
global_plc = getGlobalPlacement(ref, obj)
plc = global_plc * plc
bboxCenter = plc.Base
center = center + bboxCenter
i = i + 1
@@ -880,9 +864,15 @@ So here we want to find a placement that corresponds to a local coordinate syste
"""
def findPlacement(obj, part, elt, vtx, ignoreVertex=False):
if not obj or not part:
def findPlacement(ref, ignoreVertex=False):
if not isRefValid(ref, 2):
return App.Placement()
obj = getObject(ref)
if not obj:
return App.Placement()
elt = getElementName(ref[1][0])
vtx = getElementName(ref[1][1])
# case of origin objects.
if elt == "X_Axis" or elt == "YZ_Plane":
@@ -999,6 +989,17 @@ def findPlacement(obj, part, elt, vtx, ignoreVertex=False):
return plc
def isRefValid(ref, number_sub):
if ref is None:
return False
if len(ref) != 2:
return False
if len(ref[1]) < number_sub:
return False
return True
def round_vector(v, decimals=10):
"""Round each component of the vector to a specified number of decimal places."""
return App.Vector(round(v.x, decimals), round(v.y, decimals), round(v.z, decimals))
@@ -1045,8 +1046,8 @@ def getAssemblyShapes(assembly):
def getJointDistance(joint):
plc1 = getJcsGlobalPlc(joint.Placement1, joint.Object1, joint.Part1)
plc2 = getJcsGlobalPlc(joint.Placement2, joint.Object2, joint.Part2)
plc1 = getJcsGlobalPlc(joint.Placement1, joint.Reference1)
plc2 = getJcsGlobalPlc(joint.Placement2, joint.Reference2)
# Find the sign
sign = 1
@@ -1058,10 +1059,112 @@ def getJointDistance(joint):
def getJointXYAngle(joint):
plc1 = getJcsGlobalPlc(joint.Placement1, joint.Object1, joint.Part1)
plc2 = getJcsGlobalPlc(joint.Placement2, joint.Object2, joint.Part2)
plc1 = getJcsGlobalPlc(joint.Placement1, joint.Reference1)
plc2 = getJcsGlobalPlc(joint.Placement2, joint.Reference2)
plc3 = plc1.inverse() * plc2 # plc3 is plc2 relative to plc1
x_axis = plc3.Rotation.multVec(App.Vector(1, 0, 0))
return math.atan2(x_axis.y, x_axis.x)
def getMovingPart(assembly, ref):
# ref can be :
# [assembly, ['box.edge1', 'box.vertex2']]
# [Part, ['Assembly.box.edge1', 'Assembly.box.vertex2']]
# [assembly, ['Body.Pad.edge1', 'Body.Pad.vertex2']]
if assembly is None or ref is None or len(ref) != 2:
return None
obj = ref[0]
subs = ref[1]
if subs is None or len(subs) < 1:
return None
sub = ref[1][0] # All subs should have the same object paths.
names = [obj.Name] + sub.split(".")
try:
index = names.index(assembly.Name)
# Get the sublist starting after the after the assembly (in case of Part1/Assembly/...)
names = names[index + 1 :]
except ValueError:
return None
doc = assembly.Document
if len(names) < 2:
App.Console.PrintError(
"getMovingPart() in UtilsAssembly.py the object name is too short, at minimum it should be something like ['Box','edge16']. It shouldn't be shorter"
)
return None
for objName in names:
obj = doc.getObject(objName)
if not obj:
continue
return obj
return None
def truncateSubAtFirst(sub, target):
# target=part1 & sub=asm.part1.link1.part1.obj -> asm.part1.
names = sub.split(".")
sub = ""
for name in names:
sub = sub + name + "."
if name == target:
break
return sub
def truncateSubAtLast(sub, target):
# target=part1 & sub=asm.part1.link1.part1.obj -> asm.part1.link1.part1.
names = sub.split(".")
sub = ""
target_indices = [i for i, name in enumerate(names) if name == target]
if target_indices:
last_index = target_indices[-1]
for i, name in enumerate(names):
sub += name + "."
if i == last_index:
break
return sub
def swapElNameInSubname(sub_name, new_elName):
# turns assembly.box.edge1 into assembly.box.new_elName
names = sub_name.split(".")
# Replace the last element
names[-1] = new_elName
# Join the names back together
modified_sub = ".".join(names)
return modified_sub
def addVertexToReference(ref, vertex_name):
# Turns [obj, ['box.face1']] and 'vertex1' into [obj, ['box.face1', 'box.vertex1']]
if len(ref) == 2:
subs = ref[1]
if len(subs) > 0:
sub_name = subs[0]
vertex_full_sub = swapElNameInSubname(sub_name, vertex_name)
if len(subs) == 2: # Update the vertex sub
subs[1] = vertex_full_sub
else:
subs.append(vertex_full_sub)
ref = [ref[0], subs]
return ref