Notifications
Clear all

[Closed] Convert vector orientation to rotation offset

Hello, I’ve been scouring the forums for information related to this problem, as I’m just now beginning to learn Maxscript beyond the scope of simple macroscripting.

   Here's my approach so far
    pos = PolyOp.getfacecenter $Test_A 1
           dir = PolyOp.getFacenormal $Test_A 1
           
           p_test = point()
           p_test.dir = dir
           p_test.pos = pos
       
           newOO = $Test_A.objectTransform * (inverse p_test.Transform) 
           
           $Test_A.objectoffsetpos = newOO.translationpart 
           $Test_A.objectoffsetrot = newOO.rotationpart
           $Test_A.objectoffsetscale = newOO.scalepart
           
           $Test_A.transform = p_test.transform
           
           delete p_test
           
           pb_test = point()
           pb_test.pos = pos
           $Test_A.dir = pb_test.dir
           delete pb_test
   Imagine that $Test_A is a simple 3 edged polygon with a pivot that is aligned to world space. The plane defined by the 3 points, however, is not parallel to the world. The goal is to rotate the object so that this plane is parallel to the world, while maintaining it's orientation when viewed from above. Essentially, taking an object that's askew and flattening it out. 
   
   The code so far is primarily based off of things I've dug up through researching the topic, and I almost have what I want to do working. First I align the object's pivot to the normal of it's own face, thus modifying the pivot while maintaining the relative orientation of the object. I then use .dir property of the object to orient the poly plane to be parallel to world space, using a fresh dummy object's .dir. The problem is than in doing so, it rotates the object in all 3 axes, when I don't want the object rotated around the z axis. In other words, I want the plane to be rotated to be parallel to the ground from a top down view, without rotating around said view. It sounds like it should be easy, and I'm sure I'm missing some obvious stuff here. 
   
   Given how complicated rotations and vectors are, I'm still trying to understand exactly what is going on.

FWIW, the application of this code is trying to script a way to level out objects generated from 3d scanners, which sometimes are generated askew, but can’t be automatically corrected due to complex shapes.

   I hope what I'm saying makes sense, and thanks for your time.
12 Replies
  I am not sure to understand what you trying to achieve, but in the case of a triangle it wouldn't be too complex to do.
  
  Let's say that we have a rotated equilateral triangle, so when viewing from top it doesn't look like an equilateral triangle anymore. If we wanted to make it point straight to the world Z axis, while keeping the shape we view from top, then it can't be done by simply rotating it. Instead we would need to modify their vertices positions to make them lie on the same plane.
  
  To accomplish this, one way could be to get each of the vertices positions, set their Z component to the same value and reassign the new position to the vertex.
  
  The following code will flatten the selected Editable Poly to Z 0: 
(
      	for j = 1 to $.numverts do
      	(
      		v = polyop.getvert $ j
      		v.z = 0
      		polyop.setvert $ j v
      	)
      )

Thanks for the response. I really didn’t explain it properly, sorry. I’m not the most mathematically minded person, and tend to understand things better with metaphors, if possible. Scratch the way I described it before.

Imagine you’re holding a flat object parallel with the ground. Now imagine that you rotate it a bit along it’s x and y axis, but don’t rotate it around the z axis. If you were holding a big flat compass, north would always point north, and so on, until you started rotating x or y over 90 degrees. My situation is basically the opposite of this. I’ve been given a object that is askew in x and y, but I know that it’s pointed in the right direction along the z axis, north is north, and I’m trying to restore it back to a flat, parallel to the ground, orientation. What complicates this is that the pivot of the object is aligned perfectly to the world. However, with my current script, north doesn’t point north anymore, so to speak after I execute it. While flat, something is happening that throws off the orientation around the z axis, but I’m having a hard time understanding what’s happening.

So I have complex geometry that is askew to the eye, but not mathematically. The idea here is to pick 3 points that you know should be level with each other from the incoming geometry, generate a poly plane from them, and then use the normal vector’s relative offset in orientation from the world to know how much you should rotate the preceding geometry to level it out, to the eye.

Again, sorry if that doesn’t make much sense

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

mathematically it’s simple as ABC. it’s exactly as ABC :). but you have to specify these three points. actually six: three source and three corresponding (destination) points.
do you have any idea how the ‘ideal’ user interface might look like?

So, you don’t have a skewed object, but a rotated object and you want it to be aligned with the world?

If so, perhaps this tool can help you aligning your object. It will also reset the pivot to be aligned with the world after the object is rotated.

You need to select on or more faces that should point up, one or more that should point to the right and then just align.

In case you select more than one face, the average normal is used.

Is something like this what you need to do?

Thanks for the replies folks. I actually figured out a way to solve my problem. I’ll post it here in the event that anyone else ever needs the same thing. It’s really crude, but it does exactly what I need it to! Since this is part of a larger overall set of tools, exact naming is being used, but you could obviously change them out.

$Balancers* are 3 points that have been intersected in the z axis with the target geometry which is $Default in this script (this happens outside of this script). Their positions define a single editable_poly polygon plane with 3 vertices. The surface normal of this polygon is used to create enough dummy objects to find the rotational offset from worldspace by using some normalized vectors (this technique is spelled out in the Maxscript help). Finally, once these angles are acquired, I needed to know if the $Default object should be rotated negatively or positively, so I run the angles through a few filters.

            Pos1 = $Balancer001.pos
              Pos2 = $Balancer002.pos
              Pos3 = $Balancer003.pos
              
              Test_A = Plane lengthsegs:1 widthsegs:1
              ConvertTopoly Test_A
              delete Test_A.verts[1]
              Polyop.setvertselection Test_A #all
              V_A = (Polyop.getvertselection Test_A as array)
              Polyop.createpolygon Test_A V_A
              Polyop.flipnormals Test_A 1
              
              Test_A.verts[1].pos = Pos1
              Test_A.verts[2].pos = Pos2
              Test_A.verts[3].pos = Pos3
              
              pos = PolyOp.getfacecenter Test_A 1
              Norm_V = PolyOp.getFacenormal Test_A 1

              p_origin = point() 
              p_origin.pos = pos
              
              P_Origin_Up = point()
              P_Origin_Up.pos = pos
              move P_Origin_Up [0,0,10]
              
              P_Vector_Up = point()
              P_Vector_Up.pos = pos
              P_Vector_Up.dir = Norm_V
              in coordsys local move P_Vector_Up [0,0,10]
                  
              P_Vector_X = point()
              P_Vector_X.pos = P_Vector_Up.pos
              P_Vector_X.position.x = P_Origin_Up.position.x
              
              P_Vector_Y = point()
              P_Vector_Y.pos = P_Vector_Up.pos
              P_Vector_Y.position.y = P_Origin_Up.position.y
              
              V_1 = P_Origin_Up.pos - P_Origin.pos
              V_2 = P_Vector_X.pos - P_Origin.pos
              V_3 = P_Vector_Y.pos - P_Origin.pos
              
              Angle_X = acos (dot (normalize V_1) (normalize V_2))
              Angle_Y = acos (dot (normalize V_1) (normalize V_3))
                  
              If (P_Origin_Up.pos.y < 0) and (P_Vector_X.pos.y < 0) then
              (
                       if P_Origin_Up.pos.y < P_Vector_X.pos.y then
                       (
                           Angle_X = (-(Angle_X))
                       )
              )
              
              If (P_Origin_Up.pos.y < 0) and (P_Vector_X.pos.y > 0) then
              (
                      Angle_X = (-(Angle_X))
              )
              
              If (P_Origin_Up.pos.y > 0) and (P_Vector_X.pos.y > 0) then
                  (
                      if P_Origin_Up.pos.y < P_Vector_X.pos.y then
                      (
                          Angle_X = (-(Angle_X))
                      )
              )                
              
              
              If (P_Origin_Up.pos.x < 0) and (P_Vector_Y.pos.x < 0) then
                   (
                       if P_Origin_Up.pos.x < P_Vector_Y.pos.x then
                       (
                           Angle_Y = (-(Angle_Y))
                       )
              )
              
              If (P_Origin_Up.pos.x < 0) and (P_Vector_Y.pos.x > 0) then
                   (
                      Angle_Y = (-(Angle_Y))
                  )
              
              If (P_Origin_Up.pos.x > 0) and (P_Vector_Y.pos.x > 0) then
 (
 if P_Origin_Up.pos.x < P_Vector_Y.pos.x then
                          (
       Angle_Y = (-(Angle_Y))
                           )
 )                        
              
              rotx = angleaxis Angle_X X_axis
              roty = angleaxis Angle_Y Y_axis
              
              in coordsys world rotate $Default rotx
              in coordsys world rotate $Default roty
      
              delete p_origin
              delete p_Origin_Up
              delete P_Vector_Up
              delete P_Vector_X
              delete P_Vector_Y
              
              delete Test_A
              
              delete $Balancer001
              delete $Balancer002
              delete $Balancer003

you might not believe but it can be done it five lines of code. (i will try to find time and max tomorrow and show a solution)

Maybe you can find a working code in this thread. Denis has shown some useful functions.

I wish I could understand what your script does, but I don’t.

I’ve tried to reproduce the procedure you described, but after running the script I don’t see any alignment.

The best I could get sometimes is the node aligned to the Z axis. But there is no other alignment.

For aligning to a second axis you would need to rather specify the designation positions of the vertices (as Denis mentioned), or specify a second vector, which might be easier.

Here is a test scene I’ve created to reproduce your description:

(
	 delete objects
	 
	 node = converttopoly (box name:"Default" wirecolor:red)
	 
	 rotate node (angleaxis (random 10 60) [1,0,0])
	 rotate node (angleaxis (random 10 60) [0,1,0])
		 
	 resetxform node
	 converttopoly node
	 
	 point pos:(polyop.getvert node 8) name:"Balancer001" wirecolor:green
	 point pos:(polyop.getvert node 7) name:"Balancer002" wirecolor:green
	 point pos:(polyop.getvert node 5) name:"Balancer003" wirecolor:green
	 
)

Please try the following function and see if it gives you any good results:

(
  	fn RotateObject node p1 p2 p3 flip:false =
  	(
  		normal = normalize (cross (p1-p2) (p1-p3))
  		if flip do normal *= -1
  		tm = xformMat (matrixfromnormal normal) (matrix3 1)
  		node.transform = inverse tm
  	)
  	
  	RotateObject $Default $Balancer001.pos $Balancer002.pos $Balancer003.pos
  )

my method is almost the same that Jorge showed above…

here is a test scene:

delete objects
tm0 = translate (angleaxis 90 (normalize (random -[1,1,1] [1,1,1])) as matrix3) (random -[20,20,20] [20,20,20])
tm1 = translate (angleaxis 90 (normalize (random -[1,1,1] [1,1,1])) as matrix3) (random -[20,20,20] [20,20,20])

p0 = point axistripod:on wirecolor:yellow transform:tm0
p1 = point axistripod:on wirecolor:green transform:tm1
t = teapot radius:10 wirecolor:orange transform:tm1
	
resetxform t
converttomesh t 

the task is to transform teapot object the way as if point #2 was aligned to point #1

(
	xtm = inverse p1.transform * p0.transform
	t.objectoffsetpos = (xformmat xtm t.transform).pos
	t.objectoffsetrot = xtm.rotation
)

as i said above the only problem is to specify the right transform for point #2

1 Reply
(@polytools3d)
Joined: 11 months ago

Posts: 0

The biggest problem is not the math but how the user will interact with the tool.
In the tool I linked, I am working with objects, so the user can select faces to define the vectors.

In case of a cloud point, it is very different. Selectin 6 points to define the Up and Right vectors doesn’t seems like a good solution. In this case I would just do it in two steps, first align to Z, and then align to X.

Once the point could is correctly aligned to the Z, then it is much easier to navigate it to find 3 vertices for the Right vector.

Thanks so much for the help everyone! Jorge, that function works perfectly, and does exactly what I was trying to do. It’s much, much more elegant than what I came up with, lol. I’ve never tackled this kind of stuff before, and feel as though I’m slowly making so progress.

FWIW, the problem, I realized with my code is that it wasn’t properly flipping the Y rotation to rotate the Default object negatively, in your example scene. It levels out like it should after that correction. That being said, your function is so much simpler!

I’m trying to unpack the way you both solved the problem so that I learn things here. Again, thanks for taking the time to enlighten me.

1 Reply
(@polytools3d)
Joined: 11 months ago

Posts: 0

I am glad I could understand the problem and that the script is working.

Additionally, if you know that the Z axis (the normal defined by the 3 points) will never be negative, the function can be modified to avoid the “flip” parameter, by just getting the angle between the normal (from the 3 points) and [0,0,1].