1
# SPDX-License-Identifier: LGPL-2.1-or-later
2
# /**************************************************************************
4
# Copyright (c) 2023 Ondsel <development@ondsel.com> *
6
# This file is part of FreeCAD. *
8
# FreeCAD is free software: you can redistribute it and/or modify it *
9
# under the terms of the GNU Lesser General Public License as *
10
# published by the Free Software Foundation, either version 2.1 of the *
11
# License, or (at your option) any later version. *
13
# FreeCAD is distributed in the hope that it will be useful, but *
14
# WITHOUT ANY WARRANTY; without even the implied warranty of *
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
16
# Lesser General Public License for more details. *
18
# You should have received a copy of the GNU Lesser General Public *
19
# License along with FreeCAD. If not, see *
20
# <https://www.gnu.org/licenses/>. *
22
# **************************************************************************/
27
from PySide.QtCore import QT_TRANSLATE_NOOP
30
import FreeCADGui as Gui
31
from PySide import QtCore, QtGui, QtWidgets
34
from JointObject import TaskAssemblyCreateJoint
38
# translate = App.Qt.translate
40
__title__ = "Assembly Commands to Create Joints"
42
__url__ = "https://www.freecad.org"
45
def isCreateJointActive():
46
return UtilsAssembly.isAssemblyGrounded() and UtilsAssembly.assembly_has_at_least_n_parts(2)
49
def activateJoint(index):
50
if JointObject.activeTask:
51
JointObject.activeTask.reject()
53
panel = TaskAssemblyCreateJoint(index)
54
Gui.Control.showDialog(panel)
57
class CommandCreateJointFixed:
61
def GetResources(self):
64
"Pixmap": "Assembly_CreateJointFixed",
65
"MenuText": QT_TRANSLATE_NOOP(
66
"Assembly_CreateJointFixed",
67
"Create a Fixed Joint",
72
"Assembly_CreateJointFixed",
73
"1 - If an assembly is active : Create a joint permanently locking two parts together, preventing any movement or rotation.",
78
"Assembly_CreateJointFixed",
79
"2 - If a part is active : Position sub parts by matching selected coordinate systems. The second part selected will move.",
86
if UtilsAssembly.activePart:
87
return UtilsAssembly.assembly_has_at_least_n_parts(2)
89
return UtilsAssembly.isAssemblyGrounded() and UtilsAssembly.assembly_has_at_least_n_parts(2)
95
class CommandCreateJointRevolute:
99
def GetResources(self):
102
"Pixmap": "Assembly_CreateJointRevolute",
103
"MenuText": QT_TRANSLATE_NOOP("Assembly_CreateJointRevolute", "Create Revolute Joint"),
107
"Assembly_CreateJointRevolute",
108
"Create a Revolute Joint: Allows rotation around a single axis between selected parts.",
111
"CmdType": "ForEdit",
115
return isCreateJointActive()
121
class CommandCreateJointCylindrical:
125
def GetResources(self):
128
"Pixmap": "Assembly_CreateJointCylindrical",
129
"MenuText": QT_TRANSLATE_NOOP(
130
"Assembly_CreateJointCylindrical", "Create Cylindrical Joint"
135
"Assembly_CreateJointCylindrical",
136
"Create a Cylindrical Joint: Enables rotation along one axis while permitting movement along the same axis between assembled parts.",
139
"CmdType": "ForEdit",
143
return isCreateJointActive()
149
class CommandCreateJointSlider:
153
def GetResources(self):
156
"Pixmap": "Assembly_CreateJointSlider",
157
"MenuText": QT_TRANSLATE_NOOP("Assembly_CreateJointSlider", "Create Slider Joint"),
161
"Assembly_CreateJointSlider",
162
"Create a Slider Joint: Allows linear movement along a single axis but restricts rotation between selected parts.",
165
"CmdType": "ForEdit",
169
return isCreateJointActive()
175
class CommandCreateJointBall:
179
def GetResources(self):
182
"Pixmap": "Assembly_CreateJointBall",
183
"MenuText": QT_TRANSLATE_NOOP("Assembly_CreateJointBall", "Create Ball Joint"),
187
"Assembly_CreateJointBall",
188
"Create a Ball Joint: Connects parts at a point, allowing unrestricted movement as long as the connection points remain in contact.",
191
"CmdType": "ForEdit",
195
return isCreateJointActive()
201
class CommandCreateJointDistance:
205
def GetResources(self):
208
"Pixmap": "Assembly_CreateJointDistance",
209
"MenuText": QT_TRANSLATE_NOOP("Assembly_CreateJointDistance", "Create Distance Joint"),
213
"Assembly_CreateJointDistance",
214
"Create a Distance Joint: Fix the distance between the selected objects.",
217
"CmdType": "ForEdit",
222
return isCreateJointActive()
228
def createGroundedJoint(obj):
229
assembly = UtilsAssembly.activeAssembly()
233
joint_group = UtilsAssembly.getJointGroup(assembly)
235
obj.Label = obj.Label + " 🔒"
236
ground = joint_group.newObject("App::FeaturePython", "GroundedJoint")
237
JointObject.GroundedJoint(ground, obj)
238
JointObject.ViewProviderGroundedJoint(ground.ViewObject)
242
class CommandToggleGrounded:
246
def GetResources(self):
249
"Pixmap": "Assembly_ToggleGrounded",
250
"MenuText": QT_TRANSLATE_NOOP("Assembly_ToggleGrounded", "Toggle grounded"),
254
"Assembly_ToggleGrounded",
255
"Grounding a part permanently locks its position in the assembly, preventing any movement or rotation. You need at least one grounded part before starting to assemble.",
258
"CmdType": "ForEdit",
263
UtilsAssembly.isAssemblyCommandActive()
264
and UtilsAssembly.assembly_has_at_least_n_parts(1)
268
assembly = UtilsAssembly.activeAssembly()
272
joint_group = UtilsAssembly.getJointGroup(assembly)
274
selection = Gui.Selection.getSelectionEx("*", 0)
278
App.setActiveTransaction("Toggle grounded")
279
for sel in selection:
280
# If you select 2 solids (bodies for example) within an assembly.
281
# There'll be a single sel but 2 SubElementNames.
282
for sub in sel.SubElementNames:
283
# Only objects within the assembly.
284
objs_names, element_name = UtilsAssembly.getObjsNamesAndElement(sel.ObjectName, sub)
285
if assembly.Name not in objs_names:
288
full_element_name = UtilsAssembly.getFullElementName(sel.ObjectName, sub)
289
obj = UtilsAssembly.getObject(full_element_name)
290
part_containing_obj = UtilsAssembly.getContainingPart(full_element_name, obj)
292
# Check if part is grounded and if so delete the joint.
294
for joint in joint_group.Group:
296
hasattr(joint, "ObjectToGround")
297
and joint.ObjectToGround == part_containing_obj
299
# Remove grounded tag.
300
if part_containing_obj.Label.endswith(" 🔒"):
301
part_containing_obj.Label = part_containing_obj.Label[:-2]
302
doc = App.ActiveDocument
303
doc.removeObject(joint.Name)
310
# Create groundedJoint.
311
createGroundedJoint(part_containing_obj)
312
App.closeActiveTransaction()
316
Gui.addCommand("Assembly_ToggleGrounded", CommandToggleGrounded())
317
Gui.addCommand("Assembly_CreateJointFixed", CommandCreateJointFixed())
318
Gui.addCommand("Assembly_CreateJointRevolute", CommandCreateJointRevolute())
319
Gui.addCommand("Assembly_CreateJointCylindrical", CommandCreateJointCylindrical())
320
Gui.addCommand("Assembly_CreateJointSlider", CommandCreateJointSlider())
321
Gui.addCommand("Assembly_CreateJointBall", CommandCreateJointBall())
322
Gui.addCommand("Assembly_CreateJointDistance", CommandCreateJointDistance())