Modeling a line in 3D space using length and rotations rather than endpoints

3drotationsvector-spaces

I'm using a cad software which can only create cylinders at the origin based on diameter d and height h. For simplicity sake, in this situation we can think of the cylinder as just a line so we can treat it as a vector. The orientation can only be changed by rotating it using rotate([x,y,z]) which rotates a certain amount of degrees around each axis and can be translated using translate([x,y,z]). I'm trying to write a function that can take two 3D points p1 and p2 and automatically create a cylinder (line in our case) using d and h then rotate and translate it so that the endpoints rest on p1 and p2.

It is easy enough for me to find the h of the line using the Pythagorean theorem, but the rotations are giving me a hard time. I used a bit of vector math I learned in a class a while back to find the angles from the line to each axis using unit vectors:
$p1 = (x_1, y_1, z_1) $

$p2 = (x_2, y_2, z_2)$

$\vec{F} = ((x_2-x_1) \hat{i} +(y_2-y_1)\hat{j} + (z_2-z_1)\hat{k})$

$\vec{u_F} = \frac{\vec{F}}{||\vec{F}||} = (\cos{\alpha} \hat{i} + \cos{\beta} \hat{j} + \cos{ \gamma} \hat{k})$

$\alpha = \arccos{\frac{x_2-x_1}{||\vec{F}||}}, \beta = \arccos{\frac{y_2-y_1}{||\vec{F}||}}, \gamma = \arccos{\frac{z_2-z_1}{||\vec{F}||}}$

This information is not the whole answer unfortunately. The angle from the " "-axis is not the same as rotated around the " "-axis. In addition, the order of the rotation changes the final result. Where do I go from here?

Best Answer

Because OP is using OpenSCAD, the claims of the question aren't entirely correct: orientation can be changed not only by rotations about axes, but by other transformations as well.

I'm going to assume that the cylinder has been rotated/translated/whatever by the OP, who can surely do this, so that one end is at the origin, and the other end is at $(0,0,1)$. (Maybe OpenSCAD aligns it along $(0,1,0)$, but I don't think that adapting to this difference will present any challenges). I'm not going to write correct openSCAD code because I haven't used it in about a year, but I'll write something that should get you going in the right direction.

Here goes.

There's a rotate(v, a) function in openSCAD that rotates around a vector $v$ by angle $a$. We'll use that.

$$ \newcommand{zv}{\mathbf{\hat{z}}} \newcommand{yv}{\mathbf{\hat{y}}} \newcommand{sv}{\mathbf{{s}}} $$

Let's let zvec (in code) or $\zv$ (in math) mean $[0,0,1]$, the initial axis of the cylinder. Then

  1. Compute $v = Q - P$, the vector from the point $P$ to the point $Q$. If $v$ is zero, your input was bad (a length-zero cylinder makes no sense), and you'll have to handle this exceptional case.

  2. Compute $u = \frac{v}{ \| v\|}$. In openSCAD, you do something like v = (1/norm(v))*v, I believe. If $u$ turns out to be exactly $-\zv$, then this is another special case; because the cylinder is already aligned with the $z$-axis, all you need to do is rotate it about the $x$-axis (for instance) by 180 degrees to get it where we want; you can then skip to step 5.)

  3. Compute $w = \frac12 (\zv + u)$, or in code, w = (0.5) * (u + zvec), and then $t = \frac{w}{\|w\|}$, or t = (1/norm(w)) * w. At this point, $t$ is a unit vector in the plane of $v$ and $\zv$, and is exactly halfway between those two vectors.

  4. Rotate about $t$ by 180 degrees to take the cylinder, which used to point along the $\zv$ direction, and make it point along the $v$ direction instead.

  5. Scale everything by $\| v \| $, i.e., norm(v), which is the distance from $P$ to $Q$.

  6. Translate by $P$ to move the origin (one end of the cylinder) to $P$, and the other end (which is at the tip of $v = Q - P$) to the point $Q$.


All of that stuff works mathematically, but I should caution you that the special case in step 2 is problematic: testing equality of floating-point numbers is fraught with peril. If the vector $u$ turns out to be very near to $-\zv$, then the remaining computations may be numerically a bit unstable, and you really don't want that. On the other hand, if you're just making a model of your back porch to decide where to put the picnic table, this nasty case is unlikely to affect you, and you can stop reading.


To continue: to make this whole approach a little more numerically robust, I'll modify things from step 2 onwards: if the vector $u$ is too close to being $\pm \zv$, I'm going to rotate the cylinder to point along the $y$-axis first. I'll use $\yv$ to denote the vector $(0, 1, 0)$. Here's the revised version, math only. I leave it to you to translate to openSCAD.

  1. Compute $v = Q - P$, the vector from the point $P$ to the point $Q$. If $v$ is zero, your input was bad (a length-zero cylinder makes no sense), and you'll have to handle this exceptional case. In fact, if $\| v \|$ is smaller than, say, $10^{-5}$, you should probably treat this as an error case. (I can't recall whether openSCAD uses IEEE floats or IEEE doubles or something else to represent things, but using $10^{-5}$ as a threshold should keep you completely safe regardless. You could probably get away with using $10^{-10}$.)

  2. Compute $u = \frac{v}{ \| v\|}$. If the absolute value of the $z$-component of $u$ is no more than $0.8$, let $\sv = \zv$; if the absolute value of the $z$-component is more than $0.8$, then rotate the cylinder by $-90$ degrees about the $x$-axis, so that it now lies along the positive $y$ axis, and let $\sv = \yv$. In both cases, $\sv$ refers to "the starting orientation of the cylinder".

  3. Compute $w = \frac12 (\sv + u)$, and $t = \frac{w}{\|w\|}$, At this point, $t$ is a unit vector in the plane of $v$ and $\sv$, and is exactly halfway between those two vectors.

  4. Rotate about $t$ by 180 degrees to take the cylinder, which used to point along the $\sv$ direction, and make it point along the $v$ direction instead.

  5. Scale everything by $d = \| v \| $, the distance from $P$ to $Q$. The cylinder now lies along the $PQ$ direction, with one end at the origin, and the other end $d$ units from the origin, i.e., it has the same length and orientation as the line segment $\overline{PQ}$.

  6. Translate the cylinder by $P$ to move the origin (one end of the cylinder) to $P$, and the other end (which is at the tip of $v = Q - P$) to the point $Q$.


There's another method that involves constructing a $4 \times 4$ matrix directly, and using openSCAD's multmatrix operation, but it's probably a little messier to write and to understand, so unless you're unhappy with the solution above, I won't bother writing it out.