     POV-Ray : Newsgroups : povray.tools.general : vector direction turned into rotation angles : vector direction turned into rotation angles Server Time 8 Feb 2023 08:41:59 EST (-0500) vector direction turned into rotation angles   From: Kenneth Date: 4 Oct 2018 23:15:01 Message:    ```
{
"@context": "https://schema.org",
"@type": "DiscussionForumPosting",
"@id": "#web.5bb6d6ba23ef6b93a47873e10%40news.povray.org",
"headline": "vector direction turned into rotation angles",
"dateCreated": "2018-10-05T03:15:01+00:00",
"datePublished": "2018-10-05T03:15:01+00:00",
"author": {
"@type": "Person",
"name": "Kenneth"
}
}
Hey, I figured it out!  :-)

If you have a vector direction, or spatial point position, or a traced(...)
normal, this code will find the angles that it makes with the <0,0,0> origin.
In other words, it finds the actual rotation angles for a given <x,y,z> vector,
to use for whatever purpose-- like rotating an object to align to that vector.
Or for placing into a #debug statement.

There are already tools in POV-Ray that align objects FOR you-- like
Point_At_Trans and Reorient_Trans-- but they don't give you the actual rotation
angles that are used. That's why I wrote this.

I went back to basic trigonometry to figure it out... then had to do some
reading-up on the  asin  function  (arc-sin.) There are only a few 'special
rules' in the code, to take care of certain asin behavior.
And... no matrix math ;-)

For grins, I've included the visual trig diagrams-- the triangles. They helped
me to work out the code.

You might notice that the code produces rotations for only TWO axes, around x
and y; but that's enough for determining the proper orientation of an object.
The third rotation axis is 'synthesized' by the other two, in a manner of
speaking... and it has its own weird but understandable logic: Imagine that you
(as an 'object') are standing vertically, with your arms in front of you,
holding onto a vertical pole. As you tilt backwards, you will always still face
the pole, no matter how far you spin around it in y. Same logic here. (Of
course, you could pre-spin the object in y to face some other direction,
*before* applying the found rotations.)  I might work up some code to counteract
this-- so that an object always faces a set direction when it's rotated; but
that's icing on the cake for now.

In the code, there are two meaningful objects-- a BLACK rod, which is simply the
physical embodiment of the vector; and a WHITE rod as a 'real' object, made
later and rotated with the code's results. But you'll see only a *single* rod,
with speckles-- that's  both rods occupying the same space, to prove that the
code works. I used POV-ray's coincident-surface phenomenon as proof! ;-)  If you
mess with the code in some bad way, you'll probably see BOTH rods.

If you see a way to make the code simpler or more elegant, speak up. I
eventually want to turn it into a macro(?)

[You may have guessed that I plan to use this in my on-going 'position finder'
work, to solve a problem *there*.]

------------------
// October 3-4 2018

#version 3.7; // or 3.71 or 3.8
global_settings {assumed_gamma 1.0}
#default{finish{ambient .1 diffuse .9}}

#declare VECTOR = <-.9,.8,.6>; // or whatever values

//------------------------------
camera {
perspective
location  <.5, 1.5, -2.5>
look_at   <0, .3, 0>
right     x*image_width/image_height
angle 35
}

light_source {
0*x
color rgb .8
translate <200, 400, -200>
}

light_source {
0*x
color rgb .5
translate <-200, 100, -200>
}

background{rgb .1}

// origin indicators
union{
cylinder{<-.15,0,0>,<.15,0,0> .010}  // X
cylinder{<0,-.15,0>,<0,.15,0> .010}  // Y
cylinder{<0,0,-.15>,<0,0,.15> .010 } // Z
pigment{rgb <1,.6,0>}
}

plane{y,0
pigment{
cells color_map{[0 rgb .1][1 rgb .6]} translate 3
}
}

//-------------- THE CODE -----------
// Important 'fudge factors' due to POV-Ray's inner workings, to prevent
// fatal "uncategorized error" (because of vnormalize below?)--
#if(VECTOR.x = 0)
#declare VECTOR = <0.0001,VECTOR.y,VECTOR.z>;
#else
#end

#if(VECTOR.y = 0)
#declare VECTOR = <VECTOR.x,0.0001,VECTOR.z>;
#else
#end

#if(VECTOR.z = 0)
#declare VECTOR = <VECTOR.x,VECTOR.y,0.0001>;
#else
#end

#declare VECTOR = vnormalize(VECTOR);

// The visual stand-in for the VECTOR

// The trig construction lines...
// RED
union{
cylinder{VECTOR,<VECTOR.x,0,VECTOR.z> .007}
cylinder{0,<VECTOR.x,0,VECTOR.z> .007}
pigment{rgbt <1,0,0,.5>}
}

// GREEN
union{
cylinder{0,<VECTOR.x,0,0> .007}
cylinder{<VECTOR.x,0,0>, <VECTOR.x,0,VECTOR.z> .007}
pigment{rgbt <0,.2,0,.5>}
}

#declare X_Y_PLANE_ANGLE = 90 - degrees(asin(VECTOR.y/1));
// to get the degree of rotation, referenced to an imaginary
// 'rotated' X/Y plane-- think of it as the final 'elevation'.
// It's the arc-sin of y/R in that plane--
// R (the hypoteneuse length) is 1.0-- the VECTOR length

#declare NEW_HYPOT = sqrt(pow(VECTOR.x,2) + pow(VECTOR.z,2));
// The PROJECTION of the 'VECTOR' length onto the horizontal X/Z plane--
// its length *there* compared to its real length of 1.0. This is for use
// in the X_Z_PLANE_ANGLE equation below, as the 'R' for its trig function.
// sqrt(x^2 + y^2 = R^2) to find the length of this 'new'
// hypoteneuse-- with the Z value substituted for Y.

// NOTE: At this point, the NEW_HYPOT value is always positive-- which may or
// may not be a good thing(?). But it causes no problem.

// This #if is the KEY to getting things right...
#if(VECTOR.x < 0)
#declare X_Z_PLANE_ANGLE = degrees(asin(VECTOR.z/NEW_HYPOT));
// to get the degree of rotation, AS SEEN ON the horizontal X/Z plane--
// it will actually be the rotation around the y-axis, to be applied AFTER
// the previous x-axis 'elevation'. Arc-sin of y/R, as seen looking DOWN
// on X/Z plane. NEW_HYPOT is NOT 1.0 in this plane.
#else
#declare X_Z_PLANE_ANGLE = degrees(-asin(VECTOR.z/NEW_HYPOT)); // ditto
#end

// The special rules...
#if(VECTOR.x < 0)
#declare X_Z_PLANE_ANGLE = X_Z_PLANE_ANGLE - 90;
#else
#end

#if(VECTOR.x >= 0)
#declare X_Z_PLANE_ANGLE = X_Z_PLANE_ANGLE + 90;
#else
#end

#declare FINAL_ROT =
<X_Y_PLANE_ANGLE,X_Z_PLANE_ANGLE,0>; // the final rotations!

// --------- END OF CODE ------------

// NEW rod, using the found rotation values
union{
cylinder{0,1.0*y .015}
text{ttf "cyrvetic.ttf" "POV-Ray" 1, 0
pigment{
color_map{
[.5 rgb .6*x]
[.5 rgb .2*z]
}
scale 1.1
translate -.05*z
}
scale .5*<.2,.2,.04>
translate <-.2,1,-.02>
}

// rotate ... *y // optional, to add any desired pre-spin to the object

#if(VECTOR.y < 0) rotate 180*y #else #end // optional, and only if
// VECTOR.y goes negative--  to keep the visual orientation of the
// object consistent with positive VECTOR.y appearance.
rotate FINAL_ROT
pigment{rgb 2}    