direction angle computation from surface normal(nx, ny, nz) - matlab

I'd like to calculate direction angle from suface normal vector it has nx, ny, nz.
I've got 3D point clouds normals using princomp() on Matlab.
Each point has a normal vector(nx, ny, nz).
How can I compute the direction angle of normal vector?
My final goal is to plot range image colors are mapped in directional angle of surface normal.

If I am not wrong, the direction angle means the angle of the surface normal w.r.t the surface. (Please clarify if I am interpreting it wrongly) In that case, you can take a point on the surface (or the point corresponding to the surface normal). Let the point be P (px, py, pz)'. The normal is N (nx, ny, nz)'
Let theta be the angle b/w the surface normal and the point.
cos(theta) = P.N / |P|.|N|
so in matlab,
cos_theta = P' * N / (norm(P) * norm(N))
theta = acosd(cos_theta)

Related

Calculating a spiral in MATLAB

We have these logarithmic spirals which are circling around the centre of the coordinate system:
x = ebθ cos(θ)
y = ebθ sin(θ)
where the ebθ is the distance between the point (which is on the spiral) and the centre; and the θ is the angle between the line connecting the point and the origin and the axis x.
Consider a spiral where the angle is θ ϵ <0,10π> and the parameter is b=0.1. By thickening points on the spirals (and the angle θ) calculate the circumference with the relative precision better than 1%. Draw the spiral!
I'm preparing for a (MATLAB) test and I'm stuck with this exercise. Please help, any hint is appreciated.
Start by computing a list of x,y for your range of theta and value of b. For more accurate results, have your theta increment in smaller steps (I chose 5000 arbitrarily). Then, its simply computing the distance for each pair of consecutive points and summing them up.
t = linspace(0,10*pi,5000);
b = 0.1;
x = exp(b*t).*cos(t);
y = exp(b*t).*sin(t);
result = sum(sqrt((x(2:end) - x(1:end-1)).^2 + (y(2:end)-y(1:end-1)).^2))

Approximating relative angle between two line segments on sphere surface

I am in need of an idea! I want to model the vascular network on the eye in 3D. I have made statistics on the branching behaviour in relation to vessel diameter, length etc. What I am stuck at right now is the visualization:
The eye is approximated as a sphere E with center in origo C = [0, 0, 0] and a radius r.
What I want to achieve is that based on the following input parameters, it should be able to draw a segment on the surface/perimeter of E:
Input:
Cartesian position of previous segment ending: P_0 = [x_0, y_0, z_0]
Segment length: L
Segment diameter: d
Desired angle relative to the previous segment: a (1)
Output:
Cartesian position of resulting segment ending: P_1 = [x_1, y_1, z_1]
What I do now, is the following:
From P_0, generate a sphere with radius L, representing all the points we could possibly draw to with the correct length. This set is called pool.
Limit pool to only include points with a distance to C between r*0.95 and r, so only the points around the perimeter of the eye are included.
Select only the point that would generate a relative angle (2) closest to the desired angle a.
The problem is, that whatever angle a I desire, is actually not what is measured by the dot product. Say I want an angle at 0 (i.e. that the new segment is following the same direction as the previous`, what I actually get is an angle around 30 degrees because of the curvature of the sphere. I guess what I want is more the 2D angle when looking from an angle orthogonal from the sphere to the branching point. Please take a look at the screenshots below for a visualization.
Any ideas?
(1) The reason for this is, that the child node with the greatest diameter is usually follows the path of the previous segment, whereas smaller child nodes tend to angle differently.
(2) Calculated by acos(dot(v1/norm(v1), v2/norm(v2)))
Screenshots explaining the problem:
Yellow line: previous segment
Red line: "new" segment to one of the points (not neccesarily the correct one)
Blue x'es: Pool (text=angle in radians)
I will restate the problem with my own notation:
Given two points P and Q on the surface of a sphere centered at C with radius r, find a new point T such that the angle of the turn from PQ to QT is A and the length of QT is L.
Because the segments are small in relation to the sphere, we will use a locally-planar approximation of the sphere at the pivot point Q. (If this isn't an okay assumption, you need to be more explicit in your question.)
You can then compute T as follows.
// First compute an aligned orthonormal basis {U,V,W}.
// - {U,V} should be a basis for the plane tangent at Q.
// - W should be normal to the plane tangent at Q.
// - U should be in the direction PQ in the plane tangent at Q
W = normalize(Q - C)
U = normalize(Q - P)
U = normalize(U - W * dotprod(W, U))
V = normalize(crossprod(W, U))
// Next compute the next point S in the plane tangent at Q.
// In a regular plane, the parametric equation of a unit circle
// centered at the origin is:
// f(A) = (cos A, sin A) = (1,0) cos A + (0,1) sin A
// We just do the same thing, but with the {U,V} basis instead
// of the standard basis {(1,0),(0,1)}.
S = Q + L * (U cos A + V sin A)
// Finally project S onto the sphere, obtaining the segment QT.
T = C + r * normalize(S - C)

Incorrect angle detected between two planes

I want to calculate the angle between 2 planes, Reference plane and Plane1. When I feed the X,Y,Z co-ordinates of pointCloud to the function plane_fit.m (by Kevin Mattheus Moerman), I get the coefficients:
reference_plane_coeff: [-0.13766204 -0.070385590 130.69409]
Plane1_coeff: [0.0044337390 -0.0013548643 95.890228]
Next, I find the intersection of both planes, separately on the XZ plane and get a line equation; ref_line_XZ and plane1_line_XZ respectively. For this, I make the second coefficient 0. (Is this right?)
Aref = reference_plane_coeff(1);
Cref = reference_plane_coeff(3);
ref_line_XZ = [Aref Cref];
Arun = Plane1_coeff(1);
Crun = Plane1_coeff(3);
plane1_line_XZ = [Arun Crun];
angle_XZ = acos( dot(ref_line_XZ,plane1_line_XZ ) / (norm(ref_line_XZ) * norm(plane1_line_XZ )) )
I get the angle_XZ value as 0.0012 rad. i.e. 0.0685 degrees
When I plot these planes on a graph and view it, the angle seems to be much more than 0.0012 degrees. I'm talking about the angle made by the two lines after intersection of both planes with the XZ plane.
What am I doing wrong?
Also, when I tried to find angle between its normals, using:
angle_beta_deg = acosd( dot(reference_plane_coeff,Plane1_coeff) / (norm(reference_plane_coeff) * norm(Plane1_coeff)) )
I got the angle as 0.0713.
On visual inspection of both planes' plots and manually calculating from the plot, angle_XZ should be around 9 degrees.
plane_fit.m (by Kevin Mattheus Moerman)

How to interpolate ECEF coordinates on an WGS84 ellipsoid

Is there a direct method (not involving converting the coordinates to lat/lon) to interpolate between 2 ECEF coordinates (xyz) in order for the interpolated point to be located on the WGS84 ellispoid. The original 2 points are computed from geodetic coordinates.
Interpolating on a sphere seem obvious but I can't seem to derive a solution for the ellipsoid.
Thank you in advance.
Let assume you got 2 points p0(x,y,z) and p1(x,y,z) and want to interpolate some p(t) where t=<0.0,1.0> between the two.
you can:
rescale your ellipsoid to sphere
simply like this:
const double mz=6378137.00000/6356752.31414; // [m] equatoreal/polar radius of Earth
p0.z*=mz;
p1.z*=mz;
now you got Cartesian coordinates refering to spherical Earth model.
interpolate
simple linear interpolation would do
p(t) = p0+(p1-p0)*t
but of coarse you also need to normalize to earth curvature so:
r0 = |p0|
r1 = |p1|
p(t) = p0+(p1-p0)*t
r(t) = r0+(r1-r0)*t
p(t)*=r/|p(t)|
where |p0| means length of vector p0.
rescale back to ellipsoid
by dividing with the same value
p(t).z/=mz
This is simple and cheap but the interpolated path will not have linear time scale.
Here C++ example:
void XYZ_interpolate(double *pt,double *p0,double *p1,double t)
{
const double mz=6378137.00000/6356752.31414;
const double _mz=6356752.31414/6378137.00000;
double p[3],r,r0,r1;
// compute spherical radiuses of input points
r0=sqrt((p0[0]*p0[0])+(p0[1]*p0[1])+(p0[2]*p0[2]*mz*mz));
r1=sqrt((p1[0]*p1[0])+(p1[1]*p1[1])+(p1[2]*p1[2]*mz*mz));
// linear interpolation
r = r0 +(r1 -r0 )*t;
p[0]= p0[0]+(p1[0]-p0[0])*t;
p[1]= p0[1]+(p1[1]-p0[1])*t;
p[2]=(p0[2]+(p1[2]-p0[2])*t)*mz;
// correct radius and rescale back
r/=sqrt((p[0]*p[0])+(p[1]*p[1])+(p[2]*p[2]));
pt[0]=p[0]*r;
pt[1]=p[1]*r;
pt[2]=p[2]*r*_mz;
}
And preview:
Yellow squares are the used p0,p1 Cartesian coordinates, the White curve is the interpolated path where t=<0.0,1.0> ...

Draw circle using latitude and longitude

I want to plot a latitude and longitude using matlab. Using that latitude and longitude as center of the circle, I want to plot a circle of radius 5 Nm.
r = 5/60;
nseg = 100;
x = 25.01;
y = 55.01;
theta = 0 : (2 * pi / nseg) : (2 * pi);
pline_x = r * cos(theta) + x;
pline_y = r * sin(theta) + y;
hold all
geoshow(pline_x, pline_y)
geoshow(x, y)
The circle does not look of what I expected.
Drawing a circle on earth is more complex that it looks like.
Drawing a line or a poly line is simple, because the vertices are defined.
Not so on circle.
a circle is defined by all points having the same distance from center (in meters! not in degrees!!!)
Unfortuantley lat and lon coordinates have not the same scale.
(The distance between two degrees of latidtude is always approx. 111.3 km, while for longitude this is only true at the equator. At the poles the distance between two longitudes approach zero. In Europe the factor is about 0.6. (cos(48deg))
There are two solution, the first is more universal, usefull for nearly all problems.
convert spherical coordinate (of circle center) to cartesian plane with unit = 1m, using a transformation (e.g equidistant transformation, also called equirectangular transf., this transformation works with the cos(centerLat) compensation factor)
calculate points (e.g circle points) in x,y plane using school mathematics.
transform all (x,y) points back to spherical (lat, lon) coordinates, using the inverse transformation of point 1.
Other solution
1. write a function which draws an ellipse in defined rectangle (all cartesian x,y)
2. define bounding of the circle to draw:
2a: calculate north-south diameter of circle/ in degrees: this a bit tricky: the distance is define in meters, you need a transformation to get the latitudeSpan: one degrees of lat is approx 111.3 km (eart circumence / 360.0): With this meters_per_degree value calc the N-S disatcne in degrees.
2b: calculate E-W span in degrees: now more tricky: calculate like 2a, but now divide by cos(centerLatitude) to compensate that E-W distances need more degrees when moving north to have the same meters.
Now draw ellipseInRectangle using N-S and E_W span for heigh and width.
But a circle on a sphere looks on the projected monitor display (or paper) only like a circle in the center of the projection. This shows:
Tissot's Error Ellipse