Hi.
I´m having some trouble with making joints parametric. Let me explain what I want to achieve. So far the workbench allows the user model pretty much any mechanism using rigid bodies. For this, I am implementing the joints MBDyn has to offer as Scripted objects for FreeCAD. This is the class to create objects for a "revolute pin" joint.
Code: Select all
import FreeCAD,FreeCADGui
import Draft
from sympy import Point3D, Line3D
class Revolutepin:
def __init__(self, obj, label, node, reference1, reference2):
#Get the center of mass or the center of the two references
try:
x2 = FreeCAD.Units.Quantity(reference1.Curve.Center[0],FreeCAD.Units.Unit('mm'))
y2 = FreeCAD.Units.Quantity(reference1.Curve.Center[1],FreeCAD.Units.Unit('mm'))
z2 = FreeCAD.Units.Quantity(reference1.Curve.Center[2],FreeCAD.Units.Unit('mm'))
except:
x2 = FreeCAD.Units.Quantity(reference1.CenterOfMass[0],FreeCAD.Units.Unit('mm'))
y2 = FreeCAD.Units.Quantity(reference1.CenterOfMass[1],FreeCAD.Units.Unit('mm'))
z2 = FreeCAD.Units.Quantity(reference1.CenterOfMass[2],FreeCAD.Units.Unit('mm'))
try:
x3 = FreeCAD.Units.Quantity(reference2.Curve.Center[0],FreeCAD.Units.Unit('mm'))
y3 = FreeCAD.Units.Quantity(reference2.Curve.Center[1],FreeCAD.Units.Unit('mm'))
z3 = FreeCAD.Units.Quantity(reference2.Curve.Center[2],FreeCAD.Units.Unit('mm'))
except:
x3 = FreeCAD.Units.Quantity(reference2.CenterOfMass[0],FreeCAD.Units.Unit('mm'))
y3 = FreeCAD.Units.Quantity(reference2.CenterOfMass[1],FreeCAD.Units.Unit('mm'))
z3 = FreeCAD.Units.Quantity(reference2.CenterOfMass[2],FreeCAD.Units.Unit('mm'))
#Calculate the joint´s absolute position:
x = (x2+x3)/2
y = (y2+y3)/2
z = (z2+z3)/2
#Calculate the joint possition relative to it's node (relative offset):
x1 = x-node.absolute_position_X
y1 = y-node.absolute_position_Y
z1 = z-node.absolute_position_Z
obj.addExtension("App::GroupExtensionPython")
#Create scripted object:
obj.addProperty("App::PropertyString","label","Revolute pin","label",1).label = label
obj.addProperty("App::PropertyString","node_label","Revolute pin","node_label",1).node_label = node.label
obj.addProperty("App::PropertyString","joint","Revolute pin","joint",1).joint = 'revolute pin'
obj.addProperty("App::PropertyString","plugin_variables","Revolute pin","plugin_variables",1).plugin_variables = "none"
#pin possition relative to it's node:
obj.addProperty("App::PropertyDistance","relative_offset_X","Relative offset","relative_offset_X",1).relative_offset_X = x1
obj.addProperty("App::PropertyDistance","relative_offset_Y","Relative offset","relative_offset_Y",1).relative_offset_Y = y1
obj.addProperty("App::PropertyDistance","relative_offset_Z","Relative offset","relative_offset_Z",1).relative_offset_Z = z1
#Absolute pin position:
obj.addProperty("App::PropertyDistance","absolute_pin_position_X","Absolute pin position","absolute_pin_position_X",1).absolute_pin_position_X = x
obj.addProperty("App::PropertyDistance","absolute_pin_position_Y","Absolute pin position","absolute_pin_position_Y",1).absolute_pin_position_Y = y
obj.addProperty("App::PropertyDistance","absolute_pin_position_Z","Absolute pin position","absolute_pin_position_Z",1).absolute_pin_position_Z = z
#Animation parameters:
obj.addProperty("App::PropertyEnumeration","animate","Animation","animate")
obj.animate=['false','true']
obj.addProperty("App::PropertyEnumeration","frame","Animation","frame")
obj.frame=['global','local']
obj.addProperty("App::PropertyFloat","force vector multiplier","Animation","force vector multiplier").force_vector_multiplier = 1.0
obj.Proxy = self
#Add joint´s rotation axis. This axis determines the "absolute_pin_orientation_matrix":
p1 = FreeCAD.Vector(x2, y2, z2)
p2 = FreeCAD.Vector(x3, y3, z3)
#Create the rotation axis:
l = Draft.makeLine(p1, p2)
l.Label = 'z: joint: '+ label
l.ViewObject.LineColor = (0.00,0.00,1.00)
l.ViewObject.PointColor = (0.00,0.00,1.00)
l.ViewObject.DrawStyle = u"Dashed"
l.ViewObject.LineWidth = 1.00
l.ViewObject.PointSize = 1.00
#Add the vector to visualize reaction forces
Llength = FreeCAD.Units.Quantity(FreeCAD.ActiveDocument.getObjectsByLabel("X")[0].End[0]/4,FreeCAD.Units.Unit('mm'))
p1 = FreeCAD.Vector(x, y, z)
p2 = FreeCAD.Vector(x+Llength, y+Llength, z+Llength)
d = Draft.makeLine(p1, p2)
d.ViewObject.LineColor = (1.00,0.00,0.00)
d.ViewObject.PointColor = (1.00,0.00,0.00)
d.ViewObject.LineWidth = 1.00
d.ViewObject.PointSize = 1.00
d.ViewObject.EndArrow = True
d.ViewObject.ArrowType = u"Arrow"
d.ViewObject.ArrowSize = str(Llength/75)#+' mm'
d.Label = "jf: "+ label
#absolute orientation:
obj.addProperty("App::PropertyString","absolute_pin_orientation_matrix","Orientation","absolute_pin_orientation_matrix",1).absolute_pin_orientation_matrix = ""#"3, " + str(l1.direction[0]) + ", "+ str(l1.direction[1]) + ", " + str(l1.direction[2]) + ", " +"2, guess"
#relative orientation:
obj.addProperty("App::PropertyString","relative_orientation_matrix","Orientation","relative_orientation_matrix",1).relative_orientation_matrix = ""#"3, " + str(l1.direction[0]) +", "+ str(l1.direction[1]) + ", " + str(l1.direction[2]) + ", " +"2, guess"
def execute(self, fp):
#precission = int(FreeCAD.ActiveDocument.getObjectsByLabel('MBDyn')[0].precision)#Max number of decimal places
##############################################################################Calculate the new absolute orientation:
ZZ = FreeCAD.ActiveDocument.getObjectsByLabel("z: joint: "+fp.label)[0]#get the joint´s rotation axis
#Two 3D points that define the joint´s line:
p1, p2 = Point3D(ZZ.Start[0], ZZ.Start[1], ZZ.Start[2]), Point3D(ZZ.End[0], ZZ.End[1], ZZ.End[2])
l1 = Line3D(p1, p2)#Line that defines the joint
magnitude = (l1.direction[0]**2+l1.direction[1]**2+l1.direction[2]**2)**0.5#Calculate the vector´s magnitude
#generate the orientation matrix:
fp.absolute_pin_orientation_matrix = "3, "+ str(l1.direction[0]/magnitude) +", "+ str(l1.direction[1]/magnitude) + ", " + str(l1.direction[2]/magnitude) + ", " +"2, guess"
##############################################################################Recalculate the offset, in case the node was moved:
#get and update the absolute pin position:
x = FreeCAD.Units.Quantity((ZZ.Start[0] + ZZ.End[0])/2,FreeCAD.Units.Unit('mm'))
y = FreeCAD.Units.Quantity((ZZ.Start[1] + ZZ.End[1])/2,FreeCAD.Units.Unit('mm'))
z = FreeCAD.Units.Quantity((ZZ.Start[2] + ZZ.End[2])/2,FreeCAD.Units.Unit('mm'))
fp.absolute_pin_position_X = x
fp.absolute_pin_position_Y = y
fp.absolute_pin_position_Z = z
#get the node:
node = FreeCAD.ActiveDocument.getObjectsByLabel("structural: "+fp.node_label)[0]
#Re-calculate the joint possition relative to it's node (relative offset)
x1 = x - node.absolute_position_X
y1 = y - node.absolute_position_Y
z1 = z - node.absolute_position_Z
#Update the offset:
fp.relative_offset_X = x1
fp.relative_offset_Y = y1
fp.relative_offset_Z = z1
##############################################################################Calculate the new relative orientation:
zz = FreeCAD.ActiveDocument.getObjectsByLabel("z: joint: "+fp.label)[0]
p11, p22 = Point3D(zz.Start[0], zz.Start[1], zz.Start[2]), Point3D(zz.End[0], zz.End[1], zz.End[2])
l11 = Line3D(p11, p22)
zzmagnitude1 = (l11.direction[0]**2+l11.direction[1]**2+l11.direction[2]**2)**0.5
#generate the relative orientation matrix:
#fp.relative_orientation_matrix = "3, "+ str(round(l1.direction[0]/zzmagnitude,precission)) +", "+ str(round(l1.direction[1]/zzmagnitude,precission)) + ", " + str(round(l1.direction[2]/zzmagnitude,precission)) + ", " +"2, "+ str(round(l2.direction[0]/yymagnitude,precission)) +", "+ str(round(l2.direction[1]/yymagnitude,precission)) + ", " + str(round(l2.direction[2]/yymagnitude,precission))
fp.relative_orientation_matrix = "3, "+ str(l11.direction[0]/zzmagnitude1) +", "+ str(l11.direction[1]/zzmagnitude1) + ", " + str(l11.direction[2]/zzmagnitude1) + ", " +"2, guess"#+ str(round(l2.direction[0]/yymagnitude,precission)) +", "+ str(round(l2.direction[1]/yymagnitude,precission)) + ", " + str(round(l2.direction[2]/yymagnitude,precission))
FreeCAD.Console.PrintMessage("REVOLUTE PIN JOINT: " +fp.label+" successful recomputation...\n")
The main idea is quite simple. The constructor gets two references (reference1 and reference2), which are shapes the user selects in the GUI. The line passing through the center of mass of these two references determines the joint´s position and orientation. The position of the joint is the center point of this line, and the orientation of the joint is the same as the orientation of the line. (This is how other programs, such as Adams and Ansys motion, define joints too...)
So far it works perfectly, but the problem is that it i not parametric, so that if the CAD model is changed, the joint does not change accordingly, and the user is forced to delete and create the joint again. I am working now on making the joints parametric, and so I need to store, as a property of the scripted object, the two shapes provided as references. I have tried:
But the thing is that this seems to store a copy of the reference shape, and not a link to the reference. So that this does not help me to make the joint parametric.
I have tried using App::PropertyLinkSubList, as someone suggested, but I do not quite understand how this works and I have not been able to find any complete example either...
Any ideas will be very welcome...