Assembly: Property change from Object/Part to Reference
This commit is contained in:
committed by
Yorik van Havre
parent
336a581942
commit
4f79c675b7
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user