Rotation Transformation via Two Vectors

This a bit of a geometric problem that I can probably solve in a clunky manner but I figured I would put it out on the forum to see if anyone else has encountered the same problem has a more simple and elegant solution than the one I have come up with.

I have an initial vector, lets just say [1,0,0] for simplicity. I now have another vector [5,3,2]. Both vectors are at the origin. I want my initial vector to be aligned with the second vector, what would be the single rotational transformation to get me there.

Since a rotation transformation is given by a point, vector and angle. We would need to solve for the vector and the angle since we can assume the point is at the origin.

We could use the angle_between method to compute the angle between vector 1 and vector 2

We could then use the cross method to computer the perpendicular vector for the rotation.

This seems like the easiest way to come up with the solution but I might be missing something here.

That sounds about right. Have you tried it? :slight_smile:

1 Like

I have but its not giving me exactly the results expected.

Are the vectors normalized?

1 Like

It didn’t do what I needed it to do. I will post up my alternate solution shortly.

angle_between can only measure angles between 0 and 180 degrees; it can’t detect what direction the rotation is going.

With something like this you can get an angle in a plane (defined by the cross product as normal).

# Counter-clockwise angle from vector2 to vector1, as seen from normal.
def planar_angle(vector1, vector2, normal = Z_AXIS)
  Math.atan2((vector2 * vector1) % normal, vector1 % vector2)
end
1 Like

Just remember that if you use the cross product of the two given vectors to find the normal to the common plane, you must then normalize the result (which resizes it to a unit vector) before using it as normal in the method given by @eneroth3

Try this instead to always get the true planar angle:

def planar_angle(vector1, vector2)
  normal = (vector1 * vector2).normalize
  Math.atan2((vector2 * vector1) % normal, vector1 % vector2)
end

However, calculating (and normalizing) the cross product externally (and prior) to planar_angle yourself would be more efficient since you will also be using that cross product in additional methods after calling @eneroth3’s version.

It seems to me that, if you have 2 vectors v1 and v2, the following transformation will bring v1 aligned with v2.

t = Geom::Transformation.rotation ORIGIN, v1*v2, v1.angle_between(v2)

This means that

  • (t * v1).samedirection?(v2) will be true
  • (t.inverse * v2).samedirection?(v1) will be true too

I’m not entirely sure why but the solution Fredo has shown is what I initially ended up with but ultimately it did not work. Instead I ended up doing this:

@vecdir = @pts[1] - @pts[0]

vecdir3 = @vecdir.clone
zvalue = vecdir3.z
vecdir3.z = 0.0
		
if zvalue < 0.0
	phi = (0 - asin((vecdir3.length)/(@vecdir.length)) + 90.degrees) * -1
else
	phi = 0 - asin((vecdir3.length)/(@vecdir.length)) + 90.degrees
end

theta = atan2(vecdir3.y, vecdir3.x)
		
tr1 = Geom::Transformation.rotation(ORIGIN, [0, 1, 0], -phi)
tr2 = Geom::Transformation.rotation(ORIGIN, [0, 0, 1], theta)

tot_trans = tr2 * tr1

@medeek I am not sure what the goal is here, but when I use your code with inputs of [1,0,0] and [5,3,2], then apply the resulting tot_trans to [1,0,0], I get a result of [0.742781, 0.8557086, 0.371391] which is neither the second vector, not any multiple of it (i.e. it does not lie along [5,3,2])

The following method however works for any two input vectors (with credit to @eneroth3 for her planar_angle method which I incorporated):

def find_rot_trans (vec1, vec2)
  norm = (vec1 * vec2).normalize
  rot_angle = Math.atan2((vec1 * vec2) % norm, vec1 % vec2)
  tr = Geom::Transformation.rotation(ORIGIN, norm, rot_angle)
  return tr
end

A test for this method is:

def test_rotation (vec1, vec2)
  tr = find_rot_trans vec1, vec2    # Find rotation transformation
  vec3 = vec1.transform tr          # Test by applying to 'vec1'
  if vec3.samedirection? vec2
    puts "This works!"       # Resulting transformed vector is parallel to original 'vec2'
  else
    puts "This did NOT work"
  end
end

There is a possibility I misinterpreted your problem, in which case it would be great if you express it as a test method as I have done.

1 Like