Actually, I can detect border or edges of a convex triangular mesh by checking which edge of the triangle does not have any neighbor. So, if a mesh has some holes, then we can highlight that part easily because we have edge vertices.
But the issue is, if we just have the edge vertices or borders, how can we know that the mesh has some holes ? and how many holes the mesh has ?
I thought enough about this issue, but unable to get it, any idea ? What should be the condition or check for hole detection ?
After detection of a hole I want to fill it. But first thing is to detect it ?
Thanks.
Assuming that the mesh is connected and you can highlight all the boundaries. You are left with all the holes + one additional boundary which is that of mesh itself. You can just discard the boundary with the biggest length of them and get all the holes.
A triangular mesh derived from a scanner (eg. Kinect) can have small fragments (isolated patches) as well as small holes. I suggest a hole can generally be detected by counting the number of vertices that neighbor the vertices on the boundary. It is not a hole if there are fewer neighboring vertices than boundary vertices.
My answer will only work for a closed mesh, but it will handle the case of concave and convex holes.
For the sake of explanation, lets imagine a 2D mesh.
Calculate a bounding box for the mesh. In our example the bounding box needs to store min and max values for the X and Y axis, and a corresponding vertex index for each value:
struct BoundingBox
{
float minX,maxX,minY,maxY;
int vminX,vmaxX,vminY,vmaxY;
}
Iterate over every vertex in the mesh, growing the bounding box as you add each point. When a vertex is responsible for changing one of the min/max values, store or overwrite the corresponding vmin/vmax value with the vertex mesh index.
E.g.
BoundingBox bounds;
bounds.minX = verts[0].X;
bounds.maxX = verts[0].X;
bounds.minY = verts[0].Y;
bounds.maxY = verts[0].Y;
bounds.vminX = bounds.vmaxX = bounds.vminY = bounds.vmaxY = 0;
for (int i = 1; i < numVerts; i++)
{
Vertex v = verts[i];
if (v.X < bounds.minX) { bounds.minX = v.X; bounds.vminX = i; }
if (v.X > bounds.maxX) { bounds.maxX = v.X; bounds.vmaxX = i; }
if (v.Y < bounds.minY) { bounds.minY = v.Y; bounds.vminY = i; }
if (v.Y > bounds.maxY) { bounds.maxY = v.Y; bounds.vmaxY = i; }
}
Now iterate over your boundaries until you find one which contains ALL the vertices you gathered in the bounding box. This is your outer boundary. The remaining boundaries are holes within the mesh.
Indeed, a hole in triangular mesh is a closed loop of adjacent oriented boundary edges, each of which does not have a triangle from one side (e.g. from left).
So the algorithm to enumerate all holes can be as follows
Find all boundary edges (the edges without left triangle) and compose a set with them.
Take any boundary edge from the set, then search for the next edge in the set originating in the vertex where the previous edge terminates, remove it from the set as well and continue until edge loop is closed. This will give you one of the holes.
Repeat step 2. until the set is empty.
Some operations here can be optimized by using specialized data structures for mesh representation, e.g. half-edges.
The animation below demonstrates how starting from an arbitrary boundary edge (under the cursor), the whole hole can be found and highlighted:
(captured in MeshInspector application)
Related
As part of a procedural mesh generator, working on dual contouring to convert signed distance functions to meshes.
Hoping some folks with experience about the algorithm can help with this bit of it near the end.
We have
voxel grid edge cut-points and normals where the function surface crosses the edges
a. we visit each corner of the grid, and project edges out along the x,y, and z axis
b. each edge that exhibits a sign change in the function is deemed active
vertex points inside each voxel, calculated as the closest point of intersection of the planes defined by the cut-points and normals on the voxel edges
a. we find each voxel to generate a vertex for by visiting each active edge
b. wind around the edge in a counter-clockwise direction relative to the axis the edge is on.
generation of the mesh by winding around each active edge to connect the vertices inside of the 4 adjacent voxels into a quad.
When each face is generated, I must determine which way the face is facing so that the winding order is correct for the triangles making up the quad.
First we get the average normal for the each voxel vertex:
float3 faceNormal = new float3(0f,0f,0f);
float nCount = 0f;
for (int eachVoxel = 0; eachVoxel < 4; eachVoxel++)
{
vid = edgeVoxels[eachVoxel];
var theVoxel = _voxelsById[vid];
faceNormal += theVoxel.VertexNormal.xyz;
nCount += 1f;
}
faceNormal /= nCount;
faceNormal /= faceNormal.Length();
Then we determine if we need to reverse the order:
float faceAngle = float3.Dot(_axis[edge.EdgeDir], faceNormal);
if (faceAngle > 0.0f)
{
distinctMeshVoxels = distinctMeshVoxels.Reverse();
}
This almost works. Take this raymarched scene of an object:
Now here is the mesh imported into mesh lab. Notice the black triangle -- it's facing the wrong direction. Sharp edges seem to be a problem sometimes. I've tried various other approaches:
1. Averaging the edge cut-point normals, and
2. Finding the cutpoint nearest to the triangle centroid and using it's normal,
Nothing is even as good as the image below.
Appreciate any advice or ideas on how to perfect this part of the algorithm.
In dual contouring one first detects the edges with a sign change, which are crossed by the reconstructed 3D surface. Each such edge produces a quad in the surface. The triangles constituting the quad are oriented so that the vertex of the edge with the positive value is located in the positive half-space relative the triangle and its normal.
Here is a simple tutorial of dual contouring for more detail.
How to rotate an object, so that its vertices never overlap with any of the other rotations? With a predefined number of rotations.
Idea:
It can be achieved with relaxation. (Idea comes from Greg Turk's paper: Generating Textures on Arbitrary Surfaces Using Reaction-Diffusion)
Steps:
Generate x dodecahedrons or any object symmetric to its centre
point.
These objects should be identical in position, orientation
and size. (so we can create an easy relation between vertices =>
ones that overlap at the beginning are related)
Create a function that calculates the distance between the related points.
Maximize the average distance between related points. (every point has x-1
related points)
Problems:
This is not a simple relaxation problem with points. Here, due to the dodecahedron constraint, I cannot just translate around. Rotation matrixes/quaternions are needed.
Possible solution with Brute force
Summary:Rotate randomly until desired average distance is achieved.
Explanation:Every dodecahedron is rotated until it's vertices do not overlap any vertices of the other dodecahedrons.
Then average distance is calculated and checked against the best (minimal) so far. Save all vertex positions, and the rotation quaternion, that will turn the base dodecahedron into the rotated one.
float minThreshold <- user defined
int iterationThreshold <- user defined
float minAveDistance = Infinity;
while (minAveDistance > minThreshold || maxIteration > iterationThreshold) {
Foreach (dodecahedron) { // except the first one, that can stay as is
// rotate randomly until there are no overlapping positions with the other dodecahedrons
while (checkOverlappingWithOtherDodecahedrons(dodecahedron)) {
rotateRandomly(dodecahedron);
}
}
float aveDistance = CalculateAverageDistanceBetweenAllPointsOfDodecahedrons();
if (aveDistance < minAveDistance) {
minAveDistance = aveDistance;
SaveAllPositions(); // e.g.: to a file
SaveAllRotationQuaternionsFromStartOrientation(); // e.g.: to a file
}
}
I have list of Points (int x, int y).
together they form areas, I check if this area is closed and then I need to get inner area formed by all positions that are inside this area.
example area:
Only idea I had is to convert this area to vector and check every point if it is inside polygon or not, counting intersections of polygon a axis's of point.
But I don't think it would be the most efficient way to do it.
other idea was to first get all points that are outside, I start from corners (if corner is not part of list of points, then is 100% empty), add all neighbor points that are empty and repeat.
then all points that aren't outside and aren't in highlighted list are inside.
but again, it feels somehow cumbersome...
To find all inner grid points of grid polygon, one can exploit these observations:
for each inner grid point (x,y) also (x,y+0.5) and (x,y-0.5) are inner points.
the lines defined by y=n+0.5 have simple intersections with the grid polygon
This leads to the following algorithm:
As a prerequisite one needs all non-horizontal (i.e. vertical and diagonal) polygon edges, actually only the x-coords of the centers in ascending order for each (second) mid-row.
The grid is scanned at each second horizontal "mid-line", i.e. y=2n+0.5, where n is from a sufficient range of integers s.t. the polygon is "covered", see the blue lines in the scetch.
Starting from the left all intersections with the polygon (i.e. the non-horizontal edges) and all inner points of the form (m,2n+0.5) are to be detected, see the red and green circles (this is done by iterating over the x-coors of the edges' centers)
Now the vertical grid neighbours (m,2n) and (m,2n+1) of inner points (m,2n+0.5) are inner points, if they are not on the boundary, see the green points in the scetch.
Here is some pseudo code (C++/python inspired :-) ):
list<Point> polygon; // given polygon as list of neighbouring grid points
// get centers of non-horizontal edges organized by line
map<int, set<float> > edgeCentersX; // for each scan line the x-coords of edges in ascending order
p_i = polygon[0]
yMin, yMax = 999999, -999999
for (i=1; i<polygon.size(); ++i)
p_i1 = polygon[i] // next point after p_i
if (p_i.x == p_i1.x)
continue // horizontal edges can be ignored
yMin_i = min(p_i.y, p_i1.y)
if (yMin_i % 2 == 1)
continue // we only need to look at each second mid-row
if (yMin_i < yMin)
yMin = yMin_i
if (yMin_i > yMax)
yMax = yMin_i
cx = 0.5*(p_i.x+p_i1.x)
edgeCentersX[yMin_i].insert(cx) // store edge center (yMin_i+0.5, cx)
p_i = p_i1
list<Point> innerPoints
for (y=yMin; y<= yMax; y+=2)
inside = false
cx_i = edgeCentersX[y][0]
for (i=1; i<edgeCentersX[y].size(); ++i)
cx_i1 = edgeCentersX[y][i]
inside = !inside
if (!inside)
continue
for (x=floor(cx_i)+1; x<cx_i1; ++x)
pLower = Point(y,x)
if (!polygon.contains(pLower))
innerPoints.append(pLower)
pUpper = Point(y+1,x)
if (!polygon.contains(pUpper))
innerPoints.append(pUpper)
Pick's theorem might be the formula you are looking for. It allows rather simple computation of the area for a polygon whose corners are grid points (i.e. have integer coordinates).
I'm looking to return the coordinates of the points bounding the area of overlap between 2 arbitrary rectangles in 2D. Whats the best way to approach this that would take care of all the special cases eg:
Rectangles intersecting only on a single vertex ? (the program would have to return the lone vertex)
Rectangles which share whole or part of a side ? (the program would have to return the endpoints of the common line segment)
To add to the complexity, it has to order the points in either clockwise/anticlockwise order. As such, I can use a convex hull algorithm to order them before reporting, but if there's an algorithm that can figure out the bounding points in order directly, that'll be the best !!
Any ideas of what avenues I should be looking at ? I'm not looking for code projects etc, only a general idea of a generic algorithm for which I don't have to keep a lot of
if "special case" then "do this" kind of code.
EDIT: The rectangles are arbitrary - i.e. they may/may not be parallel to X/Y axis...
Just use the general convex polygon intersection method. Look up intersect convex polygons rotating calipers.
Alright, we'll try this again...
Consider a union of the two, made up of areas defined by drawing a line from every vertex in ABCD (in black) to EFGH (in red):
The hard part here is coming up with all of the shapes defined by these lines (both the gray lines and the original lines coming from the ABCD and EFGH rectangles.)
Once you figure that out, create a linked list of these shapes, and assume every one of these shapes exists within the intersection.
Step 1. Translate & rotate everything so that ABCD has one vertex on 0,0 and its lines are parallel/perpendicular to the x and y axes.
Step 1A. Find the lowest y-value vertex in ABCD, and then subtract it from all other verts in the scene. Let's assume for the purposes of demonstration that that vertex is C. By subtracting C from every vertex in the scene, we will effectively move the origin (0,0) to C, making rotation around C easy.
for all shapes in linked list {
for all vertices in shape {
vertex -= C;
}
}
Step 1B. Rotate everything about the origin by an angle equal to the angle between the C->B vector and the x-axis, so that B lands on the x-axis:
// see http://en.wikipedia.org/wiki/Atan2
float rotRadians = atan2(B.x, B.y); // assuming ABCD vertices are labelled clockwise
for all shapes in linked list {
for all vertices in shape {
rot(thisVert, rotRadians);
}
}
// ...
// function declaration
void rot(theVertex, theta) {
tempX = theVertex.x;
tempY = theVertex.y;
theVertex.x = cos(theta) * tempX + sin(theta) * tempY;
theVertex.y = cos(theta) * tempY - sin(theta) * tempX;
}
If ABCD vertices were labelled clockwise, the ABCD vertices should now look like this:
A = ABCDx , ABCDy
B = ABCDx , 0
C = 0 , 0
D = 0 , ABCDy
(If they were not labeled clockwise, then the "lies within" check in Step 2 won't work, so make sure the vert used in the atan2(...) call is the vertex counterclockwise from the lowest vertex.)
Step 2. Now we can easily analyze whether or not a shape lies within the ABCD rectangle, e.g. if (thisVert.x >= 0 && thisVert.y >= 0 && thisVert.x <= ABCDx && thisVert.y <= ABCDy). Traverse the linked list of shapes, and check to make sure each vertex of each shape lies within ABCD. If one vertex of a shape does not lie within ABCD, then that shape is not part of the ABCD/EFGH intersection. Mark it as not part of the intersection and skip to the next shape.
Step 3. Undo the rotation from Step 1B, then undo the translation from Step 1A.
Step 4. Repeat Steps 1-3 with EFGH instead of ABCD, and you will have your intersection set.
If the only intersection between the two sets is a line, then the above will return nothing as an intersection. If the intersection == NULL, then check for lines that intersect.
If the intersection is still NULL, then check for intersecting points.
This is probably really rough but:
object rectangle {
pos { x, y } // top-left position
size { height, width } // rectangle-size
}
collision::check (rectangle rect) {
// collision-detection logic
collision->order_coords(coords); // order-coords clockwise;
return collision_result_object; // return collided vertices, ordered clockwise, or 0 if rect hit nothing
}
collision::order_rects (rectangle *rect, opt angle) {
return clockwise_rects; // returns rectangles ordered clockwise
}
collision::order_coords (coordinate *coord, opt angle) {
return ordered_coords; // recieves coordinates and returns ordered clockwise
}
rectangle rects; // bunch of rectangles
ordered_rects = collision->order_rects (rects); // order rects starting at 12PM
loop {
foreach ordered_rects as i {
if (collision->check(i)) {
array_of_collision_result_objects[i] = collision->check(i); // start checking rects starting at 12PM, if collision found, return ordered vertexes
}
}
}
Find all the intersections of segments of rectangles. The result consists of some of them and some of initial vertices. To find them just check for every point it lies in both rectangles. Remove unnecessary points (if there are 3 or more on one line). The result is convex and no point you get is strictly inside it, so (if there are at least 3 of them) sort points from some inner point by angle and enjoy the result.
I've come up with a reasonable method that should cover all possible cases:
All we need is basically 3 steps :
Step 1:
for each side Si of R1
for each side Sj of R2
Check if Si and Sj intersect. If they do, push the point in results array
(This also has to take care of the case in case Si and Sj overlap, which is
basically checking if they have the same equation or not - if so, push in
the points of overlap. This also takes care of the case where a vertex of
R2 lies on Si).
next
next
Step 2:
for each vertex Vi of R1
Check if Vi lies inside R2, If so, push it in the results array.
next
Step 3:
for each vertex Vi of R2
Check if Vi lies inside R1, If so, push it in the results array.
next
Now, order the results array, and return
For step 2 & 3 (how to find if a point lies inside a rectangle) - I'd use this excellent article (the last algorithm stated there).
I know there are lots of posts about collision detection generally for sprites moving about a 2D plane, but my question is slightly different.
I'm inserting circles into a 2D plane. The circles have variable radii. I'm trying to optimize my method of finding a random position within the plane where I can insert a new circle without it colliding with any other circles already on the plane. Right now I'm using a very "un-optimized" approach that simply generates a random point within the plane and then checks it against all the other circles on the plane.
Are there ways to optimize this? For this particular app, the bounds of the plane can only hold 20-25 circles at a time and typically there are between 5-10 present. As you would expect, when the number of circles approaches the max that can fit, you have to test many points before finding one that works. It gets very slow.
Note: safeDistance is the radius of the circle I want to add to the plane.
Here's the code:
- (CGPoint)getSafePosition:(float)safeDistance {
// Point must be far enough from edges
// Point must be far enough from other sprites
CGPoint thePoint;
BOOL pointIsSafe = NO;
int sd = ceil(safeDistance);
while(!pointIsSafe) {
self.pointsTested++; // DEBUG
// generate a random point inside the plane boundaries to test
thePoint = CGPointMake((arc4random() % ((int)self.manager.gameView.frame.size.width - sd*2)) + sd,
(arc4random() % ((int)self.manager.gameView.frame.size.height - sd*2)) + sd);
if(self.manager.gameView.sprites.count > 0) {
for(BasicSprite *theSprite in self.manager.gameView.sprites) {
// get distance between test point and the sprite position
float distance = [BasicSprite distanceBetweenPoints:thePoint b:theSprite.position];
// check if distance is less than the sum of the min safe distances of the two entities
if(distance < (safeDistance + [theSprite minSafeDistance])) {
// point not safe
pointIsSafe = NO;
break;
}
// if we get here, the point did not collide with the last tested point
pointIsSafe = YES;
}
}
else {
pointIsSafe = YES;
}
}
return thePoint;
}
Subdivide your window into w by h blocks. You'll be maintaining a w by h array, dist. dist[x][y] contains the size of the largest circle that can be centred at (x, y). (You can use pixels as blocks, although we'll be updating the entire array with each circle placed, so you may want to choose larger blocks for improved speed, at the cost of slightly reduced packing densities.)
Initialisation
Initially, set all dist[x][y] to min(x, y, w - x, h - y). This encodes the limits given by the bounding box that is the window.
Update procedure
Every time you add a circle to the window, say one positioned at (a, b) with radius r, you need to update all elements of dist.
The update required for each position (x, y) is:
dist[x][y] = min(dist[x][y], sqrt((x - a)^2 + (y - b)^2) - r);
(Obviously, ^2 here means squaring, not XOR.) Basically, we are saying: "Set dist[x][y] to the minimum distance to the circle just placed, unless the situation is already worse than that." dist values for points inside the circle just placed will be negative, but that doesn't matter.
Finding the next location
Then, when you want to insert the next circle of radius q, just scan through dist looking for a location with dist value >= q. (If you want to randomly choose such a location, find the complete list of valid locations and then randomly choose one.)
Honestly, with only 20-25 circles, you're not going to get much of a speed boost by using a fancier algorithm or data structure (e.g. a quadtree or a kd-tree). Everything is fast for small n.
Are you absolutely sure this is the bottleneck in your application? Have you profiled? If yes, then the way you're going to speed this up is through microoptimization, not through advanced algorithms. Are you making lots of iterations through the while loop because most of the plane is unsafe?
You could split your plane in lots of little rectangles (slightly quadtree-related) and save which rectangles are hit by at least one of the circles.
When you look for a insertion-point, you'll just have to look for some "empty" ones (which doesn't need any random jumps and is possible in constant time).
The number and constellation of rectangles can be computed by the radius.
Just an outline, since this solution is fairly involved.
If you want to guarantee you always find a place to put a circle if it's possible, you can do the following. Consider each existing circle C. We will try to find a location where we can place the new circle so that it is touching C. For each circle D (other than C) that is sufficiently close to C, there will be a range of angles where placing a new circle at one of those angles around C will make it intersect with D. Some geometry will give you that range. Similarly, for each of the four boundaries that are close enough to C, there will be a range of angles where placing a new circle at one of those angles will make it intersect with the boundary. If all these intervals cover all 360 degrees around C, then you cannot place a circle adjacent to C, and you will have to try the next circle, until there are no more candidates for C. If you find a place to put the new circle, you can move it some random distance away from C so that all your new circles do not have to be adjacent to an existing circle if that is not necessary.