Skip to content

Commit bdae298

Browse files
committed
Fix point_in_mesh robustness and reinit_mesh performance
Two fixes: 1. point_in_mesh: use 3-ray majority vote instead of a single ray. A single ray can pass through a shared mesh edge, causing the deduplication to either miss or double-count a crossing and flip the inside/outside parity. Three rays in irrational directions make it extremely unlikely that more than one hits a degenerate case at the same query point. 2. reinit_mesh: skip BVH rebuild if already initialized. Unlike blocks/cylinders, mesh uses absolute coordinates and doesn't depend on the lattice basis. geom_fix_object_ptr is called hundreds of times during meep's init_sim via geometry copies; without the guard, each call rebuilt the BVH (~150ms for 9K triangles), causing ~40s of wasted time.
1 parent cb02445 commit bdae298

1 file changed

Lines changed: 32 additions & 6 deletions

File tree

utils/geom.c

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ static void display_prism_info(int indentby, geometric_object *o);
8383
static void init_prism(geometric_object *o);
8484
static void reinit_prism(geometric_object *o);
8585
static void init_mesh(geometric_object *o);
86+
static void reinit_mesh(geometric_object *o);
8687
static boolean point_in_mesh(const mesh *m, vector3 p);
8788
static vector3 normal_to_mesh(const mesh *m, vector3 p);
8889
static void get_mesh_bounding_box(const mesh *m, geom_box *box);
@@ -183,7 +184,7 @@ void geom_fix_object_ptr(geometric_object *o) {
183184
break;
184185
}
185186
case GEOM MESH: {
186-
init_mesh(o);
187+
reinit_mesh(o);
187188
break;
188189
}
189190
case GEOM GEOMETRIC_OBJECT_SELF:
@@ -2452,11 +2453,22 @@ static int count_ray_mesh_intersections(const mesh *m, vector3 origin, vector3 d
24522453
static boolean point_in_mesh(const mesh *m, vector3 p) {
24532454
if (!m->is_closed) return 0;
24542455

2455-
/* Cast a ray along a non-axis-aligned direction to reduce chance of
2456-
hitting mesh edges exactly (where deduplication is most fragile). */
2457-
vector3 dir = {0.57735026918962576, 0.57735026918962576, 0.57735026918962576};
2458-
int crossings = count_ray_mesh_intersections(m, p, dir);
2459-
return (crossings % 2) == 1;
2456+
/* Cast rays along multiple directions and take majority vote.
2457+
A single ray can give wrong parity when it passes through a mesh
2458+
edge or vertex, causing deduplication to miss or double-count
2459+
a crossing. Three irrational directions make it extremely unlikely
2460+
that more than one ray hits a degenerate case at the same point. */
2461+
static const vector3 dirs[3] = {
2462+
{0.57735026918962576, 0.57735026918962576, 0.57735026918962576}, /* (1,1,1)/√3 */
2463+
{0.80178372573727319, 0.53452248382484879, 0.26726124191242440}, /* (3,2,1)/√14 */
2464+
{0.12309149097933272, 0.49236596391733088, 0.86164043685532904}, /* (1,4,7)/√66 */
2465+
};
2466+
int votes = 0;
2467+
for (int d = 0; d < 3; d++) {
2468+
int crossings = count_ray_mesh_intersections(m, p, dirs[d]);
2469+
if (crossings % 2 == 1) votes++;
2470+
}
2471+
return votes >= 2;
24602472
}
24612473

24622474
static vector3 normal_to_mesh(const mesh *m, vector3 p) {
@@ -2622,6 +2634,20 @@ static void init_mesh(geometric_object *o) {
26222634
/* make_mesh constructors */
26232635
/***************************************************************/
26242636

2637+
static void reinit_mesh(geometric_object *o) {
2638+
mesh *m = o->subclass.mesh_data;
2639+
/* Skip if already initialized — mesh uses absolute coordinates
2640+
and doesn't depend on the lattice basis, unlike blocks/cylinders. */
2641+
if (m->bvh != NULL) return;
2642+
free(m->face_normals);
2643+
free(m->face_areas);
2644+
free(m->bvh_face_ids);
2645+
m->face_normals = NULL;
2646+
m->face_areas = NULL;
2647+
m->bvh_face_ids = NULL;
2648+
init_mesh(o);
2649+
}
2650+
26252651
static int mesh_is_auto_center(vector3 c) {
26262652
return isnan(c.x) && isnan(c.y) && isnan(c.z);
26272653
}

0 commit comments

Comments
 (0)