Assembly: Change the joint placements to be relative to the object rather than to the doc origin.

This enable 'detaching' them, so that they are not recomputed
This commit is contained in:
Paddle
2023-12-20 23:32:29 +01:00
committed by PaddleStroke
parent 4a119c43f1
commit 22e4d7f2dd
4 changed files with 178 additions and 47 deletions

View File

@@ -80,6 +80,105 @@ namespace PartApp = Part;
using namespace Assembly;
using namespace MbD;
// ======================================= Utils ======================================
Base::Placement getPlacementFromProp(App::DocumentObject* obj, const char* propName)
{
Base::Placement plc = Base::Placement();
auto* propPlacement = dynamic_cast<App::PropertyPlacement*>(obj->getPropertyByName(propName));
if (propPlacement) {
plc = propPlacement->getValue();
}
return plc;
}
/* // Currently unused
Base::Placement* getTargetPlacementRelativeTo(
App::DocumentObject* targetObj, App::DocumentObject* part, App::DocumentObject* container,
bool inContainerBranch, bool ignorePlacement = false)
{
inContainerBranch = inContainerBranch || (!ignorePlacement && part == container);
Base::Console().Warning("sub --------------\n");
if (targetObj == part && inContainerBranch && !ignorePlacement) {
Base::Console().Warning("found0\n");
return &getPlacementFromProp(targetObj, "Placement");
}
if (auto group = dynamic_cast<App::DocumentObjectGroup*>(part)) {
for (auto& obj : group->getOutList()) {
auto foundPlacement = getTargetPlacementRelativeTo(
targetObj, obj, container, inContainerBranch, ignorePlacement
);
if (foundPlacement != nullptr) {
return foundPlacement;
}
}
}
else if (auto assembly = dynamic_cast<AssemblyObject*>(part)) {
Base::Console().Warning("h3\n");
for (auto& obj : assembly->getOutList()) {
auto foundPlacement = getTargetPlacementRelativeTo(
targetObj, obj, container, inContainerBranch
);
if (foundPlacement == nullptr) {
continue;
}
if (!ignorePlacement) {
*foundPlacement = getPlacementFromProp(part, "Placement") * *foundPlacement;
}
Base::Console().Warning("found\n");
return foundPlacement;
}
}
else if (auto link = dynamic_cast<App::Link*>(part)) {
Base::Console().Warning("h4\n");
auto linked_obj = link->getLinkedObject();
if (dynamic_cast<App::Part*>(linked_obj) || dynamic_cast<AssemblyObject*>(linked_obj)) {
for (auto& obj : linked_obj->getOutList()) {
auto foundPlacement = getTargetPlacementRelativeTo(
targetObj, obj, container, inContainerBranch
);
if (foundPlacement == nullptr) {
continue;
}
*foundPlacement = getPlacementFromProp(link, "Placement") * *foundPlacement;
return foundPlacement;
}
}
auto foundPlacement = getTargetPlacementRelativeTo(
targetObj, linked_obj, container, inContainerBranch, true
);
if (foundPlacement != nullptr && !ignorePlacement) {
*foundPlacement = getPlacementFromProp(link, "Placement") * *foundPlacement;
}
Base::Console().Warning("found2\n");
return foundPlacement;
}
return nullptr;
}
Base::Placement getGlobalPlacement(App::DocumentObject* targetObj, App::DocumentObject* container =
nullptr) { bool inContainerBranch = container == nullptr; auto rootObjects =
App::GetApplication().getActiveDocument()->getRootObjects(); for (auto& part : rootObjects) { auto
foundPlacement = getTargetPlacementRelativeTo(targetObj, part, container, inContainerBranch); if
(foundPlacement != nullptr) { Base::Placement plc(foundPlacement->toMatrix()); return plc;
}
}
return Base::Placement();
}
*/
// ================================ Assembly Object ============================
PROPERTY_SOURCE(Assembly::AssemblyObject, App::Part)
AssemblyObject::AssemblyObject()
@@ -357,11 +456,7 @@ int AssemblyObject::solve(bool enableRedo)
setNewPlacements();
// The Placement1 and Placement2 of each joint needs to be updated as the parts moved.
// Note calling only recomputeJointPlacements makes a weird illegal storage access
// When solving while moving part. Happens in Py::Callable(attr).apply();
// it apparently can't access the JointObject 'updateJCSPlacements' function.
getJoints();
redrawJointPlacements(joints);
return 0;
}
@@ -709,8 +804,8 @@ AssemblyObject::makeMbdJoint(App::DocumentObject* joint)
return {};
}
std::string fullMarkerName1 = handleOneSideOfJoint(joint, "Part1", "Placement1");
std::string fullMarkerName2 = handleOneSideOfJoint(joint, "Part2", "Placement2");
std::string fullMarkerName1 = handleOneSideOfJoint(joint, "Object1", "Part1", "Placement1");
std::string fullMarkerName2 = handleOneSideOfJoint(joint, "Object2", "Part2", "Placement2");
mbdJoint->setMarkerI(fullMarkerName1);
mbdJoint->setMarkerJ(fullMarkerName2);
@@ -719,18 +814,24 @@ AssemblyObject::makeMbdJoint(App::DocumentObject* joint)
}
std::string AssemblyObject::handleOneSideOfJoint(App::DocumentObject* joint,
const char* propLinkName,
const char* propObjName,
const char* propPartName,
const char* propPlcName)
{
App::DocumentObject* obj = getLinkObjFromProp(joint, propLinkName);
App::DocumentObject* part = getLinkObjFromProp(joint, propPartName);
App::DocumentObject* obj = getObjFromNameProp(joint, propObjName, propPartName);
std::shared_ptr<ASMTPart> mbdPart = getMbDPart(obj);
std::shared_ptr<ASMTPart> mbdPart = getMbDPart(part);
Base::Placement partPlc = getPlacementFromProp(part, "Placement");
Base::Placement objPlc = getPlacementFromProp(obj, "Placement");
Base::Placement plc = getPlacementFromProp(joint, propPlcName);
// Now we have plc which is the JCS placement, but its relative to the doc origin, not to the
// obj.
// Now we have plc which is the JCS placement, but its relative to the Object, not to the
// containing Part.
plc = objPlc.inverse() * plc;
if (obj->getNameInDocument() != part->getNameInDocument()) {
// Make plc relative to the containing part
plc = objPlc * plc;
}
std::string markerName = joint->getFullName();
auto mbdMarker = makeMbdMarker(markerName, plc);
@@ -964,11 +1065,27 @@ void AssemblyObject::setNewPlacements()
}
}
void AssemblyObject::redrawJointPlacements(std::vector<App::DocumentObject*> joints)
{
// Notify the joint objects that the transform of the coin object changed.
for (auto* joint : joints) {
auto* propPlacement =
dynamic_cast<App::PropertyPlacement*>(joint->getPropertyByName("Placement1"));
if (propPlacement) {
propPlacement->setValue(propPlacement->getValue());
}
propPlacement =
dynamic_cast<App::PropertyPlacement*>(joint->getPropertyByName("Placement2"));
if (propPlacement) {
propPlacement->setValue(propPlacement->getValue());
}
}
}
void AssemblyObject::recomputeJointPlacements(std::vector<App::DocumentObject*> joints)
{
// The Placement1 and Placement2 of each joint needs to be updated as the parts moved.
for (auto* joint : joints) {
App::PropertyPythonObject* proxy = joint
? dynamic_cast<App::PropertyPythonObject*>(joint->getPropertyByName("Proxy"))
: nullptr;
@@ -1135,9 +1252,9 @@ App::DocumentObject* AssemblyObject::getLinkObjFromProp(App::DocumentObject* joi
return propObj->getValue();
}
App::DocumentObject* AssemblyObject::getLinkedObjFromNameProp(App::DocumentObject* joint,
const char* pObjName,
const char* pPart)
App::DocumentObject* AssemblyObject::getObjFromNameProp(App::DocumentObject* joint,
const char* pObjName,
const char* pPart)
{
auto* propObjName = dynamic_cast<App::PropertyString*>(joint->getPropertyByName(pObjName));
if (!propObjName) {
@@ -1151,13 +1268,13 @@ App::DocumentObject* AssemblyObject::getLinkedObjFromNameProp(App::DocumentObjec
}
if (objName == containingPart->getNameInDocument()) {
return containingPart->getLinkedObject(true);
return containingPart;
}
if (containingPart->getTypeId().isDerivedFrom(App::Link::getClassTypeId())) {
App::Link* link = dynamic_cast<App::Link*>(containingPart);
containingPart = link->getLinkedObject(true);
containingPart = link->getLinkedObject();
if (!containingPart) {
return nullptr;
}
@@ -1165,23 +1282,25 @@ App::DocumentObject* AssemblyObject::getLinkedObjFromNameProp(App::DocumentObjec
for (auto obj : containingPart->getOutList()) {
if (objName == obj->getNameInDocument()) {
return obj->getLinkedObject(true);
return obj;
}
}
return nullptr;
}
Base::Placement AssemblyObject::getPlacementFromProp(App::DocumentObject* obj, const char* propName)
App::DocumentObject* AssemblyObject::getLinkedObjFromNameProp(App::DocumentObject* joint,
const char* pObjName,
const char* pPart)
{
Base::Placement plc = Base::Placement();
auto* propPlacement = dynamic_cast<App::PropertyPlacement*>(obj->getPropertyByName(propName));
if (propPlacement) {
plc = propPlacement->getValue();
auto* obj = getObjFromNameProp(joint, pObjName, pPart);
if (obj) {
return obj->getLinkedObject(true);
}
return plc;
return nullptr;
}
/*void Part::handleChangedPropertyType(Base::XMLReader& reader, const char* TypeName, App::Property*
prop)
{

View File

@@ -104,6 +104,7 @@ public:
std::string handleOneSideOfJoint(App::DocumentObject* joint,
const char* propObjLinkName,
const char* propPartName,
const char* propPlcName);
void jointParts(std::vector<App::DocumentObject*> joints);
std::vector<App::DocumentObject*> getJoints();
@@ -124,6 +125,7 @@ public:
void swapJCS(App::DocumentObject* joint);
void setNewPlacements();
void redrawJointPlacements(std::vector<App::DocumentObject*> joints);
void recomputeJointPlacements(std::vector<App::DocumentObject*> joints);
bool isPartConnected(App::DocumentObject* obj);
@@ -141,9 +143,10 @@ public:
JointType getJointType(App::DocumentObject* joint);
const char* getElementFromProp(App::DocumentObject* obj, const char* propName);
std::string getElementTypeFromProp(App::DocumentObject* obj, const char* propName);
Base::Placement getPlacementFromProp(App::DocumentObject* obj, const char* propName);
App::DocumentObject* getLinkObjFromProp(App::DocumentObject* joint, const char* propName);
App::DocumentObject*
getObjFromNameProp(App::DocumentObject* joint, const char* pObjName, const char* pPart);
App::DocumentObject*
getLinkedObjFromNameProp(App::DocumentObject* joint, const char* pObjName, const char* pPart);
private:

View File

@@ -205,8 +205,6 @@ App::DocumentObject* ViewProviderAssembly::getActivePart()
bool ViewProviderAssembly::mouseMove(const SbVec2s& cursorPos, Gui::View3DInventorViewer* viewer)
{
// Base::Console().Warning("Mouse move\n");
// Initialize or end the dragging of parts
if (canStartDragging) {
canStartDragging = false;

View File

@@ -428,11 +428,11 @@ class Joint:
plc = obj.Placement.inverse() * plc
# change plc to be relative to the origin of the document.
global_plc = UtilsAssembly.getGlobalPlacement(obj, part)
plc = global_plc * plc
# global_plc = UtilsAssembly.getGlobalPlacement(obj, part)
# plc = global_plc * plc
# change plc to be relative to the assembly.
plc = assembly.Placement.inverse() * plc
# plc = assembly.Placement.inverse() * plc
# We apply rotation / reverse / offset it necessary, but only to the second JCS.
if isSecond:
@@ -621,7 +621,12 @@ class ViewProviderJoint:
return camera.height.getValue() / 20
def set_JCS_placement(self, soTransform, placement):
def set_JCS_placement(self, soTransform, placement, objName, part):
# change plc to be relative to the origin of the document.
obj = UtilsAssembly.getObjectInPart(objName, part)
global_plc = UtilsAssembly.getGlobalPlacement(obj, part)
placement = global_plc * placement
t = placement.Base
soTransform.translation.setValue(t.x, t.y, t.z)
@@ -632,20 +637,22 @@ 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":
plc = joint.getPropertyByName("Placement1")
if joint.getPropertyByName("Object1"):
if joint.Object1:
plc = joint.Placement1
self.switch_JCS1.whichChild = coin.SO_SWITCH_ALL
self.set_JCS_placement(self.transform1, plc)
if joint.Part1: # prevent an unwanted call by assembly.isPartConnected
self.set_JCS_placement(self.transform1, plc, joint.Object1, joint.Part1)
else:
self.switch_JCS1.whichChild = coin.SO_SWITCH_NONE
if prop == "Placement2":
plc = joint.getPropertyByName("Placement2")
if joint.getPropertyByName("Object2"):
if joint.Object2:
plc = joint.Placement2
self.switch_JCS2.whichChild = coin.SO_SWITCH_ALL
if self.areJCSReversed(joint):
plc = flipPlacement(plc, App.Vector(1, 0, 0))
self.set_JCS_placement(self.transform2, plc)
# if self.areJCSReversed(joint):
# plc = flipPlacement(plc, App.Vector(1, 0, 0))
self.set_JCS_placement(self.transform2, plc, joint.Object2, joint.Part2)
else:
self.switch_JCS2.whichChild = coin.SO_SWITCH_NONE
@@ -656,10 +663,10 @@ class ViewProviderJoint:
sameDir = zaxis1.dot(zaxis2) > 0
return not sameDir
def showPreviewJCS(self, visible, placement=None):
def showPreviewJCS(self, visible, placement=None, objName="", part=None):
if visible:
self.switch_JCS_preview.whichChild = coin.SO_SWITCH_ALL
self.set_JCS_placement(self.transform3, placement)
self.set_JCS_placement(self.transform3, placement, objName, part)
else:
self.switch_JCS_preview.whichChild = coin.SO_SWITCH_NONE
@@ -1110,16 +1117,17 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
)
isSecond = len(self.current_selection) == 1
objName = self.preselection_dict["object"].Name
part = self.preselection_dict["part"]
placement = self.joint.Proxy.findPlacement(
self.joint,
self.preselection_dict["object"].Name,
self.preselection_dict["part"],
objName,
part,
self.preselection_dict["element_name"],
self.preselection_dict["vertex_name"],
isSecond,
)
self.joint.ViewObject.Proxy.showPreviewJCS(True, placement)
self.joint.ViewObject.Proxy.showPreviewJCS(True, placement, objName, part)
self.previewJCSVisible = True
# 3D view keyboard handler
@@ -1156,6 +1164,9 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
self.current_selection.append(selection_dict)
self.updateJoint()
# We hide the preview JCS if we just added to the selection
self.joint.ViewObject.Proxy.showPreviewJCS(False)
def removeSelection(self, doc_name, obj_name, sub_name, mousePos=None):
full_element_name = UtilsAssembly.getFullElementName(obj_name, sub_name)
selected_object = UtilsAssembly.getObject(full_element_name)