# vertical angle between vectors

I'm trying to find out the horizontal and vertical angle between two vectors to determine if an object is withing the field of view of a person by comparing these angles to the maximum horizontal and vertical angles that a human can see.

If human is at position $p_h$, the direction he is facing is given by $a$ and an object is at position $p_o$, then $p_o-p_h$ gives us a vector $b$ from $p_h$ to $p_o$.

To know if the object is within the hortizontal field of view, I set $a_y$ and $b_y$ to 0 and calculate the angle between the vectors by $$\theta = cos^{-1}\frac{a\cdot b}{|a|\cdot|b|}$$

and then comparing the result to the visual field limits.

However to get the vertical angle, it seems I can't just set $a_x$ and $b_x$ to 0 as it is apparently not equivalent to setting $a_z$ and $b_z$ to 0.

I thought it should be equivalent, but after seeing some visualizations I'm convinced they are not.

I realize this should be relatively trivial, but I just can't wrap my head around it and I've been stuck on this for the last few hours. I've also looked for this in google as I thought it would be a rather common problem, but I can't find exactly what I'm looking for.

How do I get the vertical angle between these two vectors?

Here are a few graphs:

The sample vectors could be $a = (4,3,1)$ and $b= (2,7,9)$

In this site z is up, so please don't get confused.

First here is the 3D plot

Then we can project it to a top down view (equivalent to ignoring the y or in this case z component and look at the angle $\theta$). This way we get the horizontal angle between the vectors

If we then try to do the same for the vertical angle, we look at the side and front views and we can see that $\beta \neq \gamma$

• Then how are you able to set $a_y$ and $b_y$ to zero for horizontal field of view? It would be nice if you can include a sketch! – jonsno Sep 27 '17 at 16:58
• @samjoe I added a few plots, hopefully this makes it clearer – Girauder Sep 27 '17 at 20:44
• How exactly do you define the field of view here? Are its bounds the maximum “up“/“down” and “left”/“right” angles from the view direction $a$? If so, how do you define “up” relative to this vector? – amd Sep 28 '17 at 1:03
• @amd The field of view is indeed defined as that: 80° to either side of $a$ is the horizontal view angle.45° up and 60° down are vertical bounds. I'm not sure if I understand your question very well, but maybe this helps: if we have a plane with a normal vector orthogonal to $a$ and the "up" axis, then up relative to this vector is what lies above vector $a$ in this plane. – Girauder Sep 28 '17 at 18:58
• How about this: are the top and bottom edges of the visible region parallel to the $x$-$y$ plane, i.e., the camera has pitch and yaw, but no roll? – amd Sep 28 '17 at 19:22

You can’t really measure the “vertical” angle between the vectors via orthogonal projection because that distorts the angle. The underlying reason that projecting onto the $x$-$y$ plane (in the second coordinate system in the question) works for the horizontal angles but in general there’s no orthogonal projection that will get the vertical angles right is that all of the yaw rotations use the same rotation axis, but pitch rotations don’t.

You can, however, project the vectors/points onto the unit sphere, i.e., convert to spherical coordinates: $$\theta=\arccos{z\over\sqrt{x^2+y^2+z^2}} \\ \varphi = \arctan\frac yx.$$ (We don’t care about $\rho$, the distance from the origin, here.) You need to select the correct quadrant for the arc tangent, but math packages have a built-in variant of this function that takes care of this for you. Note, too, that $\theta$ increases downwards from the $z$-axis. Since there’s no camera roll, the horizontal visual angular separation is just the difference between the azimuths $\varphi$ and the vertical visual angular separation the difference in inclination $\theta$.

Using your example of $a=(4,3,1)$ and $b=(2,7,9)$, we have $\varphi_a=\arctan\frac34\approx36.870°$ and $\varphi_b=\arctan\frac72\approx74.055°$, for a difference of approximately $37.185°$, well within the field of vision. This matches the angle obtained from the projections onto the $x$-$y$ plane. It’s probably less computationally expensive to use the latter method since you can compare directly against $\cos 80°$ instead of computing an arc cosine, but this really only works because the visual field is horizontally symmetric. If you had different angles to the left and right, using the arc tangent is better since cosine doesn’t distinguish between different directions.

For the vertical angles, we get $\theta_a=\arctan 5\approx78.690°$ and $\theta_b=\arctan{\sqrt{53}\over9}\approx38.969°$ for a difference of $39.721°$, also within the field of view.

It’s also possible to compare the vertical angles by rotating $b$ so that it lies in the same vertical plane as $a$, but that seems like more work than just converting to spherical coordinates.

If you have to check a lot of points for visibility, computing all of those inverse trigonometric functions could get expensive. The field of view is bounded by four planes through the origin, so an alternative approach is to compute the equations $ax+by+cz=0$ ($\mathbf n\cdot\mathbf x=0$ in vector form) of these planes that make up the sides of this pyramid. The sign of dot product of the normal to the plane $\mathbf n$ with an arbitrary point tells you on which side of the plane the point lies: if positive, then the point lies in the direction of $\mathbf n$ from the plane; if negative, it’s on the opposite side; if zero, the point, of course, lies on the plane. You can arrange for all of the normals to be inward-pointing, so that a point is visible iff all four of these test values are non-negative.

• Thanks! I do need to check lots of points so I find your idea with the pyramid especially useful! Do you additionally have an idea on how to compute these planes efficiently? I would make four copies of the gaze direction vector and rotate them vertically and horizontally by the max bounds to make the four "corners" of the pyramid. – Girauder Oct 4 '17 at 11:50
• @Girauder If the visual field is specified in terms of visual angles, I think using spherical coordinates is the most straightforward way to go. Add/subtract angles from the line of sight and convert back to Cartesian to get direction vectors of the edges. The four plane normals are their pairwise cross products. Take care with the order of operands so that all of the normals end up inward- or outward-pointing. You could rotate the view vector directly, but that seems more expensive since you have to find the correct rotation axis. – amd Oct 4 '17 at 17:57

Indeed, setting $a_x=b_x=0$ (looking down the x-axis) is not the same as setting $a_z=b_z=0$ (). The "vertical angle between two vectors" is not unambiguously defined in an obvious way.

If you project the vectors onto a vertical plane to compare the angles, then the angle between the projections in that plane will depend on the choice of your plane, i.e. it will depend on the line you are looking down.

For example let $\mathbf{a}= (1,1,1)$ and $\mathbf{b}=(1,-1,1)$. If you look down the x-axis, the angle between these two is a right angle, but if you look down the line $x+y=0$, the angle between them is zero.

Consider the plane P that contains the two vectors $\mathbf{a}$ and $\mathbf{b}$. The angle between the vectors in that plane is well-defined. That is what we get by using between two 3-d vectors the cosine formula you have written down.

You can choose to make your own definition for "vertical angle". For example, let $\theta_a$ be the angle between the vector $\mathbf{a}$ and the xy plane. Let $\theta_b$ be the angle between the the vector $\mathbf{b}$ and the xy plane. You may choose to define the vertical angle as $|\theta_a-\theta_b|$. In the example I gave, this angle is zero.

A case where it is unambiguous is when the plane P is perpendicular to the xy plane. In that case, $|\theta_a-\theta_b|$ is the same as the result from the cosine formula. But please note that in general, that is still going to be different from what you get by looking down the x axis (setting $a_x=b_x=0$) or the y axis.