Given the circle C: (O, r) and the polygon P, how can I find the smallest sector of C that covers P?
Assume that the radius of the circle is large enough, so the main problem is to find the initial and final angles of the sector.
I tried to draw rays from center of circle toward each of angles of the polygon and check if the ray overlaps the polygon. But there might be more than two rays that only touch the polygon. I can not rely on the selection of unique rays based on their direction angle, due to double precision. So finding min and max angles in the list of touched rays is useless. Beside that, I have problem with choosing either of sectors created by two terminal angles, since initial angle can be greater than final angle when computed by atan2.
So what is the proper way to find such a sector?
Edit:
Three example polygons (in WKT format):
POLYGON((52.87404 30.85613, 42.55699 28.46292, 41.54373 24.319989, 53.57623 21.300564, 62.94891 28.46292, 49.39652 27.550071, 52.87404 30.85613))
POLYGON((52.94294 30.920592, 42.55699 28.46292, 43.61965 35.545578, 55.85037 34.862696, 59.12524 36.621547, 47.68664 39.877048, 35.69973 36.198265, 37.30512 29.196711, 31.09762 28.46292, 41.54373 24.319989, 53.57623 21.300564, 62.94891 28.46292, 49.39652 27.550071, 52.94294 30.920592))
POLYGON((52.94294 30.920592, 42.55699 28.46292, 43.61965 35.545578, 52.45594 37.266299, 59.30560 29.196711, 64.12177 33.290489, 58.81733 36.554277, 47.68664 39.877048, 35.69973 36.198265, 37.30512 29.196711, 31.09762 28.46292, 41.54373 24.319989, 53.57623 21.300564, 62.94891 28.46292, 49.39652 27.550071, 52.94294 30.920592))
Center and radius of circle for all examples:
O: (45, 30)
r: 25
For starters we can handle your data as point cloud (polygon vertexes) p[i] and some circle defined by center p0 and radius r. If your point cloud is entirely inside circle you can ignore the radius.
We can exploit atan2 however to avoid problems with crossing and sector selection we do not enlarge min/max bounds as usual for standard cartesian BBOX computation instead:
compute atan2 angle for each point and remember it in array a[]
sort a[]
find biggest distance between consequent angles in a[]
Do not forget that angle difference can be |Pi| tops so if it is more you need to +/- 2*PI. Also handle a[] as cyclic buffer.
This is my simple C++/VCL attempt:
//---------------------------------------------------------------------------
float p0[]={52.87404,30.856130,42.55699,28.46292,41.54373,24.319989,53.57623,21.300564,62.94891,28.46292,49.39652,27.550071,52.87404,30.85613,};
float p1[]={52.94294,30.920592,42.55699,28.46292,43.61965,35.545578,55.85037,34.862696,59.12524,36.621547,47.68664,39.877048,35.69973,36.198265,37.30512,29.196711,31.09762,28.46292,41.54373,24.319989,53.57623,21.300564,62.94891,28.46292,49.39652,27.550071,52.94294,30.920592,};
float p2[]={52.94294,30.920592,42.55699,28.46292,43.61965,35.545578,52.45594,37.266299,59.30560,29.196711,64.12177,33.290489,58.81733,36.554277,47.68664,39.877048,35.69973,36.198265,37.30512,29.196711,31.09762,28.46292,41.54373,24.319989,53.57623,21.300564,62.94891,28.46292,49.39652,27.550071,52.94294,30.920592,};
float x0=45.0,y0=30.0,R=25.0;
//---------------------------------------------------------------------------
template <class T> void sort_asc_bubble(T *a,int n)
{
int i,e; T a0,a1;
for (e=1;e;n--) // loop until no swap occurs
for (e=0,a0=a[0],a1=a[1],i=1;i<n;a0=a1,i++,a1=a[i])// proces unsorted part of array
if (a0>a1) // condition if swap needed
{ a[i-1]=a1; a[i]=a0; a1=a0; e=1; } // swap and allow to process array again
}
//---------------------------------------------------------------------------
void get_sector(float x0,float y0,float r,float *p,int n,float &a0,float &a1)
{
// x0,y0 circle center
// r circle radius
// p[n] polyline vertexes
// a0,a1 output angle range a0<=a1
int i,j,m=n>>1;
float x,y,*a;
a=new float[m];
// process points and compute angles
for (j=0,i=0;i<n;j++)
{
x=p[i]-x0; i++;
y=p[i]-y0; i++;
a[j]=atan2(y,x);
}
// sort by angle
sort_asc_bubble(a,m);
// get max distance
a0=a[m-1]; a1=a[0]; x=a1-a0;
while (x<-M_PI) x+=2.0*M_PI;
while (x>+M_PI) x-=2.0*M_PI;
if (x<0.0) x=-x;
for (j=1;j<m;j++)
{
y=a[j]-a[j-1];
while (y<-M_PI) y+=2.0*M_PI;
while (y>+M_PI) y-=2.0*M_PI;
if (y<0.0) y=-y;
if (y>x){ a0=a[j-1]; a1=a[j]; x=y; }
}
}
//---------------------------------------------------------------------------
void TMain::draw()
{
int i,n;
float x,y,r,*p,a0=0.0,a1=0.0;
float ax,ay,bx,by;
float zoom=7.0;
p=p0; n=sizeof(p0)/sizeof(p0[0]);
// p=p1; n=sizeof(p1)/sizeof(p1[0]);
// p=p2; n=sizeof(p2)/sizeof(p2[0]);
get_sector(x0,y0,R,p,n,a0,a1);
// clear buffer
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->FillRect(TRect(0,0,xs,ys));
// circle
x=x0; y=y0; r=R;
ax=x+R*cos(a0);
ay=y+R*sin(a0);
bx=x+R*cos(a1);
by=y+R*sin(a1);
x*=zoom; y*=zoom; r*=zoom;
ax*=zoom; ay*=zoom;
bx*=zoom; by*=zoom;
bmp->Canvas->Pen->Color=clBlue;
bmp->Canvas->Brush->Color=TColor(0x00101010);
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
bmp->Canvas->Pen->Color=clAqua;
bmp->Canvas->Brush->Color=TColor(0x00202020);
bmp->Canvas->Pie(x-r,y-r,x+r,y+r,ax,ay,bx,by);
// PCL
r=2.0;
bmp->Canvas->Pen->Color=clAqua;
bmp->Canvas->Brush->Color=clAqua;
for (i=0;i<n;)
{
x=p[i]; i++;
y=p[i]; i++;
x*=zoom; y*=zoom;
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
}
// render backbuffer
Main->Canvas->Draw(0,0,bmp);
}
//---------------------------------------------------------------------------
You can ignore the void TMain::draw() function its just example of usage and this is the preview:
However as you have polygon (lines) to avoid wrong results You have two simple options:
sample lines with more than just 2 points
this way the angular gap should be bigger than distances between points in the point cloud. So if you sample lines with enough points the result will be correct. However wrongly selected number of points per line will lead to wrong result in edge cases. On the other hand implementing this is just simple DDA interpolation added to current code.
convert to handling angle intervals instead of angles a[]
so for each line compute angular interval <a0,a1> with predefined poygon winding rule (so CW or CCW but consistent). And instead of array a[] you would have ordered list of intervals where you would either insert new interval or merge with existing if overlap. This approach is safe but merging angular intervals is not that easy. If the input data is polyline (like yours) that means each next line start from the previous line endpoint so you can ignore the list of intervals and just enlarge the single one instead but still you need to handle the enlargement correctly which is not trivial.
[Edit1] using the first approach the updated function look like this:
void get_sector_pol(float x0,float y0,float r,float *p,int n,float &a0,float &a1)
{
// x0,y0 circle center
// r circle radius
// p[n] point cloud
// a0,a1 output angle range a0<=a1
int i,j,k,N=10,m=(n>>1)*N;
float ax,ay,bx,by,x,y,dx,dy,*a,_N=1.0/N;
a=new float[m];
// process points and compute angles
bx=p[n-2]-x0; i++;
by=p[n-1]-y0; i++;
for (j=0,i=0;i<n;)
{
ax=bx; ay=by;
bx=p[i]-x0; i++;
by=p[i]-y0; i++;
dx=_N*(bx-ax); x=ax;
dy=_N*(by-ay); y=ay;
for (k=0;k<N;k++,x+=dx,y+=dy,j++) a[j]=atan2(y,x);
}
// sort by angle
sort_asc_bubble(a,m);
// get max distance
a0=a[m-1]; a1=a[0]; x=a1-a0;
while (x<-M_PI) x+=2.0*M_PI;
while (x>+M_PI) x-=2.0*M_PI;
if (x<0.0) x=-x;
for (j=1;j<m;j++)
{
y=a[j]-a[j-1];
while (y<-M_PI) y+=2.0*M_PI;
while (y>+M_PI) y-=2.0*M_PI;
if (y<0.0) y=-y;
if (y>x){ a0=a[j-1]; a1=a[j]; x=y; }
}
}
As you can see its almost the same just simple DDA is added to the first loop win N points per line. Here preview for the second polygon which fails with just point cloud approach:
I want to implement terrain collision. I have a class called GeoTerrain. Its constructor takes a picture as parameter and then builds a terrain out of triangles with vertices high for each light pixel and low for each dark pixel.
This puts me in a position where I have to implement a proper collision detection for the OBBs when they hit the terrain. But my temporary solution is far from robust.
Here is my current approach:
Each surface triangle has its own little hitbox that consists of 6 vertices (the 3 vertices on the triangle surface and three additional vertices that basically are the 3 surface vertices but shifted down the surface normal (so that they are underneath the visible surface).
Why: Because I do not know how to otherwise calculate the minimum translation vector without having a hitbox volume.
Sorry for the long code - I tried to comment as much as necessary:
// This method resides in the Hitbox class.
// The hitbox instance then checks against the other object (parameter):
private IntersectionObject TestIntersectionTerrain(GeoTerrain terra)
{
Vector3 mtv = new Vector3(0, 0, 0);
Vector3 mtvTotal = new Vector3(0, 0, 0);
float shape1Min, shape1Max, shape2Min, shape2Max;
// Select all triangles within reach of the OBB hitbox:
List<GeoTriangle> triangles = terra.GetTrianglesForHitbox(this);
// Loop through all triangles and check collision
// (cannot be more than 8 triangles, right now)
foreach (GeoTriangle triangle in triangles)
{
bool bothOverlap = false;
mtv = Vector3.Zero;
bool error;
bool breakDone = false;
float mtvDistance = float.MaxValue;
float mtvDirection = 1;
// loop through all hitbox normals (three normals):
for (int i = 0; i < mNormals.Length; i++)
{
error = false;
// project the current vertices of objects' hitbox and triangle hitbox to find
// both shapes' minimum and maximum:
SATtest(CurrentHitBoxNormals[i], CurrentHitBoxVertices, out shape1Min, out shape1Max);
SATtest(CurrentHitBoxNormals[i], triangle.VerticesHitbox, out shape2Min, out shape2Max);
if (!Overlaps(shape1Min, shape1Max, shape2Min, shape2Max))
{
bothOverlap = false;
breakDone = true;
break;
}
else
{
// calculate MTV:
CalculateOverlap(
CurrentHitBoxNormals[i], // normals (here, the meshes' hitbox normals)
ref shape1Min,
ref shape1Max,
ref shape2Min,
ref shape2Max,
out error,
ref mtvDistance,
ref mtv,
ref mtvDirection,
mCenterTranslated, // object's hitbox volume center (world space)
triangle.CenterHitbox); // triangle's hitbox volume center (world space)
}
// do the same but now for the triangle's normal
// (right now this unnecessarily also gets looped 3 times):
SATtest(triangle.Normal, CurrentHitBoxVertices, out shape1Min, out shape1Max);
SATtest(triangle.Normal, triangle.VerticesHitbox, out shape2Min, out shape2Max);
if (!Overlaps(shape1Min, shape1Max, shape2Min, shape2Max))
{
bothOverlap = false;
breakDone = true;
}
else
{
CalculateOverlap(triangle.Normal, ref shape1Min, ref shape1Max, ref shape2Min, ref shape2Max,
out error, ref mtvDistance, ref mtv, ref mtvDirection, mCenterTranslated, triangle.CenterHitbox);
bothOverlap = true;
}
}
if (bothOverlap && !breakDone && mtv != Vector3.Zero)
{
// add the current mtv to the total MTV (of all triangles)
// but only add more to it, if the current MTV has a bigger shift in
// one direction than the previous MTVs:
mtvTotal.X = Math.Abs(mtv.X) > Math.Abs(mtvTotal.X) ? mtv.X : mtvTotal.X;
mtvTotal.Y = Math.Abs(mtv.Y) > Math.Abs(mtvTotal.Y) ? mtv.Y : mtvTotal.Y;
mtvTotal.Z = Math.Abs(mtv.Z) > Math.Abs(mtvTotal.Z) ? mtv.Z : mtvTotal.Z;
}
}
if (mtvTotal != Vector3.Zero)
{
IntersectionObject o = new IntersectionObject();
o.IntersectingGameObject = terra.Owner;
o.MinimumTranslationVector = mtvTotal;
o.MeshNumber = 0;
o.MeshName = terra.Owner.Name;
return o;
}
else
{
return null;
}
}
private void CalculateOverlap(Vector3 axis, ref float shape1Min, ref float shape1Max, ref float shape2Min, ref float shape2Max, out bool error, ref float mtvDistance, ref Vector3 mtv, ref float mtvDirection, Vector3 posA, Vector3 posB)
{
float d0 = (shape2Max - shape1Min);
float d1 = (shape1Max - shape2Min);
float intersectionDepthScaled = (shape1Min < shape2Min)? (shape1Max - shape2Min) : (shape1Min - shape2Max);
float axisLengthSquared = Vector3.Dot(axis, axis);
if (axisLengthSquared < 1.0e-8f)
{
error = true;
return;
}
float intersectionDepthSquared = (intersectionDepthScaled * intersectionDepthScaled) / axisLengthSquared;
error = false;
if (intersectionDepthSquared < mtvDistance || mtvDistance < 0)
{
mtvDistance = intersectionDepthSquared;
mtv = axis * (intersectionDepthScaled / axisLengthSquared);
float notSameDirection = Vector3.Dot(posA - posB, mtv);
mtvDirection = notSameDirection < 0 ? -1.0f : 1.0f;
mtv = mtv * mtvDirection;
}
}
The problem is: it works, but not on the edges. With my approach (invisible hitbox below triangle surface) the player sometimes hits an invisible wall because the MTV tells him to move up (ok) and sideways as well (not ok). Mathematically this is correct, because if the player sinks into the fake triangle volume, the mtv may of course decide to shift him back on more than one axis if need be.
Is there any way to get the MTV from just checking against a flat triangle? I found several links that tell me "if" there is an intersection. But I found no understandable solution for getting the MTV. I think I need to find out by how much the player has to be shifted back along the surface normal?
EDIT (2019-02-12 22:00)
I updated the code according to the first answer. But I still get strange behaviour when applying the calculated MTV. This is my current algorithm:
My updated collision detection algorithm approach is so far:
get a list of the triangles that are close to the player
get the OBBs vertices and normals
loop through that triangle list and do for each triangle (maximum of ~8 right now):
use triangle's normal and project triangle's vertices and OBB's vertices.
use OBB's normal #1 and project triangle's vertices and OBB's vertices.
use OBB's normal #2 and project triangle's vertices and OBB's vertices.
use OBB's normal #3 and project triangle's vertices and OBB's vertices.
cross the vectors you told me (step 5 to 13) and project triangle's vertices and OBB's vertices on the normalised version of these vectors
Each step is only done if the previous step resulted in an overlap. The overlapping length is then stored in an array (13 cells, one for each step)
If every projection resulted in an overlap, look for the mtv by calculating the minimum overlap and the corresponding axis vector.
Multiply this vector by the overlap and use it as the MTV.
Is that about right?
The SAT (Separation Axis Theorem) algorithm will give you the minimum translation vector quite easily, for any kind of convex shapes, that can also include triangles, quads, flat polygons.
In a brute-force manner, The SAT works in two steps, a couple more to get the MTV.
Find the list of minimum separation axes to tests against.
for example, for an OBB, you will need to consider three directions for
the faces, and three directions for the edges.
Project the two convex shapes on the separation axis, to get the projection intervals.
for example, for each shape, dot product all their vertices with the axis
direction.
The min and max values will give you the intervals for each shape.
if the two intervals do not intersect, the objects are spearate and do not intersect.
if all intervals intersect on every spearation axis, the objects intersect.
to find the MTV, find the minimum overlap among all the speration axis intervals.
the interval overlap, combined with the separation axis direction, will give you the MTV.
For convex polyhedras, the separation axes to test against are quite simple. They are :
the surface normals for each shape, call them A and B.
the cross product of every edges of A against every edges of B.
There are optimisations, for examples, there is no need to test axes with
opposite direction, or similar direction.
as an example, a OBB will need to be tested for three face normals, and three edge directions.
NOTE: that the MTV may not always be the best solution to your problem, especially when we talk about terrains.
I found out an equation of a plane,from three vertices.
Now,if I have a bounding box(i.e. a large cube),How can I determine the grid positions(small cubes),where the plane cuts the large cube.
I am currently following this approach:
For each small cube center, say(Xp, Yp, Zp), calculate perpendicular distance to the plane i.e., (aXp + bYp + c*Zp + d)/ (SquareRoot Of (a^2 + b^2 + c^2)). This should be less than or equal to (length of smallCube * SquareRoot(3))/2.
If this criteria,gets satisfied,then I assume my plane to cut the large cube at this small cube position.
a,b,c,d are coefficients of the plane,of the form ax+by+cz+d = 0.
I would be really glad,if someone can let me know,if I am doing something wrong (or) also,any other simple approach.
Seems you want to get a list of small cubes (grid voxels) intersected by given plane.
The simplest approach:
Find intersection of the plane with any cube edge. For example, intersection with vertical edge of AAB (X0,Z0 are constant) might be calculated by solving this equation for unknown Y:
aX0 + bY + c*Z0 + d = 0
and checking that Y is in cube range. Get small cube coordinates (0, ky=Floor(Y/VoxelSize), 0) and then check neighbor voxels in order (account for plane coefficients to check only real candidates).
candidates:
0,ky,0
1,ky,0
0,ky-1,0
0,ky+1,0
0,ky,1
There are more advanced methods to generate voxel sequence for ray case (both 2d and 3d) like Amanatides/Woo algorithm. Perhaps something similar exists also for plane voxelization
Here is AABB-plane intersection test code from this page (contains some explanations)
// Test if AABB b intersects plane p
int TestAABBPlane(AABB b, Plane p) {
// Convert AABB to center-extents representation
Point c = (b.max + b.min) * 0.5f; // Compute AABB center
Point e = b.max - c; // Compute positive extents
// Compute the projection interval radius of b onto L(t) = b.c + t * p.n
float r = e[0]*Abs(p.n[0]) + e[1]*Abs(p.n[1]) + e[2]*Abs(p.n[2]);
// Compute distance of box center from plane
float s = Dot(p.n, c) - p.d;
// Intersection occurs when distance s falls within [-r,+r] interval
return Abs(s) <= r;
}
Note that e and r remain the same for all cubes, so calculate them once and use later.
I am looking to generate some 3D trajectory data for an aircraft simulation.
The idea is that the aircraft takes off at some location x and continues to ascend at some average ascent velocity a_v and angle a_theta until it reaches a maximum altitude m_a. The aircraft would then continue at its m_a until it reaches a certain distance d_d from its destination, at which point it will begin its descent at some angle d_theta with an average descent velocity of d_v. Finally, the aircraft lands at destination y.
I would like the function to return a list of 3D points.
I am looking to implement this in either Python (preferred) or C#.
For illustration purposes:
Does anyone know how I can achieve this? Is there perhaps some open source project which does this? I have been looking for a while now, but have not found anything.
I recommend you to solve the problem in 2 independent steps so that the airplane does not pass through the ground :
Calculate the path on the surface of a sphere.
Interpolate the height along this path.
For 1. you can use the spherical interpolation techniques on Quaternions.
Quaternion slerp(Quaternion v0, Quaternion v1, double t) {
// Only unit quaternions are valid rotations.
// Normalize to avoid undefined behavior.
v0.normalize();
v1.normalize();
// Compute the cosine of the angle between the two vectors.
double dot = dot_product(v0, v1);
const double DOT_THRESHOLD = 0.9995;
if (fabs(dot) > DOT_THRESHOLD) {
// If the inputs are too close for comfort, linearly interpolate
// and normalize the result.
Quaternion result = v0 + t*(v1 – v0);
result.normalize();
return result;
}
// If the dot product is negative, the quaternions
// have opposite handed-ness and slerp won't take
// the shorter path. Fix by reversing one quaternion.
if (dot < 0.0f) {
v1 = -v1;
dot = -dot;
}
Clamp(dot, -1, 1); // Robustness: Stay within domain of acos()
double theta_0 = acos(dot); // theta_0 = angle between input vectors
double theta = theta_0*t; // theta = angle between v0 and result
Quaternion v2 = v1 – v0*dot;
v2.normalize(); // { v0, v2 } is now an orthonormal basis
return v0*cos(theta) + v2*sin(theta);
}
You didn't write any code, so I won't write any either. Python with math package is more than enough to solve this problem.
Required steps:
The plane should fly on a great circle. This means you only need one distance to describe X and Y.
You could place the origin at X and specify Y with a latitude.
Calculate the tangent of the Earth at X, and rotate by a_theta. Find the point where it reaches m_a altitude.
Calculate the tangent of the Earth at Y, and rotate by d_theta. Find the point where it reaches m_a altitude.
Draw an arc between the two previous points, with a radius of EarthRadius + m_a
Every coordinate is known in the 2D of the great circle, you just need to rotate them back to 3D coordinates.
For a list of 3D points, you don't need either a_v, d_v or d_d.
I need a C# code snippet calculating the surface and vertex normals. Kind of surface is triangulated 3D closed mesh. The required code snippet must be able to use a vertex set and triangleindices. These are ready to use at the moment. The surface of 3D mesh object is not smooth, so it needs to be smoothed.
Could you help me.
It sounds like you're trying to display your 3D mesh and apply a smooth shading appearance by interpolating surface normals, such as in Phong shading, and you need to calculate the normals first. This is different from smoothing the surface of the mesh itself, since that implies altering the positions of its vertices.
Surface normals can be calculated by getting the vector cross product of two edges of a triangle.
As far as code, I'm unaware of any C# examples, but here is one in C++ that should be easy to port. It is taken from the popular NeHe tutorials for OpenGL:
void calcNormal(float v[3][3], float out[3]) // Calculates Normal For A Quad Using 3 Points
{
float v1[3],v2[3]; // Vector 1 (x,y,z) & Vector 2 (x,y,z)
static const int x = 0; // Define X Coord
static const int y = 1; // Define Y Coord
static const int z = 2; // Define Z Coord
// Finds The Vector Between 2 Points By Subtracting
// The x,y,z Coordinates From One Point To Another.
// Calculate The Vector From Point 1 To Point 0
v1[x] = v[0][x] - v[1][x]; // Vector 1.x=Vertex[0].x-Vertex[1].x
v1[y] = v[0][y] - v[1][y]; // Vector 1.y=Vertex[0].y-Vertex[1].y
v1[z] = v[0][z] - v[1][z]; // Vector 1.z=Vertex[0].y-Vertex[1].z
// Calculate The Vector From Point 2 To Point 1
v2[x] = v[1][x] - v[2][x]; // Vector 2.x=Vertex[0].x-Vertex[1].x
v2[y] = v[1][y] - v[2][y]; // Vector 2.y=Vertex[0].y-Vertex[1].y
v2[z] = v[1][z] - v[2][z]; // Vector 2.z=Vertex[0].z-Vertex[1].z
// Compute The Cross Product To Give Us A Surface Normal
out[x] = v1[y]*v2[z] - v1[z]*v2[y]; // Cross Product For Y - Z
out[y] = v1[z]*v2[x] - v1[x]*v2[z]; // Cross Product For X - Z
out[z] = v1[x]*v2[y] - v1[y]*v2[x]; // Cross Product For X - Y
ReduceToUnit(out); // Normalize The Vectors
}
The normalization function ReduceToUnit() can be found there as well.
Note that this calculates a surface normal for a single triangle. Since you give no information about how your vertices and indices are stored, I will leave it up to you to derive the set of triangles you need to pass to this function.
EDIT: As an additional note, I think the "winding direction" of your triangles is significant. Winding in the wrong direction will cause the normal to point in the opposite direction as well.