Assembly: Change Joint References to remove cyclic dependency. (#25513)
* Assembly: Change Joint References to remove cyclic dependency. * Update JointObject.py * Update TestCore.py * Update JointObject.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update AssemblyUtils.cpp * Update UtilsAssembly.py * Update JointObject.py * small fix for link groups --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -532,8 +532,6 @@ void AssemblyLink::synchronizeJoints()
|
||||
|
||||
assemblyLinkJoints = getJoints();
|
||||
|
||||
AssemblyObject::recomputeJointPlacements(assemblyLinkJoints);
|
||||
|
||||
for (auto* joint : assemblyLinkJoints) {
|
||||
joint->purgeTouched();
|
||||
}
|
||||
@@ -546,87 +544,55 @@ void AssemblyLink::handleJointReference(
|
||||
const char* refName
|
||||
)
|
||||
{
|
||||
AssemblyObject* assembly = getLinkedAssembly();
|
||||
|
||||
auto prop1 = dynamic_cast<App::PropertyXLinkSubHidden*>(joint->getPropertyByName(refName));
|
||||
auto prop2 = dynamic_cast<App::PropertyXLinkSubHidden*>(lJoint->getPropertyByName(refName));
|
||||
auto prop1 = dynamic_cast<App::PropertyXLinkSub*>(joint->getPropertyByName(refName));
|
||||
auto prop2 = dynamic_cast<App::PropertyXLinkSub*>(lJoint->getPropertyByName(refName));
|
||||
if (!prop1 || !prop2) {
|
||||
return;
|
||||
}
|
||||
|
||||
App::DocumentObject* obj1 = nullptr;
|
||||
App::DocumentObject* obj2 = prop2->getValue();
|
||||
std::vector<std::string> subs1 = prop1->getSubValues();
|
||||
std::vector<std::string> subs2 = prop2->getSubValues();
|
||||
if (subs1.empty()) {
|
||||
// 1. Get the external component prop1 is [ExternalPart, "Sub"]
|
||||
App::DocumentObject* externalComponent = prop1->getValue();
|
||||
if (!externalComponent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Example :
|
||||
// Obj1 = docA-Asm1 Subs1 = ["part1.body.pad.face0", "part1.body.pad.vertex1"]
|
||||
// Obj1 = docA-Part Subs1 = ["Asm1.part1.body.pad.face0", "Asm1.part1.body.pad.vertex1"] // some
|
||||
// user may put the assembly inside a part... should become : Obj2 = docB-Asm2 Subs2 =
|
||||
// ["Asm1Link.part1.linkTobody.pad.face0", "Asm1Link.part1.linkTobody.pad.vertex1"] Obj2 =
|
||||
// docB-Part Sub2 = ["Asm2.Asm1Link.part1.linkTobody.pad.face0",
|
||||
// "Asm2.Asm1Link.part1.linkTobody.pad.vertex1"]
|
||||
|
||||
std::string asmLink = getNameInDocument();
|
||||
for (auto& sub : subs1) {
|
||||
// First let's remove 'Asm1' name and everything before if any.
|
||||
sub = removeUpToName(sub, assembly->getNameInDocument());
|
||||
// Then we add the assembly link name.
|
||||
sub = asmLink + "." + sub;
|
||||
// Then the question is, is there more to prepend? Because the parent assembly may have some
|
||||
// parents So we check assemblyLink parents and prepend necessary parents.
|
||||
bool first = true;
|
||||
std::vector<App::DocumentObject*> inList = getInList();
|
||||
int limit = 0;
|
||||
while (!inList.empty() && limit < 20) {
|
||||
++limit;
|
||||
bool found = false;
|
||||
for (auto* obj : inList) {
|
||||
if (obj->isDerivedFrom<App::Part>()) {
|
||||
found = true;
|
||||
if (first) {
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
std::string obj1Name = obj1->getNameInDocument();
|
||||
sub = obj1Name + "." + sub;
|
||||
}
|
||||
obj1 = obj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
inList = obj1->getInList();
|
||||
}
|
||||
else {
|
||||
inList = {};
|
||||
}
|
||||
}
|
||||
|
||||
// Lastly we need to replace the object name by its link name.
|
||||
auto* obj = getObjFromRef(prop1);
|
||||
auto* link = objLinkMap[obj];
|
||||
if (!obj || !link) {
|
||||
return;
|
||||
}
|
||||
std::string objName = obj->getNameInDocument();
|
||||
std::string linkName = link->getNameInDocument();
|
||||
sub = replaceLastOccurrence(sub, objName, linkName);
|
||||
// 2. Map to local link
|
||||
auto it = objLinkMap.find(externalComponent);
|
||||
if (it == objLinkMap.end()) {
|
||||
Base::Console().warning(
|
||||
"AssemblyLink: Could not map external component %s to a local link for joint %s\n",
|
||||
externalComponent->getNameInDocument(),
|
||||
joint->getNameInDocument()
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Now obj1 and the subs1 are what should be in obj2 and subs2 if the joint did not changed
|
||||
if (obj1 != obj2) {
|
||||
prop2->setValue(obj1);
|
||||
App::DocumentObject* localLink = it->second;
|
||||
|
||||
// 3. Set the new reference
|
||||
// The local joint now points to the local link [LocalLink, "Sub"]
|
||||
if (prop2->getValue() != localLink) {
|
||||
prop2->setValue(localLink);
|
||||
}
|
||||
|
||||
// 4. Sync sub-elements
|
||||
// The sub-elements (e.g. "Body.Face1") are relative to the component.
|
||||
// Since the LocalLink points to the ExternalPart, the relative path is identical.
|
||||
std::vector<std::string> subs1 = prop1->getSubValues();
|
||||
std::vector<std::string> subs2 = prop2->getSubValues();
|
||||
|
||||
bool changed = false;
|
||||
for (size_t i = 0; i < subs1.size(); ++i) {
|
||||
if (i >= subs2.size() || subs1[i] != subs2[i]) {
|
||||
changed = true;
|
||||
break;
|
||||
if (subs1.size() != subs2.size()) {
|
||||
changed = true;
|
||||
}
|
||||
else {
|
||||
for (size_t i = 0; i < subs1.size(); ++i) {
|
||||
if (subs1[i] != subs2[i]) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
prop2->setSubValues(std::move(subs1));
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ App::DocumentObjectExecReturn* AssemblyObject::execute()
|
||||
"User parameter:BaseApp/Preferences/Mod/Assembly"
|
||||
);
|
||||
if (hGrp->GetBool("SolveOnRecompute", true)) {
|
||||
solve();
|
||||
solve(false, false); // No need to update jcs since recompute updated them.
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -488,6 +488,7 @@ bool AssemblyObject::validateNewPlacements()
|
||||
void AssemblyObject::postDrag()
|
||||
{
|
||||
mbdAssembly->runPostDrag(); // Do this after last drag
|
||||
purgeTouched();
|
||||
}
|
||||
|
||||
void AssemblyObject::savePlacementsForUndo()
|
||||
@@ -607,48 +608,27 @@ void AssemblyObject::redrawJointPlacement(App::DocumentObject* joint)
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify the joint object that the transform of the coin object changed.
|
||||
auto* pPlc = dynamic_cast<App::PropertyPlacement*>(joint->getPropertyByName("Placement1"));
|
||||
if (pPlc) {
|
||||
pPlc->setValue(pPlc->getValue());
|
||||
}
|
||||
pPlc = dynamic_cast<App::PropertyPlacement*>(joint->getPropertyByName("Placement2"));
|
||||
if (pPlc) {
|
||||
pPlc->setValue(pPlc->getValue());
|
||||
}
|
||||
joint->purgeTouched();
|
||||
}
|
||||
|
||||
void AssemblyObject::recomputeJointPlacements(std::vector<App::DocumentObject*> joints)
|
||||
{
|
||||
// The Placement1 and Placement2 of each joint needs to be updated as the parts moved.
|
||||
Base::PyGILStateLocker lock;
|
||||
for (auto* joint : joints) {
|
||||
if (!joint) {
|
||||
continue;
|
||||
}
|
||||
|
||||
App::PropertyPythonObject* proxy = joint
|
||||
? dynamic_cast<App::PropertyPythonObject*>(joint->getPropertyByName("Proxy"))
|
||||
: nullptr;
|
||||
App::PropertyPythonObject* proxy = joint
|
||||
? dynamic_cast<App::PropertyPythonObject*>(joint->getPropertyByName("Proxy"))
|
||||
: nullptr;
|
||||
|
||||
if (!proxy) {
|
||||
continue;
|
||||
}
|
||||
if (!proxy) {
|
||||
return;
|
||||
}
|
||||
|
||||
Py::Object jointPy = proxy->getValue();
|
||||
Py::Object jointPy = proxy->getValue();
|
||||
|
||||
if (!jointPy.hasAttr("updateJCSPlacements")) {
|
||||
continue;
|
||||
}
|
||||
if (!jointPy.hasAttr("redrawJointPlacements")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Py::Object attr = jointPy.getAttr("updateJCSPlacements");
|
||||
if (attr.ptr() && attr.isCallable()) {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::asObject(joint->getPyObject()));
|
||||
Py::Callable(attr).apply(args);
|
||||
joint->purgeTouched();
|
||||
}
|
||||
Py::Object attr = jointPy.getAttr("redrawJointPlacements");
|
||||
if (attr.ptr() && attr.isCallable()) {
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::asObject(joint->getPyObject()));
|
||||
Py::Callable(attr).apply(args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,8 +667,8 @@ App::DocumentObject* AssemblyObject::getJointOfPartConnectingToGround(
|
||||
continue;
|
||||
}
|
||||
|
||||
App::DocumentObject* part1 = getMovingPartFromRef(this, joint, "Reference1");
|
||||
App::DocumentObject* part2 = getMovingPartFromRef(this, joint, "Reference2");
|
||||
App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1");
|
||||
App::DocumentObject* part2 = getMovingPartFromRef(joint, "Reference2");
|
||||
if (!part1 || !part2) {
|
||||
continue;
|
||||
}
|
||||
@@ -764,8 +744,8 @@ std::vector<App::DocumentObject*> AssemblyObject::getJoints(bool updateJCS, bool
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* part1 = getMovingPartFromRef(this, joint, "Reference1");
|
||||
auto* part2 = getMovingPartFromRef(this, joint, "Reference2");
|
||||
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)
|
||||
@@ -791,11 +771,6 @@ std::vector<App::DocumentObject*> AssemblyObject::getJoints(bool updateJCS, bool
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the joints are up to date.
|
||||
if (updateJCS) {
|
||||
recomputeJointPlacements(joints);
|
||||
}
|
||||
|
||||
return joints;
|
||||
}
|
||||
|
||||
@@ -834,8 +809,8 @@ std::vector<App::DocumentObject*> AssemblyObject::getJointsOfObj(App::DocumentOb
|
||||
std::vector<App::DocumentObject*> jointsOf;
|
||||
|
||||
for (auto joint : joints) {
|
||||
App::DocumentObject* obj1 = getObjFromRef(joint, "Reference1");
|
||||
App::DocumentObject* obj2 = getObjFromRef(joint, "Reference2");
|
||||
App::DocumentObject* obj1 = getObjFromJointRef(joint, "Reference1");
|
||||
App::DocumentObject* obj2 = getObjFromJointRef(joint, "Reference2");
|
||||
if (obj == obj1 || obj == obj2) {
|
||||
jointsOf.push_back(joint);
|
||||
}
|
||||
@@ -854,8 +829,8 @@ std::vector<App::DocumentObject*> AssemblyObject::getJointsOfPart(App::DocumentO
|
||||
std::vector<App::DocumentObject*> jointsOf;
|
||||
|
||||
for (auto joint : joints) {
|
||||
App::DocumentObject* part1 = getMovingPartFromRef(this, joint, "Reference1");
|
||||
App::DocumentObject* part2 = getMovingPartFromRef(this, joint, "Reference2");
|
||||
App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1");
|
||||
App::DocumentObject* part2 = getMovingPartFromRef(joint, "Reference2");
|
||||
if (part == part1 || part == part2) {
|
||||
jointsOf.push_back(joint);
|
||||
}
|
||||
@@ -961,7 +936,7 @@ bool AssemblyObject::isJointConnectingPartToGround(App::DocumentObject* joint, c
|
||||
return false;
|
||||
}
|
||||
|
||||
App::DocumentObject* part = getMovingPartFromRef(this, joint, propname);
|
||||
App::DocumentObject* part = getMovingPartFromRef(joint, propname);
|
||||
if (!part) {
|
||||
return false;
|
||||
}
|
||||
@@ -1055,8 +1030,8 @@ void AssemblyObject::removeUnconnectedJoints(
|
||||
joints.begin(),
|
||||
joints.end(),
|
||||
[&](App::DocumentObject* joint) {
|
||||
App::DocumentObject* obj1 = getMovingPartFromRef(this, joint, "Reference1");
|
||||
App::DocumentObject* obj2 = getMovingPartFromRef(this, joint, "Reference2");
|
||||
App::DocumentObject* obj1 = getMovingPartFromRef(joint, "Reference1");
|
||||
App::DocumentObject* obj2 = getMovingPartFromRef(joint, "Reference2");
|
||||
return (
|
||||
!isObjInSetOfObjRefs(obj1, connectedParts)
|
||||
|| !isObjInSetOfObjRefs(obj2, connectedParts)
|
||||
@@ -1100,8 +1075,8 @@ std::vector<ObjRef> AssemblyObject::getConnectedParts(
|
||||
continue;
|
||||
}
|
||||
|
||||
App::DocumentObject* obj1 = getMovingPartFromRef(this, joint, "Reference1");
|
||||
App::DocumentObject* obj2 = getMovingPartFromRef(this, joint, "Reference2");
|
||||
App::DocumentObject* obj1 = getMovingPartFromRef(joint, "Reference1");
|
||||
App::DocumentObject* obj2 = getMovingPartFromRef(joint, "Reference2");
|
||||
|
||||
if (!obj1 || !obj2) {
|
||||
continue;
|
||||
@@ -1674,12 +1649,12 @@ std::string AssemblyObject::handleOneSideOfJoint(
|
||||
const char* propPlcName
|
||||
)
|
||||
{
|
||||
App::DocumentObject* part = getMovingPartFromRef(this, joint, propRefName);
|
||||
App::DocumentObject* obj = getObjFromRef(joint, propRefName);
|
||||
App::DocumentObject* part = getMovingPartFromRef(joint, propRefName);
|
||||
App::DocumentObject* obj = getObjFromJointRef(joint, propRefName);
|
||||
|
||||
if (!part || !obj) {
|
||||
Base::Console()
|
||||
.warning("The property %s of Joint %s is bad.", propRefName, joint->getFullName());
|
||||
.warning("The property %s of Joint %s is bad.\n", propRefName, joint->getFullName());
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -1735,15 +1710,15 @@ void AssemblyObject::getRackPinionMarkers(
|
||||
swapJCS(joint); // make sure that rack is first.
|
||||
}
|
||||
|
||||
App::DocumentObject* part1 = getMovingPartFromRef(this, joint, "Reference1");
|
||||
App::DocumentObject* obj1 = getObjFromRef(joint, "Reference1");
|
||||
App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1");
|
||||
App::DocumentObject* obj1 = getObjFromJointRef(joint, "Reference1");
|
||||
Base::Placement plc1 = getPlacementFromProp(joint, "Placement1");
|
||||
|
||||
App::DocumentObject* obj2 = getObjFromRef(joint, "Reference2");
|
||||
App::DocumentObject* obj2 = getObjFromJointRef(joint, "Reference2");
|
||||
Base::Placement plc2 = getPlacementFromProp(joint, "Placement2");
|
||||
|
||||
if (!part1 || !obj1) {
|
||||
Base::Console().warning("Reference1 of Joint %s is bad.", joint->getFullName());
|
||||
Base::Console().warning("Reference1 of Joint %s is bad.\n", joint->getFullName());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1809,21 +1784,21 @@ void AssemblyObject::getRackPinionMarkers(
|
||||
|
||||
int AssemblyObject::slidingPartIndex(App::DocumentObject* joint)
|
||||
{
|
||||
App::DocumentObject* part1 = getMovingPartFromRef(this, joint, "Reference1");
|
||||
App::DocumentObject* obj1 = getObjFromRef(joint, "Reference1");
|
||||
App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1");
|
||||
App::DocumentObject* obj1 = getObjFromJointRef(joint, "Reference1");
|
||||
boost::ignore_unused(obj1);
|
||||
Base::Placement plc1 = getPlacementFromProp(joint, "Placement1");
|
||||
|
||||
App::DocumentObject* part2 = getMovingPartFromRef(this, joint, "Reference2");
|
||||
App::DocumentObject* obj2 = getObjFromRef(joint, "Reference2");
|
||||
App::DocumentObject* part2 = getMovingPartFromRef(joint, "Reference2");
|
||||
App::DocumentObject* obj2 = getObjFromJointRef(joint, "Reference2");
|
||||
boost::ignore_unused(obj2);
|
||||
Base::Placement plc2 = getPlacementFromProp(joint, "Placement2");
|
||||
|
||||
int slidingFound = 0;
|
||||
for (auto* jt : getJoints(false, false)) {
|
||||
if (getJointType(jt) == JointType::Slider) {
|
||||
App::DocumentObject* jpart1 = getMovingPartFromRef(this, jt, "Reference1");
|
||||
App::DocumentObject* jpart2 = getMovingPartFromRef(this, jt, "Reference2");
|
||||
App::DocumentObject* jpart1 = getMovingPartFromRef(jt, "Reference1");
|
||||
App::DocumentObject* jpart2 = getMovingPartFromRef(jt, "Reference2");
|
||||
int found = 0;
|
||||
Base::Placement plcjt, plci;
|
||||
if (jpart1 == part1 || jpart1 == part2) {
|
||||
@@ -1857,8 +1832,8 @@ bool AssemblyObject::isMbDJointValid(App::DocumentObject* joint)
|
||||
// When dragging a part, we are bundling fixed parts together.
|
||||
// This may lead to a conflicting joint that is self referencing a MbD part.
|
||||
// The solver crash when fed such a bad joint. So we make sure it does not happen.
|
||||
App::DocumentObject* part1 = getMovingPartFromRef(this, joint, "Reference1");
|
||||
App::DocumentObject* part2 = getMovingPartFromRef(this, joint, "Reference2");
|
||||
App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1");
|
||||
App::DocumentObject* part2 = getMovingPartFromRef(joint, "Reference2");
|
||||
if (!part1 || !part2) {
|
||||
return false;
|
||||
}
|
||||
@@ -1898,8 +1873,8 @@ AssemblyObject::MbDPartData AssemblyObject::getMbDData(App::DocumentObject* part
|
||||
for (auto* joint : joints) {
|
||||
JointType jointType = getJointType(joint);
|
||||
if (jointType == JointType::Fixed) {
|
||||
App::DocumentObject* part1 = getMovingPartFromRef(this, joint, "Reference1");
|
||||
App::DocumentObject* part2 = getMovingPartFromRef(this, joint, "Reference2");
|
||||
App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1");
|
||||
App::DocumentObject* part2 = getMovingPartFromRef(joint, "Reference2");
|
||||
App::DocumentObject* partToAdd = currentPart == part1 ? part2 : part1;
|
||||
|
||||
if (objectPartMap.find(partToAdd) != objectPartMap.end()) {
|
||||
@@ -2030,7 +2005,7 @@ App::DocumentObject* AssemblyObject::getUpstreamMovingPart(
|
||||
return part;
|
||||
}
|
||||
|
||||
part = getMovingPartFromRef(this, joint, name == "Reference1" ? "Reference2" : "Reference1");
|
||||
part = getMovingPartFromRef(joint, name == "Reference1" ? "Reference2" : "Reference1");
|
||||
|
||||
return getUpstreamMovingPart(part, joint, name);
|
||||
}
|
||||
|
||||
@@ -108,7 +108,6 @@ public:
|
||||
Base::Placement getMbdPlacement(std::shared_ptr<MbD::ASMTPart> mbdPart);
|
||||
bool validateNewPlacements();
|
||||
void setNewPlacements();
|
||||
static void recomputeJointPlacements(std::vector<App::DocumentObject*> joints);
|
||||
static void redrawJointPlacements(std::vector<App::DocumentObject*> joints);
|
||||
static void redrawJointPlacement(App::DocumentObject* joint);
|
||||
|
||||
|
||||
@@ -520,14 +520,19 @@ App::DocumentObject* getObjFromProp(const App::DocumentObject* joint, const char
|
||||
return propObj->getValue();
|
||||
}
|
||||
|
||||
App::DocumentObject* getObjFromRef(const App::DocumentObject* obj, const std::string& sub)
|
||||
App::DocumentObject* getObjFromRef(App::DocumentObject* comp, const std::string& sub)
|
||||
{
|
||||
if (!obj) {
|
||||
if (!comp) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto* doc = obj->getDocument();
|
||||
const auto names = Base::Tools::splitSubName(sub);
|
||||
const auto* doc = comp->getDocument();
|
||||
auto names = Base::Tools::splitSubName(sub);
|
||||
names.insert(names.begin(), comp->getNameInDocument());
|
||||
|
||||
if (names.size() <= 2) {
|
||||
return comp;
|
||||
}
|
||||
|
||||
// Lambda function to check if the typeId is a BodySubObject
|
||||
const auto isBodySubObject = [](App::DocumentObject* obj) -> bool {
|
||||
@@ -618,7 +623,7 @@ App::DocumentObject* getObjFromRef(const App::PropertyXLinkSub* prop)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const App::DocumentObject* obj = prop->getValue();
|
||||
App::DocumentObject* obj = prop->getValue();
|
||||
if (!obj) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -631,7 +636,7 @@ App::DocumentObject* getObjFromRef(const App::PropertyXLinkSub* prop)
|
||||
return getObjFromRef(obj, subs[0]);
|
||||
}
|
||||
|
||||
App::DocumentObject* getObjFromRef(const App::DocumentObject* joint, const char* pName)
|
||||
App::DocumentObject* getObjFromJointRef(const App::DocumentObject* joint, const char* pName)
|
||||
{
|
||||
if (!joint) {
|
||||
return nullptr;
|
||||
@@ -647,13 +652,13 @@ App::DocumentObject* getLinkedObjFromRef(const App::DocumentObject* joint, const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (const auto* obj = getObjFromRef(joint, pObj)) {
|
||||
if (const auto* obj = getObjFromJointRef(joint, pObj)) {
|
||||
return obj->getLinkedObject(true);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
App::DocumentObject* getMovingPartFromRef(
|
||||
App::DocumentObject* getMovingPartFromSel(
|
||||
const AssemblyObject* assemblyObject,
|
||||
App::DocumentObject* obj,
|
||||
const std::string& sub
|
||||
@@ -710,39 +715,23 @@ App::DocumentObject* getMovingPartFromRef(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
App::DocumentObject* getMovingPartFromRef(
|
||||
const AssemblyObject* assemblyObject,
|
||||
App::PropertyXLinkSub* prop
|
||||
)
|
||||
App::DocumentObject* getMovingPartFromRef(App::PropertyXLinkSub* prop)
|
||||
{
|
||||
if (!prop) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
App::DocumentObject* obj = prop->getValue();
|
||||
if (!obj) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::vector<std::string> subs = prop->getSubValues();
|
||||
if (subs.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return getMovingPartFromRef(assemblyObject, obj, subs[0]);
|
||||
return prop->getValue();
|
||||
}
|
||||
|
||||
App::DocumentObject* getMovingPartFromRef(
|
||||
const AssemblyObject* assemblyObject,
|
||||
App::DocumentObject* joint,
|
||||
const char* pName
|
||||
)
|
||||
App::DocumentObject* getMovingPartFromRef(App::DocumentObject* joint, const char* pName)
|
||||
{
|
||||
if (!joint) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* prop = joint->getPropertyByName<App::PropertyXLinkSub>(pName);
|
||||
return getMovingPartFromRef(assemblyObject, prop);
|
||||
return getMovingPartFromRef(prop);
|
||||
}
|
||||
|
||||
void syncPlacements(App::DocumentObject* src, App::DocumentObject* to)
|
||||
|
||||
@@ -165,27 +165,24 @@ AssemblyExport App::DocumentObject* getObjFromProp(
|
||||
const App::DocumentObject* joint,
|
||||
const char* propName
|
||||
);
|
||||
AssemblyExport App::DocumentObject* getObjFromRef(const App::DocumentObject* obj, const std::string& sub);
|
||||
AssemblyExport App::DocumentObject* getObjFromRef(App::DocumentObject* obj, const std::string& sub);
|
||||
AssemblyExport App::DocumentObject* getObjFromRef(const App::PropertyXLinkSub* prop);
|
||||
AssemblyExport App::DocumentObject* getObjFromRef(const App::DocumentObject* joint, const char* propName);
|
||||
AssemblyExport App::DocumentObject* getObjFromJointRef(
|
||||
const App::DocumentObject* joint,
|
||||
const char* propName
|
||||
);
|
||||
AssemblyExport App::DocumentObject* getLinkedObjFromRef(
|
||||
const App::DocumentObject* joint,
|
||||
const char* propName
|
||||
);
|
||||
AssemblyExport App::DocumentObject* getMovingPartFromRef(
|
||||
// Get the moving part from a selection, which has the full path.
|
||||
AssemblyExport App::DocumentObject* getMovingPartFromSel(
|
||||
const AssemblyObject* assemblyObject,
|
||||
App::DocumentObject* obj,
|
||||
const std::string& sub
|
||||
);
|
||||
AssemblyExport App::DocumentObject* getMovingPartFromRef(
|
||||
const AssemblyObject* assemblyObject,
|
||||
const App::PropertyXLinkSub* prop
|
||||
);
|
||||
AssemblyExport App::DocumentObject* getMovingPartFromRef(
|
||||
const AssemblyObject* assemblyObject,
|
||||
App::DocumentObject* joint,
|
||||
const char* pName
|
||||
);
|
||||
AssemblyExport App::DocumentObject* getMovingPartFromRef(const App::PropertyXLinkSub* prop);
|
||||
AssemblyExport App::DocumentObject* getMovingPartFromRef(App::DocumentObject* joint, const char* pName);
|
||||
AssemblyExport std::vector<std::string> getSubAsList(const App::PropertyXLinkSub* prop);
|
||||
AssemblyExport std::vector<std::string> getSubAsList(
|
||||
const App::DocumentObject* joint,
|
||||
|
||||
@@ -206,8 +206,8 @@ class TestCore(unittest.TestCase):
|
||||
JointObject.Joint(joint, 0)
|
||||
|
||||
refs = [
|
||||
[self.assembly, [box2.Name + ".Face6", box2.Name + ".Vertex7"]],
|
||||
[self.assembly, [box.Name + ".Face6", box.Name + ".Vertex7"]],
|
||||
[box2, ["Face6", "Vertex7"]],
|
||||
[box, ["Face6", "Vertex7"]],
|
||||
]
|
||||
|
||||
joint.Proxy.setJointConnectors(joint, refs)
|
||||
|
||||
@@ -416,6 +416,8 @@ def createGroundedJoint(obj):
|
||||
)
|
||||
Gui.doCommand(commands)
|
||||
Gui.doCommandGui("JointObject.ViewProviderGroundedJoint(ground.ViewObject)")
|
||||
|
||||
Gui.doCommand("UtilsAssembly.activeAssembly().Document.recompute()")
|
||||
return Gui.doCommandEval("ground")
|
||||
|
||||
|
||||
@@ -471,8 +473,11 @@ class CommandToggleGrounded:
|
||||
Gui.doCommand(commands)
|
||||
continue
|
||||
|
||||
ref = [sel.Object, [sub, sub]]
|
||||
moving_part = UtilsAssembly.getMovingPart(assembly, ref)
|
||||
moving_part, new_sub = UtilsAssembly.getComponentReference(
|
||||
assembly, sel.Object, sub
|
||||
)
|
||||
if not moving_part:
|
||||
continue
|
||||
|
||||
# Only objects within the assembly.
|
||||
if moving_part is None:
|
||||
|
||||
@@ -562,7 +562,8 @@ class ExplodedViewSelGate:
|
||||
self.viewObj = viewObj
|
||||
|
||||
def allow(self, doc, obj, sub):
|
||||
if (obj.Name == self.assembly.Name and sub) or self.assembly.hasObject(obj, True):
|
||||
comp, new_sub = UtilsAssembly.getComponentReference(self.assembly, obj, sub)
|
||||
if comp:
|
||||
# Objects within the assembly.
|
||||
return True
|
||||
|
||||
@@ -662,6 +663,7 @@ class TaskAssemblyCreateView(QtCore.QObject):
|
||||
def reject(self):
|
||||
self.deactivate()
|
||||
App.closeActiveTransaction(True)
|
||||
App.activeDocument().recompute()
|
||||
return True
|
||||
|
||||
def deactivate(self):
|
||||
@@ -709,9 +711,14 @@ class TaskAssemblyCreateView(QtCore.QObject):
|
||||
continue
|
||||
|
||||
for sub_name in sel.SubElementNames:
|
||||
ref = [sel.Object, [sub_name]]
|
||||
moving_part, new_sub = UtilsAssembly.getComponentReference(
|
||||
self.assembly, sel.Object, sub_name
|
||||
)
|
||||
if not moving_part:
|
||||
continue
|
||||
|
||||
ref = [moving_part, [new_sub]]
|
||||
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.
|
||||
@@ -733,6 +740,7 @@ class TaskAssemblyCreateView(QtCore.QObject):
|
||||
ref[1][0] = UtilsAssembly.truncateSubAtFirst(ref[1][0], obj.Name)
|
||||
|
||||
if not obj in self.selectedObjs and hasattr(obj, "Placement"):
|
||||
ref = [sel.Object, [sub_name]]
|
||||
self.selectedRefs.append(ref)
|
||||
self.selectedObjs.append(obj)
|
||||
self.selectedObjsInitPlc.append(App.Placement(obj.Placement))
|
||||
@@ -992,9 +1000,12 @@ class TaskAssemblyCreateView(QtCore.QObject):
|
||||
return
|
||||
|
||||
else:
|
||||
ref = [App.getDocument(doc_name).getObject(obj_name), [sub_name]]
|
||||
rootObj = App.getDocument(doc_name).getObject(obj_name)
|
||||
moving_part, new_sub = UtilsAssembly.getComponentReference(
|
||||
self.assembly, rootObj, sub_name
|
||||
)
|
||||
ref = [moving_part, [new_sub]]
|
||||
obj = UtilsAssembly.getObject(ref)
|
||||
moving_part = UtilsAssembly.getMovingPart(self.assembly, ref)
|
||||
|
||||
if obj is None or moving_part is None:
|
||||
return
|
||||
|
||||
@@ -64,9 +64,8 @@ class CommandSolveAssembly:
|
||||
if not assembly:
|
||||
return
|
||||
|
||||
Gui.addModule("UtilsAssembly")
|
||||
App.setActiveTransaction("Solve assembly")
|
||||
Gui.doCommand("UtilsAssembly.activeAssembly().solve()")
|
||||
assembly.recompute(True)
|
||||
App.closeActiveTransaction()
|
||||
|
||||
|
||||
|
||||
@@ -199,10 +199,10 @@ bool ViewProviderAssembly::canDragObjectToTarget(App::DocumentObject* obj, App::
|
||||
|
||||
for (auto joint : allJoints) {
|
||||
// getLinkObjFromProp returns nullptr if the property doesn't exist.
|
||||
App::DocumentObject* part1 = getMovingPartFromRef(assemblyPart, joint, "Reference1");
|
||||
App::DocumentObject* part2 = getMovingPartFromRef(assemblyPart, joint, "Reference2");
|
||||
App::DocumentObject* obj1 = getObjFromRef(joint, "Reference1");
|
||||
App::DocumentObject* obj2 = getObjFromRef(joint, "Reference2");
|
||||
App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1");
|
||||
App::DocumentObject* part2 = getMovingPartFromRef(joint, "Reference2");
|
||||
App::DocumentObject* obj1 = getObjFromJointRef(joint, "Reference1");
|
||||
App::DocumentObject* obj2 = getObjFromJointRef(joint, "Reference2");
|
||||
App::DocumentObject* obj3 = getObjFromProp(joint, "ObjectToGround");
|
||||
if (obj == obj1 || obj == obj2 || obj == part1 || obj == part2 || obj == obj3) {
|
||||
if (!prompted) {
|
||||
@@ -782,7 +782,7 @@ bool ViewProviderAssembly::getSelectedObjectsWithinAssembly(bool addPreselection
|
||||
App::DocumentObject* selRoot = Gui::Selection().getPreselection().Object.getObject();
|
||||
std::string sub = Gui::Selection().getPreselection().pSubName;
|
||||
|
||||
App::DocumentObject* obj = getMovingPartFromRef(assemblyPart, selRoot, sub);
|
||||
App::DocumentObject* obj = getMovingPartFromSel(assemblyPart, selRoot, sub);
|
||||
if (canDragObjectIn3d(obj)) {
|
||||
|
||||
bool alreadyIn = false;
|
||||
@@ -850,7 +850,7 @@ void ViewProviderAssembly::collectMovableObjects(
|
||||
return;
|
||||
}
|
||||
|
||||
App::DocumentObject* part = getMovingPartFromRef(assemblyPart, selRoot, subNamePrefix);
|
||||
App::DocumentObject* part = getMovingPartFromSel(assemblyPart, selRoot, subNamePrefix);
|
||||
|
||||
if (onlySolids && assemblyPart->isPartConnected(part)) {
|
||||
return; // No dragger for connected parts.
|
||||
@@ -955,7 +955,7 @@ ViewProviderAssembly::DragMode ViewProviderAssembly::findDragMode()
|
||||
if (!ref) {
|
||||
return DragMode::Translation;
|
||||
}
|
||||
auto* obj = getObjFromRef(movingJoint, pName.c_str());
|
||||
auto* obj = getObjFromJointRef(movingJoint, pName.c_str());
|
||||
Base::Placement global_plc = App::GeoFeature::getGlobalPlacement(obj, ref);
|
||||
jcsGlobalPlc = global_plc * jcsPlc;
|
||||
|
||||
@@ -1022,6 +1022,7 @@ void ViewProviderAssembly::tryInitMove(const SbVec2s& cursorPos, Gui::View3DInve
|
||||
else if (visible) {
|
||||
joint->Visibility.setValue(false);
|
||||
}
|
||||
joint->purgeTouched();
|
||||
}
|
||||
|
||||
SbVec3f vec;
|
||||
@@ -1100,6 +1101,7 @@ void ViewProviderAssembly::endMove()
|
||||
bool visible = pair.first->Visibility.getValue();
|
||||
if (visible != pair.second) {
|
||||
pair.first->Visibility.setValue(pair.second);
|
||||
pair.first->purgeTouched();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1468,10 +1470,8 @@ void ViewProviderAssembly::isolateJointReferences(App::DocumentObject* joint, Is
|
||||
return;
|
||||
}
|
||||
|
||||
AssemblyObject* assembly = getObject<AssemblyObject>();
|
||||
|
||||
App::DocumentObject* part1 = getMovingPartFromRef(assembly, joint, "Reference1");
|
||||
App::DocumentObject* part2 = getMovingPartFromRef(assembly, joint, "Reference2");
|
||||
App::DocumentObject* part1 = getMovingPartFromRef(joint, "Reference1");
|
||||
App::DocumentObject* part2 = getMovingPartFromRef(joint, "Reference2");
|
||||
if (!part1 || !part2) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -201,16 +201,11 @@ class Joint:
|
||||
self.migrationScript4(joint)
|
||||
self.migrationScript5(joint)
|
||||
self.migrationScript6(joint)
|
||||
self.migrationScript7(joint)
|
||||
|
||||
# First Joint Connector
|
||||
if not hasattr(joint, "Reference1"):
|
||||
joint.addProperty(
|
||||
"App::PropertyXLinkSubHidden",
|
||||
"Reference1",
|
||||
"Joint Connector 1",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The first reference of the joint"),
|
||||
locked=True,
|
||||
)
|
||||
self.addReference1Property(joint)
|
||||
|
||||
if not hasattr(joint, "Placement1"):
|
||||
joint.addProperty(
|
||||
@@ -250,13 +245,7 @@ class Joint:
|
||||
|
||||
# Second Joint Connector
|
||||
if not hasattr(joint, "Reference2"):
|
||||
joint.addProperty(
|
||||
"App::PropertyXLinkSubHidden",
|
||||
"Reference2",
|
||||
"Joint Connector 2",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The second reference of the joint"),
|
||||
locked=True,
|
||||
)
|
||||
self.addReference2Property(joint)
|
||||
|
||||
if not hasattr(joint, "Placement2"):
|
||||
joint.addProperty(
|
||||
@@ -373,6 +362,24 @@ class Joint:
|
||||
joint.setPropertyStatus("LengthMin", "AllowNegativeValues")
|
||||
joint.setPropertyStatus("LengthMax", "AllowNegativeValues")
|
||||
|
||||
def addReference1Property(self, joint):
|
||||
joint.addProperty(
|
||||
"App::PropertyXLinkSub",
|
||||
"Reference1",
|
||||
"Joint Connector 1",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The first reference of the joint"),
|
||||
locked=True,
|
||||
)
|
||||
|
||||
def addReference2Property(self, joint):
|
||||
joint.addProperty(
|
||||
"App::PropertyXLinkSub",
|
||||
"Reference2",
|
||||
"Joint Connector 2",
|
||||
QT_TRANSLATE_NOOP("App::Property", "The second reference of the joint"),
|
||||
locked=True,
|
||||
)
|
||||
|
||||
def addAngleProperty(self, joint):
|
||||
joint.addProperty(
|
||||
"App::PropertyAngle",
|
||||
@@ -681,6 +688,54 @@ class Joint:
|
||||
self.addAngleMaxProperty(joint)
|
||||
joint.AngleMax = old_value
|
||||
|
||||
def migrationScript7(self, joint):
|
||||
"""
|
||||
Migrates PropertyXLinkSubHidden (Assembly-rooted) to PropertyXLinkSub (Component-rooted).
|
||||
"""
|
||||
|
||||
def migrate_prop(prop_name, add_method, assembly):
|
||||
if not hasattr(joint, prop_name):
|
||||
return
|
||||
|
||||
# Only migrate if it's the old type
|
||||
if joint.getTypeIdOfProperty(prop_name) != "App::PropertyXLinkSubHidden":
|
||||
return
|
||||
|
||||
old_ref = getattr(joint, prop_name)
|
||||
|
||||
joint.setPropertyStatus(prop_name, "-LockDynamic")
|
||||
joint.removeProperty(prop_name)
|
||||
|
||||
add_method(joint)
|
||||
|
||||
if old_ref:
|
||||
root = old_ref[0]
|
||||
old_subs = old_ref[1]
|
||||
if old_subs:
|
||||
comp, first_new_sub = UtilsAssembly.getComponentReference(
|
||||
assembly, root, old_subs[0]
|
||||
)
|
||||
|
||||
if comp:
|
||||
new_subs = [first_new_sub]
|
||||
|
||||
# We can deduce the prefix length from the difference
|
||||
# between old and new of the first item.
|
||||
prefix_len = len(old_subs[0]) - len(first_new_sub)
|
||||
|
||||
for i in range(1, len(old_subs)):
|
||||
sub = old_subs[i]
|
||||
if len(sub) >= prefix_len:
|
||||
new_subs.append(sub[prefix_len:])
|
||||
else:
|
||||
new_subs.append(sub)
|
||||
|
||||
setattr(joint, prop_name, [comp, new_subs])
|
||||
|
||||
assembly = self.getAssembly(joint)
|
||||
migrate_prop("Reference1", self.addReference1Property, assembly)
|
||||
migrate_prop("Reference2", self.addReference2Property, assembly)
|
||||
|
||||
def dumps(self):
|
||||
return None
|
||||
|
||||
@@ -772,6 +827,8 @@ class Joint:
|
||||
):
|
||||
raise Exception(errStr + "Reference2")
|
||||
|
||||
self.updateJCSPlacements(joint)
|
||||
|
||||
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)
|
||||
@@ -812,6 +869,14 @@ class Joint:
|
||||
if not joint.Detach2:
|
||||
joint.Placement2 = self.findPlacement(joint, joint.Reference2, 1)
|
||||
|
||||
self.redrawJointPlacements(joint)
|
||||
|
||||
def redrawJointPlacements(self, joint):
|
||||
if joint.ViewObject:
|
||||
proxy = joint.ViewObject.Proxy
|
||||
if proxy:
|
||||
proxy.redrawJointPlacements(joint)
|
||||
|
||||
"""
|
||||
So here we want to find a placement that corresponds to a local coordinate system that would be placed at the selected vertex.
|
||||
- obj is usually a App::Link to a PartDesign::Body, or primitive, fasteners. But can also be directly the object.1
|
||||
@@ -850,8 +915,8 @@ class Joint:
|
||||
if reverse:
|
||||
sameDir = not sameDir
|
||||
|
||||
part1 = UtilsAssembly.getMovingPart(assembly, joint.Reference1)
|
||||
part2 = UtilsAssembly.getMovingPart(assembly, joint.Reference2)
|
||||
part1 = UtilsAssembly.getMovingPart(joint.Reference1)
|
||||
part2 = UtilsAssembly.getMovingPart(joint.Reference2)
|
||||
|
||||
if not part1 or not part2:
|
||||
return False
|
||||
@@ -915,7 +980,8 @@ class Joint:
|
||||
part.Placement = plc
|
||||
self.partsMovedByPresolved = {}
|
||||
|
||||
joint.Placement1 = joint.Placement1 # Make sure plc1 is redrawn
|
||||
if joint.ViewObject:
|
||||
joint.ViewObject.Proxy.redrawJointPlacements(joint)
|
||||
|
||||
def preventParallel(self, joint):
|
||||
# Angle and perpendicular joints in the solver cannot handle the situation where both JCS are Parallel
|
||||
@@ -925,8 +991,8 @@ class Joint:
|
||||
|
||||
assembly = self.getAssembly(joint)
|
||||
|
||||
part1 = UtilsAssembly.getMovingPart(assembly, joint.Reference1)
|
||||
part2 = UtilsAssembly.getMovingPart(assembly, joint.Reference2)
|
||||
part1 = UtilsAssembly.getMovingPart(joint.Reference1)
|
||||
part2 = UtilsAssembly.getMovingPart(joint.Reference2)
|
||||
|
||||
isAssembly = assembly.Type == "Assembly"
|
||||
if isAssembly:
|
||||
@@ -999,24 +1065,25 @@ class ViewProviderJoint:
|
||||
|
||||
def updateData(self, joint, prop):
|
||||
"""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 hasattr(joint, "Reference1") and joint.Reference1:
|
||||
plc = joint.Placement1
|
||||
self.switch_JCS1.whichChild = coin.SO_SWITCH_ALL
|
||||
if prop == "Placement1" and hasattr(joint, "Reference1"):
|
||||
self.redrawJointPlacement(self.switch_JCS1, joint.Placement1, joint.Reference1)
|
||||
|
||||
self.switch_JCS1.set_marker_placement(plc, joint.Reference1)
|
||||
else:
|
||||
self.switch_JCS1.whichChild = coin.SO_SWITCH_NONE
|
||||
if prop == "Placement2" and hasattr(joint, "Reference2"):
|
||||
self.redrawJointPlacement(self.switch_JCS2, joint.Placement2, joint.Reference2)
|
||||
|
||||
if prop == "Placement2":
|
||||
if hasattr(joint, "Reference2") and joint.Reference2:
|
||||
plc = joint.Placement2
|
||||
self.switch_JCS2.whichChild = coin.SO_SWITCH_ALL
|
||||
def redrawJointPlacements(self, joint):
|
||||
if not hasattr(joint, "Reference1") or not hasattr(joint, "Reference2"):
|
||||
return
|
||||
|
||||
self.switch_JCS2.set_marker_placement(plc, joint.Reference2)
|
||||
else:
|
||||
self.switch_JCS2.whichChild = coin.SO_SWITCH_NONE
|
||||
self.redrawJointPlacement(self.switch_JCS1, joint.Placement1, joint.Reference1)
|
||||
self.redrawJointPlacement(self.switch_JCS2, joint.Placement2, joint.Reference2)
|
||||
|
||||
def redrawJointPlacement(self, jcs, plc, ref):
|
||||
if ref:
|
||||
jcs.whichChild = coin.SO_SWITCH_ALL
|
||||
jcs.set_marker_placement(plc, ref)
|
||||
else:
|
||||
jcs.whichChild = coin.SO_SWITCH_NONE
|
||||
|
||||
def showPreviewJCS(self, visible, placement=None, ref=None):
|
||||
if visible:
|
||||
@@ -1091,7 +1158,7 @@ class ViewProviderJoint:
|
||||
assembly = self.app_obj.Proxy.getAssembly(self.app_obj)
|
||||
# Assuming Reference1 corresponds to the first part link
|
||||
if hasattr(self.app_obj, "Reference1"):
|
||||
part = UtilsAssembly.getMovingPart(assembly, self.app_obj.Reference1)
|
||||
part = UtilsAssembly.getMovingPart(self.app_obj.Reference1)
|
||||
if part is not None and not assembly.isPartConnected(part):
|
||||
overlays[Gui.IconPosition.BottomLeft] = "Part_Detached"
|
||||
|
||||
@@ -1531,7 +1598,6 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
|
||||
|
||||
self.deactivate()
|
||||
|
||||
solveIfAllowed(self.assembly)
|
||||
if self.activeType == "Assembly":
|
||||
self.joint.Visibility = self.visibilityBackup
|
||||
else:
|
||||
@@ -1540,14 +1606,15 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
|
||||
cmds = UtilsAssembly.generatePropertySettings(self.joint)
|
||||
Gui.doCommand(cmds)
|
||||
|
||||
self.assembly.recompute(True)
|
||||
|
||||
App.closeActiveTransaction()
|
||||
return True
|
||||
|
||||
def reject(self):
|
||||
self.deactivate()
|
||||
App.closeActiveTransaction(True)
|
||||
if not self.creating: # update visibility only if we are editing the joint
|
||||
self.joint.Visibility = self.visibilityBackup
|
||||
self.assembly.recompute(True)
|
||||
return True
|
||||
|
||||
def autoClosedOnTransactionChange(self):
|
||||
@@ -1596,8 +1663,15 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
|
||||
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)
|
||||
|
||||
moving_part, new_sub = UtilsAssembly.getComponentReference(
|
||||
self.assembly, sel.Object, sub_name
|
||||
)
|
||||
if not moving_part:
|
||||
break
|
||||
|
||||
# Construct the reference using the Component as the root
|
||||
ref = [moving_part, [new_sub, new_sub]]
|
||||
|
||||
# Only objects within the assembly.
|
||||
if moving_part is None:
|
||||
@@ -1628,9 +1702,12 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
|
||||
joint_group = UtilsAssembly.getJointGroup(self.assembly)
|
||||
self.joint = joint_group.newObject("App::FeaturePython", "Joint")
|
||||
self.joint.Label = self.jointName
|
||||
joint_group.purgeTouched()
|
||||
self.assembly.purgeTouched()
|
||||
|
||||
Joint(self.joint, type_index)
|
||||
ViewProviderJoint(self.joint.ViewObject)
|
||||
self.joint.purgeTouched()
|
||||
|
||||
def onJointTypeChanged(self, index):
|
||||
self.jType = JointTypes[self.jForm.jointType.currentIndex()]
|
||||
@@ -1836,14 +1913,15 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
|
||||
ref1 = self.joint.Reference1
|
||||
ref2 = self.joint.Reference2
|
||||
|
||||
self.refs.append(ref1)
|
||||
self.refs.append(ref2)
|
||||
if UtilsAssembly.isRefValid(ref1, 2):
|
||||
self.refs.append(ref1)
|
||||
sub1 = UtilsAssembly.addTipNameToSub(ref1)
|
||||
Gui.Selection.addSelection(ref1[0].Document.Name, ref1[0].Name, sub1)
|
||||
|
||||
sub1 = UtilsAssembly.addTipNameToSub(ref1)
|
||||
sub2 = UtilsAssembly.addTipNameToSub(ref2)
|
||||
|
||||
Gui.Selection.addSelection(ref1[0].Document.Name, ref1[0].Name, sub1)
|
||||
Gui.Selection.addSelection(ref2[0].Document.Name, ref2[0].Name, sub2)
|
||||
if UtilsAssembly.isRefValid(ref2, 2):
|
||||
self.refs.append(ref2)
|
||||
sub2 = UtilsAssembly.addTipNameToSub(ref2)
|
||||
Gui.Selection.addSelection(ref2[0].Document.Name, ref2[0].Name, sub2)
|
||||
|
||||
self.jForm.angleSpinbox.setProperty("rawValue", self.joint.Angle.Value)
|
||||
self.jForm.distanceSpinbox.setProperty("rawValue", self.joint.Distance.Value)
|
||||
@@ -2025,7 +2103,7 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
|
||||
self._removeSelectedItems(selected_indexes)
|
||||
|
||||
def getMovingPart(self, ref):
|
||||
return UtilsAssembly.getMovingPart(self.assembly, ref)
|
||||
return UtilsAssembly.getMovingPart(ref)
|
||||
|
||||
# selectionObserver stuff
|
||||
def addSelection(self, doc_name, obj_name, sub_name, mousePos):
|
||||
@@ -2038,7 +2116,15 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
|
||||
|
||||
sub_name = UtilsAssembly.fixBodyExtraFeatureInSub(doc_name, sub_name)
|
||||
|
||||
ref = [rootObj, [sub_name]]
|
||||
comp, new_sub = UtilsAssembly.getComponentReference(self.assembly, rootObj, sub_name)
|
||||
if not comp:
|
||||
# Selection was not valid (not inside assembly or logic failed)
|
||||
Gui.Selection.removeSelection(doc_name, obj_name, sub_name)
|
||||
return
|
||||
|
||||
# Construct the reference using the Component as the root
|
||||
ref = [comp, [new_sub]]
|
||||
|
||||
moving_part = self.getMovingPart(ref)
|
||||
|
||||
# Check if the addition is acceptable (we are not doing this in selection gate to let user move objects)
|
||||
@@ -2085,12 +2171,16 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
|
||||
|
||||
sub_name = UtilsAssembly.fixBodyExtraFeatureInSub(doc_name, sub_name)
|
||||
|
||||
comp, new_sub = UtilsAssembly.getComponentReference(self.assembly, rootObj, sub_name)
|
||||
if not comp:
|
||||
return
|
||||
|
||||
for reference in self.refs[:]:
|
||||
ref_obj = reference[0]
|
||||
ref_element_name = reference[1][0] if len(reference[1]) > 0 else ""
|
||||
|
||||
# match both object and processed element name for precise identification
|
||||
if ref_obj == rootObj and ref_element_name == sub_name:
|
||||
if ref_obj == comp and ref_element_name == new_sub:
|
||||
self.refs.remove(reference)
|
||||
break
|
||||
else:
|
||||
@@ -2103,7 +2193,13 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
|
||||
self.presel_ref = None
|
||||
return
|
||||
|
||||
self.presel_ref = [App.getDocument(doc_name).getObject(obj_name), [sub_name]]
|
||||
rootObj = App.getDocument(doc_name).getObject(obj_name)
|
||||
|
||||
comp, new_sub = UtilsAssembly.getComponentReference(self.assembly, rootObj, sub_name)
|
||||
if not comp:
|
||||
return
|
||||
|
||||
self.presel_ref = [comp, [new_sub]]
|
||||
|
||||
def clearSelection(self, doc_name):
|
||||
self.refs.clear()
|
||||
|
||||
@@ -128,7 +128,7 @@ def isLinkGroup(obj):
|
||||
|
||||
|
||||
def getObject(ref):
|
||||
if len(ref) != 2:
|
||||
if len(ref) != 2 or ref[0] is None:
|
||||
return None
|
||||
subs = ref[1]
|
||||
if len(subs) < 1:
|
||||
@@ -139,7 +139,10 @@ def getObject(ref):
|
||||
# or "LinkOrAssembly1.LinkOrPart1.LinkOrBody.pad.Edge16"
|
||||
# or "Assembly.LinkOrAssembly1.LinkOrPart1.LinkOrBody.Local_CS.X"
|
||||
# We want either LinkOrBody or LinkOrBox or Local_CS.
|
||||
# Note since the ref now holds the moving part, sub_name can now be just the element name.
|
||||
# In this case the obj we need is the reference obj
|
||||
names = sub_name.split(".")
|
||||
names.insert(0, ref[0].Name)
|
||||
|
||||
if len(names) < 2:
|
||||
return None
|
||||
@@ -347,24 +350,6 @@ def getRootPath(obj, part):
|
||||
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(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(assembly, jcsPlc, ref):
|
||||
obj_relative_plc = getObjPlcRelativeToPart(assembly, ref)
|
||||
return obj_relative_plc * jcsPlc
|
||||
|
||||
|
||||
# Return the jcs global placement
|
||||
def getJcsGlobalPlc(jcsPlc, ref):
|
||||
obj_global_plc = getGlobalPlacement(ref)
|
||||
@@ -382,6 +367,15 @@ def getGlobalPlacement(ref, targetObj=None):
|
||||
|
||||
rootObj = ref[0]
|
||||
subName = ref[1][0]
|
||||
# ref[0] is no longer the root object. Now it's the moving part.
|
||||
# So to get the correct global placement in case user transformed the assembly,
|
||||
# or if he has nested the assembly, we need to adjust it.
|
||||
# Example : Part / Assembly / Cylinder / Face1
|
||||
# ref is [(Cylinder, 'Face1')]
|
||||
parents = rootObj.Parents # [(<Part object>, 'Assembly.Cylinder.')]
|
||||
if parents is not None and len(parents) == 1 and len(parents[0]) == 2:
|
||||
rootObj = parents[0][0]
|
||||
subName = parents[0][1] + subName
|
||||
|
||||
return rootObj.getPlacementOf(subName, targetObj)
|
||||
|
||||
@@ -398,7 +392,7 @@ def getElementName(full_name):
|
||||
# We want either Edge16.
|
||||
parts = full_name.split(".")
|
||||
|
||||
if len(parts) < 2:
|
||||
if len(parts) < 1:
|
||||
# At minimum "Box.edge16". It shouldn't be shorter
|
||||
return ""
|
||||
|
||||
@@ -1214,59 +1208,79 @@ def getJointXYAngle(joint):
|
||||
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:
|
||||
def getMovingPart(ref):
|
||||
if 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
|
||||
return obj
|
||||
|
||||
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
|
||||
def getComponentReference(assembly, root_obj, sub_string):
|
||||
"""
|
||||
Takes a full selection path (root + subnames) and normalizes it
|
||||
to be rooted at the Assembly Component (Moving Part).
|
||||
|
||||
Returns: (ComponentObject, RelativeSubString) or (None, "")
|
||||
"""
|
||||
if not assembly or not root_obj:
|
||||
return None, ""
|
||||
|
||||
doc = assembly.Document
|
||||
|
||||
if len(names) < 2:
|
||||
App.Console.PrintError(
|
||||
f"getMovingPart() in UtilsAssembly.py the object name {names} is too short. It should be at least similar to ['Box','edge16'], not shorter.\n"
|
||||
)
|
||||
return None
|
||||
# 1. Reconstruct full path
|
||||
# e.g. ['Part', 'Assembly', 'Cylinder', 'Face1']
|
||||
names = [root_obj.Name] + sub_string.split(".")
|
||||
|
||||
for objName in names:
|
||||
obj = doc.getObject(objName)
|
||||
# 2. Find Assembly in path
|
||||
try:
|
||||
asm_idx = names.index(assembly.Name)
|
||||
except ValueError:
|
||||
return None, ""
|
||||
|
||||
# 3. Identify Component (first valid object after Assembly)
|
||||
candidates = names[asm_idx + 1 :]
|
||||
if not candidates:
|
||||
return None, ""
|
||||
|
||||
component = None
|
||||
comp_idx = -1
|
||||
|
||||
for i, obj_name in enumerate(candidates):
|
||||
obj = doc.getObject(obj_name)
|
||||
if not obj:
|
||||
continue
|
||||
|
||||
if obj.TypeId == "App::DocumentObjectGroup":
|
||||
continue # we ignore groups.
|
||||
|
||||
# We ignore dynamic sub-assemblies.
|
||||
if obj.isDerivedFrom("Assembly::AssemblyLink") and obj.Rigid == False:
|
||||
# Skips (Groups / Flexible Links / LinkGroups / non-geofeature objects)
|
||||
if obj.isDerivedFrom("App::DocumentObjectGroup"):
|
||||
continue
|
||||
|
||||
# If it is a LinkGroup then we skip it
|
||||
if obj.isDerivedFrom("Assembly::AssemblyLink"):
|
||||
if hasattr(obj, "Rigid") and not obj.Rigid:
|
||||
continue
|
||||
if isLinkGroup(obj):
|
||||
continue
|
||||
|
||||
return obj
|
||||
if isLink(obj):
|
||||
linkedObj = obj.getLinkedObject()
|
||||
if linkedObj and not linkedObj.isDerivedFrom("App::GeoFeature"):
|
||||
continue
|
||||
elif not obj.isDerivedFrom("App::GeoFeature"):
|
||||
continue
|
||||
|
||||
return None
|
||||
component = obj
|
||||
comp_idx = asm_idx + 1 + i
|
||||
break
|
||||
|
||||
if not component:
|
||||
return None, ""
|
||||
|
||||
# 4. Construct new sub-string
|
||||
# Everything after the component in the original names list
|
||||
relative_parts = names[comp_idx + 1 :]
|
||||
new_sub = ".".join(relative_parts)
|
||||
|
||||
return component, new_sub
|
||||
|
||||
|
||||
def truncateSubAtFirst(sub, target):
|
||||
|
||||
Reference in New Issue
Block a user