Smallest sphere to encapsulate a triangle in 3D? - algorithm

At first I figured you sum the vertices and scale by 1/3 to find the origin then take the largest distance from the vertex to the origin. This results in a sphere that contains the triangle, but it isn't necessarily the smallest.
Is there a known method for determining the smallest sphere to fully encapsulate an arbitrary triangle in 3D?

Used the answers here and wikipedia to come up with something in c++ that works for me, I hope this helps someone!
static Sphere makeMinimumBoundingSphere(const Vec3 &p1, const Vec3 &p2, const Vec3 &p3) {
Sphere s;
// Calculate relative distances
float A = (p1 - p2).distance();
float B = (p2 - p3).distance();
float C = (p3 - p1).distance();
// Re-orient triangle (make A longest side)
const Vec3 *a = &p3, *b = &p1, *c = &p2;
if (B < C) swap(B, C), swap(b, c);
if (A < B) swap(A, B), swap(a, b);
// If obtuse, just use longest diameter, otherwise circumscribe
if ((B*B) + (C*C) <= (A*A)) {
s.radius = A / 2.f;
s.position = (*b + *c) / 2.f;
} else {
// http://en.wikipedia.org/wiki/Circumscribed_circle
precision cos_a = (B*B + C*C - A*A) / (B*C*2);
s.radius = A / (sqrt(1 - cos_a*cos_a)*2.f);
Vec3 alpha = *a - *c, beta = *b - *c;
s.position = (beta * alpha.dot(alpha) - alpha * beta.dot(beta)).cross(alpha.cross(beta)) /
(alpha.cross(beta).dot(alpha.cross(beta)) * 2.f) + *c;
}
return s;
}

The smallest sphere to encapsulate the triangle is just the circumsribed cirlce extended into the third dimension.
Update: Scratch that, of course it isn't. It's the sphere that you get if you rotate the smallest circle around its diameter. The reason being that for any containing sphere that has its origin out of the plane of the triangle there is a smaller one that has its origin on the plane (by projecting the origin orthogonally onto the plane).

You are trying to find the smallest enclosing ball MB(P) of a point set P, so you could use an algorithm as implemented here https://github.com/hbf/miniball. (Note: "ball" and "sphere" are synonyms in this context.)
However, this is overkill in your case, since the point set P at hand contains exactly 3 points (the vertices of the triangle). In this particular case, you can use the fact that the smallest enclosing ball MB(P) of P={p,q,r} equals either:
B(p,q) if r is contained in B(p,q), or
B(p,r) if q is contained in B(p,r), or
B(q,r) if p is contained in B(q,r), or
B(p,q,r) otherwise.
Here, B(x,y) is the smallest ball containing the points x,y and B(x,y,z) is the smallest ball containing the points x,y,z on the boundary. B(x,y) and B(x,y,z) can be computed by solving a linear system of equations.
Note: I am the author of https://github.com/hbf/miniball.

Assuming that the sphere is simply a trivial extension of a circle (2-D) into 3-D (using both the same center point and the same radius), I believe what you are looking for is called circumscribed circle of a triangle.
Apparently I didn't consider the case of an obtuse triangle which if you have the vertices (points) of the triangle on the circle, then the circle is not the smallest bounding circle (and thus smallest bounding sphere).
Now I believe that you are looking for the minimum bounding sphere, which is a known and studied problem in mathematics, and computer graphics. "Smallest Enclosing Circle Problem" is a description of an O( n^{2} ) and a linear O(n) algorithms.
And as far as I know the minimal bounding circle does produce the minimal bounding sphere, using the same parameters (center point and radius) projected into three dimensions.

Related

Displace 3D vertices along a 2 dimension plane using normals

I have this one triangle with arbitrary vertices positioned in a 3D space.
I have that finding the centroid of such triangle is easy by doing:
float centroid[3] = { 0, 0, 0 };
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
centroid[i] += points[j][i];
}
centroid[i] /= 3.0;
}
It's also easy to find the normal for it with something called plane equation:
crossProduct(points[1] - points[0], points[2] - points[0]);
There is a very simple method for moving the vertices away from the centroid, but that is too linear. I can only move the pointers back and forth.
What is the formula that I need to be able to freely move the vertices in a pseudo X/Y axis that is formed from the perspective of the triangle normal?
For reference, I'm using C++ and QT for the vectors and matrices. I'm rendering with basic OpenGL.
To build coordinate axes in triangle plane, you can use axis pseudoX from centroid to any vertex and perpendicular axis pseudoY = pseudoX.cross.Normal.
The choice of vertex as base vector seems rather natural. If you want to add some randomness, rotate this pseudoX by arbitrary angle and generate new pseudoY as cross product again.
Another method to generate vector in that plane - from normal only. Choose normal component with the largest magnitude, negate it and exchange with component with the second magnitude, make the smallest component zero. For example, if
|ny|>=|nz|>=|nx|
Vec = (0, nz, -ny)
note that Vec.dot.Normal = 0, so Vec lies in triangle plane

Polygon Algorithm

I'm trying to code a general algorithm that can find a polygon from the area swept out by a circle (red line) that follows some known path (green line), and where the circle gets bigger as it moves further down the known path. Basically, can anyone point me down a direction to solve this, please? I can't seem to nail down which tangent points are part of the polygon for any point (and thus circle) on the path.
Any help is appreciated.
Well, the easiest is to approximate your path by small segments on which your path is linear, and your circle grows linearly.
Your segments and angles will likely be small, but for the sake of the example, let's take bigger (and more obvious) angles.
Going through the geometry
Good lines for the edges of your polygon are the tangents to both circles. Note that there aren't always close to the lines defined by the intersections between the circles and the orthogonal line to the path, especially with stronger growth speeds. See the figure below, where (AB) is the path, we want the (OE) and (OF) lines, but not the (MN) one for example :
The first step is to identify the point O. It is the only point that defines a homothetic transformation between both circles, with a positive ratio.
Thus ratio = OA/OB = (radius C) / (radius C') and O = A + AB/(1-ratio)
Now let u be the vector from O to A normalized, and v a vector orthogonal to u (let us take it in the direction from A to M).
Let us call a the vector from O to E normalized, and beta the angle EOA. Then, since (OE) and (AE) are perpendicular, sin(beta) = (radius C) / OA. We also have the scalar product a.u = cos(beta) and since the norm of a is 1, a = u * cos(beta) + v * sin(beta)
Then it comes easily that with b the vector from O to F normalized, b = u * cos(beta) - v * sin(beta)
Since beta is an angle less than 90° (otherwise the growth of the circle would be so much faster than it going forward, that the second circle contains the first completely), we know that cos(beta) > 0.
Pseudo-code-ish solution
For the first and last circles you can do something closer to them -- fort the sake of simplicity, I'm just going to use the intersection between the lines I'm building and the tangent to the circle that's orthogonal to the first (or last) path, as illustrated in the first figure of this post.
Along the path, you can make your polygon arbitrarily close to the real swept area by making the segments smaller.
Also, I assume you have a function find_intersection that, given two parametric equations of two lines, returns the point of intersection between them. First of all, it makes it trivial to see if they are parallel (which they should never be), and it allows to easily represent vertical lines.
w = 1; // width of the first circle
C = {x : 0, y : 0}; // first circle center
while( (new_C, new_w) = next_step )
{
// the vector (seg_x, seg_y) is directing the segment
seg = new_C - C;
norm_seg = sqrt( seg.x * seg.x + seg.y * seg.y );
// the vector (ortho_x, ortho_y) is orthogonal to the segment, with same norm
ortho = { x = -seg.y, y = seg.x };
// apply the formulas we devised : get ratio-1
fact = new_w / w - 1;
O = new_C - seg / fact;
sin_beta = w * fact / norm_seg;
cos_beta = sqrt(1 - sin_beta * sin_beta);
// here you know the two lines, parametric equations are O+t*a and O+t*b
a = cos_beta * seg + sin_beta * ortho;
b = cos_beta * seg - sin_beta * ortho;
if( first iteration )
{
// initialize both "old lines" to a line perpendicular to the first segment
// that passes through the opposite side of the circle
old_a = ortho;
old_b = -ortho;
old_O = C - seg * (w / norm_seg);
}
P = find_intersection(old_O, old_a, O, a);
// add P to polygon construction clockwise
Q = find_intersection(old_O, old_b, O, b);
// add Q to polygon construction clockwise
old_a = a;
old_b = b;
old_O = O;
w = new_w;
C = new_C;
}
// Similarly, finish with line orthogonal to last direction, that is tangent to last circle
O = C + seg * (w / norm_seg);
a = ortho;
b = -ortho;
P = find_intersection(old_O, old_a, O, a);
// add P to polygon construction clockwise
Q = find_intersection(old_O, old_b, O, b);
// add Q to polygon construction clockwise
Let's suppose the centers are along the positive x-axis, and the lines in the envelope are y=mx and y=-mx for some m>0. The distance from (x,0) to y=mx is mx/sqrt(1+m^2). So, if the radius is increasing at a rate of m/sqrt(1+m^2) times the distance moved along the x-axis, the enveloping lines are y=mx and y=-mx.
Inverting this, if you put a circle of radius cx at the center of (x,0), then c=m/sqrt(1+m^2) so
m = c/sqrt(1-c^2).
If c=1 then you get a vertical line, and if c>1 then every point in the plane is included in some circle.
This is how you can tell how much faster than sound a supersonic object is moving from the Mach angle of the envelope of the disturbed medium.
You can rotate this to nonhorizontal lines. It may help to use the angle formulation mu = arcsin(c), where mu is the angle between the envelope and the path, and the Mach number is 1/c.

Merging two overlapping rectangles into the resulting polygon

I am looking for an algorithm that, given two rectangles that overlap partially or totally, finds the ordered list of vertexes that defines the polygon representing the sum of the two rectangles.
To be more specific:
I have as input two ordered list of points, representing the two rectangles
I know how to find the vertexes of the resulting polygon, which is formed by the vertexes of each rectangle which are outside the other rectangle, plus the intersection points between each edge of one rectangle with each edge of the other
I don't currently know how to order into an array the points, obtained as explained above, so that the element j and j+1 of the array represents the two vertexes of the same edge (this is what I mean by ordered list of vertexes).
Thanks in advance for any help
UPDATE :
I found a way to sort vertexes to obtain a polygon, as follows:
compute the vertexes centroid ( coords average )
sort the vertexes by the angle formed between the segment from the centroid and the vertex and any reference line passing by the centroid (e.g. the X axis ).
However, although I consistently obtain a polygon enclosing the two rectangles, without holes or intersecting edges, it is not always the polygon I want ( sometimes it includes extra area not belonging to one of the input rectangles ).
So I'm going back to the solution pointed in one of the comments, which is also described here:
polygon union without holes
Once you have the 4 vertices, you merely find the farther point using the distance formula (since it seems we can't make the assumption of a collinear or unrotated beginning rects)
So if you have points a = (xA, yA), b, c, d and you know these 4 points make a rectangle
float dist(Point a, Point b){
float dx = a.x - b.x;
float dy = a.y - b.y;
return Math.sqrt(dx * dx + dy * dy);
}
//somewhere else, where u need it
//put point A into index 0
Point curFarthest = b;
float distance = dist(a, b);
if (dist(a, c) > distance){
curFarther = c;
distance = dist(a, c);
} else if (dist(a, d) > distance){
curFarther = d;
curFarthest = dist(a, d);
}
//store curFarthest into index 2
// store the rest (exculding points a and curFarthest)
// into index 1 and 3 in no particular order
I am working on the same problem but I use a different approach(work still in progress).
Find the intersection points.
Distance of each point(vertices) with its neighboring connected points.
Using Dinics Algorithm find the Maxmimum flow.
Note: there will be a few special cases. But then again my problems revolves around polygons having 1 common point(vertice).

Calculating the Area of Intersection Between a Plane and Rectangular Prism

If I have a plane, let's say the xy plane, and a rectangular prism that can be arbitrarily rotated/translated in 3 dimensions. Are there any cool algorithms/methods that can be used to determine the area of intersection between the two?
One approach would be to explicitly find the polygonal region of intersection R between the prism and the plane, triangulate R and sum the areas of the triangles to give the total intersection area.
The vertices of the intersecting polygon R can be found by performing a series of line-plane intersection tests between the edges of the prism and the plane.
Based on the relative orientation of the plane/prism, the intersecting polygon could take a number of different configurations (i.e. it won't always be a rectangle!). Given a regular prism the intersecting region should always be convex though, allowing the triangulation to be obtained as a simple fan.
Given a triangulation of R the total area of intersection is simply the sum of the triangle areas.
Once you have the polygonal region of intersection, you don't need to triangulate it to compute its area. There's a much simpler algorithm:
float area = 0.0f;
// Run through all segments
for (int i = 0; i < corners.Length; i++)
{
// Get end points of segments
Vector2 A = corners[i];
Vector2 B = corners[(i+1) % corners.Length];
// Add the signed(!) area of a quadrangle with two corners A, B
// and two corners with same y values on the y axis
//
// |---------A
// | + /
// |-------B
//
// |-------B
// | - \
// |---------A
//
area += 0.5f * (A.x + B.x) * (B.y - A.y);
}
Cf. http://alienryderflex.com/polygon_area/

Calculating the Bounding Rectangle at an Angle of a Polygon

I have the need to determine the bounding rectangle for a polygon at an arbitrary angle. This picture illustrates what I need to do:
alt text http://kevlar.net/RotatedBoundingRectangle.png
The pink rectangle is what I need to determine at various angles for simple 2d polygons.
Any solutions are much appreciated!
Edit:
Thanks for the answers, I got it working once I got the center points correct. You guys are awesome!
To get a bounding box with a certain angle, rotate the polygon the other way round by that angle. Then you can use the min/max x/y coordinates to get a simple bounding box and rotate that by the angle to get your final result.
From your comment it seems you have problems with getting the center point of the polygon. The center of a polygon should be the average of the coordinate sums of each point. So for points P1,...,PN, calculate:
xsum = p1.x + ... + pn.x;
ysum = p1.y + ... + pn.y;
xcenter = xsum / n;
ycenter = ysum / n;
To make this complete, I also add some formulas for the rotation involved. To rotate a point (x,y) around a center point (cx, cy), do the following:
// Translate center to (0,0)
xt = x - cx;
yt = y - cy;
// Rotate by angle alpha (make sure to convert alpha to radians if needed)
xr = xt * cos(alpha) - yt * sin(alpha);
yr = xt * sin(alpha) + yt * cos(alpha);
// Translate back to (cx, cy)
result.x = xr + cx;
result.y = yr + cx;
To get the smallest rectangle you should get the right angle. This can acomplished by an algorithm used in collision detection: oriented bounding boxes.
The basic steps:
Get all vertices cordinates
Build a covariance matrix
Find the eigenvalues
Project all the vertices in the eigenvalue space
Find max and min in every eigenvalue space.
For more information just google OBB "colision detection"
Ps: If you just project all vertices and find maximum and minimum you're making AABB (axis aligned bounding box). Its easier and requires less computational effort, but doesn't guarantee the minimum box.
I'm interpreting your question to mean "For a given 2D polygon, how do you calculate the position of a bounding rectangle for which the angle of orientation is predetermined?"
And I would do it by rotating the polygon against the angle of orientation, then use a simple search for its maximum and minimum points in the two cardinal directions using whatever search algorithm is appropriate for the structure the points of the polygon are stored in. (Simply put, you need to find the highest and lowest X values, and highest and lowest Y values.)
Then the minima and maxima define your rectangle.
You can do the same thing without rotating the polygon first, but your search for minimum and maximum points has to be more sophisticated.
To get a rectangle with minimal area enclosing a polygon, you can use a rotating calipers algorithm.
The key insight is that (unlike in your sample image, so I assume you don't actually require minimal area?), any such minimal rectangle is collinear with at least one edge of (the convex hull of) the polygon.
Here is a python implementation for the answer by #schnaader.
Given a pointset with coordinates x and y and the degree of the rectangle to bound those points, the function returns a point set with the four corners (and a repetition of the first corner).
def BoundingRectangleAnglePoints(x,y, alphadeg):
#convert to radians and reverse direction
alpha = np.radians(alphadeg)
#calculate center
cx = np.mean(x)
cy = np.mean(y)
#Translate center to (0,0)
xt = x - cx
yt = y - cy
#Rotate by angle alpha (make sure to convert alpha to radians if needed)
xr = xt * np.cos(alpha) - yt * np.sin(alpha)
yr = xt * np.sin(alpha) + yt * np.cos(alpha)
#Find the min and max in rotated space
minx_r = np.min(xr)
miny_r = np.min(yr)
maxx_r = np.max(xr)
maxy_r = np.max(yr)
#Set up the minimum and maximum points of the bounding rectangle
xbound_r = np.asarray([minx_r, minx_r, maxx_r, maxx_r,minx_r])
ybound_r = np.asarray([miny_r, maxy_r, maxy_r, miny_r,miny_r])
#Rotate and Translate back to (cx, cy)
xbound = (xbound_r * np.cos(-alpha) - ybound_r * np.sin(-alpha))+cx
ybound = (xbound_r * np.sin(-alpha) + ybound_r * np.cos(-alpha))+cy
return xbound, ybound

Resources