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:
@@ -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
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user