Intersection of ray and ellipsoid - c++

Yes, I know that it is a popular problem. But I found nowhere the full clear implementing code without using OpenGL classes or a lot of headers files.
Okay, the math solution is to transfer ellipsoid to sphere. Then find intersections dots (if they exist of course) and make inverse transformation. Because affine transformation respect intersection.
But I have difficulties when trying to implement this.
I tried something for sphere but it is completely incorrect.
double CountDelta(Point X, Point Y, Sphere S)
{
double a = 0.0;
for(int i = 0; i < 3; i++){
a += (Y._coordinates[i] - X._coordinates[i]) * (Y._coordinates[i] - X._coordinates[i]);
}
double b = 0.0;
for(int i = 0; i < 3; i++)
b += (Y._coordinates[i] - X._coordinates[i]) * (X._coordinates[i] - S._coordinates[i]);
b *= 2;
double c = - S.r * S.r;
for(int i = 0; i < 3; i++)
c += (X._coordinates[i] - S._coordinates[i]) * (X._coordinates[i] - S._coordinates[i]);
return b * b - 4 * a * c;
}
Let I have start point P = (Px, Py, Pz), direction V = (Vx, Vy, Vz), ellipsoid = (Ex, Ey, Ec) and (a, b, c). How to construct clear code?

Let a line from P to P + D intersecting a sphere of center C and radius R.
WLOG, C can be the origin and R unit (otherwise translate by -C and scale by 1/R). Now using the parametric equation of the line and the implicit equation of the sphere,
(Px + t Dx)² + (Py + t Dy)² + (Pz + t Dz)² = 1
or
(Dx² + Dy² + Dz²) t² + 2 (Dx Px + Dy Py + Dz Pz) t + Px² + Py² + Pz² - 1 = 0
(Vectorially, D² t² + 2 D P t + P² - 1 = 0 and t = (- D P ±√((D P)² - D²(P² - 1))) / D².)
Solve this quadratic equation for t and get the two intersections as P + t D. (Don't forget to invert the initial transformations.)
For the ellipsoid, you can either plug the parametric equation of the line directly into the implicit equation of the conic, or reduce the conic (and the points simultaneously) and plug in the reduced equation.

Related

How to check if a point in a triangle (or on it's edge)

I'm trying to write an algorithm to determine if point is located inside a triangle or on it's edge in 3D coordinate space.
For example, I try to reach such results for different cases
I've figured out how to check if point P inside the triangle, I calculated normal vectors for triangles ABP, BCP, CAP and checked if they are similar.
Can someone explain how to check if a point is on the edge of a triangle (but not outside of a triangle)? You can provide formulas or code as you wish.
Make vectors:
r = p - A (r.x = p.x - A.x, r.y = p.y - A.y, r.z = p.z - A.z)
s = B - A
q = C - A
Calculate normal to ABC plane:
n = s x q (vector product)
Check if p lies in ABC plane using dot product:
dp = n.dot.r
If dp is zero (or has very small value like 1.0e-10 due to the floating point errors, then p is in the plane, and we can continue
Decompose vector p by base vectors s and q. At first check if z-component of normal (n.z) is non-zero. If so, use the next pair of equations (otherwise choose equations for x/z or y/z components):
px = a * sx + b * qx
py = a * sy + b * qy
Solve this system
a = (sy * qx - sx * qy) / (py * qx - px * qy)
b = (px - a * sx) / qx
If resulting coefficients a and b fulfill limits:
a >= 0
b >= 0
a + b <= 1.0
then point p lies in triangle plane inside it.

line-width for ellipse is not constant

I am drawing hollow ellipse using opengl. I calculate vertices in c++ code using standard ellipse formula. In fragment shader i just assign color to each fragment. The ellipse that i see on the screen has thinner line width on the sharper curves as compared to that where curve is not that sharp. So question is, how to make line-width consistent across the entire parameter of ellipse? Please see the image below:
C++ code :
std::vector<float> BCCircleHelper::GetCircleLine(float centerX, float centerY, float radiusX, float radiusY, float lineWidth, int32_t segmentCount)
{
auto vertexCount = (segmentCount + 1) * 2;
auto floatCount = vertexCount * 3;
std::vector<float> array(floatCount);
const std::vector<float>& data = GetCircleData (segmentCount);
float halfWidth = lineWidth * 0.5f;
for (int32_t i = 0; i < segmentCount + 1; ++i)
{
float sin = data [i * 2];
float cos = data [i * 2 + 1];
array [i * 6 + 0] = centerX + sin * (radiusX - halfWidth);
array [i * 6 + 1] = centerY + cos * (radiusY - halfWidth);
array [i * 6 + 3] = centerX + sin * (radiusX + halfWidth);
array [i * 6 + 4] = centerY + cos * (radiusY + halfWidth);
array [i * 6 + 2] = 0;
array [i * 6 + 5] = 0;
}
return std::move(array);
}
const std::vector<float>& BCCircleHelper::GetCircleData(int32_t segmentCount)
{
int32_t floatCount = (segmentCount + 1) * 2;
float segmentAngle = static_cast<float>(M_PI * 2) / segmentCount;
std::vector<float> array(floatCount);
for (int32_t i = 0; i < segmentCount + 1; ++i)
{
array[i * 2 + 0] = sin(segmentAngle * i);
array[i * 2 + 1] = cos(segmentAngle * i);
}
return array;
}
Aiming this:
The problem is likely that your fragments are basically line segments radiating from the center of the ellipse.
If you draw a line, from the center of the ellipse through the ellipse you've drawn, at any point on the perimeter, you could probably convince yourself that the distance covered by that red line is in fact the width that you're after (roughly, since you're working at low spatial resolution; somewhat pixelated). But since this is an ellipse, that distance is not perpendicular to the path being traced. And that's the problem. This works great for circles, because a ray from the center is always perpendicular to the circle. But for these flattened ellipses, it's very oblique!
How to fix it? Can you draw circles at each point on the ellipse, instead of line segments?
If not, you might need to recalculate what it means to be that thick when measured at that oblique angle - it's no longer your line width, may require some calculus, and a bit more trigonometry.
Ok, so a vector tangent to the curve described by
c(i) = (a * cos(i), b * sin(i))
is
c'(i) = (- a * sin(i), b * cos(i))
(note that this is not a unit vector). The perpendicular to this is
c'perp = (b * cos(i), a * sin(i))
You should be able to convince yourself that this is true by computing their dot product.
Lets calculate the magnitude of c'perp, and call it k for now:
k = sqrt(b * b * cos(i) * cos(i) + a * a * sin(i) * sin(i))
So we go out to a point on the ellipse (c(i)) and we want to draw a segement that's perpendicular to the curve - that means we want to add on a scaled version of c'perp. The scaling is to divide by the magnitude (k), and then multiply by half your line width. So the two end points are:
P1 = c(i) + halfWidth * c'perp / k
P2 = c(i) - halfWidth * c'perp / k
I haven't tested this, but I'm pretty sure it's close. Here's the geometry you're working with:
--
Edit:
So the values for P1 and P2 that I give above are end-points of a line-segment that's perpendicular to the ellipse. If you really wanted to continue with just altering the radiusX and radiusY values the way you were doing, you could do this. You just need to figure out what the 'Not w' length is at each angle, and use half of this value in place of halfWidth in radiusX +/- halfWidth and radiusY +/- halfwidth. I leave that bit of geometry as an exercise for the reader.

Create a 3D sphere and 3D box C++

I need to implement a tool to detect intersction between a 3D box and 3D Sphere in c++. Write now I find a way how to detect the intersection using that code.
inline float squared(float v) { return v * v; }
bool doesCubeIntersectSphere(vec3 C1, vec3 C2, vec3 S, float R)
{
float dist_squared = R * R;
/* assume C1 and C2 are element-wise sorted, if not, do that now */
if (S.X < C1.X) dist_squared -= squared(S.X - C1.X);
else if (S.X > C2.X) dist_squared -= squared(S.X - C2.X);
if (S.Y < C1.Y) dist_squared -= squared(S.Y - C1.Y);
else if (S.Y > C2.Y) dist_squared -= squared(S.Y - C2.Y);
if (S.Z < C1.Z) dist_squared -= squared(S.Z - C1.Z);
else if (S.Z > C2.Z) dist_squared -= squared(S.Z - C2.Z);
return dist_squared > 0;
}
What I need is an example of C++ code to create a 3D sphere using origin vector and a radius and
I need to create a 3D sphere using origin vector and a radius and a 3D box through its maximum and minimum corner vector.
I may be mistaken, but (assuming Axis-Aligned boxes):
The length of a vector from origin to corner C1 or C2 should be the radius r, right?
Explanation for my deriviation below: An Axis-Aligned-box with equal distance from center to all corners is a perfect cube. Translating such a cube to the origin puts two of the corners exactly at the line from the origin through the point {x=1,y=1,z=1}. Thus those two corners will have coordinates {d,d,d} and {-d, -d, -d}, where d is "distance" of the corner along the axises X,Y,Z. The distance to say the first corner is squaring and adding all components of the vector, and taking the square root, e.g:
|C1| = |{d,d,d}| = sqrt(d * d + d * d + d * d) = sqrt(3 * d * d)
Therefore solve:
r = sqrt(3 *d * d)
<=>
r * r = 3 * d * d
<=>
d = sqrt(r*r/3)
<=>
d = r/sqrt(3)
This needs to be translated back to the center of the Sphere, thus:
C1 = { S.x+d, S.y+d, S.z+d}
C2 = { S.x-d, S.y-d, S.z-d}
Your explanation is a little vague, so I made some assumptions. Perhaps I'm dead wrong. Anyway here is some non-tested code showing what I mean:
void makeCube(vec3 S, float R, vec3* C1, vec3* C2)
{
static const float sqrt_one_third = sqrtf(1.0f/3.0f);
float d = R * sqrt_one_third;
C1->X = S.X + d;
C1->Y = S.Y + d;
C1->Z = S.Z + d;
C2->X = S.X - d;
C2->Y = S.Y - d;
C2->Z = S.Z - d;
}

Point translation ortghogonally to the line

I am currently working on a project of drawing thick polylines and I am using interpolation in OpenGL. I managed to calculate all the necessary points but I need to draw two more points. I need to translate one point orthogonally to the line connecting two points. The scatch below shows what are the points. Point L is to be translated for the distance between L and nJ orthogonally to the line AB (B is the central point). Similar thing is with translation to the nK.
I have written the code:
float alpha = atan2(B.y - A.y,B.x - A.x) - deg90;
float alpha2 = atan2(C.y - B.y, C.x - B.x) - deg90;
nJ.x = L.x + w*cos(alpha); // w is distance between A1 and A2
nJ.y = L.y + w*sin(alpha);
nK.x = L.x + w*cos(alpha2);
nK.y = L.y + w*sin(alpha2);
The code works only for some points, not all. I need to fix + sing in above calculations of nJ and nK, but I do not know how. Anyone having suggestion?
First you need the left-hand-side function:
lhs(v) = [-v.y, v.x]
This turns a vector 90 degrees counter-clockwise.
Now you need the turn function:
turn(u, v, w) = sign(lhs(v - u), w - v)
If you have a polyline from u to v to w, turn(u,v,w) tells you whether it's a left turn (counter-clockwise turn) (positive), right turn (clockwise turn) (negative), or colinear (0).
There are four infinite lines in your picture that run parallel to ab and bc, with a distance of w between each pair.
The lines on the lower part are:
f(s) = (a + 0.5 * w * normalize(lhs(b - a))) + (b - a) * s
g(t) = (b + 0.5 * w * normalize(lhs(c - b))) + (c - b) * t
You want to find the intersection of the two lines; i.e., you want to solve for s and t in f(s) = g(t). This is just a system of two linear equations with two unknowns.
The solution is your point L = f(s) = g(t) in the picture.
To compute I you can use the exact same idea:
f(s) = (a - 0.5 * w * normalize(lhs(b - a))) + (b - a) * s
g(t) = (b - 0.5 * w * normalize(lhs(c - b))) + (c - b) * t
Solve for I = f(s) = g(t).
Update
Once you have L you can compute Kn and Jn as follows.
Kn = L - w * normalize(lhs(b - a))
Jn = L - w * normalize(lhs(c - b))
In computational geometry code, trigonometry is usually a code smell - it's not always wrong, but it usually is wrong. Try to stick to linear algebra.

Line-Circle Algorithm not quite working as expected

First, see:
https://math.stackexchange.com/questions/105180/positioning-a-widget-involving-intersection-of-line-and-a-circle
I have an algorithm that solves for the height of an object given a circle and an offset.
It sort of works but the height is always off:
Here is the formula:
and here is a sketch of what it is supposed to do:
And here is sample output from the application:
In the formula, offset = 10 and widthRatio is 3. This is why it is (1 / 10) because (3 * 3) + 1 = 10.
The problem, as you can see is the height of the blue rectangle is not correct. I set the bottom left offsets to be the desired offset (in this case 10) so you can see the bottom left corner is correct. The top right corner is wrong because from the top right corner, I should only have to go 10 pixels until I touch the circle.
The code I use to set the size and location is:
void DataWidgetsHandler::resize( int w, int h )
{
int tabSz = getProportions()->getTableSize() * getProportions()->getScale();
int r = tabSz / 2;
agui::Point tabCenter = agui::Point(
w * getProportions()->getTableOffset().getX(),
h * getProportions()->getTableOffset().getY());
float widthRatio = 3.0f;
int offset = 10;
int height = solveHeight(offset,widthRatio,tabCenter.getX(),tabCenter.getY(),r);
int width = height * widthRatio;
int borderMargin = height;
m_frame->setLocation(offset,
h - height - offset);
m_frame->setSize(width,height);
m_borderLayout->setBorderMargins(0,0,borderMargin,borderMargin);
}
I can assert that the table radius and table center location are correct.
This is my implementation of the formula:
int DataWidgetsHandler::solveHeight( int offset, float widthRatio, float h, float k, float r ) const
{
float denom = (widthRatio * widthRatio) + 1.0f;
float rSq = denom * r * r;
float eq = widthRatio * offset - offset - offset + h - (widthRatio * k);
eq *= eq;
return (1.0f / denom) *
((widthRatio * h) + k - offset - (widthRatio * (offset + offset)) - sqrt(rSq - eq) );
}
It uses the quadratic formula to find what the height should be so that the distance between the top right of the rectangle, bottom left, amd top left are = offset.
Is there something wrong with the formula or implementation? The problem is the height is never long enough.
Thanks
Well, here's my solution, which looks to resemble your solveHeight function. There might be some arithmetic errors in the below, but the method is sound.
You can think in terms of matching the coordinates at the point of the circle across
from the rectangle (P).
Let o_x,o_y be the lower left corner offset distances, w and h be the
height of the rectangle, w_r be the width ratio, dx be the desired
distance between the top right hand corner of the rectangle and the
circle (moving horizontally), c_x and c_y the coordinates of the
circle's centre, theta the angle, and r the circle radius.
Labelling it is half the work! Simply write down the coordinates of the point P:
P_x = o_x + w + dx = c_x + r cos(theta)
P_y = o_y + h = c_y + r sin(theta)
and we know w = w_r * h.
To simplify the arithmetic, let's collect some of the constant terms, and let X = o_x + dx - c_x and Y = o_y - c_y. Then we have
X + w_r * h = r cos(theta)
Y + h = r sin(theta)
Squaring and summing gives a quadratic in h:
(w_r^2 + 1) * h^2 + 2 (X*w_r + Y) h + (X^2+Y^2-r^2) == 0
If you compare this with your effective quadratic, then as long as we made different mistakes :-), you might be able to figure out what's going on.
To be explicit: we can solve this using the quadratic formula, setting
a = (w_r^2 + 1)
b = 2 (X*w_r + Y)
c = (X^2+Y^2-r^2)