-
Notifications
You must be signed in to change notification settings - Fork 618
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prism geometry bug #864
Comments
I'm taking a look at this bug. The plan is to start by updating |
The code from the paper seems to be having its own problems with our implementation; now almost none of the prism shows up correctly: This is with boolean node_in_or_on_polygon(vector3 q0, vector3 *nodes, int num_nodes,
boolean include_boundaries)
{
vector3 u = {0.0, -1.0, 0.0};
int nn, edges_crossed=0;
for(nn=0; nn<num_nodes; nn++)
{ int status = intersect_ray_with_segment(q0, nodes[nn], nodes[(nn+1)%num_nodes], u, 0);
if (status==IN_SEGMENT)
return include_boundaries;
else if (status==INTERSECTING)
edges_crossed++;
else if (status==ON_RAY)
{ vector3 nm1 = nodes[ (nn==0 ? num_nodes-1 : nn-1) ];
vector3 n0 = nodes[ nn ];
vector3 np1 = nodes[ (nn+1) % num_nodes ];
vector3 np2 = nodes[ (nn+2) % num_nodes ];
int last_status = intersect_ray_with_segment(q0, nm1, n0, u, 0);
if (last_status==INTERSECTING) edges_crossed--;
int next_status = intersect_ray_with_segment(q0, np1, np2, u, 0);
if (next_status==INTERSECTING) edges_crossed--;
}
}
return (edges_crossed % 2);
} to boolean node_in_or_on_polygon(vector3 q0, vector3 *nodes, int num_nodes,
boolean include_boundaries)
{
// Create axes
vector3 xAxisPos = {1.0, 0.0. 0.0};
vector3 xAxisNeg = {-1.0, 0.0, 0.0};
// Initial start point
vector3 startPoint;
vector3 endPoint;
int startNodePosition = -1;
int nn, edges_crossed=0;
// Is q0 on a vertex or edge?
// Transform coordinate system of nodes such that q0 is at 0|0
for(nn=0; nn<num_nodes; nn++) {
int status = intersect_ray_with_segment(q0, nodes[nn], nodes[(nn+1)%%num_nodes], xAxisPos, 0);
if (status==IN_SEGMENT) {
return include_boundaries;
}
nodes[nn].x -= q0.x;
nodes[nn].y -= q0.y;
// Find start point which is not on the x axis (from q0)
if (nodes[nn].y != 0) {
startPoint.x = nodes[nn].x;
startPoint.y = nodes[nn].y;
startNodePosition = nn;
}
}
// Move q0 to 0|0
q0.x = 0;
q0.y = 0;
// No start point found and point is not on an edge or node
// --> point is outside
if (startNodePosition == -1) {
return 0;
}
int checkedPoints = 0;
nn = startNodePosition;
// Consider all edges
while (checkedPoints < num_points) {
int savedX = nodes[ (nn+1)%num_nodes ].x;
int savedIndex = (nn+1)%num_nodes;
// Move to next point which is not on the x-axis
do {
nn = (nn+1)%num_nodes;
checkedPoints++;
} while (nodes[nn].y == 0);
// Found end point
endPoint.x = nodes[nn].x;
endPoint.y = nodes[nn].y;
// Only intersect lines that cross the x-axis
if (startPoint.y * endPoint.y < 0) {
// No nodes have been skipped and the successor node
// has been chose as the end point
if (savedIndex == nn) {
status = intersect_ray_with_segment(q0, startPoint, endPoint, xAxisPos, 0);
if (status == INTERSECTING) {
edges_crossed++;
}
}
// If at least one node on the right side has been skipped,
// the original edge would have been intersected
// --> intersect with full x-axis
else if (savedX > 0) {
int statusPos = intersect_ray_with_segment(q0, startPoint, endPoint, xAxisPos, 0);
int statusNeg = intersect_ray_with_segment(q0, startPoint, endPoint, xAxisNeg, 0);
if (statusPos == INTERSECTING || statusNeg == INTERSECTING) {
edges_crossed++;
}
}
}
// End point is the next start point
startPoint = endPoint;
}
// Odd count --> in the polygon (1)
// Even count --> outside (0)
return count % 2;
} Note this code is heavily based off the demo from the paper with the "perfect" algorithm. |
What polygon are you testing it on? Did you try with a simple polygon that you know works with the existing method? |
Note that all of the computational geometry calculations are done in https://github.com/NanoComp/libctl/blob/master/utils/geom.c — everything else in Meep calls that, so if you fix the problem in libctl (and recompile/reinstall libctl and recompile Meep) then it will be fixed everywhere. |
Some progress has been made in boolean node_in_or_on_polygon(vector3 q0, vector3 *nodes, int num_nodes,
boolean include_boundaries)
{
// Create axes
vector3 xAxis = {1.0, 0.0, 0.0};
// Initial start point
vector3 startPoint;
vector3 endPoint;
int startNodePosition = -1;
int nn, edges_crossed=0;
// Is q0 on a vertex or edge?
// Transform coordinate system of nodes such that q0 is at 0|0
for(nn=0; nn<num_nodes; nn++) {
int status = intersect_ray_with_segment(q0, nodes[nn], nodes[(nn+1)%num_nodes], xAxis, 0);
if (status==IN_SEGMENT) {
return include_boundaries;
}
// Find start point which is not on the x axis (from q0)
if (nodes[nn].y - q0.y != 0) {
startPoint.x = nodes[nn].x;
startPoint.y = nodes[nn].y;
startNodePosition = nn;
}
}
// No start point found and point is not on an edge or node
// --> point is outside
if (startNodePosition == -1) {
return 0;
}
int checkedPoints = 0;
nn = startNodePosition;
// Consider all edges
while (checkedPoints < num_nodes) {
int savedX = nodes[ (nn+1)%num_nodes ].x;
int savedIndex = (nn+1)%num_nodes;
// Move to next point which is not on the x-axis
do {
nn = (nn+1)%num_nodes;
checkedPoints++;
} while (nodes[nn].y - q0.y == 0);
// Found end point
endPoint.x = nodes[nn].x;
endPoint.y = nodes[nn].y;
// Only intersect lines that cross the x-axis
if ((startPoint.y - q0.y) * (endPoint.y - q0.y) < 0) {
// No nodes have been skipped and the successor node
// has been chose as the end point
if (savedIndex == nn) {
int status = intersect_ray_with_segment(q0, startPoint, endPoint, xAxis, 0);
if (status == INTERSECTING) {
edges_crossed++;
}
}
// If at least one node on the right side has been skipped,
// the original edge would have been intersected
// --> intersect with full x-axis
else if (savedX > 0) {
int status = intersect_line_with_segment(q0, startPoint, endPoint, xAxis, 0);
if (status == INTERSECTING) {
edges_crossed++;
}
}
}
// End point is the next start point
startPoint = endPoint;
}
// Odd count --> in the polygon (1)
// Even count --> outside (0)
return edges_crossed % 2;
} When I build libctl and then test in meep, Alec's script from above (loading in the There is still something wrong with the new version of |
There must have simply been a build error yesterday or something because today the same code builds to have the curved rectangle look correct with both |
With some more updates to For reference, this is the code for the new version of boolean node_in_or_on_polygon(vector3 q0, vector3 *nodes, int num_nodes,
boolean include_boundaries)
{
// Create axes
vector3 xAxis = {1.0, 0.0, 0.0};
// Initial start point
vector3 startPoint;
vector3 endPoint;
int startNodePosition = -1;
int nn, edges_crossed = 0;
// Is q0 on a vertex or edge?
// Transform coordinate system of nodes such that q0 is at 0|0
for(nn = 0; nn < num_nodes; nn++) {
int status = intersect_ray_with_segment(q0, nodes[nn], nodes[(nn+1)%num_nodes], xAxis, 0);
if (status == IN_SEGMENT) {
return include_boundaries;
}
// Find start point which is not on the x axis (from q0)
if (fabs(nodes[nn].y - q0.y) > THRESH) {
startNodePosition = nn;
startPoint = nodes[startNodePosition];
}
}
// No start point found and point is not on an edge or node
// --> point is outside
if (startNodePosition == -1) {
return 0;
}
int checkedPoints = 0;
nn = startNodePosition;
// Consider all edges
while (checkedPoints < num_nodes) {
int savedIndex = (nn+1)%num_nodes;
int savedX = nodes[savedIndex].x;
// Move to next point which is not on the x-axis
do {
nn = (nn+1)%num_nodes;
checkedPoints++;
} while (fabs(nodes[nn].y - q0.y) < THRESH);
// Found end point
endPoint = nodes[nn];
// Only intersect lines that cross the x-axis (don't need to correct for rounding
// error in the if statement because startPoint and endPoint are screened to
// never lie on the x-axis)
if ((startPoint.y - q0.y) * (endPoint.y - q0.y) < 0) {
// No nodes have been skipped and the successor node
// has been chose as the end point
if (savedIndex == nn) {
int status = intersect_ray_with_segment(q0, startPoint, endPoint, xAxis, 0);
if (status == INTERSECTING) {
edges_crossed++;
}
}
// If at least one node on the right side has been skipped,
// the original edge would have been intersected
// --> intersect with full x-axis
else if (savedX > THRESH) {
int status = intersect_line_with_segment(q0, startPoint, endPoint, xAxis, 0);
if (status == INTERSECTING) {
edges_crossed++;
}
}
}
// End point is the next start point
startPoint = endPoint;
}
// Odd count --> in the polygon (1)
// Even count --> outside (0)
return edges_crossed % 2;
} You can see in this imgur gallery the results from comparison run between the two old and new versions of In the old version, I found that the This artifact introduction is typified in polygons with orthogonal vertices. In the batch of 80 such polygons I tested, 78 introduced artifacts to the render, but it shows up elsewhere, too. Over the 263 polygons I tested, about 90 showed this error. By comparison, the new version of Over the 263 polygons I tested, 49 showed this error. I think it's safe to say that, on average, the new version introduces smaller errors less often than the old version and that libctl probably wouldn't be hurt with an update containing the new version of To solve the error with lines, I would say we should start by looking at the possibility of floating-point error. I've noticed that all or nearly all of the line errors occur to the left of another vertex. The algorithm I used is supposed to account for this possibility (see this paper), but perhaps there's a floating-point leak somewhere. |
Can you file a new issue (in libctl, not in Meep) with a simple case exhibiting the spurious lines? |
It looks like there is a bug within the prism geometry implementation. I'm assuming it's a bug within the
node_in_or_on_polygon()
method oflibctl
(similar to #400).I drew a simple arc and saved the geometry as a GDS file that could easily be imported using the
gdspy
library (I bypassed thelibGDSii
library to rule out any problems there).Using the following code:
meep renders the following geometry:
If I had to guess, I would say this is one of those weird boundary cases that apply to most even-odd point-in-polygon algorithms. I've done a little digging to see if anyone has developed an algorithm resilient to these kinds of errors.
Here's a paper that compares several different even-odd methods and finds errors with all of them.
Here's another paper referencing the previous paper that implements a new even-odd algorithm that claims to be "perfect". It's not terribly different from @HomerReid's current implementation.
Here's the GDS.
Here's a tarball with all of the test cases used by the first paper (stored as text files with all of the vertices). Perhaps this could be used to generate a more comprehensive test engine.
It's probably best if @HomerReid takes a look at everything first, but I'm happy to help!
The text was updated successfully, but these errors were encountered: