Community
Meshmixer
Welcome to Autodesk’s Meshmixer Forums. Share your knowledge, ask questions, and explore popular Meshmixer topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

MM API: Arguments for rotating and aligning

9 REPLIES 9
SOLVED
Reply
Message 1 of 10
info6DAXH
981 Views, 9 Replies

MM API: Arguments for rotating and aligning

Hi an sorry for my newbie questions:

 

I would like to rotate an object e.g. 90° on the x-Axis and then to align it on the groundPlane.

But its hard for me to know the right arguments for the set_toolparam(remote,"???", 90) for the transform method as well as for the align method. In some IDEs is an autocomplete feature, which makes it more easy if you don't have a API Reference. 

9 REPLIES 9
Message 2 of 10
MagWeb
in reply to: info6DAXH

Wasn't the documentation built in mm-api-master/distrib/documentation/python_html/index.html? (run it)

Anyhow: This documentation isn't complete. Look into StoredCommands.h instead (ToolStrings: line 155 - 230, Parameter strings: line 246 - 708).

 

vector3f is a 3-float list, matrix3f is a 9-float 3x3 row-major matrix

ToolString: "align" 

  • "sourceType" : integer (Source_ObjectBaseCenter = 1, Source_BoundingBoxCenter = 0, Source_SingleSurfacePoint = 10, Source_SurfaceSamples = 11, Source_SurfaceDisc = 12, Source_Pivot = 13, Source_LastToolFrame = 20)
  • "targetType" : integer (Target_WorldOrigin_Yup = 0, Target_WorldOrigin_Zup = 1, Target_WorldOrigin_Xup = 2, Target_GroundPlanePoint = 9, Target_SingleSurfacePoint = 10, Target_SurfaceSamples = 11, Target_SurfaceDisc = 12, Target_Pivot = 13)
  • "transformMode" : integer (Translate = 0, TranslateAndRotate = 1)
  • "flip" : boolean
  • "origin" : vector3f
  • "rotation" : matrix3f
  • "sourceFrame" : frame3f
  • "targetFrame" : frame3

ToolString: "transform"

  • "pivotFrameMode" : integer (WorldFrame = 0, LocalFrame = 1)
  • "origin" : vector3f
  • "originWorld" : vector3f
  • "translation" : vector3f
  • "translationWorld" : vector3f
  • "scale" : vector3f
  • "dimensions" : vector3f
  • "rotation" : matrix3f
  • "enableSnapping" : boolean
  • "absoluteSnapping" : boolean
  • "snapStepSize" : float
  • "initialFrameOrigin" : vector3f
  • "initialFrameOrientation" : matrix3f

Note whenever it comes to coordinates: Internally MM uses a normalized coordinate system built by the widest dimension of the first object added to a scene.  This widest dimension is set to 2 (ranging from -1 to 1) and the ratio of World to Scene coordinates is kept in memory. As this is a relative system (depending on the size of the first object) you need to convert Scene- to World coordinates if you query dimensions from the scene. Lets say you get 0.8 from MM (this is a Scene value) you need to run

value_in_world = mm.to_world(remote, 0.8)

Lets say this returns 230 world units (its physical size). Now I add something in World units and this gives 250. So I need to convert back:

value_in_scene = mm.to_scene(remote, 250)

(you can use mm.to_W(remote, 0.8), mm.to_S(remote, 250) instead. Same function)

In some tools (e.g. Transform) one can use World coordinates directly (e.g. setting "originWorld" and "translationWorld") while "origin" and "translation" require Scene units)



Gunter Weber
Triangle Artisan

Message 3 of 10
MagWeb
in reply to: MagWeb

Sorry: Typos in post above:

mm.to_W needs to be mm.toW

mm.to_S needs to be mm.toS

 



Gunter Weber
Triangle Artisan

Message 4 of 10
ebraden266K9
in reply to: info6DAXH

I'm curious why rotation is a 3x3 matrix. I've got the list working with translation, but I'm unsure what the other six values for rotation should be (and where in the matrix they should go).

Message 5 of 10
ebraden266K9
in reply to: info6DAXH

For anyone following along at home: I've revealed my lack of knowledge of mathematics. It seems the 3x3 matrix to describe rotation is a known thing (Rotation formalisms in three dimensions - Wikipedia). I just need to do the work to figure out a way to translate degrees to those matrices.

Message 6 of 10
ebraden266K9
in reply to: info6DAXH

Last reply, I promise. MagWeb, you're a hero. Also, I didn't realize I was necro'ing a thread from a year ago, not today. But I'm posting the completed code here for posterity.

 

import mmapi
from mmRemote import *;
import mm
import math

# initialize connection
remote = mmRemote();
remote.connect();

# construct commands to run

mm.begin_tool(remote, "transform")

#set parameters
#translation in mm
tX = float(input('Enter Translate X ')/25.4)
tY = float(input('Enter Translate Y ')/25.4)
tZ = float(input('Enter Translate Z ')/25.4)

#rotation in degrees
rX = float(input('Enter Rotate X '))
rY = float(input('Enter Rotate Y '))
rZ = float(input('Enter Rotate Z '))

#convert degrees to the transformation vector stuff
r1 = math.cos(rY ) * math.cos(rZ)
r2 = math.sin(rZ)
r3 = -math.sin(rY) * math.cos(rZ)
r4 = -math.cos(rY) * math.sin(rZ) * math.cos(rX) + math.sin(rY) * math.sin(rX)
r5 = math.cos(rZ) * math.cos(rX)
r6 = math.sin(rY) * math.sin(rZ) * math.cos(rX) + math.cos(rY) * math.sin(rX)
r7 = math.cos(rY) * math.sin(rZ) * math.sin(rX) + math.sin(rY) * math.cos(rX)
r8 = -math.cos(rZ) * math.sin(rX)
r9 = -math.sin(rY) * math.sin(rZ) * math.sin(rX) + math.cos(rY) * math.cos(rX)

tList = [tX, tY, tZ]
rMatrix = [r1,r2,r3,r4,r5,r6,r7,r8,r9]

mm.set_toolparam(remote,"translation",tList)
mm.set_toolparam(remote,"rotation",rMatrix)
mm.accept_tool(remote)
Message 7 of 10
ebraden266K9
in reply to: info6DAXH

The above code doesn't work because the expected input is in degrees, but the formulas require radians. So, I just added math.radians() to the angle input parts.

Message 8 of 10

Hello @MagWeb I am attempting to capture the origin of a part as a mmFrame for automatically aligning photogrammetry scans that have a flat ground area in them. By generating FaceGroups I can easily isolate the ground plane but I want to find a way to snap that ground plane to the world X-Z plane.

Is there a way to program faceTransform to get both Origin and Orientation for finding the center of a current face selection? I've noticed when you open faceTransform with the ground FaceGroup selected the transform slider is  perfectly aligned with the ground plane average and thus the mmFrame I want to use to snap to the world frame, so if I could capture that it would be ideal. My earlier method of trying to ray intersect the ground was not proving very effective because a single point might have a small rock or piece of small dirt that could move the normal off. I tried finding the face selection centroid too but that just seems to give a vector3f rather than a full mmFrame.

# initialize connection
remote = mmRemote()
remote.connect()

###################################################

#Generate Face Groups to seperate ground from object
mm.begin_tool(remote, "generateFaceGroups")
mm.set_toolparam(remote, "angleThreshold", 15 )
mm.set_toolparam(remote, "smallGroupThreshold", 10000 )
mm.accept_tool(remote)

#Select Facegroup representing Ground
cmd = mmapi.StoredCommands()
mm.select_all(remote)
if cmd.AppendSelectCommand_HasValidSelection:
    groups_list = mm.list_selected_groups(remote)
    ground = [0] * 1
    object = [0] * 1 
    ground[0] = groups_list[0]
    object[0] = groups_list[1]

mm.clear_face_selection(remote)
mm.select_facegroups(remote, ground)

#Find origin of selected plane to be properly aligned with world plane
mm.begin_tool(remote, "faceTransform")
mmframe = mm.get_toolparam(remote, "initialFrameOrigin")
mmframeRotate = [0] * 9
mmframeRotate = mm.get_toolparam(remote, "initialFrameOrientation")
print (mmframe)
print (mmframeRotate)
mm.accept_tool(remote)
mm.clear_face_selection(remote)

#Find a way to create an mmFrame out of the faceTransform origin and then put a pivot there to show it is successful
#cmd = mmapi.StoredCommands()
#cmd.AppendSceneCommand_CreatePivot( mmframe )
#remote.runCommand(cmd)

 The mmFrame so close and yet so far.PNG

Message 9 of 10

Your parameter queries should not return something. FaceTransform simply doesn't own the parameter 'InitialFrameOrigin' nor 'InitialFrameOrientation'. That's different to Transform on a  total object.

Here two ways to get a frame from a selection.

The first, which I prefer, is to fit a primitive plane to the selection. This averages the selection which might be useful if you deal with scanning errors.

The other is to find the closest point towards the selection's centroid and to generate the frame on that closest point. This might own some error though if the selection isn't that flat at this very point.

 

import mmapi
from mmRemote import *
import mm

# initialize connection
remote = mmRemote()
remote.connect()

###################################################

#Generate Face Groups to seperate ground from object
mm.begin_tool(remote, "generateFaceGroups")
mm.set_toolparam(remote, "angleThreshold", 15 )
mm.set_toolparam(remote, "smallGroupThreshold", 10000 )
mm.accept_tool(remote)

#Select Facegroup representing Ground
cmd = mmapi.StoredCommands()
mm.select_all(remote)
if cmd.AppendSelectCommand_HasValidSelection:
    groups_list = mm.list_selected_groups(remote)
    ground = [0] * 1
    object = [0] * 1 
    ground[0] = groups_list[0]
    object[0] = groups_list[1]

mm.clear_face_selection(remote)

# option1 using a temporary plane object fitting to the selection
mm.select_facegroups(remote, ground)
mm.begin_tool(remote, 'fitPrimitive')
mm.set_toolparam(remote, 'primitiveType', 0) #creates a square
mm.set_toolparam(remote, 'singlePrimitive', True) #creates a single square even if there are different selection islands
mm.set_toolparam(remote, 'createNewObjects', True) #creates a new object
mm.accept_tool(remote)
planeID = mm.find_object_by_name(remote, "Fit Primitive 1")
sel_frame = mm.get_object_frame(remote, planeID[1])
mm.delete_objects(remote, [planeID[1]])

print (sel_frame.origin)
print (sel_frame.x)
print (sel_frame.y)
print (sel_frame.z)

# option2 finding the nearest point on the surface towards the selection's centroid
mm.select_facegroups(remote, ground)
centroid = mm.get_face_selection_centroid(remote)
sel_frame = mm.find_nearest(remote, centroid)[1]

print (sel_frame.origin)
print (sel_frame.x)
print (sel_frame.y)
print (sel_frame.z)


remote.shutdown()

 



Gunter Weber
Triangle Artisan

Message 10 of 10

I tried Option 1 and it worked perfectly. This is better than the original idea because fitting a plane captures the average that I wanted. Thank you so much @MagWeb You're the best.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report