Calculating a rotation with two parameters?

Have some feature requests, feedback, cool stuff to share, or want to know where FreeCAD is going? This is the place.
Forum rules
Be nice to others! Read the FreeCAD code of conduct!
freedman
Veteran
Posts: 3441
Joined: Thu Mar 22, 2018 3:02 am
Location: Washington State, USA

Re: Calculating a rotation with two parameters?

Post by freedman »

Ed, I haven't tried to run your code yet but my issues are normally based around the GUI and control. I create a spinbox and then need to give the user control of something simple enough to use. That's why I like this:

Code: Select all

box_obj.Placement.Matrix.rotateX(math.radians(1))
It's one line of code that corresponds directly with a editable field angle. In my case building a rotation, to keep it simple, two fields, essentially lat. and Long. control of a rotation.

I also have to say it's one line of code I don't have to think about when I look at it, after a week of or so I forgot the rotation stuff. :)

Here is a video of the control that I have over each of the coil Bodies as I build the armature.
Thanks all
Attachments
motor_vid1.gif
motor_vid1.gif (931.53 KiB) Viewed 1148 times
edwilliams16
Veteran
Posts: 3107
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Calculating a rotation with two parameters?

Post by edwilliams16 »

@freedman

What happens if you adjust both azimuth and inclination in your model? Does it matter in which order they were adjusted?

The most obvious difficulty with the rotateX, rotateY, rotateZ API I see is generating rotations about axes other than the principal ones. You probably have to use attachments to line up the principal axes with the ones you need. Nothing wrong with that - I'm just pointing out where you could run into a road block.
freedman
Veteran
Posts: 3441
Joined: Thu Mar 22, 2018 3:02 am
Location: Washington State, USA

Re: Calculating a rotation with two parameters?

Post by freedman »

Video; the order doesn't matter, I think it can be seen.

And yes I am applying this to a sketch placement. I take the current sketch placement and make a copy, then apply all my placement changes to the copy, so there can be multiple. Once all the changes are cranked in I copy the new Placement back to the sketch. This gives really good performance visually.

Lots of control for a couple inputs.

Thanks
Attachments
Rot1.gif
Rot1.gif (471.5 KiB) Viewed 1073 times
edwilliams16
Veteran
Posts: 3107
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Calculating a rotation with two parameters?

Post by edwilliams16 »

@freedman
So you are adjusting the parameters of a App.Placement(yaw, pitch, roll) not directly manipulating the object the rotateX. rotateY, rotateZ API?
In that case the order is built-in to the constructor.

BTW, the best way to animate a rotation is using the slerp() method:https://wiki.freecadweb.org/Sandbox:Edw ... polation.) or sclerp() for a placement change. It provides a simple interpolation scheme between two rotations or placements respectively. You just vary a parameter from 0 to 1 to go from one to the other. Much simpler than calculating the increment by hand.
freedman
Veteran
Posts: 3441
Joined: Thu Mar 22, 2018 3:02 am
Location: Washington State, USA

Re: Calculating a rotation with two parameters?

Post by freedman »

Here is the core code of the assembler: If you feed it a Sketch and a Body it will connect the origins and do the rotations shown in the videos. Ed, your the expert on modifing placements, if you see any way to simplify this I am interested. The Conx_ stuff is supporting added properties.

I have 1200 lines of macro code to control everything and it's all based on these few lines ....

Code: Select all

def go_ASM(self,my_sketch,my_body):
    my_body.Placement.Base.x = 0
    my_body.Placement.Base.y = 0
    try:
        self.sk_att = my_sketch.AttachmentOffset.copy()
    except: return   
    self.sk_plc = my_sketch.Placement.copy()

    my_sketch.Placement.Matrix.rotateX(math.radians(my_body.Conx_XAxisRot))
    my_sketch.Placement.Matrix.rotateZ(math.radians(my_body.Conx_ZAxisRot))

    delta_global = App.Placement (App.Vector(0,0,my_body.Conx_Zoffset),App.Rotation(my_body.Conx_RotZ,0,0),App.Vector(0,0,0))  
    my_sketch.Placement = my_sketch.Placement.multiply(delta_global) # use the changed copy placement to build new Placement                      
    rot_orig = FreeCAD.ActiveDocument.getObject(my_sketch.Name).getGlobalPlacement()    # build rotation with placement copy
    my_sketch.AttachmentOffset = self.sk_att  # copy back in the attachment offset
    my_sketch.Placement = self.sk_plc   # copy back in the placement
    prt_rot = my_body.getGlobalPlacement().multiply(my_body.Placement.inverse()) 
    my_body.Placement = prt_rot.inverse().multiply(rot_orig)     
lamikr
Posts: 14
Joined: Fri Jul 08, 2022 11:44 pm

Re: Calculating a rotation with two parameters?

Post by lamikr »

edwilliams16 wrote: Tue Jan 31, 2023 4:34 am @freedman
So you are adjusting the parameters of a App.Placement(yaw, pitch, roll) not directly manipulating the object the rotateX. rotateY, rotateZ API?
In that case the order is built-in to the constructor.
In general if you have for example a cubic and rotate it to different directions, then the order in which you rotate x, y, z matters to the end result in which position the cubic ends up being. So I was wondering how is this possible, but your explanation of that the order is already baked in to constructor could explain why it works.

Another thing that usually needs testing is what happens when rotating close to 90 or degree rotations. In some api's it easy to make weird rotation in corner cases. https://wang-yimu.com/gimbal-lock/
edwilliams16
Veteran
Posts: 3107
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Calculating a rotation with two parameters?

Post by edwilliams16 »

edwilliams16 wrote: Tue Jan 31, 2023 4:34 am BTW, the best way to animate a rotation is using the slerp() method:https://wiki.freecadweb.org/Sandbox:Edw ... polation.) or sclerp() for a placement change. It provides a simple interpolation scheme between two rotations or placements respectively. You just vary a parameter from 0 to 1 to go from one to the other. Much simpler than calculating the increment by hand.
Here's sclerp in action:

Code: Select all

import Part, time

doc = App.ActiveDocument
Gui.ActiveDocument.ActiveView.setAxisCross(True)

# Add a colored box
box_obj = doc.addObject('Part::Box', 'Box')
box_obj.recompute()
blue, green, red = (0.,0.,1.,0.), (0.,0.4,0.,0.), (1.,0.,0.,0.)
box_obj.ViewObject.DiffuseColor = [blue, (0.,0.,0.,1.),green ,(0.,0.,1.,1.),red ,(0.,0.,1.,1.)]

#reposition Box xyz if you like, -5 is default Box center
box_pl_start = App.Placement(App.Vector(-5.0,-5.0,-5.0), App.Rotation())
box_obj.Placement = box_pl_start
box_pl_end = App.Placement(App.Vector(25.0,25.0,25.0), App.Rotation(App.Vector(0, 0, 1), 45))
box_obj.recompute()

link = doc.addObject('App::Link','Link')
link.setLink(doc.Box)
link.Label = 'FinalBox'
link.Placement = box_pl_end

nsteps = 100
secs = 5
for i in range(nsteps+1):
    inc = i/nsteps
    time.sleep(secs/nsteps)
    box_obj.Placement = box_pl_start.sclerp(box_pl_end, inc)
    App.Gui.updateGui()

doc.recompute()

slerpdemo.gif
slerpdemo.gif (70.59 KiB) Viewed 864 times
edwilliams16
Veteran
Posts: 3107
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Calculating a rotation with two parameters?

Post by edwilliams16 »

freedman wrote: Tue Jan 31, 2023 6:17 am Here is the core code of the assembler: If you feed it a Sketch and a Body it will connect the origins and do the rotations shown in the videos. Ed, your the expert on modifing placements, if you see any way to simplify this I am interested. The Conx_ stuff is supporting added properties.
I couldn't disentangle your code in a reasonable amount of time. However, I did notice you used getGlobalPlacement(). This can be problematical if you have linked copies of the same sketch as in my demo file below.
In this situation, getGlobalPlacement() will always return the global placement of the original sketch - which is

Code: Select all

Placement [Pos=(0,-10,2.22045e-15), Yaw-Pitch-Roll=(1.3494e-14,-45,90)]
(select a sketch, send to the Python Console with ctrl-shift-P and run obj.getGlobalPlacement() )

while that of the linked copy is actually

Code: Select all

Placement [Pos=(7.07107,-7.07107,25), Yaw-Pitch-Roll=(45,-45,90)]
This may not be an issue depending on your usage of links.
Attachments
linkedsketches.FCStd
(7.23 KiB) Downloaded 15 times
dtay
Posts: 20
Joined: Fri Jan 17, 2020 12:52 pm

Re: Calculating a rotation with two parameters?

Post by dtay »

Hello Ed and Freedman,

It's great to see that the matrix works well for Freedman's needs. I concur with Ed that the matrix has its limitations and the Placement API is more suitable for complex rotations.

Regarding the sclerp code, it's certainly an interesting topic. However, it seems to have a quirk when crossing 180 degrees as it takes the shortest distance and rotates in the opposite direction.

I may have gotten carried away playing with/learning the Draft workbench in this code:

Code: Select all

import Part, time, Draft

# Set final rotation angle - CCW<180  CW>180
finAng = 180

# Set dim text size
txSize = 2

doc = App.ActiveDocument
fDoc= FreeCADGui.ActiveDocument

try:
    if fDoc.Box:
        doc.removeObject('Box')
        doc.removeObject('Link')
        doc.removeObject('Dimension')
        doc.removeObject('Dimension001')
        doc.removeObject('Dimension002')
except: pass

# Add box
box_obj = doc.addObject('Part::Box', 'Box')
box_obj.recompute()
# Color box - RGB
red, green, blue = (1.,0.,0.,0.), (0.,1.,0.,0.), (0.,0.,1.,0.)
box_obj.ViewObject.DiffuseColor = [red, (0.,0.,0.,1.),green ,(0.,0.,0.,1.),blue ,(0.,0.,0.,1.)]

#reposition Box, -5 is default Box center
box_pl_start = App.Placement(App.Vector(20,10.0,-25.0), App.Rotation())
box_obj.Placement = box_pl_start
box_pl_end = App.Placement(App.Vector(-20.0,-10.0,25.0), App.Rotation(App.Vector(0, 0, 1), finAng))

# Add XYZ dimensions
xDim = Draft.make_linear_dimension(App.Vector(0,0,0),App.Vector(0,0,0))
yDim = Draft.make_linear_dimension(App.Vector(0,0,0),App.Vector(0,0,0))
zDim = Draft.make_linear_dimension(App.Vector(0,0,0),App.Vector(0,0,0))

# set dim normals - best viewed from ISO
xDim.Normal, yDim.Normal, zDim.Normal = (0,0,1), (0,0,1), (1,0,0)

# Format dimensions
xD = fDoc.Dimension
yD = fDoc.Dimension001
zD = fDoc.Dimension002

dRed, dGreen, dBlue = (1.,0.,0.), (0.,1.,0.), (0.,0.,1.)
xD.ArrowType, xD.LineColor, xD.TextColor, xD.FontSize = u"Arrow", dRed, dRed, str(txSize)
yD.ArrowType, yD.LineColor, yD.TextColor, yD.FontSize = u"Arrow", dGreen, dGreen, str(txSize)
zD.ArrowType, zD.LineColor, zD.TextColor, zD.FontSize = u"Arrow", dBlue, dBlue, str(txSize)

#use Box placement to update dim lengths via expression
xDim.setExpression('.End.x', u'Box.Placement.Base.x')
yDim.setExpression('.End.y', u'Box.Placement.Base.y')
zDim.setExpression('.End.z', u'Box.Placement.Base.z')

def updateDims():
    #Flip dimension text on +/- side of coord 
    xD.FlipText = doc.Box.Placement.Base.x < 0
    yD.FlipText = doc.Box.Placement.Base.y < 0
    zD.FlipText = doc.Box.Placement.Base.z < 0
    
    #use Box placement to update dim line positions
    yDim.Dimline = (doc.Box.Placement.Base.x,0,0)
    zDim.Dimline = (doc.Box.Placement.Base.x, doc.Box.Placement.Base.y,0)
    Gui.Selection.clearSelection()

link = doc.addObject('App::Link','Link')
link.setLink(doc.Box)
link.Label = 'FinalBox'
link.Placement = box_pl_end

nsteps = 100
secs = 5
for i in range(nsteps+1):
    inc = i/nsteps
    time.sleep(secs/nsteps)
    box_obj.Placement = box_pl_start.sclerp(box_pl_end, inc)
    updateDims()
    App.Gui.updateGui()
    doc.recompute()
edit: code fix
Last edited by dtay on Wed Feb 01, 2023 12:36 pm, edited 1 time in total.
edwilliams16
Veteran
Posts: 3107
Joined: Thu Sep 24, 2020 10:31 pm
Location: Hawaii
Contact:

Re: Calculating a rotation with two parameters?

Post by edwilliams16 »

dtay wrote: Wed Feb 01, 2023 2:06 am
Regarding the sclerp code, it's certainly an interesting topic. However, it seems to have a quirk when crossing 180 degrees as it takes the shortest distance and rotates in the opposite direction.
Yes, slerp/sclerp will take shortest route. You'd need to break it up into segments to turn more than 180 degrees. Probably simpler to interpolate angle in an Axis/Angle representation in that case. Of course, the rotation axis becomes undefined at exactly 180 degrees.

EDIT: BTW, I think your script needs a

Code: Select all

from FreeCAD import Vector as av
Post Reply