Assembly : Add support for perspective camera.

Remove exportASMT from toolbar.
Fix Fixedjoint name and tooltip
Fix various bugs
Introduce solver drag functions.
This commit is contained in:
Paddle
2024-01-11 16:51:54 +01:00
committed by PaddleStroke
parent 46b1a1ebfc
commit cdb1af6543
9 changed files with 760 additions and 623 deletions

View File

@@ -2737,7 +2737,7 @@ SbVec3f View3DInventorViewer::getPointOnLine(const SbVec2s& pnt, const SbVec3f&
SbLine projectedLine = projectLineOntoPlane(axisCenter, axisCenter + axis, focalPlane);
ptOnFocalPlaneAndOnLine = projectedLine.getClosestPoint(ptOnFocalPlane);
// now we need the intersection point between
// now we need the intersection point between
// - the line passing by ptOnFocalPlaneAndOnLine normal to focalPlane
// - The line (axisCenter, axisCenter + axis)
@@ -4077,4 +4077,4 @@ void View3DInventorViewer::dragLeaveEvent(QDragLeaveEvent* ev)
inherited::dragLeaveEvent(ev);
}
#include "moc_View3DInventorViewer.cpp"
#include "moc_View3DInventorViewer.cpp" // NOLINT

File diff suppressed because it is too large Load Diff

View File

@@ -50,6 +50,7 @@ class Placement;
class Rotation;
} // namespace Base
namespace Assembly
{
@@ -82,11 +83,25 @@ public:
return "AssemblyGui::ViewProviderAssembly";
}
/* Solve the assembly. It will update first the joints, solve, update placements of the parts
and redraw the joints Args : enableRedo : This store initial positions to enable undo while
being in an active transaction (joint creation).*/
int solve(bool enableRedo = false);
void preDrag(std::vector<App::DocumentObject*> dragParts);
void doDragStep();
void postDrag();
void savePlacementsForUndo();
void undoSolve();
void clearUndo();
void exportAsASMT(std::string fileName);
void setNewPlacements();
void recomputeJointPlacements(std::vector<App::DocumentObject*> joints);
void redrawJointPlacements(std::vector<App::DocumentObject*> joints);
// Ondsel Solver interface
std::shared_ptr<MbD::ASMTAssembly> makeMbdAssembly();
std::shared_ptr<MbD::ASMTPart>
makeMbdPart(std::string& name, Base::Placement plc = Base::Placement(), double mass = 1.0);
@@ -101,22 +116,24 @@ public:
std::shared_ptr<MbD::ASMTJoint> makeMbdJointDistanceFaceEdge(App::DocumentObject* joint);
std::shared_ptr<MbD::ASMTJoint> makeMbdJointDistanceEdgeEdge(App::DocumentObject* joint);
std::shared_ptr<MbD::ASMTJoint> makeMbdJointDistanceFaceFace(App::DocumentObject* joint);
std::string handleOneSideOfJoint(App::DocumentObject* joint,
const char* propObjLinkName,
const char* propPartName,
const char* propPlcName);
void jointParts(std::vector<App::DocumentObject*> joints);
JointGroup* getJointGroup();
std::vector<App::DocumentObject*> getJoints(bool updateJCS = true);
std::vector<App::DocumentObject*> getGroundedJoints();
std::vector<App::DocumentObject*> getJointsOfObj(App::DocumentObject* obj);
std::vector<App::DocumentObject*> getJointsOfPart(App::DocumentObject* part);
App::DocumentObject* getJointOfPartConnectingToGround(App::DocumentObject* part,
std::string& name);
bool isJointConnectingPartToGround(App::DocumentObject* joint, const char* partPropName);
std::vector<App::DocumentObject*> getGroundedJoints();
void fixGroundedPart(App::DocumentObject* obj, Base::Placement& plc, std::string& jointName);
std::vector<App::DocumentObject*> fixGroundedParts();
std::vector<App::DocumentObject*> getGroundedParts();
std::vector<App::DocumentObject*> fixGroundedParts();
void fixGroundedPart(App::DocumentObject* obj, Base::Placement& plc, std::string& jointName);
bool isJointConnectingPartToGround(App::DocumentObject* joint, const char* partPropName);
void removeUnconnectedJoints(std::vector<App::DocumentObject*>& joints,
std::vector<App::DocumentObject*> groundedObjs);
@@ -125,17 +142,9 @@ public:
const std::vector<App::DocumentObject*>& joints);
std::vector<App::DocumentObject*>
getConnectedParts(App::DocumentObject* part, const std::vector<App::DocumentObject*>& joints);
JointGroup* getJointGroup();
void swapJCS(App::DocumentObject* joint);
void setNewPlacements();
void redrawJointPlacements(std::vector<App::DocumentObject*> joints);
void recomputeJointPlacements(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<App::DocumentObject*> getUpstreamParts(App::DocumentObject* part, int limit = 0);
@@ -144,17 +153,13 @@ public:
double getObjMass(App::DocumentObject* obj);
void setObjMasses(std::vector<std::pair<App::DocumentObject*, double>> objectMasses);
bool isEdgeType(App::DocumentObject* obj, const char* elName, GeomAbs_CurveType type);
bool isFaceType(App::DocumentObject* obj, const char* elName, GeomAbs_SurfaceType type);
double getFaceRadius(App::DocumentObject* obj, const char* elName);
double getEdgeRadius(App::DocumentObject* obj, const char* elName);
private:
std::shared_ptr<MbD::ASMTAssembly> mbdAssembly;
std::unordered_map<App::DocumentObject*, std::shared_ptr<MbD::ASMTPart>> objectPartMap;
std::vector<std::pair<App::DocumentObject*, double>> objMasses;
std::vector<std::shared_ptr<MbD::ASMTPart>> dragMbdParts;
std::vector<std::pair<App::DocumentObject*, Base::Placement>> previousPositions;
@@ -166,6 +171,13 @@ public:
// Can't put the functions by themselves in AssemblyUtils.cpp :
// see https://forum.freecad.org/viewtopic.php?p=729577#p729577
void swapJCS(App::DocumentObject* joint);
bool isEdgeType(App::DocumentObject* obj, const char* elName, GeomAbs_CurveType type);
bool isFaceType(App::DocumentObject* obj, const char* elName, GeomAbs_SurfaceType type);
double getFaceRadius(App::DocumentObject* obj, const char* elName);
double getEdgeRadius(App::DocumentObject* obj, const char* elName);
// getters to get from properties
static void setJointActivated(App::DocumentObject* joint, bool val);
static bool getJointActivated(App::DocumentObject* joint);

View File

@@ -64,13 +64,19 @@ class CommandCreateJointFixed:
"Pixmap": "Assembly_CreateJointFixed",
"MenuText": QT_TRANSLATE_NOOP(
"Assembly_CreateJointFixed",
"If an assembly is active : Create a Fixed Joint.\n If a part is active : Position sub parts by matching.",
"Create a Fixed Joint",
),
"Accel": "J",
"ToolTip": "<p>"
+ QT_TRANSLATE_NOOP(
"Assembly_CreateJointFixed",
"Create a Fixed Joint: Permanently locks two parts together, preventing any movement or rotation.",
"1 - If an assembly is active : Create a joint permanently locking two parts together, preventing any movement or rotation.",
)
+ "</p>"
+ "<p>"
+ QT_TRANSLATE_NOOP(
"Assembly_CreateJointFixed",
"2 - If a part is active : Position sub parts by matching seleted coordinate systems. The second part selected will move.",
)
+ "</p>",
"CmdType": "ForEdit",
@@ -270,6 +276,7 @@ class CommandToggleGrounded:
part_containing_obj = UtilsAssembly.getContainingPart(full_element_name, obj)
# Check if part is grounded and if so delete the joint.
ungrounded = False
for joint in joint_group.Group:
if (
hasattr(joint, "ObjectToGround")
@@ -281,10 +288,12 @@ class CommandToggleGrounded:
doc = App.ActiveDocument
doc.removeObject(joint.Name)
doc.recompute()
return
ungrounded = True
break
if ungrounded:
continue
# Create groundedJoint.
part_containing_obj.Label = part_containing_obj.Label + " 🔒"
ground = joint_group.newObject("App::FeaturePython", "GroundedJoint")
JointObject.GroundedJoint(ground, part_containing_obj)

View File

@@ -75,6 +75,7 @@ PROPERTY_SOURCE(AssemblyGui::ViewProviderAssembly, Gui::ViewProviderPart)
ViewProviderAssembly::ViewProviderAssembly()
: SelectionObserver(true)
, dragMode(DragMode::None)
, canStartDragging(false)
, partMoving(false)
, enableMovement(true)
@@ -116,7 +117,7 @@ bool ViewProviderAssembly::doubleClicked()
bool ViewProviderAssembly::canDragObject(App::DocumentObject* obj) const
{
Base::Console().Warning("ViewProviderAssembly::canDragObject\n");
// The user should not be able to drag the joint group out of the assembly
if (!obj || obj->getTypeId() == Assembly::JointGroup::getClassTypeId()) {
return false;
}
@@ -210,7 +211,7 @@ void ViewProviderAssembly::unsetEdit(int ModNum)
PARTKEY);
}
bool ViewProviderAssembly::isInEditMode()
bool ViewProviderAssembly::isInEditMode() const
{
App::DocumentObject* activePart = getActivePart();
if (!activePart) {
@@ -220,20 +221,18 @@ bool ViewProviderAssembly::isInEditMode()
return activePart == this->getObject();
}
App::DocumentObject* ViewProviderAssembly::getActivePart()
App::DocumentObject* ViewProviderAssembly::getActivePart() const
{
App::DocumentObject* activePart = nullptr;
auto activeDoc = Gui::Application::Instance->activeDocument();
if (!activeDoc) {
activeDoc = getDocument();
}
auto activeView = activeDoc->setActiveView(this);
auto activeView = activeDoc->getActiveView();
if (!activeView) {
return nullptr;
}
activePart = activeView->getActiveObject<App::DocumentObject*>(PARTKEY);
return activePart;
return activeView->getActiveObject<App::DocumentObject*>(PARTKEY);
}
bool ViewProviderAssembly::mouseMove(const SbVec2s& cursorPos, Gui::View3DInventorViewer* viewer)
@@ -243,21 +242,21 @@ bool ViewProviderAssembly::mouseMove(const SbVec2s& cursorPos, Gui::View3DInvent
canStartDragging = false;
if (enableMovement && getSelectedObjectsWithinAssembly()) {
moveMode = findMoveMode();
dragMode = findDragMode();
if (moveMode == MoveMode::None) {
if (dragMode == DragMode::None) {
return false;
}
SbVec3f vec;
if (moveMode == MoveMode::RotationOnPlane
|| moveMode == MoveMode::TranslationOnAxisAndRotationOnePlane) {
if (dragMode == DragMode::RotationOnPlane
|| dragMode == DragMode::TranslationOnAxisAndRotationOnePlane) {
vec = viewer->getPointOnXYPlaneOfPlacement(cursorPos, jcsGlobalPlc);
initialPositionRot = Base::Vector3d(vec[0], vec[1], vec[2]);
}
if (moveMode == MoveMode::TranslationOnAxis
|| moveMode == MoveMode::TranslationOnAxisAndRotationOnePlane) {
if (dragMode == DragMode::TranslationOnAxis
|| dragMode == DragMode::TranslationOnAxisAndRotationOnePlane) {
Base::Vector3d zAxis =
jcsGlobalPlc.getRotation().multVec(Base::Vector3d(0., 0., 1.));
Base::Vector3d pos = jcsGlobalPlc.getPosition();
@@ -266,9 +265,10 @@ bool ViewProviderAssembly::mouseMove(const SbVec2s& cursorPos, Gui::View3DInvent
vec = viewer->getPointOnLine(cursorPos, axisCenter, axis);
initialPosition = Base::Vector3d(vec[0], vec[1], vec[2]);
}
else if (moveMode != MoveMode::RotationOnPlane) {
else if (dragMode != DragMode::RotationOnPlane) {
vec = viewer->getPointOnFocalPlane(cursorPos);
initialPosition = Base::Vector3d(vec[0], vec[1], vec[2]);
prevPosition = initialPosition;
}
initMove();
@@ -278,14 +278,14 @@ bool ViewProviderAssembly::mouseMove(const SbVec2s& cursorPos, Gui::View3DInvent
// Do the dragging of parts
if (partMoving) {
Base::Vector3d newPos, newPosRot;
if (moveMode == MoveMode::RotationOnPlane
|| moveMode == MoveMode::TranslationOnAxisAndRotationOnePlane) {
if (dragMode == DragMode::RotationOnPlane
|| dragMode == DragMode::TranslationOnAxisAndRotationOnePlane) {
SbVec3f vec = viewer->getPointOnXYPlaneOfPlacement(cursorPos, jcsGlobalPlc);
newPosRot = Base::Vector3d(vec[0], vec[1], vec[2]);
}
if (moveMode == MoveMode::TranslationOnAxis
|| moveMode == MoveMode::TranslationOnAxisAndRotationOnePlane) {
if (dragMode == DragMode::TranslationOnAxis
|| dragMode == DragMode::TranslationOnAxisAndRotationOnePlane) {
Base::Vector3d zAxis = jcsGlobalPlc.getRotation().multVec(Base::Vector3d(0., 0., 1.));
Base::Vector3d pos = jcsGlobalPlc.getPosition();
SbVec3f axisCenter(pos.x, pos.y, pos.z);
@@ -293,7 +293,7 @@ bool ViewProviderAssembly::mouseMove(const SbVec2s& cursorPos, Gui::View3DInvent
SbVec3f vec = viewer->getPointOnLine(cursorPos, axisCenter, axis);
newPos = Base::Vector3d(vec[0], vec[1], vec[2]);
}
else if (moveMode != MoveMode::RotationOnPlane) {
else if (dragMode != DragMode::RotationOnPlane) {
SbVec3f vec = viewer->getPointOnFocalPlane(cursorPos);
newPos = Base::Vector3d(vec[0], vec[1], vec[2]);
}
@@ -307,7 +307,7 @@ bool ViewProviderAssembly::mouseMove(const SbVec2s& cursorPos, Gui::View3DInvent
Base::Placement plc = pair.second;
// Base::Console().Warning("newPos %f %f %f\n", newPos.x, newPos.y, newPos.z);
if (moveMode == MoveMode::RotationOnPlane) {
if (dragMode == DragMode::RotationOnPlane) {
Base::Vector3d center = jcsGlobalPlc.getPosition();
Base::Vector3d norm =
jcsGlobalPlc.getRotation().multVec(Base::Vector3d(0., 0., -1.));
@@ -320,13 +320,18 @@ bool ViewProviderAssembly::mouseMove(const SbVec2s& cursorPos, Gui::View3DInvent
Base::Placement jcsPlcRelativeToPart = plc.inverse() * jcsGlobalPlc;
plc = rotatedGlovalJcsPlc * jcsPlcRelativeToPart.inverse();
}
else if (moveMode == MoveMode::TranslationOnAxisAndRotationOnePlane) {
else if (dragMode == DragMode::TranslationOnAxis) {
Base::Vector3d pos = plc.getPosition() + (newPos - initialPosition);
plc.setPosition(pos);
}
else if (dragMode == DragMode::TranslationOnAxisAndRotationOnePlane) {
Base::Vector3d pos = plc.getPosition() + (newPos - initialPosition);
plc.setPosition(pos);
Base::Placement newJcsGlobalPlc = jcsGlobalPlc;
newJcsGlobalPlc.setPosition(jcsGlobalPlc.getPosition()
+ (newPos - initialPosition));
Base::Vector3d center = newJcsGlobalPlc.getPosition();
Base::Vector3d norm =
newJcsGlobalPlc.getRotation().multVec(Base::Vector3d(0., 0., -1.));
@@ -342,8 +347,12 @@ bool ViewProviderAssembly::mouseMove(const SbVec2s& cursorPos, Gui::View3DInvent
Base::Placement jcsPlcRelativeToPart = plc.inverse() * newJcsGlobalPlc;
plc = rotatedGlovalJcsPlc * jcsPlcRelativeToPart.inverse();
}
else {
Base::Vector3d pos = newPos + (plc.getPosition() - initialPosition);
else { // DragMode::Translation
Base::Vector3d delta = newPos - prevPosition;
prevPosition = newPos;
Base::Vector3d pos = propPlacement->getValue().getPosition() + delta;
// Base::Vector3d pos = newPos + (plc.getPosition() - initialPosition);
plc.setPosition(pos);
}
propPlacement->setValue(plc);
@@ -356,6 +365,7 @@ bool ViewProviderAssembly::mouseMove(const SbVec2s& cursorPos, Gui::View3DInvent
if (solveOnMove) {
auto* assemblyPart = static_cast<AssemblyObject*>(getObject());
assemblyPart->solve();
// assemblyPart->doDragStep();
}
}
return false;
@@ -532,7 +542,7 @@ App::DocumentObject* ViewProviderAssembly::getObjectFromSubNames(std::vector<std
return appDoc->getObject(objName.c_str());
}
ViewProviderAssembly::MoveMode ViewProviderAssembly::findMoveMode()
ViewProviderAssembly::DragMode ViewProviderAssembly::findDragMode()
{
if (docsToMove.size() == 1) {
auto* assemblyPart = static_cast<AssemblyObject*>(getObject());
@@ -541,7 +551,7 @@ ViewProviderAssembly::MoveMode ViewProviderAssembly::findMoveMode()
assemblyPart->getJointOfPartConnectingToGround(docsToMove[0].first, partPropName);
if (!movingJoint) {
return MoveMode::Translation;
return DragMode::Translation;
}
JointType jointType = AssemblyObject::getJointType(movingJoint);
@@ -553,7 +563,7 @@ ViewProviderAssembly::MoveMode ViewProviderAssembly::findMoveMode()
assemblyPart->getUpstreamMovingPart(docsToMove[0].first);
docsToMove.clear();
if (!upstreamPart) {
return MoveMode::None;
return DragMode::None;
}
auto* propPlacement =
@@ -565,7 +575,7 @@ ViewProviderAssembly::MoveMode ViewProviderAssembly::findMoveMode()
movingJoint =
assemblyPart->getJointOfPartConnectingToGround(docsToMove[0].first, partPropName);
if (!movingJoint) {
return MoveMode::Translation;
return DragMode::Translation;
}
jointType = AssemblyObject::getJointType(movingJoint);
}
@@ -597,20 +607,23 @@ ViewProviderAssembly::MoveMode ViewProviderAssembly::findMoveMode()
}
if (jointType == JointType::Revolute) {
return MoveMode::RotationOnPlane;
return DragMode::RotationOnPlane;
}
else if (jointType == JointType::Slider) {
return MoveMode::TranslationOnAxis;
return DragMode::TranslationOnAxis;
}
else if (jointType == JointType::Cylindrical) {
return MoveMode::TranslationOnAxisAndRotationOnePlane;
return DragMode::TranslationOnAxisAndRotationOnePlane;
}
else if (jointType == JointType::Ball) {
// return DragMode::Ball;
}
else if (jointType == JointType::Distance) {
// depends on the type of distance. For example plane-plane:
// return MoveMode::TranslationOnPlane;
// return DragMode::TranslationOnPlane;
}
}
return MoveMode::Translation;
return DragMode::Translation;
}
void ViewProviderAssembly::initMove()
@@ -627,14 +640,23 @@ void ViewProviderAssembly::initMove()
viewerNotConst->setSelectionEnabled(false);
}
objectMasses.clear();
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Assembly");
bool solveOnMove = hGrp->GetBool("SolveOnMove", true);
if (solveOnMove) {
objectMasses.clear();
for (auto& pair : docsToMove) {
objectMasses.push_back({pair.first, 10.0});
}
for (auto& pair : docsToMove) {
objectMasses.push_back({pair.first, 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);
}
assemblyPart->preDrag(dragParts);*/
}
auto* assemblyPart = static_cast<AssemblyObject*>(getObject());
assemblyPart->setObjMasses(objectMasses);
}
void ViewProviderAssembly::endMove()
@@ -658,13 +680,18 @@ void ViewProviderAssembly::endMove()
viewerNotConst->setSelectionEnabled(true);
}
auto* assemblyPart = static_cast<AssemblyObject*>(getObject());
assemblyPart->setObjMasses({});
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Assembly");
bool solveOnMove = hGrp->GetBool("SolveOnMove", true);
if (solveOnMove) {
auto* assemblyPart = static_cast<AssemblyObject*>(getObject());
// assemblyPart->postDrag();
assemblyPart->setObjMasses({});
}
Gui::Command::commitCommand();
}
void ViewProviderAssembly::onSelectionChanged(const Gui::SelectionChanges& msg)
{
if (msg.Type == Gui::SelectionChanges::AddSelection

View File

@@ -45,6 +45,18 @@ class AssemblyGuiExport ViewProviderAssembly: public Gui::ViewProviderPart,
Q_DECLARE_TR_FUNCTIONS(AssemblyGui::ViewProviderAssembly)
PROPERTY_HEADER_WITH_OVERRIDE(AssemblyGui::ViewProviderAssembly);
enum class DragMode
{
Translation,
TranslationOnAxis,
TranslationOnPlane,
Rotation,
RotationOnPlane,
TranslationOnAxisAndRotationOnePlane,
Ball,
None,
};
public:
ViewProviderAssembly();
~ViewProviderAssembly() override;
@@ -59,7 +71,7 @@ public:
//@{
bool setEdit(int ModNum) override;
void unsetEdit(int ModNum) override;
bool isInEditMode();
bool isInEditMode() const;
/// Ask the view provider if it accepts object deletions while in edit
bool acceptDeletionsInEdit() override
@@ -69,19 +81,8 @@ public:
bool canDragObject(App::DocumentObject*) const override;
App::DocumentObject* getActivePart();
App::DocumentObject* getActivePart() const;
enum class MoveMode
{
Translation,
TranslationOnAxis,
TranslationOnPlane,
Rotation,
RotationOnPlane,
TranslationOnAxisAndRotationOnePlane,
None,
};
MoveMode moveMode;
/// is called when the provider is in edit and the mouse is moved
bool mouseMove(const SbVec2s& pos, Gui::View3DInventorViewer* viewer) override;
@@ -90,18 +91,11 @@ public:
bool pressed,
const SbVec2s& cursorPos,
const Gui::View3DInventorViewer* viewer) override;
MoveMode findMoveMode();
/// Finds what drag mode should be used based on the user selection.
DragMode findDragMode();
void initMove();
void endMove();
bool getSelectedObjectsWithinAssembly();
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;
virtual void setEnableMovement(bool enable = true)
{
enableMovement = enable;
@@ -111,17 +105,26 @@ public:
return enableMovement;
}
bool getSelectedObjectsWithinAssembly();
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;
// protected:
/// get called by the container whenever a property has been changed
// void onChanged(const App::Property* prop) override;
void onSelectionChanged(const Gui::SelectionChanges& msg) override;
DragMode dragMode;
bool canStartDragging;
bool partMoving;
bool enableMovement;
bool jointVisibilityBackup;
int numberOfSel;
Base::Vector3d prevPosition;
Base::Vector3d initialPosition;
Base::Vector3d initialPositionRot;
Base::Placement jcsPlc;

View File

@@ -75,10 +75,13 @@ class AssemblyWorkbench(Workbench):
FreeCADGui.addPreferencePage(PreferencesPage, QT_TRANSLATE_NOOP("QObject", "Assembly"))
# build commands list
cmdlist = [
cmdList = [
"Assembly_CreateAssembly",
"Assembly_InsertLink",
"Assembly_SolveAssembly",
]
cmdListMenuOnly = [
"Assembly_ExportASMT",
]
@@ -93,12 +96,12 @@ class AssemblyWorkbench(Workbench):
"Assembly_CreateJointDistance",
]
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Assembly"), cmdlist)
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Assembly"), cmdList)
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Assembly Joints"), cmdListJoints)
self.appendMenu(
[QT_TRANSLATE_NOOP("Workbench", "&Assembly")],
cmdlist + ["Separator"] + cmdListJoints,
cmdList + cmdListMenuOnly + ["Separator"] + cmdListJoints,
)
print("Assembly workbench loaded")

View File

@@ -83,8 +83,6 @@ def solveIfAllowed(assembly, storePrev=False):
class Joint:
def __init__(self, joint, type_index):
self.Type = "Joint"
joint.Proxy = self
joint.addProperty(
@@ -237,12 +235,11 @@ class Joint:
self.setJointConnectors(joint, [])
def __getstate__(self):
return self.Type
def dumps(self):
return None
def __setstate__(self, state):
if state:
self.Type = state
def loads(self, state):
return None
def getAssembly(self, joint):
return joint.InList[0]
@@ -256,23 +253,25 @@ class Joint:
# App.Console.PrintMessage("Change property: " + str(prop) + "\n")
if prop == "Rotation" or prop == "Offset" or prop == "Distance":
if hasattr(
joint, "Vertex1"
): # during loading the onchanged may be triggered before full init.
# during loading the onchanged may be triggered before full init.
if hasattr(joint, "Vertex1"): # so we check Vertex1
self.updateJCSPlacements(joint)
obj1 = UtilsAssembly.getObjectInPart(joint.Object1, joint.Part1)
obj2 = UtilsAssembly.getObjectInPart(joint.Object2, joint.Part2)
presolved = self.preSolve(
joint,
obj1,
joint.Part1,
obj2,
joint.Part2,
False,
)
isAssembly = self.getAssembly(joint).Type == "Assembly"
if isAssembly:
if isAssembly and not presolved:
solveIfAllowed(self.getAssembly(joint))
else:
self.updateJCSPlacements(joint)
obj1 = UtilsAssembly.getObjectInPart(joint.Object1, joint.Part1)
obj2 = UtilsAssembly.getObjectInPart(joint.Object2, joint.Part2)
self.preSolve(
joint,
obj1,
joint.Part1,
obj2,
joint.Part2,
)
def execute(self, fp):
"""Do something when doing a recomputation, this method is mandatory"""
@@ -380,6 +379,8 @@ class Joint:
elt_type, elt_index = UtilsAssembly.extract_type_and_number(elt)
vtx_type, vtx_index = UtilsAssembly.extract_type_and_number(vtx)
isLine = False
if elt_type == "Vertex":
vertex = obj.Shape.Vertexes[elt_index - 1]
plc.Base = (vertex.X, vertex.Y, vertex.Z)
@@ -406,6 +407,7 @@ class Joint:
plc.Rotation = App.Rotation(curve.Rotation)
if curve.TypeId == "Part::GeomLine":
isLine = True
plane_normal = curve.Direction
plane_origin = App.Vector(0, 0, 0)
plane = Part.Plane(plane_origin, plane_normal)
@@ -457,6 +459,15 @@ class Joint:
# change plc to be relative to the object placement.
plc = obj.Placement.inverse() * plc
# post-process of plc for some special cases
if elt_type == "Vertex":
plc.Rotation = App.Rotation()
elif isLine:
plane_normal = plc.Rotation.multVec(App.Vector(0, 0, 1))
plane_origin = App.Vector(0, 0, 0)
plane = Part.Plane(plane_origin, plane_normal)
plc.Rotation = App.Rotation(plane.Rotation)
# change plc to be relative to the origin of the document.
# global_plc = UtilsAssembly.getGlobalPlacement(obj, part)
# plc = global_plc * plc
@@ -535,7 +546,7 @@ class Joint:
return App.Vector(res[0].X, res[0].Y, res[0].Z)
return surface.Center
def preSolve(self, joint, obj1, part1, obj2, part2):
def preSolve(self, joint, obj1, part1, obj2, part2, savePlc=True):
# The goal of this is to put the part in the correct position to avoid wrong placement by the solve.
# we actually don't want to match perfectly the JCS, it is best to match them
@@ -543,8 +554,9 @@ class Joint:
sameDir = self.areJcsSameDir(joint)
if hasattr(self, "part2Connected") and not self.part2Connected:
self.partMovedByPresolved = joint.Part2
self.presolveBackupPlc = joint.Part2.Placement
if savePlc:
self.partMovedByPresolved = joint.Part2
self.presolveBackupPlc = joint.Part2.Placement
globalJcsPlc1 = UtilsAssembly.getJcsGlobalPlc(
joint.Placement1, joint.Object1, joint.Part1
@@ -555,10 +567,12 @@ class Joint:
if not sameDir:
jcsPlc2 = self.flipPlacement(jcsPlc2)
joint.Part2.Placement = globalJcsPlc1 * jcsPlc2.inverse()
return True
elif hasattr(self, "part1Connected") and not self.part1Connected:
self.partMovedByPresolved = joint.Part1
self.presolveBackupPlc = joint.Part1.Placement
if savePlc:
self.partMovedByPresolved = joint.Part1
self.presolveBackupPlc = joint.Part1.Placement
globalJcsPlc2 = UtilsAssembly.getJcsGlobalPlc(
joint.Placement2, joint.Object2, joint.Part2
@@ -569,6 +583,8 @@ class Joint:
if not sameDir:
jcsPlc1 = self.flipPlacement(jcsPlc1)
joint.Part1.Placement = globalJcsPlc2 * jcsPlc1.inverse()
return True
return False
def undoPreSolve(self):
if self.partMovedByPresolved:
@@ -608,7 +624,10 @@ class ViewProviderJoint:
camera = Gui.ActiveDocument.ActiveView.getCameraNode()
self.cameraSensor = coin.SoFieldSensor(self.camera_callback, camera)
self.cameraSensor.attach(camera.height)
if isinstance(camera, coin.SoPerspectiveCamera):
self.cameraSensor.attach(camera.focalDistance)
elif isinstance(camera, coin.SoOrthographicCamera):
self.cameraSensor.attach(camera.height)
self.app_obj = vobj.Object
@@ -711,7 +730,14 @@ class ViewProviderJoint:
def get_JCS_size(self):
camera = Gui.ActiveDocument.ActiveView.getCameraNode()
if not camera:
# Check if the camera is a perspective camera
if isinstance(camera, coin.SoPerspectiveCamera):
return camera.focalDistance.getValue() / 20
elif isinstance(camera, coin.SoOrthographicCamera):
return camera.height.getValue() / 20
else:
# Default value if camera type is unknown
return 10
return camera.height.getValue() / 20
@@ -736,7 +762,8 @@ class ViewProviderJoint:
plc = joint.Placement1
self.switch_JCS1.whichChild = coin.SO_SWITCH_ALL
self.set_JCS_placement(self.transform1, plc, joint.Object1, joint.Part1)
if joint.Part1:
self.set_JCS_placement(self.transform1, plc, joint.Object1, joint.Part1)
else:
self.switch_JCS1.whichChild = coin.SO_SWITCH_NONE
@@ -745,7 +772,8 @@ class ViewProviderJoint:
plc = joint.Placement2
self.switch_JCS2.whichChild = coin.SO_SWITCH_ALL
self.set_JCS_placement(self.transform2, plc, joint.Object2, joint.Part2)
if joint.Part2:
self.set_JCS_placement(self.transform2, plc, joint.Object2, joint.Part2)
else:
self.switch_JCS2.whichChild = coin.SO_SWITCH_NONE
@@ -827,7 +855,6 @@ class ViewProviderJoint:
class GroundedJoint:
def __init__(self, joint, obj_to_ground):
self.Type = "GoundedJoint"
joint.Proxy = self
self.joint = joint
@@ -852,12 +879,11 @@ class GroundedJoint:
joint.Placement = obj_to_ground.Placement
def __getstate__(self):
return self.Type
def dumps(self):
return None
def __setstate__(self, state):
if state:
self.Type = state
def loads(self, state):
return None
def onChanged(self, fp, prop):
"""Do something when a property has changed"""

View File

@@ -220,7 +220,7 @@ def getContainingPart(full_name, selected_object, activeAssemblyOrPart=None):
if linked_obj.hasObject(selected_object, True):
if not activeAssemblyOrPart:
return obj
elif (
elif (linked_obj.Document == activeAssemblyOrPart.Document) and (
linked_obj.hasObject(activeAssemblyOrPart, True)
or linked_obj == activeAssemblyOrPart
):
@@ -282,9 +282,14 @@ def getJcsGlobalPlc(jcsPlc, objName, part):
# 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):
if targetObj is None:
return App.Placement()
inContainerBranch = container is None
for part in App.activeDocument().RootObjects:
foundPlacement = getTargetPlacementRelativeTo(targetObj, part, container, inContainerBranch)
for rootObj in App.activeDocument().RootObjects:
foundPlacement = getTargetPlacementRelativeTo(
targetObj, rootObj, container, inContainerBranch
)
if foundPlacement is not None:
return foundPlacement
@@ -330,6 +335,8 @@ def getTargetPlacementRelativeTo(
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: