Assembly: Adds limit and RackPinion/Screw/Gears

This commit is contained in:
PaddleStroke
2024-04-04 17:03:49 +02:00
committed by Yorik van Havre
parent 1a7f62e522
commit 60b902fc7e
7 changed files with 714 additions and 193 deletions

View File

@@ -58,6 +58,7 @@
#include <OndselSolver/ASMTPart.h>
#include <OndselSolver/ASMTJoint.h>
#include <OndselSolver/ASMTFixedJoint.h>
#include <OndselSolver/ASMTGearJoint.h>
#include <OndselSolver/ASMTRevoluteJoint.h>
#include <OndselSolver/ASMTCylindricalJoint.h>
#include <OndselSolver/ASMTTranslationalJoint.h>
@@ -69,6 +70,8 @@
#include <OndselSolver/ASMTRevCylJoint.h>
#include <OndselSolver/ASMTCylSphJoint.h>
#include <OndselSolver/ASMTRackPinionJoint.h>
#include <OndselSolver/ASMTRotationLimit.h>
#include <OndselSolver/ASMTTranslationLimit.h>
#include <OndselSolver/ASMTScrewJoint.h>
#include <OndselSolver/ASMTSphSphJoint.h>
#include <OndselSolver/ASMTTime.h>
@@ -84,6 +87,25 @@ namespace PartApp = Part;
using namespace Assembly;
using namespace MbD;
void printPlacement(Base::Placement plc, const char* name)
{
Base::Vector3d pos = plc.getPosition();
Base::Vector3d axis;
double angle;
Base::Rotation rot = plc.getRotation();
rot.getRawValue(axis, angle);
Base::Console().Warning(
"placement %s : position (%.1f, %.1f, %.1f) - axis (%.1f, %.1f, %.1f) angle %.1f\n",
name,
pos.x,
pos.y,
pos.z,
axis.x,
axis.y,
axis.z,
angle);
}
// ================================ Assembly Object ============================
PROPERTY_SOURCE(Assembly::AssemblyObject, App::Part)
@@ -115,7 +137,7 @@ App::DocumentObjectExecReturn* AssemblyObject::execute()
return ret;
}
int AssemblyObject::solve(bool enableRedo)
int AssemblyObject::solve(bool enableRedo, bool updateJCS)
{
mbdAssembly = makeMbdAssembly();
objectPartMap.clear();
@@ -126,7 +148,7 @@ int AssemblyObject::solve(bool enableRedo)
return -6;
}
std::vector<App::DocumentObject*> joints = getJoints();
std::vector<App::DocumentObject*> joints = getJoints(updateJCS);
removeUnconnectedJoints(joints, groundedObjs);
@@ -153,6 +175,7 @@ int AssemblyObject::solve(bool enableRedo)
void AssemblyObject::preDrag(std::vector<App::DocumentObject*> dragParts)
{
Base::Console().Warning("pre drag\n");
solve();
dragMbdParts.clear();
@@ -163,8 +186,9 @@ void AssemblyObject::preDrag(std::vector<App::DocumentObject*> dragParts)
mbdAssembly->runPreDrag();
}
void AssemblyObject::doDragStep()
void AssemblyObject::doDragStep(Base::Vector3d delta)
{
Base::Console().Warning("doDragStep\n");
for (auto& mbdPart : dragMbdParts) {
App::DocumentObject* part = nullptr;
for (auto& pair : objectPartMap) {
@@ -177,7 +201,13 @@ void AssemblyObject::doDragStep()
continue;
}
Base::Placement plc = getPlacementFromProp(part, "Placement");
FColDsptr pos3D, delta2;
pos3D = mbdPart->position3D;
delta2 = std::make_shared<FullColumn<double>>(ListD {delta.x, delta.y, delta.z});
mbdPart->updateMbDFromPosition3D(pos3D->plusFullColumn(delta2));
/*Base::Placement plc = getPlacementFromProp(part, "Placement");
printPlacement(plc, "init plc");
Base::Vector3d pos = plc.getPosition();
mbdPart->setPosition3D(pos.x, pos.y, pos.z);
@@ -187,7 +217,7 @@ void AssemblyObject::doDragStep()
Base::Vector3d r0 = mat.getRow(0);
Base::Vector3d r1 = mat.getRow(1);
Base::Vector3d r2 = mat.getRow(2);
mbdPart->setRotationMatrix(r0.x, r0.y, r0.z, r1.x, r1.y, r1.z, r2.x, r2.y, r2.z);
mbdPart->setRotationMatrix(r0.x, r0.y, r0.z, r1.x, r1.y, r1.z, r2.x, r2.y, r2.z);*/
}
auto dragPartsVec = std::make_shared<std::vector<std::shared_ptr<ASMTPart>>>(dragMbdParts);
@@ -198,6 +228,7 @@ void AssemblyObject::doDragStep()
void AssemblyObject::postDrag()
{
Base::Console().Warning("post drag \n");
mbdAssembly->runPostDrag(); // Do this after last drag
}
@@ -614,6 +645,9 @@ void AssemblyObject::fixGroundedPart(App::DocumentObject* obj,
bool AssemblyObject::isJointConnectingPartToGround(App::DocumentObject* joint, const char* propname)
{
if (!isJointTypeConnecting(joint)) {
return false;
}
auto* propPart = dynamic_cast<App::PropertyLink*>(joint->getPropertyByName(propname));
if (!propPart) {
@@ -661,6 +695,13 @@ bool AssemblyObject::isJointConnectingPartToGround(App::DocumentObject* joint, c
return isConnected;
}
bool AssemblyObject::isJointTypeConnecting(App::DocumentObject* joint)
{
JointType jointType = getJointType(joint);
return jointType != JointType::RackPinion && jointType != JointType::Screw
&& jointType != JointType::Gears && jointType != JointType::Pulleys;
}
void AssemblyObject::removeUnconnectedJoints(std::vector<App::DocumentObject*>& joints,
std::vector<App::DocumentObject*> groundedObjs)
{
@@ -716,6 +757,10 @@ AssemblyObject::getConnectedParts(App::DocumentObject* part,
{
std::vector<App::DocumentObject*> connectedParts;
for (auto joint : joints) {
if (!isJointTypeConnecting(joint)) {
continue;
}
App::DocumentObject* obj1 = getLinkObjFromProp(joint, "Part1");
App::DocumentObject* obj2 = getLinkObjFromProp(joint, "Part2");
if (obj1 == part) {
@@ -802,6 +847,22 @@ std::shared_ptr<ASMTJoint> AssemblyObject::makeMbdJointOfType(App::DocumentObjec
else if (type == JointType::Distance) {
return makeMbdJointDistance(joint);
}
else if (type == JointType::RackPinion) {
auto mbdJoint = CREATE<ASMTRackPinionJoint>::With();
mbdJoint->pitchRadius = getJointDistance(joint);
return mbdJoint;
}
else if (type == JointType::Screw) {
auto mbdJoint = CREATE<ASMTScrewJoint>::With();
mbdJoint->pitch = getJointDistance(joint);
return mbdJoint;
}
else if (type == JointType::Gears) {
auto mbdJoint = CREATE<ASMTGearJoint>::With();
mbdJoint->radiusI = getJointDistance(joint);
mbdJoint->radiusJ = getJointDistance2(joint);
return mbdJoint;
}
return nullptr;
}
@@ -1009,11 +1070,70 @@ AssemblyObject::makeMbdJoint(App::DocumentObject* joint)
std::string fullMarkerName1 = handleOneSideOfJoint(joint, "Object1", "Part1", "Placement1");
std::string fullMarkerName2 = handleOneSideOfJoint(joint, "Object2", "Part2", "Placement2");
std::string jointName = joint->getFullName();
mbdJoint->setName(joint->getFullName());
mbdJoint->setMarkerI(fullMarkerName1);
mbdJoint->setMarkerJ(fullMarkerName2);
// Add limits if needed.
auto* prop = dynamic_cast<App::PropertyBool*>(joint->getPropertyByName("EnableLimits"));
if (prop && prop->getValue()) {
if (jointType == JointType::Slider || jointType == JointType::Cylindrical) {
auto* propLenMin =
dynamic_cast<App::PropertyFloat*>(joint->getPropertyByName("LengthMin"));
if (propLenMin) {
auto limit = ASMTTranslationLimit::With();
limit->setName(joint->getFullName() + "-LimitLenMin");
limit->setMarkerI(fullMarkerName1);
limit->setMarkerJ(fullMarkerName2);
// limit->setmotionJoint(jointName);
limit->settype("=>");
limit->setlimit(std::to_string(propLenMin->getValue()));
limit->settol("1.0e-9");
mbdAssembly->addLimit(limit);
}
auto* propLenMax =
dynamic_cast<App::PropertyFloat*>(joint->getPropertyByName("LengthMax"));
if (propLenMax) {
auto limit = ASMTTranslationLimit::With();
limit->setName(joint->getFullName() + "-LimitLenMax");
limit->setMarkerI(fullMarkerName1);
limit->setMarkerJ(fullMarkerName2);
limit->settype("=<");
limit->setlimit(std::to_string(propLenMax->getValue()));
limit->settol("1.0e-9");
mbdAssembly->addLimit(limit);
}
}
if (jointType == JointType::Revolute || jointType == JointType::Cylindrical) {
auto* propRotMin =
dynamic_cast<App::PropertyFloat*>(joint->getPropertyByName("AngleMin"));
if (propRotMin) {
auto limit = ASMTRotationLimit::With();
limit->setName(joint->getFullName() + "-LimitRotMin");
limit->setMarkerI(fullMarkerName1);
limit->setMarkerJ(fullMarkerName2);
limit->settype("=>");
limit->setlimit(std::to_string(propRotMin->getValue()) + "*pi/180.0");
limit->settol("1.0e-9");
mbdAssembly->addLimit(limit);
}
auto* propRotMax =
dynamic_cast<App::PropertyFloat*>(joint->getPropertyByName("AngleMax"));
if (propRotMax) {
auto limit = ASMTRotationLimit::With();
limit->setName(joint->getFullName() + "-LimiRotMax");
limit->setMarkerI(fullMarkerName1);
limit->setMarkerJ(fullMarkerName2);
limit->settype("=<");
limit->setlimit(std::to_string(propRotMax->getValue()) + "*pi/180.0");
limit->settol("1.0e-9");
mbdAssembly->addLimit(limit);
}
}
}
return {mbdJoint};
}
@@ -1626,25 +1746,6 @@ DistanceType AssemblyObject::getDistanceType(App::DocumentObject* joint)
return DistanceType::Other;
}
void printPlacement(Base::Placement plc, const char* name)
{
Base::Vector3d pos = plc.getPosition();
Base::Vector3d axis;
double angle;
Base::Rotation rot = plc.getRotation();
rot.getRawValue(axis, angle);
Base::Console().Warning(
"placement %s : position (%.1f, %.1f, %.1f) - axis (%.1f, %.1f, %.1f) angle %.1f\n",
name,
pos.x,
pos.y,
pos.z,
axis.x,
axis.y,
axis.z,
angle);
}
void AssemblyObject::setJointActivated(App::DocumentObject* joint, bool val)
{
auto* propActivated = dynamic_cast<App::PropertyBool*>(joint->getPropertyByName("Activated"));
@@ -1794,6 +1895,18 @@ double AssemblyObject::getJointDistance(App::DocumentObject* joint)
return distance;
}
double AssemblyObject::getJointDistance2(App::DocumentObject* joint)
{
double distance = 0.0;
auto* prop = dynamic_cast<App::PropertyFloat*>(joint->getPropertyByName("Distance2"));
if (prop) {
distance = prop->getValue();
}
return distance;
}
JointType AssemblyObject::getJointType(App::DocumentObject* joint)
{
JointType jointType = JointType::Fixed;

View File

@@ -66,6 +66,10 @@ enum class JointType
Slider,
Ball,
Distance,
RackPinion,
Screw,
Gears,
Pulleys,
};
enum class DistanceType
@@ -138,9 +142,9 @@ public:
/* 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);
int solve(bool enableRedo = false, bool updateJCS = true);
void preDrag(std::vector<App::DocumentObject*> dragParts);
void doDragStep();
void doDragStep(Base::Vector3d delta);
void postDrag();
void savePlacementsForUndo();
void undoSolve();
@@ -182,6 +186,7 @@ public:
void fixGroundedPart(App::DocumentObject* obj, Base::Placement& plc, std::string& jointName);
bool isJointConnectingPartToGround(App::DocumentObject* joint, const char* partPropName);
bool isJointTypeConnecting(App::DocumentObject* joint);
void removeUnconnectedJoints(std::vector<App::DocumentObject*>& joints,
std::vector<App::DocumentObject*> groundedObjs);
@@ -234,6 +239,7 @@ public:
static void setJointActivated(App::DocumentObject* joint, bool val);
static bool getJointActivated(App::DocumentObject* joint);
static double getJointDistance(App::DocumentObject* joint);
static double getJointDistance2(App::DocumentObject* joint);
static JointType getJointType(App::DocumentObject* joint);
static const char* getElementFromProp(App::DocumentObject* obj, const char* propName);
static std::string getElementTypeFromProp(App::DocumentObject* obj, const char* propName);

View File

@@ -232,13 +232,102 @@ class CommandCreateJointDistance:
}
def IsActive(self):
# return False
return isCreateJointActive()
def Activated(self):
activateJoint(5)
class CommandCreateJointRackPinion:
def __init__(self):
pass
def GetResources(self):
return {
"Pixmap": "Assembly_CreateJointRackPinion",
"MenuText": QT_TRANSLATE_NOOP(
"Assembly_CreateJointRackPinion", "Create Rack and Pinion Joint"
),
"Accel": "P",
"ToolTip": "<p>"
+ QT_TRANSLATE_NOOP(
"Assembly_CreateJointRackPinion",
"Create a Rack and Pinion Joint: Links a part with a sliding joint with a part with a revolute joint.",
)
+ "</p><p>"
+ QT_TRANSLATE_NOOP(
"Assembly_CreateJointRackPinion",
"The pitch radius defines the movement ratio between the rack and the pinion.",
)
+ "</p>",
"CmdType": "ForEdit",
}
def IsActive(self):
return isCreateJointActive()
def Activated(self):
activateJoint(6)
class CommandCreateJointScrew:
def __init__(self):
pass
def GetResources(self):
return {
"Pixmap": "Assembly_CreateJointScrew",
"MenuText": QT_TRANSLATE_NOOP("Assembly_CreateJointScrew", "Create Screw Joint"),
"Accel": "W",
"ToolTip": "<p>"
+ QT_TRANSLATE_NOOP(
"Assembly_CreateJointScrew",
"Create a Screw Joint: Links a part with a sliding joint with a part with a revolute joint.",
)
+ "</p><p>"
+ QT_TRANSLATE_NOOP(
"Assembly_CreateJointScrew",
"The pitch radius defines the movement ratio between the rotating screw and the sliding part.",
)
+ "</p>",
"CmdType": "ForEdit",
}
def IsActive(self):
return isCreateJointActive()
def Activated(self):
activateJoint(7)
class CommandCreateJointGears:
def __init__(self):
pass
def GetResources(self):
return {
"Pixmap": "Assembly_CreateJointGears",
"MenuText": QT_TRANSLATE_NOOP("Assembly_CreateJointGears", "Create Gears Joint"),
"Accel": "X",
"ToolTip": "<p>"
+ QT_TRANSLATE_NOOP(
"Assembly_CreateJointGears",
"Create a Gears Joint: Links two rotating gears together.",
)
+ "</p>",
"CmdType": "ForEdit",
}
def IsActive(self):
return isCreateJointActive()
def Activated(self):
activateJoint(8)
def createGroundedJoint(obj):
assembly = UtilsAssembly.activeAssembly()
if not assembly:
@@ -330,3 +419,6 @@ if App.GuiUp:
Gui.addCommand("Assembly_CreateJointSlider", CommandCreateJointSlider())
Gui.addCommand("Assembly_CreateJointBall", CommandCreateJointBall())
Gui.addCommand("Assembly_CreateJointDistance", CommandCreateJointDistance())
Gui.addCommand("Assembly_CreateJointRackPinion", CommandCreateJointRackPinion())
Gui.addCommand("Assembly_CreateJointScrew", CommandCreateJointScrew())
Gui.addCommand("Assembly_CreateJointGears", CommandCreateJointGears())

View File

@@ -14,13 +14,13 @@
<string>Create Joint</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<item row="0" column="0" colspan="2">
<widget class="QComboBox" name="jointType"/>
</item>
<item row="1" column="0">
<item row="1" column="0" colspan="2">
<widget class="QListWidget" name="featureList"/>
</item>
<item row="2" column="0">
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout" name="hLayoutDistance">
<item>
<widget class="QLabel" name="distanceLabel">
@@ -44,7 +44,31 @@
</item>
</layout>
</item>
<item row="3" column="0">
<item row="3" column="0" colspan="2">
<layout class="QHBoxLayout" name="hLayoutDistance2">
<item>
<widget class="QLabel" name="distanceLabel2">
<property name="text">
<string>Radius 2</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::QuantitySpinBox" name="distanceSpinbox2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0" colspan="2">
<layout class="QHBoxLayout" name="hLayout">
<item>
<widget class="QLabel" name="offsetLabel">
@@ -68,7 +92,7 @@
</item>
</layout>
</item>
<item row="4" column="0">
<item row="5" column="0" colspan="2">
<layout class="QHBoxLayout" name="hLayoutRotation">
<item>
<widget class="QLabel" name="rotationLabel">
@@ -92,7 +116,7 @@
</item>
</layout>
</item>
<item row="5" column="0">
<item row="6" column="0">
<widget class="QToolButton" name="PushButtonReverse">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
@@ -112,6 +136,96 @@
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="LimitCheckbox">
<property name="text">
<string>Limits</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="limitLenMinSpinboxLabel">
<property name="text">
<string>Length min</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="Gui::QuantitySpinBox" name="limitLenMinSpinbox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="limitLenMaxSpinboxLabel">
<property name="text">
<string>Length max</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="Gui::QuantitySpinBox" name="limitLenMaxSpinbox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="unit" stdset="0">
<string notr="true">mm</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="limitRotMinSpinboxLabel">
<property name="text">
<string>Angle min</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="Gui::QuantitySpinBox" name="limitRotMinSpinbox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="unit" stdset="0">
<string notr="true">deg</string>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="limitRotMaxSpinboxLabel">
<property name="text">
<string>Angle max</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="Gui::QuantitySpinBox" name="limitRotMaxSpinbox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="unit" stdset="0">
<string notr="true">deg</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>

View File

@@ -412,6 +412,12 @@ bool ViewProviderAssembly::mouseMove(const SbVec2s& cursorPos, Gui::View3DInvent
propPlacement->setValue(plc);
}
}
// Delta drag test :
/*SbVec3f vec = viewer->getPointOnFocalPlane(cursorPos);
Base::Vector3d newPos = Base::Vector3d(vec[0], vec[1], vec[2]);
Base::Vector3d delta = newPos - prevPosition;*/
prevPosition = newPos;
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath(
@@ -419,8 +425,8 @@ bool ViewProviderAssembly::mouseMove(const SbVec2s& cursorPos, Gui::View3DInvent
bool solveOnMove = hGrp->GetBool("SolveOnMove", true);
if (solveOnMove) {
auto* assemblyPart = static_cast<AssemblyObject*>(getObject());
assemblyPart->solve();
// assemblyPart->doDragStep();
assemblyPart->solve(/*enableRedo = */ false, /*updateJCS = */ false);
// assemblyPart->doDragStep(delta);
}
}
return false;
@@ -773,11 +779,12 @@ void ViewProviderAssembly::initMove(const SbVec2s& cursorPos, Gui::View3DInvento
auto* assemblyPart = static_cast<AssemblyObject*>(getObject());
assemblyPart->setObjMasses(objectMasses);
/*std::vector<App::DocumentObject*> dragParts;
std::vector<App::DocumentObject*> dragParts;
for (auto& pair : docsToMove) {
dragParts.push_back(pair.first);
}
assemblyPart->preDrag(dragParts);*/
assemblyPart->solve();
// assemblyPart->preDrag(dragParts);
}
}

View File

@@ -94,6 +94,10 @@ class AssemblyWorkbench(Workbench):
"Assembly_CreateJointSlider",
"Assembly_CreateJointBall",
"Assembly_CreateJointDistance",
"Separator",
"Assembly_CreateJointRackPinion",
"Assembly_CreateJointScrew",
"Assembly_CreateJointGears",
]
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Assembly"), cmdList)

View File

@@ -51,6 +51,10 @@ TranslatedJointTypes = [
translate("Assembly", "Slider"),
translate("Assembly", "Ball"),
translate("Assembly", "Distance"),
translate("Assembly", "RackPinion"),
translate("Assembly", "Screw"),
translate("Assembly", "Gears"),
# translate("Assembly", "Pulleys"),
]
JointTypes = [
@@ -60,10 +64,21 @@ JointTypes = [
"Slider",
"Ball",
"Distance",
"RackPinion",
"Screw",
"Gears",
# "Pulleys",
]
JointUsingDistance = [
"Distance",
"RackPinion",
"Screw",
"Gears",
]
JointUsingDistance2 = [
"Gears",
]
JointUsingOffset = [
@@ -84,6 +99,16 @@ JointUsingReverse = [
"Distance",
]
JointUsingLimitLength = [
"Cylindrical",
"Slider",
]
JointUsingLimitAngle = [
"Revolute",
"Cylindrical",
]
def solveIfAllowed(assembly, storePrev=False):
if assembly.Type == "Assembly" and Preferences.preferences().GetBool(
@@ -131,151 +156,241 @@ class Joint:
joint.JointType = JointTypes # sets the list
joint.JointType = JointTypes[type_index] # set the initial value
# First Joint Connector
joint.addProperty(
"App::PropertyString", # Not PropertyLink because they don't support external objects
"Object1",
"Joint Connector 1",
QT_TRANSLATE_NOOP("App::Property", "The first object of the joint"),
)
joint.addProperty(
"App::PropertyLink",
"Part1",
"Joint Connector 1",
QT_TRANSLATE_NOOP("App::Property", "The first part of the joint"),
)
joint.addProperty(
"App::PropertyString",
"Element1",
"Joint Connector 1",
QT_TRANSLATE_NOOP("App::Property", "The selected element of the first object"),
)
joint.addProperty(
"App::PropertyString",
"Vertex1",
"Joint Connector 1",
QT_TRANSLATE_NOOP("App::Property", "The selected vertex of the first object"),
)
joint.addProperty(
"App::PropertyPlacement",
"Placement1",
"Joint Connector 1",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the local coordinate system within object1 that will be used for the joint.",
),
)
joint.addProperty(
"App::PropertyBool",
"Detach1",
"Joint Connector 1",
QT_TRANSLATE_NOOP(
"App::Property",
"This prevents Placement1 from recomputing, enabling custom positioning of the placement.",
),
)
# Second Joint Connector
joint.addProperty(
"App::PropertyString",
"Object2",
"Joint Connector 2",
QT_TRANSLATE_NOOP("App::Property", "The second object of the joint"),
)
joint.addProperty(
"App::PropertyLink",
"Part2",
"Joint Connector 2",
QT_TRANSLATE_NOOP("App::Property", "The second part of the joint"),
)
joint.addProperty(
"App::PropertyString",
"Element2",
"Joint Connector 2",
QT_TRANSLATE_NOOP("App::Property", "The selected element of the second object"),
)
joint.addProperty(
"App::PropertyString",
"Vertex2",
"Joint Connector 2",
QT_TRANSLATE_NOOP("App::Property", "The selected vertex of the second object"),
)
joint.addProperty(
"App::PropertyPlacement",
"Placement2",
"Joint Connector 2",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the local coordinate system within object2 that will be used for the joint.",
),
)
joint.addProperty(
"App::PropertyBool",
"Detach2",
"Joint Connector 2",
QT_TRANSLATE_NOOP(
"App::Property",
"This prevents Placement2 from recomputing, enabling custom positioning of the placement.",
),
)
joint.addProperty(
"App::PropertyFloat",
"Distance",
"Joint",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the distance of the joint. It is used only by the distance joint.",
),
)
joint.addProperty(
"App::PropertyFloat",
"Rotation",
"Joint",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the rotation of the joint.",
),
)
joint.addProperty(
"App::PropertyVector",
"Offset",
"Joint",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the offset vector of the joint.",
),
)
joint.addProperty(
"App::PropertyBool",
"Activated",
"Joint",
QT_TRANSLATE_NOOP(
"App::Property",
"This indicates if the joint is active.",
),
)
joint.Activated = True
self.createProperties(joint)
self.setJointConnectors(joint, [])
def onDocumentRestored(self, joint):
self.createProperties(joint)
def createProperties(self, joint):
# First Joint Connector
if not hasattr(joint, "Object1"):
joint.addProperty(
"App::PropertyString", # Not PropertyLink because they don't support external objects
"Object1",
"Joint Connector 1",
QT_TRANSLATE_NOOP("App::Property", "The first object of the joint"),
)
if not hasattr(joint, "Part1"):
joint.addProperty(
"App::PropertyLink",
"Part1",
"Joint Connector 1",
QT_TRANSLATE_NOOP("App::Property", "The first part of the joint"),
)
if not hasattr(joint, "Element1"):
joint.addProperty(
"App::PropertyString",
"Element1",
"Joint Connector 1",
QT_TRANSLATE_NOOP("App::Property", "The selected element of the first object"),
)
if not hasattr(joint, "Vertex1"):
joint.addProperty(
"App::PropertyString",
"Vertex1",
"Joint Connector 1",
QT_TRANSLATE_NOOP("App::Property", "The selected vertex of the first object"),
)
if not hasattr(joint, "Placement1"):
joint.addProperty(
"App::PropertyPlacement",
"Placement1",
"Joint Connector 1",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the local coordinate system within object1 that will be used for the joint.",
),
)
if not hasattr(joint, "Detach1"):
joint.addProperty(
"App::PropertyBool",
"Detach1",
"Joint Connector 1",
QT_TRANSLATE_NOOP(
"App::Property",
"This prevents Placement1 from recomputing, enabling custom positioning of the placement.",
),
)
# Second Joint Connector
if not hasattr(joint, "Object2"):
joint.addProperty(
"App::PropertyString",
"Object2",
"Joint Connector 2",
QT_TRANSLATE_NOOP("App::Property", "The second object of the joint"),
)
if not hasattr(joint, "Part2"):
joint.addProperty(
"App::PropertyLink",
"Part2",
"Joint Connector 2",
QT_TRANSLATE_NOOP("App::Property", "The second part of the joint"),
)
if not hasattr(joint, "Element2"):
joint.addProperty(
"App::PropertyString",
"Element2",
"Joint Connector 2",
QT_TRANSLATE_NOOP("App::Property", "The selected element of the second object"),
)
if not hasattr(joint, "Vertex2"):
joint.addProperty(
"App::PropertyString",
"Vertex2",
"Joint Connector 2",
QT_TRANSLATE_NOOP("App::Property", "The selected vertex of the second object"),
)
if not hasattr(joint, "Placement2"):
joint.addProperty(
"App::PropertyPlacement",
"Placement2",
"Joint Connector 2",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the local coordinate system within object2 that will be used for the joint.",
),
)
if not hasattr(joint, "Detach2"):
joint.addProperty(
"App::PropertyBool",
"Detach2",
"Joint Connector 2",
QT_TRANSLATE_NOOP(
"App::Property",
"This prevents Placement2 from recomputing, enabling custom positioning of the placement.",
),
)
if not hasattr(joint, "Distance"):
joint.addProperty(
"App::PropertyFloat",
"Distance",
"Joint",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the distance of the joint. It is used only by the distance joint and by RackPinion (pitch radius), Screw and Gears(radius1)",
),
)
if not hasattr(joint, "Distance2"):
joint.addProperty(
"App::PropertyFloat",
"Distance2",
"Joint",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the second distance of the joint. It is used only by the gear joint to store the second radius.",
),
)
if not hasattr(joint, "Rotation"):
joint.addProperty(
"App::PropertyFloat",
"Rotation",
"Joint",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the rotation of the joint.",
),
)
if not hasattr(joint, "Offset"):
joint.addProperty(
"App::PropertyVector",
"Offset",
"Joint",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the offset vector of the joint.",
),
)
if not hasattr(joint, "Activated"):
joint.addProperty(
"App::PropertyBool",
"Activated",
"Joint",
QT_TRANSLATE_NOOP(
"App::Property",
"This indicates if the joint is active.",
),
)
joint.Activated = True
if not hasattr(joint, "EnableLimits"):
joint.addProperty(
"App::PropertyBool",
"EnableLimits",
"Limits",
QT_TRANSLATE_NOOP(
"App::Property",
"Is this joint using limits.",
),
)
joint.EnableLimits = False
if not hasattr(joint, "LengthMin"):
joint.addProperty(
"App::PropertyFloat",
"LengthMin",
"Limits",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the minimum limit for the length between both coordinate systems (along their Z axis).",
),
)
if not hasattr(joint, "LengthMax"):
joint.addProperty(
"App::PropertyFloat",
"LengthMax",
"Limits",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the maximum limit for the length between both coordinate systems (along their Z axis).",
),
)
if not hasattr(joint, "AngleMin"):
joint.addProperty(
"App::PropertyFloat",
"AngleMin",
"Limits",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the minimum limit for the angle between both coordinate systems (between their X axis).",
),
)
if not hasattr(joint, "AngleMax"):
joint.addProperty(
"App::PropertyFloat",
"AngleMax",
"Limits",
QT_TRANSLATE_NOOP(
"App::Property",
"This is the maximum limit for the angle between both coordinate systems (between their X axis).",
),
)
def dumps(self):
return None
def loads(self, state):
return None
def getAssembly(self, joint):
@@ -313,7 +428,7 @@ class Joint:
else:
self.updateJCSPlacements(joint)
if prop == "Distance":
if prop == "Distance" and joint.JointType == "Distance":
# during loading the onchanged may be triggered before full init.
if hasattr(joint, "Vertex1"): # so we check Vertex1
solveIfAllowed(self.getAssembly(joint))
@@ -723,6 +838,12 @@ class ViewProviderJoint:
return ":/icons/Assembly_CreateJointBall.svg"
elif self.app_obj.JointType == "Distance":
return ":/icons/Assembly_CreateJointDistance.svg"
elif self.app_obj.JointType == "RackPinion":
return ":/icons/Assembly_CreateJointRackPinion.svg"
elif self.app_obj.JointType == "Screw":
return ":/icons/Assembly_CreateJointScrew.svg"
elif self.app_obj.JointType == "Gears":
return ":/icons/Assembly_CreateJointGears.svg"
return ":/icons/Assembly_CreateJoint.svg"
@@ -1023,7 +1144,7 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
self.activeType = "Assembly"
self.doc = self.assembly.Document
self.gui_doc = Gui.getDocument(doc)
self.gui_doc = Gui.getDocument(self.doc)
self.view = self.gui_doc.activeView()
@@ -1046,6 +1167,11 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
self.form.offsetSpinbox.valueChanged.connect(self.onOffsetChanged)
self.form.rotationSpinbox.valueChanged.connect(self.onRotationChanged)
self.form.PushButtonReverse.clicked.connect(self.onReverseClicked)
self.form.LimitCheckbox.stateChanged.connect(self.adaptUi)
self.form.limitLenMinSpinbox.valueChanged.connect(self.onLimitLenMinChanged)
self.form.limitLenMaxSpinbox.valueChanged.connect(self.onLimitLenMaxChanged)
self.form.limitRotMinSpinbox.valueChanged.connect(self.onLimitRotMinChanged)
self.form.limitRotMaxSpinbox.valueChanged.connect(self.onLimitRotMaxChanged)
if jointObj:
Gui.Selection.clearSelection()
@@ -1073,10 +1199,7 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
self.visibilityBackup = False
self.handleInitialSelection()
self.toggleDistanceVisibility()
self.toggleOffsetVisibility()
self.toggleRotationVisibility()
self.toggleReverseVisibility()
self.adaptUi()
self.setJointsPickableState(False)
@@ -1211,10 +1334,7 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
def onJointTypeChanged(self, index):
self.joint.Proxy.setJointType(self.joint, JointTypes[self.form.jointType.currentIndex()])
self.toggleDistanceVisibility()
self.toggleOffsetVisibility()
self.toggleRotationVisibility()
self.toggleReverseVisibility()
self.adaptUi()
def onDistanceChanged(self, quantity):
self.joint.Distance = self.form.distanceSpinbox.property("rawValue")
@@ -1225,39 +1345,98 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
def onRotationChanged(self, quantity):
self.joint.Rotation = self.form.rotationSpinbox.property("rawValue")
def onLimitLenMinChanged(self, quantity):
self.joint.LengthMin = self.form.limitLenMinSpinbox.property("rawValue")
def onLimitLenMaxChanged(self, quantity):
self.joint.LengthMax = self.form.limitLenMaxSpinbox.property("rawValue")
def onLimitRotMinChanged(self, quantity):
self.joint.AngleMin = self.form.limitRotMinSpinbox.property("rawValue")
def onLimitRotMaxChanged(self, quantity):
self.joint.AngleMax = self.form.limitRotMaxSpinbox.property("rawValue")
def onReverseClicked(self):
self.joint.Proxy.flipOnePart(self.joint)
def toggleDistanceVisibility(self):
if JointTypes[self.form.jointType.currentIndex()] in JointUsingDistance:
def adaptUi(self):
jType = JointTypes[self.form.jointType.currentIndex()]
if jType in JointUsingDistance:
self.form.distanceLabel.show()
self.form.distanceSpinbox.show()
if jType == "Distance":
self.form.distanceLabel.setText("Distance")
elif jType == "Gears":
self.form.distanceLabel.setText("Radius 1")
else:
self.form.distanceLabel.setText("Pitch radius")
else:
self.form.distanceLabel.hide()
self.form.distanceSpinbox.hide()
def toggleOffsetVisibility(self):
if JointTypes[self.form.jointType.currentIndex()] in JointUsingOffset:
if jType in JointUsingDistance2:
self.form.distanceLabel2.show()
self.form.distanceSpinbox2.show()
else:
self.form.distanceLabel2.hide()
self.form.distanceSpinbox2.hide()
if jType in JointUsingOffset:
self.form.offsetLabel.show()
self.form.offsetSpinbox.show()
else:
self.form.offsetLabel.hide()
self.form.offsetSpinbox.hide()
def toggleRotationVisibility(self):
if JointTypes[self.form.jointType.currentIndex()] in JointUsingRotation:
if jType in JointUsingRotation:
self.form.rotationLabel.show()
self.form.rotationSpinbox.show()
else:
self.form.rotationLabel.hide()
self.form.rotationSpinbox.hide()
def toggleReverseVisibility(self):
if JointTypes[self.form.jointType.currentIndex()] in JointUsingReverse:
if jType in JointUsingReverse:
self.form.PushButtonReverse.show()
else:
self.form.PushButtonReverse.hide()
needLengthLimits = jType in JointUsingLimitLength
needAngleLimits = jType in JointUsingLimitAngle
showLimits = False
if needLengthLimits or needAngleLimits:
self.form.LimitCheckbox.show()
showLimits = True
else:
self.form.LimitCheckbox.hide()
showLimits = showLimits and self.form.LimitCheckbox.isChecked()
self.joint.EnableLimits = showLimits
if needLengthLimits and showLimits:
self.form.limitLenMinSpinboxLabel.show()
self.form.limitLenMaxSpinboxLabel.show()
self.form.limitLenMinSpinbox.show()
self.form.limitLenMaxSpinbox.show()
else:
self.form.limitLenMinSpinboxLabel.hide()
self.form.limitLenMaxSpinboxLabel.hide()
self.form.limitLenMinSpinbox.hide()
self.form.limitLenMaxSpinbox.hide()
if needAngleLimits and showLimits:
self.form.limitRotMinSpinboxLabel.show()
self.form.limitRotMaxSpinboxLabel.show()
self.form.limitRotMinSpinbox.show()
self.form.limitRotMaxSpinbox.show()
else:
self.form.limitRotMinSpinboxLabel.hide()
self.form.limitRotMaxSpinboxLabel.hide()
self.form.limitRotMinSpinbox.hide()
self.form.limitRotMaxSpinbox.hide()
def updateTaskboxFromJoint(self):
self.current_selection = []
self.preselection_dict = None
@@ -1297,6 +1476,12 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
self.form.offsetSpinbox.setProperty("rawValue", self.joint.Offset.z)
self.form.rotationSpinbox.setProperty("rawValue", self.joint.Rotation)
self.form.LimitCheckbox.setChecked(self.joint.EnableLimits)
self.form.limitLenMinSpinbox.setProperty("rawValue", self.joint.LengthMin)
self.form.limitLenMaxSpinbox.setProperty("rawValue", self.joint.LengthMax)
self.form.limitRotMinSpinbox.setProperty("rawValue", self.joint.AngleMin)
self.form.limitRotMaxSpinbox.setProperty("rawValue", self.joint.AngleMax)
self.form.jointType.setCurrentIndex(JointTypes.index(self.joint.JointType))
self.updateJointList()