Calculating XYZ Euler rotation values for arbitrary plane

Hi, all.

Was trying to approach this problem, no luck so far.

I have an arbitrary plane, or an arbitrary shape, consisting of several faces, but let’s presume it’s lying on a plane, or “almost lying on a plane”, and I have the 4 vertices coordinates of the plane.

Now I need to translate and rotate another object so it “lands” on a plane. Or in other words I need to calculate rotation XYZ values of the plane, so I can translate the object to the center of the plane, and then apply the same rotation values the plane has.

Thanks in advance,
Denis.

To align them you should just need to copy the ‘rotation_euler’ parameters.


objToAlign.location = Vector( (1,0,0) )  # This Vector should be the computed 'center' of your plane.
objToAlign.rotation_euler = plane.rotation_euler

Problem is, the rotation_euler of the plane is (0,0,0). I need to align an object with a set of faces, that are more or less on the same plane. I can build a plane that has all the faces on it, but nor plane neither faces have rotation.

Ah, I thought you had a rotated plane already. I guess you are dynamically computing a plane from some subset of faces in a mesh and you want to align an object to that plane?

One technique would be to

  1. Compute the normal vector of your plane. This is the vector that you want to align your object with.
  2. Compute the axis-angle rotation between your object’s ‘up’ vector and the plane normal vector.
  3. Convert the axis-angle representation into a Euler angles.

This page explains how to compute the normal for a plane given 3 points that lie in the plane (Step 1):

This page explains computing the axis-angle and Euler angles conversion (Step 2 and 3):

Exactly!

I’ll try this, thanks!

It works to align two objects on 2 angles, but 3-rd angle, which is the rotation around the normal, doesn’t seem to work that well. I guess it has something to do with z coordinate of axis angle representation always being 0.

Not sure what I said makes sense, so I’ll add a screenshot.

As you see the plane and the mountain are still slightly misaligned.

Off the top of my head, I don’t think there is a reason why you should have difficulties with the Z-axis. Have you tried with some simple test cases like aligning a cube to the plane so you can more easily see where computations are going wrong?

Still misaligned for some reason.

http://i.imgur.com/9SeJeI8.png

My calculations were the following:

Plane’s normal
>>> norm = D.objects[3].data.polygons[0].normal

Cube’s up vector
up = Vector((0,0,1))

Cross between up and vec
vec = np.cross(up,norm) -> array([-0.65319091, -0.16190439, 0. ])

Rotation Angle
angle = acos(np.dot(up,norm)) -> 0.7381994907343972

Then apply angle and vec to cube in Axis Angle as WXYZ and move the cube to plane’s center of mass.

As I said, for some reason the Z coord of cross product is always 0, which perhaps causing the problem?

I’ll check tonight but directions transform differently than locations. I have lots of matrix math and alignment code examples so I will try to post some stripped doWn version. I prefer matrix > quaternion > eules when dealing with rotations. However I learned them in opposite order.

Figures this can be fixed by rotating the second plane around local Z axis. I suppose I need to find an angle between 2 vectors on both planes, for instance vectors from center of plane to one of the vertices. Then I just apply rotation around second plane’s local Z and that’s it.

But then again, since I’m a total math noob, there might be a more elegant way of doing it :slight_smile:

You are right that simply finding the angle between two z vecs does not describe a unique rotational transform…eg, there are infinite orientations rotated around the z axis. So you need more info. Luckily, building a rotation matrix when you know x,y,z orthogonal directions is easy. check back around 7pm, I’ll be at a conputer. Or you may be able to Google between now and then.

There’s another weird behavior here though. I wrote an operator, that uses wavefront(obj) importer to load an object, then it moves the object and applies rotation via object.rotation_axis_angle property. For some reason in the properties pane the rotation is (0,0,1,0) even though when I fetch the values via console, the values are the ones I’ve applied. Also the rotation is not actually applied. I tried calling object.transform_apply, but it still didn’t help.

Thanks, will do!

OK, so if you know your final x,y,z orthogonal (normalized) vectors, making the rotation matrix is simple. The vectors are just the colums of the rotation matrix. There are proofs and discussions of this on the internet.


#make a rotation matrix
rmx = Matrix.Identity(4)
rmx[0][0], rmx[0][1], rmx[0][2] = X[0], X[1], X[2]
rmx[1][0], rmx[1][1], rmx[1][2] = Y[0], Y[1], Y[2]
rmx[2][0], rmx[2][1], rmx[2][2] = Z[0], Z[1], Z[2]

The weird behavior you are experience I think is based on how Blender updates it’s data. I think information generally flows form matrix_world down to the other properties like, the rotation_euler and rotation_quaternion properties.

The other thing is that the “rotation_mode” may change how information is updated. Eg if rotation_mode == ‘XYZ’ then you may need to edit the euler rotation and that will update the quaternion. I’m not 100% sure on the updating issue but those are my best guesses.

Also, you may want to read this for future transformation of directions and normals.

First let’s assume that verts a,b,c,d form a rectangle just to simplify stuff at the beginning. We have something like this:

d----c
|      |
|      |
a----b

For aligning object with this plane we need at least 2 axes (vectors). Normal vector is not enough. You experienced this already when trying to “rotate” object such that its z axis matches the normal of your plane.
I said we need 2 axes… Well… We need all 3 axes, but when 2 of them are specified - the third one can easily be computed as cross product of the other two.

So say a,b,c,d are coordinates of the plane and this plane IS a rectangle, so all angles between edges of it are right angles.
For rotation matrix we need normalized vectors.
Having coords of verts we can define x and y as for example:
x is vector ab
y is vector ad
We need them normalized, so:

x = (b - a).normalized()
y = (d - a).normalized()

Then we can compute z that should be equal to normal of the plane. To be sure that it’s not flipped we can calculate this as cross product of x and y, so we have:

x = (b - a).normalized()
y = (d - a).normalized()
z = x.cross(y)

In case of z we don’t even have to normalize it, because cross product of two perpendicular unit vectors will always result in unit vector perpendicular to both of them. If however you don’t trust me on that, you can do: z = x.cross(y).normalized()

Now x,y,z can be used as columns of 3x3 rotation matrix.

x = (b - a).normalized()
y = (d - a).normalized()
z = x.cross(y)
mat3x3 = Matrix().to_3x3()
mat3x3.col[0] = x
mat3x3.col[1] = y
mat3x3.col[2] = z

Now we need to apply rotation defined by such matrix onto our object.
First I’d store original scale of our object in a variable, because applying rotation matrix on our object will reset the scale. So the final code would look like this:


import bpy
from mathutils import *  # to be able to perform vector math
# a, b, c, d are defined as coords of the verts of rectangle like this:
# d----c
# |      |
# |      |
# a----b
#
ob = bpy.data.objects['name_of_your_object']
scale = ob.scale.copy()
# Compute the middle of abcd rectangle to be used as new location of ob
loc = 0.25 * (a + b + c + d)
x = (b - a).normalized()
y = (d - a).normalized()
z = x.cross(y)
mat3x3 = Matrix().to_3x3()
mat3x3.col[0] = x
mat3x3.col[1] = y
mat3x3.col[2] = z
mat = mat3x3.to_4x4()
ob.matrix_world = mat
ob.location = loc
ob.scale = scale

Please mark that this will work ONLY when a,b,c,d form a proper rectangle. If not - we’re not lost, there are ways to handle this, but I’ll not dive into it now.
What do you think about such solution? I don’t know if this is the only way, maybe there are simpler solutions.

So basically what you’re saying is that I need to align local XYZ of the plane and object?

In my particular case, I don’t have the vertices of the plane, I have a selection of faces, which I can treat as lying on a plane. As you see from screenshot above, I have kind of a surface, consisting of hexagons, and I’m trying to align terrain objects to selection of hexagons. But I guess I can build those 3 vectors in any case. Depending on the amount and configuration of selection of the faces, I’ll probably be able to build those 3 vectors.

Thanks, I’ll give it a try!

Ohhhh…you have a collection of planes and points. So, what you may want to do is PCA (principle component analysis) to derive your X,Y,Z directions. Luckily NumPy has some linear algebra modules with it.

You may also want to do a “plane fit” or a 3 point registration or even an iterative registration.

Thanks, guys! The solution with 3 orthogonal angles works and it’s pretty short and simple, I’ve tested it for arbitrary plane and the alignment is perfect. I’ll also look into PCA and plane fit algorithms, which will also be very useful to determine the minimal plane my faces selection fits into.

Thanks everyone for sharing the knowledge.

Basically yes. The point however is that there is no such thing as “local XYZ” of the plane, so I just proposed some way to determine XYZ coords.
I managed to figure out that you are trying to find solution to some more complex problem.
I thought however that you found the solution to the issue of “converting” your selection to kind of “plane representation” as in your first post you said:

That’s why I didn’t focus on finding the “plane” you were talking about, but on how to Align object with arbitrary plane.
That was my understanding of the problem you had. I thought that “arbitrary plane” has already been determined.
Now when we all know where we are, we can try to figure out the real solution.
I will definitely try to tackle this and when I come up with something useful I’ll let you know.

@patmo141: PCA is something I have no knowledge about whatsoever, so thank you for introducing it, I’ll try to get some knowledge about it.
I will also take a closer look at NumPy, as it probably gives more possibilities than I thought. Let me admit that I didn’t yet use it.

Here are a few of my Blender projects which use NumPy, PCA, SVD etc.

This code has a PCA example in one of the minimum bounding box algorithms

This code uses Singuar Value Decomposition

both of these implementations has been adapted from other sources, so while I did write these, it stands soundly on the work of others.