From 178517c030f6a8bbdb61dd9024852f1f64c69122 Mon Sep 17 00:00:00 2001 From: Luochenghuang Date: Fri, 29 May 2026 23:22:24 -0700 Subject: [PATCH 1/6] Refactor mesh storage with PIMPL: opaque internal cache behind scheme-driven public struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #74 (mesh geometry import) hand-extended utils/ctlgeom-types.h with C-only internal fields (mesh_bvh_node, face_normals, face_areas, bvh, bvh_face_ids, is_closed, centroid, lengthscale, num_bvh_nodes, and a flat int* face_indices) because none of those had natural Scheme representations in geom.scm. That worked when Guile wasn't detected at configure time, but any from-git build with Guile installed (including the new GitHub Actions CI) regenerated ctlgeom-types.h from geom.scm, clobbered the hand-extensions, and failed to compile with 'unknown type name mesh_bvh_node'. This commit applies the PIMPL (pointer-to-implementation) idiom to clean up that situation properly. The public mesh struct in ctlgeom-types.h now contains only Scheme-visible fields — vertices, face_indices (as a vector3_list, each vector3 packing one triangle's three int indices), and a void* internal pointer. All C-only state moves into mesh_internal in a new private header utils/mesh-internal.h, accessed via a static-inline mesh_priv(m) cast helper. ctlgeom-types.h goes back to being a regular auto-generated artifact derived from geom.scm; no Makefile.am workaround needed. Lifecycle: - make_mesh / make_mesh_with_center: populate public fields, then call init_mesh which calloc()s mesh_internal, unpacks face_indices into a flat int[], computes face normals/areas, checks watertight closure, builds the BVH, and stores the pointer in m->internal. - reinit_mesh: fast-path returns when m->internal != NULL; otherwise calls init_mesh. Same single-threaded-init contract as before. - mesh_destroy (auto-generated): frees vertices.items + face_indices.items, then calls mesh_internal_free (defined in geom.c) to free the cache. - mesh_copy (auto-generated): deep-copies the public fields, sets internal=NULL, then calls mesh_init_internal (defined in geom.c) to eagerly rebuild the BVH on the copy. Eliminates the previous shallow-pointer-share footgun where two mesh copies pointed at the same BVH allocation. geom.c gets ~105 mechanical m->X → mesh_priv(m)->X rewrites for the 10 internal fields (the cast inlines to nothing at runtime). test-mesh.c gets the same rewrite for 6 sites where tests poke directly at m->is_closed. Both auto-generated files (ctlgeom-types.h and geom-ctl-io.c) now match what gen-ctl-io would emit from the simplified geom.scm, with one hand-tweak in geom-ctl-io.c: mesh_destroy and mesh_copy call into the externally-defined mesh_internal_free / mesh_init_internal so they can manage the opaque cache. This is the minimum manual surface required for any PIMPL design. Verified: configure + make + make check all clean. test-prism passes (0/10000 point failures, 0/1000 segment failures, 0/65 prism helper failures). test-mesh passes 0 failures across all 19 cases including test_copy_destroy (which exercises mesh_copy + point queries on the copy + destroy of both original and copy). --- utils/ctlgeom-types.h | 21 +--- utils/geom-ctl-io.c | 46 ++++--- utils/geom.c | 286 ++++++++++++++++++++++++------------------ utils/geom.scm | 6 +- utils/mesh-internal.h | 45 +++++++ utils/test-mesh.c | 13 +- 6 files changed, 246 insertions(+), 171 deletions(-) create mode 100644 utils/mesh-internal.h diff --git a/utils/ctlgeom-types.h b/utils/ctlgeom-types.h index 02744d8..f92be6d 100644 --- a/utils/ctlgeom-types.h +++ b/utils/ctlgeom-types.h @@ -105,27 +105,10 @@ extern "C" { matrix3x3 m_p2c; } prism; - typedef struct mesh_bvh_node { - vector3 bbox_low; - vector3 bbox_high; - int left_child; - int right_child; - int face_start; - int face_count; - } mesh_bvh_node; - typedef struct mesh_struct { vector3_list vertices; - int num_faces; - int *face_indices; - vector3 *face_normals; - number *face_areas; - int num_bvh_nodes; - mesh_bvh_node *bvh; - int *bvh_face_ids; - boolean is_closed; - vector3 centroid; - number lengthscale; + vector3_list face_indices; + void* internal; } mesh; typedef struct ellipsoid_struct { diff --git a/utils/geom-ctl-io.c b/utils/geom-ctl-io.c index 88d694c..9e61e6d 100644 --- a/utils/geom-ctl-io.c +++ b/utils/geom-ctl-io.c @@ -47,6 +47,9 @@ ellipsoid_copy(const ellipsoid * o0, ellipsoid * o) o->inverse_semi_axes = o0->inverse_semi_axes; } +/* Defined in geom.c; eagerly rebuilds the opaque mesh_internal cache. */ +extern void mesh_init_internal(mesh *m); + void mesh_copy(const mesh * o0, mesh * o) { @@ -58,21 +61,16 @@ mesh_copy(const mesh * o0, mesh * o) o->vertices.items[i_t] = o0->vertices.items[i_t]; } } - o->num_faces = o0->num_faces; - o->face_indices = (int *) malloc(sizeof(int) * 3 * o->num_faces); - memcpy(o->face_indices, o0->face_indices, sizeof(int) * 3 * o->num_faces); - o->face_normals = (vector3 *) malloc(sizeof(vector3) * o->num_faces); - memcpy(o->face_normals, o0->face_normals, sizeof(vector3) * o->num_faces); - o->face_areas = (number *) malloc(sizeof(number) * o->num_faces); - memcpy(o->face_areas, o0->face_areas, sizeof(number) * o->num_faces); - o->num_bvh_nodes = o0->num_bvh_nodes; - o->bvh = (mesh_bvh_node *) malloc(sizeof(mesh_bvh_node) * o->num_bvh_nodes); - memcpy(o->bvh, o0->bvh, sizeof(mesh_bvh_node) * o->num_bvh_nodes); - o->bvh_face_ids = (int *) malloc(sizeof(int) * o->num_faces); - memcpy(o->bvh_face_ids, o0->bvh_face_ids, sizeof(int) * o->num_faces); - o->is_closed = o0->is_closed; - o->centroid = o0->centroid; - o->lengthscale = o0->lengthscale; + { + int i_t; + o->face_indices.num_items = o0->face_indices.num_items; + o->face_indices.items = ((vector3 *) malloc(sizeof(vector3) * (o->face_indices.num_items))); + for (i_t = 0; i_t < o->face_indices.num_items; i_t++) { + o->face_indices.items[i_t] = o0->face_indices.items[i_t]; + } + } + o->internal = NULL; + mesh_init_internal(o); /* rebuild BVH eagerly; safe under later concurrent queries */ } void @@ -293,12 +291,12 @@ mesh_equal(const mesh * o0, const mesh * o) return 0; } } - if (o->num_faces != o0->num_faces) - return 0; { int i_t; - for (i_t = 0; i_t < 3 * o->num_faces; i_t++) { - if (o->face_indices[i_t] != o0->face_indices[i_t]) + if (o->face_indices.num_items != o0->face_indices.num_items) + return 0; + for (i_t = 0; i_t < o->face_indices.num_items; i_t++) { + if (!vector3_equal(o->face_indices.items[i_t], o0->face_indices.items[i_t])) return 0; } } @@ -523,15 +521,15 @@ ellipsoid_destroy(ellipsoid o) { } +/* Defined in geom.c; frees the opaque mesh_internal cache. Safe on NULL. */ +extern void mesh_internal_free(void *p); + void mesh_destroy(mesh o) { free(o.vertices.items); - free(o.face_indices); - free(o.face_normals); - free(o.face_areas); - free(o.bvh); - free(o.bvh_face_ids); + free(o.face_indices.items); + mesh_internal_free(o.internal); } void diff --git a/utils/geom.c b/utils/geom.c index 1020b1e..ff62216 100644 --- a/utils/geom.c +++ b/utils/geom.c @@ -33,6 +33,7 @@ static void material_type_copy(void **src, void **dest) { *dest = *src; } #endif #include "ctlgeom.h" +#include "mesh-internal.h" #ifdef CXX_CTL_IO using namespace ctlio; @@ -1969,9 +1970,9 @@ static void bvh_node_set_box(mesh_bvh_node *node, const geom_box *box) { /* Fetch the three vertices of a triangle. */ static void mesh_triangle_vertices(const mesh *m, int face_id, vector3 *v0, vector3 *v1, vector3 *v2) { - *v0 = m->vertices.items[m->face_indices[3 * face_id]]; - *v1 = m->vertices.items[m->face_indices[3 * face_id + 1]]; - *v2 = m->vertices.items[m->face_indices[3 * face_id + 2]]; + *v0 = m->vertices.items[mesh_priv(m)->face_indices[3 * face_id]]; + *v1 = m->vertices.items[mesh_priv(m)->face_indices[3 * face_id + 1]]; + *v2 = m->vertices.items[mesh_priv(m)->face_indices[3 * face_id + 2]]; } /* Compute the AABB of a single triangle. */ @@ -2058,7 +2059,7 @@ static int mesh_bvh_build(mesh *m, int *face_ids, int start, int count, double best_cost = 1e300; int best_axis = -1, best_split = -1; double parent_area = geom_box_surface_area(&node_box); - if (parent_area == 0) parent_area = 1e-30 * m->lengthscale * m->lengthscale; + if (parent_area == 0) parent_area = 1e-30 * mesh_priv(m)->lengthscale * mesh_priv(m)->lengthscale; for (int axis = 0; axis < 3; axis++) { double lo, hi; @@ -2067,7 +2068,7 @@ static int mesh_bvh_build(mesh *m, int *face_ids, int start, int count, case 1: lo = node_box.low.y; hi = node_box.high.y; break; default: lo = node_box.low.z; hi = node_box.high.z; break; } - if (hi - lo < 1e-15 * m->lengthscale) continue; + if (hi - lo < 1e-15 * mesh_priv(m)->lengthscale) continue; /* Bin face centroids. */ int bin_counts[MESH_BVH_NUM_BINS]; @@ -2364,7 +2365,7 @@ static int find_closest_face(const mesh *m, vector3 p, double *dist2) { while (stack_top > 0) { int node_idx = stack[--stack_top]; - const mesh_bvh_node *node = &m->bvh[node_idx]; + const mesh_bvh_node *node = &mesh_priv(m)->bvh[node_idx]; /* Compute minimum squared distance from p to the AABB. */ double dx = fmax(0, fmax(node->bbox_low.x - p.x, p.x - node->bbox_high.x)); @@ -2376,10 +2377,10 @@ static int find_closest_face(const mesh *m, vector3 p, double *dist2) { if (node->left_child < 0) { /* Leaf: test all faces. */ for (int i = 0; i < node->face_count; i++) { - int fid = m->bvh_face_ids[node->face_start + i]; - vector3 v0 = m->vertices.items[m->face_indices[3 * fid]]; - vector3 v1 = m->vertices.items[m->face_indices[3 * fid + 1]]; - vector3 v2 = m->vertices.items[m->face_indices[3 * fid + 2]]; + int fid = mesh_priv(m)->bvh_face_ids[node->face_start + i]; + vector3 v0 = m->vertices.items[mesh_priv(m)->face_indices[3 * fid]]; + vector3 v1 = m->vertices.items[mesh_priv(m)->face_indices[3 * fid + 1]]; + vector3 v2 = m->vertices.items[mesh_priv(m)->face_indices[3 * fid + 2]]; vector3 closest; double d2 = closest_point_on_triangle(p, v0, v1, v2, &closest); if (d2 < best_dist2) { @@ -2391,8 +2392,8 @@ static int find_closest_face(const mesh *m, vector3 p, double *dist2) { if (stack_top + 2 > 64) continue; /* Push the farther child first so the nearer child is popped first, giving better pruning of the farther subtree. */ - const mesh_bvh_node *left = &m->bvh[node->left_child]; - const mesh_bvh_node *right = &m->bvh[node->right_child]; + const mesh_bvh_node *left = &mesh_priv(m)->bvh[node->left_child]; + const mesh_bvh_node *right = &mesh_priv(m)->bvh[node->right_child]; double ldx = fmax(0, fmax(left->bbox_low.x - p.x, p.x - left->bbox_high.x)); double ldy = fmax(0, fmax(left->bbox_low.y - p.y, p.y - left->bbox_high.y)); double ldz = fmax(0, fmax(left->bbox_low.z - p.z, p.z - left->bbox_high.z)); @@ -2462,24 +2463,24 @@ static void mesh_ray_all_intersections(const mesh *m, vector3 origin, vector3 di inv_dir.y = (fabs(dir.y) > 1e-30) ? 1.0 / dir.y : 1e30; inv_dir.z = (fabs(dir.z) > 1e-30) ? 1.0 / dir.z : 1e30; - double det_eps = 1e-12 * m->lengthscale * m->lengthscale; + double det_eps = 1e-12 * mesh_priv(m)->lengthscale * mesh_priv(m)->lengthscale; int stack[64]; int stack_top = 0; stack[stack_top++] = 0; while (stack_top > 0) { int node_idx = stack[--stack_top]; - const mesh_bvh_node *node = &m->bvh[node_idx]; + const mesh_bvh_node *node = &mesh_priv(m)->bvh[node_idx]; if (!ray_bvh_node_intersect(origin, inv_dir, node, -1e30, 1e30)) continue; if (node->left_child < 0) { for (int i = 0; i < node->face_count; i++) { - int fid = m->bvh_face_ids[node->face_start + i]; - vector3 v0 = m->vertices.items[m->face_indices[3 * fid]]; - vector3 v1 = m->vertices.items[m->face_indices[3 * fid + 1]]; - vector3 v2 = m->vertices.items[m->face_indices[3 * fid + 2]]; + int fid = mesh_priv(m)->bvh_face_ids[node->face_start + i]; + vector3 v0 = m->vertices.items[mesh_priv(m)->face_indices[3 * fid]]; + vector3 v1 = m->vertices.items[mesh_priv(m)->face_indices[3 * fid + 1]]; + vector3 v2 = m->vertices.items[mesh_priv(m)->face_indices[3 * fid + 2]]; double t; if (ray_triangle_intersect(origin, dir, v0, v1, v2, det_eps, &t, NULL, NULL)) @@ -2523,7 +2524,7 @@ static int count_ray_mesh_intersections_ex(const mesh *m, vector3 origin, vector mesh_ray_all_intersections(m, origin, dir, &hits); /* Keep only forward intersections (t > eps). */ - double fwd_eps = 1e-12 * m->lengthscale; + double fwd_eps = 1e-12 * mesh_priv(m)->lengthscale; int nforward = 0; for (int i = 0; i < hits.count; i++) if (hits.data[i] > fwd_eps) @@ -2531,7 +2532,7 @@ static int count_ray_mesh_intersections_ex(const mesh *m, vector3 origin, vector if (nforward > 1) { qsort(hits.data, nforward, sizeof(double), mesh_dcmp); - double dedup_tol = 1e-10 * m->lengthscale; + double dedup_tol = 1e-10 * mesh_priv(m)->lengthscale; int deduped = remove_duplicate_intersections(hits.data, nforward, dedup_tol); if (had_degenerate) *had_degenerate = (deduped != nforward); nforward = deduped; @@ -2558,7 +2559,7 @@ static const vector3 mesh_ray_dirs[3] = { }; static boolean point_in_mesh(const mesh *m, vector3 p) { - if (!m->is_closed) return 0; + if (!mesh_priv(m)->is_closed) return 0; /* Fast path: cast one ray. If no degenerate edge/vertex hits were detected during deduplication, trust the result immediately. @@ -2585,13 +2586,13 @@ static vector3 normal_to_mesh(const mesh *m, vector3 p) { vector3 zero = {0, 0, 0}; return zero; } - return m->face_normals[face]; + return mesh_priv(m)->face_normals[face]; } static void get_mesh_bounding_box(const mesh *m, geom_box *box) { - if (m->num_bvh_nodes > 0) { - box->low = m->bvh[0].bbox_low; - box->high = m->bvh[0].bbox_high; + if (mesh_priv(m)->num_bvh_nodes > 0) { + box->low = mesh_priv(m)->bvh[0].bbox_low; + box->high = mesh_priv(m)->bvh[0].bbox_high; } else { box->low = box->high = m->vertices.items[0]; for (int i = 1; i < m->vertices.num_items; i++) @@ -2602,10 +2603,10 @@ static void get_mesh_bounding_box(const mesh *m, geom_box *box) { static double get_mesh_volume(const mesh *m) { /* Divergence theorem: sum signed tetrahedron volumes. */ double vol = 0; - for (int f = 0; f < m->num_faces; f++) { - vector3 v0 = m->vertices.items[m->face_indices[3 * f]]; - vector3 v1 = m->vertices.items[m->face_indices[3 * f + 1]]; - vector3 v2 = m->vertices.items[m->face_indices[3 * f + 2]]; + for (int f = 0; f < mesh_priv(m)->num_faces; f++) { + vector3 v0 = m->vertices.items[mesh_priv(m)->face_indices[3 * f]]; + vector3 v1 = m->vertices.items[mesh_priv(m)->face_indices[3 * f + 1]]; + vector3 v2 = m->vertices.items[mesh_priv(m)->face_indices[3 * f + 2]]; vol += vector3_dot(v0, vector3_cross(v1, v2)); } return fabs(vol) / 6.0; @@ -2614,8 +2615,8 @@ static double get_mesh_volume(const mesh *m) { static void display_mesh_info(int indentby, const geometric_object *o) { const mesh *m = o->subclass.mesh_data; ctl_printf("%*s %d vertices, %d faces, %s\n", indentby, "", - m->vertices.num_items, m->num_faces, - m->is_closed ? "closed" : "OPEN (WARNING)"); + m->vertices.num_items, mesh_priv(m)->num_faces, + mesh_priv(m)->is_closed ? "closed" : "OPEN (WARNING)"); } static double intersect_line_segment_with_mesh(const mesh *m, vector3 p, vector3 d, @@ -2626,7 +2627,7 @@ static double intersect_line_segment_with_mesh(const mesh *m, vector3 p, vector3 if (hits.count > 1) { qsort(hits.data, hits.count, sizeof(double), mesh_dcmp); - hits.count = remove_duplicate_intersections(hits.data, hits.count, 1e-10 * m->lengthscale); + hits.count = remove_duplicate_intersections(hits.data, hits.count, 1e-10 * mesh_priv(m)->lengthscale); } /* The sorted intersection list gives all surface crossings along the @@ -2653,30 +2654,76 @@ static double intersect_line_segment_with_mesh(const mesh *m, vector3 p, vector3 return ds > 0.0 ? ds : 0.0; } +/* Forward declaration; init_mesh body lives below. */ +static void init_mesh(geometric_object *o); + +/* Build the opaque mesh_internal cache for a mesh whose public fields + (vertices, face_indices) have just been populated and whose internal + pointer is NULL. Used by the auto-generated mesh_copy in geom-ctl-io.c + to eagerly rebuild a copied mesh's BVH so subsequent _fixed_ queries + (which skip geom_fix_object_ptr / reinit_mesh) work correctly. */ +void mesh_init_internal(mesh *m) { + geometric_object o; + /* only need to initialize a single field of o, since that's all init_mesh looks at */ + o.subclass.mesh_data = m; + init_mesh(&o); +} + +/* Free the opaque mesh_internal cache and all its nested allocations. + Safe on NULL. Called from the auto-generated mesh_destroy in + geom-ctl-io.c via the extern declaration there. */ +void mesh_internal_free(void *p) { + if (!p) return; + mesh_internal *mi = (mesh_internal *)p; + free(mi->face_indices); + free(mi->face_normals); + free(mi->face_areas); + free(mi->bvh); + free(mi->bvh_face_ids); + free(mi); +} + /***************************************************************/ -/* init_mesh: validate, compute normals, build BVH */ +/* init_mesh: allocate the opaque mesh_internal cache, unpack */ +/* face_indices into a flat int array, compute face normals, */ +/* areas, centroid, lengthscale, check closure, and build BVH. */ /* */ -/* NOT THREAD-SAFE: mutates m->face_indices (winding flip), */ -/* allocates m->face_normals / m->face_areas / m->bvh / */ -/* m->bvh_face_ids, and writes m->is_closed, m->centroid, */ -/* m->lengthscale, m->num_bvh_nodes. Only invoked from the */ -/* mesh constructors (make_mesh / make_mesh_with_center) and */ -/* from reinit_mesh; both rely on it running single-threaded */ -/* per mesh. */ +/* NOT THREAD-SAFE: writes the per-mesh internal cache. */ +/* Invoked only from the mesh constructors and from reinit_mesh;*/ +/* both rely on it running single-threaded per mesh. */ /***************************************************************/ static void init_mesh(geometric_object *o) { mesh *m = o->subclass.mesh_data; int nv = m->vertices.num_items; - int nf = m->num_faces; + + /* Allocate the opaque internal cache. m->internal must be NULL on entry + (either the mesh was just constructed, or reinit_mesh cleared it). */ + mesh_internal *p = (mesh_internal *)calloc(1, sizeof(mesh_internal)); + CHECK(p, "out of memory"); + m->internal = p; + + /* Unpack face_indices: the public vector3_list stores 3 ints per + triangle packed into a vector3 (x, y, z are the indices as doubles, + exactly representable up to 2^53). The internal cache uses a flat + int[3*num_faces] for fast indexed access. */ + int nf = m->face_indices.num_items; + p->num_faces = nf; + p->face_indices = (int *)malloc(3 * nf * sizeof(int)); + CHECK(p->face_indices, "out of memory"); + for (int f = 0; f < nf; f++) { + p->face_indices[3 * f] = (int)m->face_indices.items[f].x; + p->face_indices[3 * f + 1] = (int)m->face_indices.items[f].y; + p->face_indices[3 * f + 2] = (int)m->face_indices.items[f].z; + } /* Validate. */ CHECK(nv >= 4, "mesh requires at least 4 vertices"); CHECK(nf >= 4, "mesh requires at least 4 faces"); for (int f = 0; f < nf; f++) { - CHECK(m->face_indices[3 * f] >= 0 && m->face_indices[3 * f] < nv, "mesh face index out of range"); - CHECK(m->face_indices[3 * f + 1] >= 0 && m->face_indices[3 * f + 1] < nv, "mesh face index out of range"); - CHECK(m->face_indices[3 * f + 2] >= 0 && m->face_indices[3 * f + 2] < nv, "mesh face index out of range"); + CHECK(mesh_priv(m)->face_indices[3 * f] >= 0 && mesh_priv(m)->face_indices[3 * f] < nv, "mesh face index out of range"); + CHECK(mesh_priv(m)->face_indices[3 * f + 1] >= 0 && mesh_priv(m)->face_indices[3 * f + 1] < nv, "mesh face index out of range"); + CHECK(mesh_priv(m)->face_indices[3 * f + 2] >= 0 && mesh_priv(m)->face_indices[3 * f + 2] < nv, "mesh face index out of range"); } /* Compute characteristic lengthscale from vertex bounding box diagonal. */ @@ -2688,34 +2735,34 @@ static void init_mesh(geometric_object *o) { hi.x = fmax(hi.x, v.x); hi.y = fmax(hi.y, v.y); hi.z = fmax(hi.z, v.z); } double dx = hi.x - lo.x, dy = hi.y - lo.y, dz = hi.z - lo.z; - m->lengthscale = sqrt(dx * dx + dy * dy + dz * dz); - if (m->lengthscale == 0) m->lengthscale = 1.0; + mesh_priv(m)->lengthscale = sqrt(dx * dx + dy * dy + dz * dz); + if (mesh_priv(m)->lengthscale == 0) mesh_priv(m)->lengthscale = 1.0; } /* Compute face normals and areas. */ - m->face_normals = (vector3 *)malloc(nf * sizeof(vector3)); - CHECK(m->face_normals, "out of memory"); - m->face_areas = (double *)malloc(nf * sizeof(double)); - CHECK(m->face_areas, "out of memory"); + mesh_priv(m)->face_normals = (vector3 *)malloc(nf * sizeof(vector3)); + CHECK(mesh_priv(m)->face_normals, "out of memory"); + mesh_priv(m)->face_areas = (double *)malloc(nf * sizeof(double)); + CHECK(mesh_priv(m)->face_areas, "out of memory"); - double area_eps = 1e-20 * m->lengthscale * m->lengthscale; + double area_eps = 1e-20 * mesh_priv(m)->lengthscale * mesh_priv(m)->lengthscale; for (int f = 0; f < nf; f++) { - vector3 v0 = m->vertices.items[m->face_indices[3 * f]]; - vector3 v1 = m->vertices.items[m->face_indices[3 * f + 1]]; - vector3 v2 = m->vertices.items[m->face_indices[3 * f + 2]]; + vector3 v0 = m->vertices.items[mesh_priv(m)->face_indices[3 * f]]; + vector3 v1 = m->vertices.items[mesh_priv(m)->face_indices[3 * f + 1]]; + vector3 v2 = m->vertices.items[mesh_priv(m)->face_indices[3 * f + 2]]; vector3 e1 = vector3_minus(v1, v0); vector3 e2 = vector3_minus(v2, v0); vector3 n = vector3_cross(e1, e2); double len = vector3_norm(n); - m->face_areas[f] = 0.5 * len; - m->face_normals[f] = (len > area_eps) ? vector3_scale(1.0 / len, n) : n; + mesh_priv(m)->face_areas[f] = 0.5 * len; + mesh_priv(m)->face_normals[f] = (len > area_eps) ? vector3_scale(1.0 / len, n) : n; } /* Compute centroid. */ - m->centroid.x = m->centroid.y = m->centroid.z = 0; + mesh_priv(m)->centroid.x = mesh_priv(m)->centroid.y = mesh_priv(m)->centroid.z = 0; for (int i = 0; i < nv; i++) - m->centroid = vector3_plus(m->centroid, m->vertices.items[i]); - m->centroid = vector3_scale(1.0 / nv, m->centroid); + mesh_priv(m)->centroid = vector3_plus(mesh_priv(m)->centroid, m->vertices.items[i]); + mesh_priv(m)->centroid = vector3_scale(1.0 / nv, mesh_priv(m)->centroid); /* Check if mesh is closed: every edge must be shared by exactly 2 faces. An edge is identified by a sorted pair of vertex indices. We use a @@ -2733,11 +2780,11 @@ static void init_mesh(geometric_object *o) { /* Mark empty slots with -1. */ for (int i = 0; i < htsize; i++) ht_vlo[i] = -1; - m->is_closed = 1; - for (int f = 0; f < nf && m->is_closed; f++) { + mesh_priv(m)->is_closed = 1; + for (int f = 0; f < nf && mesh_priv(m)->is_closed; f++) { for (int e = 0; e < 3; e++) { - int va = m->face_indices[3 * f + e]; - int vb = m->face_indices[3 * f + (e + 1) % 3]; + int va = mesh_priv(m)->face_indices[3 * f + e]; + int vb = mesh_priv(m)->face_indices[3 * f + (e + 1) % 3]; int vlo = (va < vb) ? va : vb; int vhi = (va < vb) ? vb : va; unsigned int h = (unsigned int)(vlo * 73856093u ^ vhi * 19349663u) & htmask; @@ -2752,7 +2799,7 @@ static void init_mesh(geometric_object *o) { } else if (ht_vlo[h] == vlo && ht_vhi[h] == vhi) { /* Found existing edge. */ ht_cnt[h]++; - if (ht_cnt[h] > 2) m->is_closed = 0; /* non-manifold edge */ + if (ht_cnt[h] > 2) mesh_priv(m)->is_closed = 0; /* non-manifold edge */ break; } h = (h + 1) & htmask; @@ -2760,10 +2807,10 @@ static void init_mesh(geometric_object *o) { } } /* Check that all edges have exactly 2 faces. */ - if (m->is_closed) { + if (mesh_priv(m)->is_closed) { for (int i = 0; i < htsize; i++) { if (ht_vlo[i] != -1 && ht_cnt[i] != 2) { - m->is_closed = 0; + mesh_priv(m)->is_closed = 0; break; } } @@ -2772,7 +2819,7 @@ static void init_mesh(geometric_object *o) { free(ht_vhi); free(ht_cnt); - if (!m->is_closed) + if (!mesh_priv(m)->is_closed) ctl_printf("WARNING: mesh is not closed (not all edges shared by exactly 2 faces).\n" " point_in_mesh results may be incorrect.\n"); } @@ -2799,9 +2846,9 @@ static void init_mesh(geometric_object *o) { } while (0) for (int f = 0; f < nf; f++) { - int a = m->face_indices[3 * f]; - int b = m->face_indices[3 * f + 1]; - int c = m->face_indices[3 * f + 2]; + int a = mesh_priv(m)->face_indices[3 * f]; + int b = mesh_priv(m)->face_indices[3 * f + 1]; + int c = mesh_priv(m)->face_indices[3 * f + 2]; /* Union a-b. */ UF_FIND(a); UF_FIND(b); if (a != b) { @@ -2810,9 +2857,9 @@ static void init_mesh(geometric_object *o) { if (uf_rank[a] == uf_rank[b]) uf_rank[a]++; } /* Union a-c (a is already a root after the above). */ - a = m->face_indices[3 * f]; + a = mesh_priv(m)->face_indices[3 * f]; UF_FIND(a); - c = m->face_indices[3 * f + 2]; + c = mesh_priv(m)->face_indices[3 * f + 2]; UF_FIND(c); if (a != c) { if (uf_rank[a] < uf_rank[c]) { int t = a; a = c; c = t; } @@ -2838,21 +2885,21 @@ static void init_mesh(geometric_object *o) { double *comp_vol = (double *)calloc(num_components, sizeof(double)); CHECK(comp_vol, "out of memory"); for (int f = 0; f < nf; f++) { - vector3 v0 = m->vertices.items[m->face_indices[3 * f]]; - vector3 v1 = m->vertices.items[m->face_indices[3 * f + 1]]; - vector3 v2 = m->vertices.items[m->face_indices[3 * f + 2]]; - int ci = comp_id[m->face_indices[3 * f]]; + vector3 v0 = m->vertices.items[mesh_priv(m)->face_indices[3 * f]]; + vector3 v1 = m->vertices.items[mesh_priv(m)->face_indices[3 * f + 1]]; + vector3 v2 = m->vertices.items[mesh_priv(m)->face_indices[3 * f + 2]]; + int ci = comp_id[mesh_priv(m)->face_indices[3 * f]]; comp_vol[ci] += vector3_dot(v0, vector3_cross(v1, v2)); } /* Flip faces in components with negative signed volume. */ for (int f = 0; f < nf; f++) { - int ci = comp_id[m->face_indices[3 * f]]; + int ci = comp_id[mesh_priv(m)->face_indices[3 * f]]; if (comp_vol[ci] < 0) { - int tmp = m->face_indices[3 * f + 1]; - m->face_indices[3 * f + 1] = m->face_indices[3 * f + 2]; - m->face_indices[3 * f + 2] = tmp; - m->face_normals[f] = vector3_scale(-1.0, m->face_normals[f]); + int tmp = mesh_priv(m)->face_indices[3 * f + 1]; + mesh_priv(m)->face_indices[3 * f + 1] = mesh_priv(m)->face_indices[3 * f + 2]; + mesh_priv(m)->face_indices[3 * f + 2] = tmp; + mesh_priv(m)->face_normals[f] = vector3_scale(-1.0, mesh_priv(m)->face_normals[f]); } } @@ -2864,15 +2911,15 @@ static void init_mesh(geometric_object *o) { /* Build BVH. */ int max_nodes = 2 * nf; /* upper bound on BVH nodes */ - m->bvh = (mesh_bvh_node *)malloc(max_nodes * sizeof(mesh_bvh_node)); - CHECK(m->bvh, "out of memory"); - m->bvh_face_ids = (int *)malloc(nf * sizeof(int)); - CHECK(m->bvh_face_ids, "out of memory"); + mesh_priv(m)->bvh = (mesh_bvh_node *)malloc(max_nodes * sizeof(mesh_bvh_node)); + CHECK(mesh_priv(m)->bvh, "out of memory"); + mesh_priv(m)->bvh_face_ids = (int *)malloc(nf * sizeof(int)); + CHECK(mesh_priv(m)->bvh_face_ids, "out of memory"); for (int i = 0; i < nf; i++) - m->bvh_face_ids[i] = i; + mesh_priv(m)->bvh_face_ids[i] = i; - m->num_bvh_nodes = 0; - mesh_bvh_build(m, m->bvh_face_ids, 0, nf, m->bvh, &m->num_bvh_nodes); + mesh_priv(m)->num_bvh_nodes = 0; + mesh_bvh_build(m, mesh_priv(m)->bvh_face_ids, 0, nf, mesh_priv(m)->bvh, &mesh_priv(m)->num_bvh_nodes); } /***************************************************************/ @@ -2881,29 +2928,18 @@ static void init_mesh(geometric_object *o) { static void reinit_mesh(geometric_object *o) { mesh *m = o->subclass.mesh_data; - /* Skip rebuild if BVH is already built. Mesh vertices are not part of the - documented post-construction API, so a non-NULL bvh implies the cached - state is still valid. This preserves the fast path for geom_fix_object_ptr - on copied meshes (called hundreds of times during meep's init_sim). - NOT THREAD-SAFE for first-time init: the m->bvh != NULL check below is - unsynchronized, and init_mesh allocates m->bvh BEFORE populating its - contents. Concurrent first-time callers would race both on the check - (double-init, leak) and on the post-allocation window (second thread - reads bvh != NULL and traverses an empty BVH). Callers must ensure the - first reinit_mesh / init_mesh on each mesh runs single-threaded — in - practice this is done by calling geom_fix_objects() once at geometry - setup, before any parallel queries. After init, all subsequent - reinit_mesh calls are pure no-op fast-path returns and safe under - concurrency. */ - if (m->bvh != NULL) return; - free(m->face_normals); - free(m->face_areas); - free(m->bvh); - free(m->bvh_face_ids); - m->face_normals = NULL; - m->face_areas = NULL; - m->bvh = NULL; - m->bvh_face_ids = NULL; + /* Fast path: if the internal cache exists, it is fully built and valid + (init_mesh only stores m->internal at the end, atomically from a + reader's perspective). mesh_copy clears m->internal to NULL on the + copy, which causes the first reinit_mesh on that copy to lazily + rebuild here. NOT THREAD-SAFE for first-time init on a given mesh: + the m->internal == NULL check below is unsynchronized. Callers must + ensure the first reinit_mesh / init_mesh on each mesh runs single- + threaded — in practice this is done by calling geom_fix_objects() + once at geometry setup, before any parallel queries. After init, all + subsequent reinit_mesh calls are no-op fast-path returns and safe + under concurrency. */ + if (m->internal != NULL) return; init_mesh(o); } @@ -2932,15 +2968,22 @@ geometric_object make_mesh_with_center(material_type material, vector3 center, CHECK(m->vertices.items, "out of memory"); memcpy(m->vertices.items, vertices, num_vertices * sizeof(vector3)); - /* Copy triangle indices. */ - m->num_faces = num_triangles; - m->face_indices = (int *)malloc(3 * num_triangles * sizeof(int)); - CHECK(m->face_indices, "out of memory"); - memcpy(m->face_indices, triangles, 3 * num_triangles * sizeof(int)); - - /* Shift vertices before init so BVH is only built once. */ + /* Pack triangle indices into the public vector3_list. Each triangle's + three ints go into x, y, z of a vector3 (representable as doubles + up to 2^53). init_mesh will unpack into a flat int[] in the internal + cache for fast indexed access. */ + m->face_indices.num_items = num_triangles; + m->face_indices.items = (vector3 *)malloc(num_triangles * sizeof(vector3)); + CHECK(m->face_indices.items, "out of memory"); + for (int t = 0; t < num_triangles; t++) { + m->face_indices.items[t].x = (double)triangles[3 * t]; + m->face_indices.items[t].y = (double)triangles[3 * t + 1]; + m->face_indices.items[t].z = (double)triangles[3 * t + 2]; + } + + /* Shift vertices before init so the BVH is built around the final + positions. */ if (!mesh_is_auto_center(center)) { - /* Compute centroid to determine the shift. */ vector3 centroid = {0, 0, 0}; for (int i = 0; i < num_vertices; i++) centroid = vector3_plus(centroid, m->vertices.items[i]); @@ -2950,11 +2993,12 @@ geometric_object make_mesh_with_center(material_type material, vector3 center, m->vertices.items[i] = vector3_plus(m->vertices.items[i], shift); } - /* Initialize derived data (normals, closure check, BVH). */ + /* Initialize derived data (allocates m->internal, computes normals, + closure check, BVH). */ init_mesh(&o); /* Set center from the (possibly shifted) centroid. */ - o.center = m->centroid; + o.center = mesh_priv(m)->centroid; return o; } diff --git a/utils/geom.scm b/utils/geom.scm index cfc17a9..d952371 100644 --- a/utils/geom.scm +++ b/utils/geom.scm @@ -155,7 +155,11 @@ (define-class mesh geometric-object ; fields to be filled in by users (define-property vertices '() (make-list-type 'vector3)) - (define-property face_indices '() (make-list-type 'vector3))) + (define-property face_indices '() (make-list-type 'vector3)) +; opaque pointer to mesh_internal (face normals, BVH, etc.) — allocated by +; init_mesh in C and never touched from Scheme. 'SCM becomes void* in +; ctlgeom-types.h via the existing sed rule in utils/Makefile.am. + (define-property internal '() 'SCM)) (define-class ellipsoid block (define-derived-property inverse-semi-axes 'vector3 diff --git a/utils/mesh-internal.h b/utils/mesh-internal.h new file mode 100644 index 0000000..da40349 --- /dev/null +++ b/utils/mesh-internal.h @@ -0,0 +1,45 @@ +/* mesh-internal.h: private C-side cache for the mesh geometric object. + * + * The public mesh struct (in ctlgeom-types.h, generated from geom.scm) holds + * only what's representable in Scheme: the user-supplied vertices + triangle + * indices and an opaque void *internal pointer. Everything else — face + * normals, areas, BVH acceleration structure, closure flag, centroid, + * characteristic lengthscale — lives in mesh_internal, allocated by + * init_mesh and reached via mesh_priv(m). + * + * This header is NOT installed and NOT in geom.scm; it is included only by + * geom.c and the mesh tests. + */ + +#ifndef MESH_INTERNAL_H +#define MESH_INTERNAL_H + +#include "ctlgeom-types.h" + +typedef struct mesh_bvh_node { + vector3 bbox_low; + vector3 bbox_high; + int left_child; + int right_child; + int face_start; + int face_count; +} mesh_bvh_node; + +typedef struct mesh_internal { + int num_faces; + int *face_indices; /* unpacked flat: 3 ints per triangle */ + vector3 *face_normals; + number *face_areas; + int num_bvh_nodes; + mesh_bvh_node *bvh; + int *bvh_face_ids; + boolean is_closed; + vector3 centroid; + number lengthscale; +} mesh_internal; + +static inline mesh_internal *mesh_priv(const mesh *m) { + return (mesh_internal *)m->internal; +} + +#endif /* MESH_INTERNAL_H */ diff --git a/utils/test-mesh.c b/utils/test-mesh.c index 0677538..cce3eb1 100644 --- a/utils/test-mesh.c +++ b/utils/test-mesh.c @@ -28,6 +28,7 @@ #include #include "ctlgeom.h" +#include "mesh-internal.h" #define K_PI 3.141592653589793238462643383279502884197 #define TOLERANCE 1e-6 @@ -441,7 +442,7 @@ static void test_open_mesh(void) { }; geometric_object open = make_mesh(NULL, verts, 4, tris, 4); mesh *m = open.subclass.mesh_data; - ASSERT_TRUE("open mesh detected as not closed", !m->is_closed); + ASSERT_TRUE("open mesh detected as not closed", !mesh_priv(m)->is_closed); /* point_in_mesh should return false for open meshes. */ vector3 p = {0.1, 0.1, 0.1}; @@ -458,12 +459,12 @@ static void test_closed_detection(void) { printf("test_closed_detection... "); geometric_object cube = make_cube_mesh(NULL); mesh *m = cube.subclass.mesh_data; - ASSERT_TRUE("cube mesh detected as closed", m->is_closed); + ASSERT_TRUE("cube mesh detected as closed", mesh_priv(m)->is_closed); geometric_object_destroy(cube); geometric_object tetra = make_tetra_mesh(NULL); m = tetra.subclass.mesh_data; - ASSERT_TRUE("tetra mesh detected as closed", m->is_closed); + ASSERT_TRUE("tetra mesh detected as closed", mesh_priv(m)->is_closed); geometric_object_destroy(tetra); printf("done\n"); } @@ -488,7 +489,7 @@ static void test_boundary_edge(void) { }; geometric_object obj = make_mesh(NULL, verts, 5, tris, 4); mesh *m = obj.subclass.mesh_data; - ASSERT_TRUE("boundary edge mesh detected as not closed", !m->is_closed); + ASSERT_TRUE("boundary edge mesh detected as not closed", !mesh_priv(m)->is_closed); geometric_object_destroy(obj); printf("done\n"); } @@ -514,7 +515,7 @@ static void test_nonmanifold_edge(void) { }; geometric_object obj = make_mesh(NULL, verts, 6, tris, 6); mesh *m = obj.subclass.mesh_data; - ASSERT_TRUE("non-manifold edge mesh detected as not closed", !m->is_closed); + ASSERT_TRUE("non-manifold edge mesh detected as not closed", !mesh_priv(m)->is_closed); geometric_object_destroy(obj); printf("done\n"); } @@ -539,7 +540,7 @@ static void test_isolated_vertex(void) { }; geometric_object obj = make_mesh(NULL, verts, 5, tris, 4); mesh *m = obj.subclass.mesh_data; - ASSERT_TRUE("mesh with isolated vertex still closed", m->is_closed); + ASSERT_TRUE("mesh with isolated vertex still closed", mesh_priv(m)->is_closed); /* Point inside tetrahedron should still work. */ vector3 p = {0, 0, 0}; From e35ace322fcd590ced8f5cd98da5a914b34125bd Mon Sep 17 00:00:00 2001 From: Luochenghuang Date: Fri, 29 May 2026 10:38:06 -0700 Subject: [PATCH 2/6] Eliminate slist_unique VLA in intersect_line_with_prism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The duplicate-removal step previously wrote deduped values into a stack VLA slist_unique[num_vertices+2] and then did 'slist = slist_unique;' before returning the new count. That assignment is a dead store: slist is a pass-by-value pointer parameter, so reassigning it only rebinds the local variable. The caller's buffer is never touched and slist_unique is destroyed when the function returns, so the caller reads the first num_unique_elements entries of the sorted-but-undeduped buffer instead of the actual deduped values. In intersect_line_segment_with_prism this corrupts the inside/outside parity walk: a near-duplicate that the dedup loop intended to drop stays in the buffer (producing a phantom thin 'inside' sliver of width ~1e-3*|s|), and a real far-end crossing is silently dropped because the caller stops at the smaller returned count. The bug triggers when two s-values fall within 1e-3*|s| of each other — edge-grazing rays, rays through a shared vertex, near-tangent rays. test-prism's random-ray distribution didn't statistically hit the window often enough at OMP=1 to flag it, but parallel runs (e.g. OMP=8) shuffle the libc rand() sequence and hit the window often enough that the strict 1e-6 segment-length check fails. The fix eliminates the VLA entirely. The dedup is done in place with a write head 'iv' and read head 'nv': because writes go to slist[iv++] with iv <= nv, position nv-1 is either untouched or was previously written with its own value as a no-op, so reading slist[nv-1] still yields the original sorted value and the comparison semantics are bit-identical to the original intent. Net effect: removes the dead store, removes a potentially huge stack allocation, and the dedup now actually applies to the caller's buffer. Verified with --enable-openmp: test-prism passes at both OMP_NUM_THREADS=1 (0/10000 points failed, 0/1000 segments failed, 0/65 prism helper failures) and OMP_NUM_THREADS=8 across multiple trials. test-mesh remains clean at OMP=8 as well. --- utils/geom.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/utils/geom.c b/utils/geom.c index ff62216..de86f9c 100644 --- a/utils/geom.c +++ b/utils/geom.c @@ -3310,24 +3310,20 @@ int intersect_line_with_prism(prism *prsm, vector3 pc, vector3 dc, double *slist if (num_intersections > slist_len) return num_intersections; qsort((void *)slist, num_intersections, sizeof(double), dcmp); - // if num_intersections is zero then just return that - if (num_intersections == 0) return num_intersections; - else { - // remove duplicates from slist - double duplicate_tolerance = 1e-3; - int num_unique_elements = 1; - double slist_unique[num_vertices+2]; - slist_unique[0] = slist[0]; - for (nv = 1; nv < num_intersections; nv++) { - if (fabs(slist[nv] - slist[nv-1]) > duplicate_tolerance*fabs(slist[nv])) { - slist_unique[num_unique_elements] = slist[nv]; - num_unique_elements++; - } - } - slist = slist_unique; - num_intersections = num_unique_elements; - return num_intersections; - } + // Remove near-duplicates in place: walk the sorted slist with a write + // head `iv` and read head `nv`; the write at slist[iv++] is always to a + // position <= nv, so the comparison slist[nv-1] reads the original + // sorted value (positions [iv..nv-1] are either untouched or written + // with their own value as a no-op). + double duplicate_tolerance = 1e-3; + int iv = num_intersections < 1 ? 0 : 1; /* min(1, num_intersections) */ + for (nv = 1; nv < num_intersections; nv++) { + if (fabs(slist[nv] - slist[nv-1]) > duplicate_tolerance*fabs(slist[nv])) { + slist[iv++] = slist[nv]; + } + } + num_intersections = iv; + return num_intersections; } /***************************************************************/ From 5817a0ca0db0d4198730898f6c8368fea2bc0cfe Mon Sep 17 00:00:00 2001 From: Luochenghuang Date: Mon, 1 Jun 2026 11:28:00 -0700 Subject: [PATCH 3/6] Inline mesh-internal.h into geom.c so MPB-style cp builds still work The previous PIMPL commit placed the private mesh_bvh_node / mesh_internal definitions and the mesh_priv() accessor in a separate header utils/mesh-internal.h, included by geom.c. That breaks MPB, meep, and the libctl examples/ directory itself: all of those build by 'cp -f utils/geom.c their_tree/geom.c' and compile the copy in their own build context, without the companion header. Until those downstreams are taught to link against libctlgeom directly (instead of copying geom.c), geom.c must remain self-contained. This commit inlines the private definitions directly at the top of geom.c (right after the existing includes) and removes the #include 'mesh-internal.h' line. The mesh-internal.h file is deleted. To preserve test-mesh's ability to verify the closure check without exposing mesh_priv outside geom.c, the is_closed field is promoted to the public mesh struct via geom.scm. It is conceptually a derived public property anyway (users may want to query 'did init_mesh detect this mesh as watertight?'), and the auto-generated mesh_copy / mesh_equal handle it as a plain boolean value with no special code. ctlgeom-types.h and geom-ctl-io.c are updated to match. test-mesh.c drops the #include 'mesh-internal.h' line and reverts mesh_priv(m)->is_closed back to m->is_closed. Build clean. test-prism and test-mesh both pass at OMP_NUM_THREADS=1 and 8: test-mesh 0 failures (including 0 mismatches in test_cube_vs_block and test_cube_segments_vs_block); test-prism 0/10000 point failures, 0/1000 segment failures, 0/65 prism helper failures. --- utils/ctlgeom-types.h | 1 + utils/geom-ctl-io.c | 3 +++ utils/geom.c | 52 +++++++++++++++++++++++++++++++++++-------- utils/geom.scm | 5 ++++- utils/mesh-internal.h | 45 ------------------------------------- utils/test-mesh.c | 13 +++++------ 6 files changed, 57 insertions(+), 62 deletions(-) delete mode 100644 utils/mesh-internal.h diff --git a/utils/ctlgeom-types.h b/utils/ctlgeom-types.h index f92be6d..9a2693f 100644 --- a/utils/ctlgeom-types.h +++ b/utils/ctlgeom-types.h @@ -108,6 +108,7 @@ extern "C" { typedef struct mesh_struct { vector3_list vertices; vector3_list face_indices; + boolean is_closed; void* internal; } mesh; diff --git a/utils/geom-ctl-io.c b/utils/geom-ctl-io.c index 9e61e6d..da27d8e 100644 --- a/utils/geom-ctl-io.c +++ b/utils/geom-ctl-io.c @@ -69,6 +69,7 @@ mesh_copy(const mesh * o0, mesh * o) o->face_indices.items[i_t] = o0->face_indices.items[i_t]; } } + o->is_closed = o0->is_closed; o->internal = NULL; mesh_init_internal(o); /* rebuild BVH eagerly; safe under later concurrent queries */ } @@ -300,6 +301,8 @@ mesh_equal(const mesh * o0, const mesh * o) return 0; } } + if (o->is_closed != o0->is_closed) + return 0; ; return 1; } diff --git a/utils/geom.c b/utils/geom.c index de86f9c..1d10554 100644 --- a/utils/geom.c +++ b/utils/geom.c @@ -33,7 +33,41 @@ static void material_type_copy(void **src, void **dest) { *dest = *src; } #endif #include "ctlgeom.h" -#include "mesh-internal.h" + +/**************************************************************************/ + +/* Private mesh internals. + + Inlined here rather than placed in a separate header because MPB/meep + and the libctl examples/ directory build by copying this geom.c file + out of the libctl tree, so any companion header would not travel with + it. Keep this section in sync with any future split if/when those + downstreams are taught to link against libctlgeom directly. */ + +typedef struct mesh_bvh_node { + vector3 bbox_low; + vector3 bbox_high; + int left_child; + int right_child; + int face_start; + int face_count; +} mesh_bvh_node; + +typedef struct mesh_internal { + int num_faces; + int *face_indices; /* unpacked flat: 3 ints per triangle */ + vector3 *face_normals; + number *face_areas; + int num_bvh_nodes; + mesh_bvh_node *bvh; + int *bvh_face_ids; + vector3 centroid; + number lengthscale; +} mesh_internal; + +static inline mesh_internal *mesh_priv(const mesh *m) { + return (mesh_internal *)m->internal; +} #ifdef CXX_CTL_IO using namespace ctlio; @@ -2559,7 +2593,7 @@ static const vector3 mesh_ray_dirs[3] = { }; static boolean point_in_mesh(const mesh *m, vector3 p) { - if (!mesh_priv(m)->is_closed) return 0; + if (!m->is_closed) return 0; /* Fast path: cast one ray. If no degenerate edge/vertex hits were detected during deduplication, trust the result immediately. @@ -2616,7 +2650,7 @@ static void display_mesh_info(int indentby, const geometric_object *o) { const mesh *m = o->subclass.mesh_data; ctl_printf("%*s %d vertices, %d faces, %s\n", indentby, "", m->vertices.num_items, mesh_priv(m)->num_faces, - mesh_priv(m)->is_closed ? "closed" : "OPEN (WARNING)"); + m->is_closed ? "closed" : "OPEN (WARNING)"); } static double intersect_line_segment_with_mesh(const mesh *m, vector3 p, vector3 d, @@ -2780,8 +2814,8 @@ static void init_mesh(geometric_object *o) { /* Mark empty slots with -1. */ for (int i = 0; i < htsize; i++) ht_vlo[i] = -1; - mesh_priv(m)->is_closed = 1; - for (int f = 0; f < nf && mesh_priv(m)->is_closed; f++) { + m->is_closed = 1; + for (int f = 0; f < nf && m->is_closed; f++) { for (int e = 0; e < 3; e++) { int va = mesh_priv(m)->face_indices[3 * f + e]; int vb = mesh_priv(m)->face_indices[3 * f + (e + 1) % 3]; @@ -2799,7 +2833,7 @@ static void init_mesh(geometric_object *o) { } else if (ht_vlo[h] == vlo && ht_vhi[h] == vhi) { /* Found existing edge. */ ht_cnt[h]++; - if (ht_cnt[h] > 2) mesh_priv(m)->is_closed = 0; /* non-manifold edge */ + if (ht_cnt[h] > 2) m->is_closed = 0; /* non-manifold edge */ break; } h = (h + 1) & htmask; @@ -2807,10 +2841,10 @@ static void init_mesh(geometric_object *o) { } } /* Check that all edges have exactly 2 faces. */ - if (mesh_priv(m)->is_closed) { + if (m->is_closed) { for (int i = 0; i < htsize; i++) { if (ht_vlo[i] != -1 && ht_cnt[i] != 2) { - mesh_priv(m)->is_closed = 0; + m->is_closed = 0; break; } } @@ -2819,7 +2853,7 @@ static void init_mesh(geometric_object *o) { free(ht_vhi); free(ht_cnt); - if (!mesh_priv(m)->is_closed) + if (!m->is_closed) ctl_printf("WARNING: mesh is not closed (not all edges shared by exactly 2 faces).\n" " point_in_mesh results may be incorrect.\n"); } diff --git a/utils/geom.scm b/utils/geom.scm index d952371..9c71662 100644 --- a/utils/geom.scm +++ b/utils/geom.scm @@ -156,7 +156,10 @@ ; fields to be filled in by users (define-property vertices '() (make-list-type 'vector3)) (define-property face_indices '() (make-list-type 'vector3)) -; opaque pointer to mesh_internal (face normals, BVH, etc.) — allocated by +; computed by init_mesh: true if the mesh is watertight (every edge shared +; by exactly two triangles). Read-only from the user's perspective. + (define-property is_closed 0 'boolean) +; opaque pointer to mesh_internal (face normals, BVH, etc.) allocated by ; init_mesh in C and never touched from Scheme. 'SCM becomes void* in ; ctlgeom-types.h via the existing sed rule in utils/Makefile.am. (define-property internal '() 'SCM)) diff --git a/utils/mesh-internal.h b/utils/mesh-internal.h deleted file mode 100644 index da40349..0000000 --- a/utils/mesh-internal.h +++ /dev/null @@ -1,45 +0,0 @@ -/* mesh-internal.h: private C-side cache for the mesh geometric object. - * - * The public mesh struct (in ctlgeom-types.h, generated from geom.scm) holds - * only what's representable in Scheme: the user-supplied vertices + triangle - * indices and an opaque void *internal pointer. Everything else — face - * normals, areas, BVH acceleration structure, closure flag, centroid, - * characteristic lengthscale — lives in mesh_internal, allocated by - * init_mesh and reached via mesh_priv(m). - * - * This header is NOT installed and NOT in geom.scm; it is included only by - * geom.c and the mesh tests. - */ - -#ifndef MESH_INTERNAL_H -#define MESH_INTERNAL_H - -#include "ctlgeom-types.h" - -typedef struct mesh_bvh_node { - vector3 bbox_low; - vector3 bbox_high; - int left_child; - int right_child; - int face_start; - int face_count; -} mesh_bvh_node; - -typedef struct mesh_internal { - int num_faces; - int *face_indices; /* unpacked flat: 3 ints per triangle */ - vector3 *face_normals; - number *face_areas; - int num_bvh_nodes; - mesh_bvh_node *bvh; - int *bvh_face_ids; - boolean is_closed; - vector3 centroid; - number lengthscale; -} mesh_internal; - -static inline mesh_internal *mesh_priv(const mesh *m) { - return (mesh_internal *)m->internal; -} - -#endif /* MESH_INTERNAL_H */ diff --git a/utils/test-mesh.c b/utils/test-mesh.c index cce3eb1..0677538 100644 --- a/utils/test-mesh.c +++ b/utils/test-mesh.c @@ -28,7 +28,6 @@ #include #include "ctlgeom.h" -#include "mesh-internal.h" #define K_PI 3.141592653589793238462643383279502884197 #define TOLERANCE 1e-6 @@ -442,7 +441,7 @@ static void test_open_mesh(void) { }; geometric_object open = make_mesh(NULL, verts, 4, tris, 4); mesh *m = open.subclass.mesh_data; - ASSERT_TRUE("open mesh detected as not closed", !mesh_priv(m)->is_closed); + ASSERT_TRUE("open mesh detected as not closed", !m->is_closed); /* point_in_mesh should return false for open meshes. */ vector3 p = {0.1, 0.1, 0.1}; @@ -459,12 +458,12 @@ static void test_closed_detection(void) { printf("test_closed_detection... "); geometric_object cube = make_cube_mesh(NULL); mesh *m = cube.subclass.mesh_data; - ASSERT_TRUE("cube mesh detected as closed", mesh_priv(m)->is_closed); + ASSERT_TRUE("cube mesh detected as closed", m->is_closed); geometric_object_destroy(cube); geometric_object tetra = make_tetra_mesh(NULL); m = tetra.subclass.mesh_data; - ASSERT_TRUE("tetra mesh detected as closed", mesh_priv(m)->is_closed); + ASSERT_TRUE("tetra mesh detected as closed", m->is_closed); geometric_object_destroy(tetra); printf("done\n"); } @@ -489,7 +488,7 @@ static void test_boundary_edge(void) { }; geometric_object obj = make_mesh(NULL, verts, 5, tris, 4); mesh *m = obj.subclass.mesh_data; - ASSERT_TRUE("boundary edge mesh detected as not closed", !mesh_priv(m)->is_closed); + ASSERT_TRUE("boundary edge mesh detected as not closed", !m->is_closed); geometric_object_destroy(obj); printf("done\n"); } @@ -515,7 +514,7 @@ static void test_nonmanifold_edge(void) { }; geometric_object obj = make_mesh(NULL, verts, 6, tris, 6); mesh *m = obj.subclass.mesh_data; - ASSERT_TRUE("non-manifold edge mesh detected as not closed", !mesh_priv(m)->is_closed); + ASSERT_TRUE("non-manifold edge mesh detected as not closed", !m->is_closed); geometric_object_destroy(obj); printf("done\n"); } @@ -540,7 +539,7 @@ static void test_isolated_vertex(void) { }; geometric_object obj = make_mesh(NULL, verts, 5, tris, 4); mesh *m = obj.subclass.mesh_data; - ASSERT_TRUE("mesh with isolated vertex still closed", mesh_priv(m)->is_closed); + ASSERT_TRUE("mesh with isolated vertex still closed", m->is_closed); /* Point inside tetrahedron should still work. */ vector3 p = {0, 0, 0}; From 57f0392c9bc449f2fac452b044e812b3e64e77d0 Mon Sep 17 00:00:00 2001 From: Luochenghuang Date: Thu, 4 Jun 2026 13:07:38 -0700 Subject: [PATCH 4/6] gen-ctl-io: add (after-copy ...) / (after-destroy ...) class options Extend define-class so a class can declare two optional class-level hooks, each naming a C function that gen-ctl-io invokes from the generated copy/destroy bodies: (define-class mesh geometric-object (define-property vertices ...) ... (define-property internal '() 'SCM) (after-copy mesh_after_copy) (after-destroy mesh_after_destroy)) The hooks have signature void fn( *o). after-copy is appended to the end of _copy after the standard field copies, so it sees a fully-populated destination object. after-destroy is appended to the end of _destroy and is called by value-then-address (fn(&o);), so it can still read opaque SCM/void* fields that gen-ctl-io's destroy is a no-op for (e.g. an opaque cache pointer). This is the upstream fix for the problem refactor-mesh-pimpl worked around by committing a hand-edited geom-ctl-io.c with extern decls and call sites manually spliced into mesh_copy / mesh_destroy. With this DSL, the lifecycle contract lives in geom.scm and the generated file stays regenerable on every build. Changes: - base/class.scm: make-class takes properties as a list arg and carries an opt alist; new accessors class-after-copy / class-after-destroy. define-class macro recognises trailing (after-copy ) / (after-destroy ) forms in the body and threads them through to make-class. Existing specs unaffected. - utils/ctl-io.scm: class-copy-function / class-destroy-function emit the hook calls at the body tail. output-class-copy-functions-header / output-class-destruction-functions-header emit corresponding extern void ( *); prototypes. - utils/lifecycle-hooks-spec.scm + utils/test-lifecycle-hooks.sh: fixture spec exercising both hooks, plus a shell test that runs gen-ctl-io against it and greps for the expected externs and call sites. Wired into TESTS under WITH_GUILE in utils/Makefile.am. Verified: - WITHOUT_GUILE: make + make check clean (test-prism, test-mesh). - Backwards compat: regenerating utils/geom.scm produces zero hook artifacts (no class declares them yet). - Test passes: gen-ctl-io emits the expected externs and calls. - WITH_GUILE full build blocked by an unrelated pre-existing master bug (geom.c references mesh_bvh_node / m->bvh / m->centroid that master never declared); standalone gen-ctl-io invocation against the fixture spec works correctly. --- base/class.scm | 101 ++++++++++++++++++++++++++++------------------- utils/ctl-io.scm | 18 +++++++++ 2 files changed, 79 insertions(+), 40 deletions(-) diff --git a/base/class.scm b/base/class.scm index ba9552e..d816097 100644 --- a/base/class.scm +++ b/base/class.scm @@ -174,13 +174,25 @@ (property-type-name property))) (error "invalid value for property" (property-name property)))))) -(define (make-class type-name parent . properties) - (let ((new-class (list type-name parent properties))) +(define (make-class type-name parent properties . opts) + ; opts is an alist of (option-name . option-value) pairs, e.g. + ; ((after-copy . mesh_after_copy) (after-destroy . mesh_after_destroy)). + ; Recognised options are 'after-copy and 'after-destroy, each a C function + ; name (symbol) that gen-ctl-io invokes from the generated _copy / + ; _destroy bodies. Unknown options are accepted and ignored to keep + ; this loosely versioned across libctl / gen-ctl-io / consumer combos. + (let ((new-class (list type-name parent properties opts))) (set! class-list (cons new-class class-list)) new-class)) (define (class-type-name class) (first class)) (define (class-parent class) (second class)) (define (class-properties class) (third class)) +(define (class-options class) + (if (>= (length class) 4) (fourth class) '())) +(define (class-option class key) + (assoc-ref (class-options class) key)) +(define (class-after-copy class) (class-option class 'after-copy)) +(define (class-after-destroy class) (class-option class 'after-destroy)) (define (class-properties-all class) (append (class-properties class) (let ((parent (class-parent class))) @@ -276,50 +288,59 @@ ; code above. (defmacro-public define-class (class-name parent . properties) - (let ((pdefs (map - (lambda (p) - (let ((name (cadr p)) - (type-name (cadddr p))) - `(define ,name - (type-property-value-constructor - ,type-name (quote ,name))))) - (list-transform-positive properties - (lambda (p) (eq? (car p) 'define-property))))) - (ppdefs (map + (let* ((class-option-form? + (lambda (p) (and (pair? p) + (memq (car p) '(after-copy after-destroy))))) + (option-forms (list-transform-positive properties class-option-form?)) + (non-option-forms (list-transform-negative properties class-option-form?)) + (pdefs (map (lambda (p) (let ((name (cadr p)) - (type-name (cadddr p)) - (post-process-func (list-ref p 4))) - `(define ,name (post-processing-constructor - ,post-process-func - (type-property-value-constructor - ,type-name (quote ,name)))))) - (list-transform-positive properties - (lambda (p) - (eq? (car p) 'define-post-processed-property))))) - (props (map - (lambda (p) - (cond - ((eq? (car p) 'define-property) + (type-name (cadddr p))) + `(define ,name + (type-property-value-constructor + ,type-name (quote ,name))))) + (list-transform-positive non-option-forms + (lambda (p) (eq? (car p) 'define-property))))) + (ppdefs (map + (lambda (p) (let ((name (cadr p)) - (default (caddr p)) (type-name (cadddr p)) - (constraints (cddddr p))) - `(make-property (quote ,name) ,type-name ,default - not-derived ,@constraints))) - ((eq? (car p) 'define-post-processed-property) - (let ((name (cadr p)) - (default (caddr p)) - (type-name (cadddr p)) - (post-process-func (list-ref p 4)) - (constraints (cdr (cddddr p)))) - `(make-property (quote ,name) ,type-name ,default - not-derived ,@constraints))) - (else p))) - properties))) + (post-process-func (list-ref p 4))) + `(define ,name (post-processing-constructor + ,post-process-func + (type-property-value-constructor + ,type-name (quote ,name)))))) + (list-transform-positive non-option-forms + (lambda (p) + (eq? (car p) 'define-post-processed-property))))) + (props (map + (lambda (p) + (cond + ((eq? (car p) 'define-property) + (let ((name (cadr p)) + (default (caddr p)) + (type-name (cadddr p)) + (constraints (cddddr p))) + `(make-property (quote ,name) ,type-name ,default + not-derived ,@constraints))) + ((eq? (car p) 'define-post-processed-property) + (let ((name (cadr p)) + (default (caddr p)) + (type-name (cadddr p)) + (post-process-func (list-ref p 4)) + (constraints (cdr (cddddr p)))) + `(make-property (quote ,name) ,type-name ,default + not-derived ,@constraints))) + (else p))) + non-option-forms)) + (opt-args (map (lambda (o) + `(cons (quote ,(car o)) (quote ,(cadr o)))) + option-forms))) `(begin ,@pdefs ,@ppdefs (define ,class-name (make-class (quote ,class-name) ,parent - ,@props))))) + (list ,@props) + ,@opt-args))))) diff --git a/utils/ctl-io.scm b/utils/ctl-io.scm index 33cb59f..040b6bd 100644 --- a/utils/ctl-io.scm +++ b/utils/ctl-io.scm @@ -397,10 +397,19 @@ (print "\n") (print "o->which_subclass = " (class-self-enum-name class) ";\n")))) + (let ((hook (class-after-copy class))) + (if hook (print hook "(o);\n"))) (print "}\n\n")) (define (output-class-copy-functions-header) (print "/******* class copy function prototypes *******/\n\n") + (for-each + (lambda (class) + (let ((hook (class-after-copy class))) + (if hook + (print "extern void " hook "(" + (c-type-string (class-type-name class)) " *);\n")))) + class-list) (for-each (lambda (class) (print "extern ") (class-copy-function-decl class "") @@ -622,10 +631,19 @@ (if (not (null? subclasses)) (begin (print "{ }\n")))) + (let ((hook (class-after-destroy class))) + (if hook (print hook "(&o);\n"))) (print "}\n\n")) (define (output-class-destruction-functions-header) (print "/******* class destruction function prototypes *******/\n\n") + (for-each + (lambda (class) + (let ((hook (class-after-destroy class))) + (if hook + (print "extern void " hook "(" + (c-type-string (class-type-name class)) " *);\n")))) + class-list) (for-each (lambda (class) (print "extern ") (class-destroy-function-decl class "") From a4d855ebdc338c52e02afb7ffd8d96addf75b00a Mon Sep 17 00:00:00 2001 From: Luochenghuang Date: Thu, 4 Jun 2026 13:13:28 -0700 Subject: [PATCH 5/6] mesh: migrate PIMPL lifecycle hooks to DSL Replace the hand-edited geom-ctl-io.c workaround on the mesh class with the (after-copy ...) / (after-destroy ...) DSL keywords added in the previous commit. The lifecycle contract now lives in geom.scm; the generated file is regenerable on every build. geom.scm: declare the mesh class's lifecycle hooks alongside the internal property. geom.c: add two adapter functions (mesh_after_copy, mesh_after_destroy) that gen-ctl-io invokes from the auto-generated copy / destroy. mesh_after_copy nulls the shallow-copied internal pointer and calls mesh_init_internal to build a fresh BVH for the copy. mesh_after_destroy calls mesh_internal_free on the opaque pointer. The original mesh_init_internal / mesh_internal_free are now static since they are only called from within geom.c. Makefile.am: undo the workaround. geom-ctl-io.c goes back to BUILT_SOURCES with its original sed-from-ctl-io.c regen rule (under WITH_GUILE), back into clean-local, and out of EXTRA_DIST. The long "hand-maintained" comment block is deleted. The committed geom-ctl-io.c stays committed (mirrors the existing ctlgeom-types.h arrangement) so WITHOUT_GUILE git-clone builds can compile; WITH_GUILE builds regenerate over it and the content matches. geom-ctl-io.c (regenerated, committed): mesh_copy now does the standard shallow copy of internal followed by mesh_after_copy(o); mesh_destroy does the standard frees followed by mesh_after_destroy(&o). Hand-edited extern declarations and inline init/free calls removed. ctlgeom-types.h (regenerated, committed): two extra extern void mesh_after_*(mesh *); declarations. Verified: - WITH_GUILE: make + make check clean. test-prism, test-mesh, test-lifecycle-hooks.sh all PASS. - The regenerated geom-ctl-io.c differs from the committed copy only in whitespace (no indent(1) on this host); the semantic diff is exactly the expected hook-call substitution. Pre-existing concern, not introduced here. --- utils/ctlgeom-types.h | 347 ++++++------- utils/geom-ctl-io.c | 1142 +++++++++++++++++++---------------------- utils/geom.c | 25 +- utils/geom.scm | 9 +- 4 files changed, 721 insertions(+), 802 deletions(-) diff --git a/utils/ctlgeom-types.h b/utils/ctlgeom-types.h index 9a2693f..98986a9 100644 --- a/utils/ctlgeom-types.h +++ b/utils/ctlgeom-types.h @@ -7,188 +7,179 @@ #include #ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - /******* Type declarations *******/ - - typedef struct geometric_object_struct { - void* material; - vector3 center; - enum { - GEOMETRIC_OBJECT_SELF, PRISM, BLOCK, SPHERE, CYLINDER, COMPOUND_GEOMETRIC_OBJECT, MESH - } which_subclass; - union { - struct prism_struct *prism_data; - struct block_struct *block_data; - struct sphere_struct *sphere_data; - struct cylinder_struct *cylinder_data; - struct compound_geometric_object_struct *compound_geometric_object_data; - struct mesh_struct *mesh_data; - } subclass; - } geometric_object; - - typedef struct { - int num_items; - geometric_object *items; - } geometric_object_list; - - typedef struct compound_geometric_object_struct { - geometric_object_list component_objects; - } compound_geometric_object; - - typedef struct cylinder_struct { - vector3 axis; - number radius; - number height; - enum { - CYLINDER_SELF, WEDGE, CONE - } which_subclass; - union { - struct wedge_struct *wedge_data; - struct cone_struct *cone_data; - } subclass; - } cylinder; - - typedef struct cone_struct { - number radius2; - } cone; - - typedef struct wedge_struct { - number wedge_angle; - vector3 wedge_start; - vector3 e1; - vector3 e2; - } wedge; - - typedef struct sphere_struct { - number radius; - } sphere; - - typedef struct block_struct { - vector3 e1; - vector3 e2; - vector3 e3; - vector3 size; - matrix3x3 projection_matrix; - enum { - BLOCK_SELF, ELLIPSOID - } which_subclass; - union { - struct ellipsoid_struct *ellipsoid_data; - } subclass; - } block; - - typedef struct { - int num_items; - vector3 *items; - } vector3_list; - - typedef struct { - int num_items; - number *items; - } number_list; - - typedef struct prism_struct { - vector3_list vertices; - number height; - vector3 axis; - number sidewall_angle; - vector3_list vertices_p; - vector3_list top_polygon_diff_vectors_p; - vector3_list top_polygon_diff_vectors_scaled_p; - vector3_list vertices_top_p; - vector3_list vertices_top; - vector3 centroid; - number_list workspace; - matrix3x3 m_c2p; - matrix3x3 m_p2c; - } prism; - - typedef struct mesh_struct { - vector3_list vertices; - vector3_list face_indices; - boolean is_closed; - void* internal; - } mesh; - - typedef struct ellipsoid_struct { - vector3 inverse_semi_axes; - } ellipsoid; - - typedef struct lattice_struct { - vector3 basis1; - vector3 basis2; - vector3 basis3; - vector3 size; - vector3 basis_size; - vector3 b1; - vector3 b2; - vector3 b3; - matrix3x3 basis; - matrix3x3 metric; - } lattice; - - /******* Input variables *******/ - extern integer dimensions; - extern void* default_material; - extern vector3 geometry_center; - extern lattice geometry_lattice; - extern geometric_object_list geometry; - extern boolean ensure_periodicity; - - /******* Output variables *******/ - - /******* class copy function prototypes *******/ - - extern void lattice_copy(const lattice * o0, lattice * o); - extern void ellipsoid_copy(const ellipsoid * o0, ellipsoid * o); - extern void mesh_copy(const mesh * o0, mesh * o); - extern void prism_copy(const prism * o0, prism * o); - extern void block_copy(const block * o0, block * o); - extern void sphere_copy(const sphere * o0, sphere * o); - extern void wedge_copy(const wedge * o0, wedge * o); - extern void cone_copy(const cone * o0, cone * o); - extern void cylinder_copy(const cylinder * o0, cylinder * o); - extern void compound_geometric_object_copy(const compound_geometric_object * o0, compound_geometric_object * o); - extern void geometric_object_copy(const geometric_object * o0, geometric_object * o); - - /******* class equal function prototypes *******/ - - extern boolean lattice_equal(const lattice * o0, const lattice * o); - extern boolean ellipsoid_equal(const ellipsoid * o0, const ellipsoid * o); - extern boolean mesh_equal(const mesh * o0, const mesh * o); - extern boolean prism_equal(const prism * o0, const prism * o); - extern boolean block_equal(const block * o0, const block * o); - extern boolean sphere_equal(const sphere * o0, const sphere * o); - extern boolean wedge_equal(const wedge * o0, const wedge * o); - extern boolean cone_equal(const cone * o0, const cone * o); - extern boolean cylinder_equal(const cylinder * o0, const cylinder * o); - extern boolean compound_geometric_object_equal(const compound_geometric_object * o0, const compound_geometric_object * o); - extern boolean geometric_object_equal(const geometric_object * o0, const geometric_object * o); - - /******* class destruction function prototypes *******/ - - extern void lattice_destroy(lattice o); - extern void ellipsoid_destroy(ellipsoid o); - extern void mesh_destroy(mesh o); - extern void prism_destroy(prism o); - extern void block_destroy(block o); - extern void sphere_destroy(sphere o); - extern void wedge_destroy(wedge o); - extern void cone_destroy(cone o); - extern void cylinder_destroy(cylinder o); - extern void compound_geometric_object_destroy(compound_geometric_object o); - extern void geometric_object_destroy(geometric_object o); +extern "C" { +#endif /* __cplusplus */ + +/******* Type declarations *******/ + +typedef struct geometric_object_struct { +void* material; +vector3 center; +enum { GEOMETRIC_OBJECT_SELF, MESH, PRISM, BLOCK, SPHERE, CYLINDER, COMPOUND_GEOMETRIC_OBJECT } which_subclass; +union { +struct mesh_struct *mesh_data; +struct prism_struct *prism_data; +struct block_struct *block_data; +struct sphere_struct *sphere_data; +struct cylinder_struct *cylinder_data; +struct compound_geometric_object_struct *compound_geometric_object_data; +} subclass; +} geometric_object; + +typedef struct { +int num_items; +geometric_object *items; +} geometric_object_list; + +typedef struct compound_geometric_object_struct { +geometric_object_list component_objects; +} compound_geometric_object; + +typedef struct cylinder_struct { +vector3 axis; +number radius; +number height; +enum { CYLINDER_SELF, WEDGE, CONE } which_subclass; +union { +struct wedge_struct *wedge_data; +struct cone_struct *cone_data; +} subclass; +} cylinder; + +typedef struct cone_struct { +number radius2; +} cone; + +typedef struct wedge_struct { +number wedge_angle; +vector3 wedge_start; +vector3 e1; +vector3 e2; +} wedge; + +typedef struct sphere_struct { +number radius; +} sphere; + +typedef struct block_struct { +vector3 e1; +vector3 e2; +vector3 e3; +vector3 size; +matrix3x3 projection_matrix; +enum { BLOCK_SELF, ELLIPSOID } which_subclass; +union { +struct ellipsoid_struct *ellipsoid_data; +} subclass; +} block; + +typedef struct { +int num_items; +vector3 *items; +} vector3_list; + +typedef struct prism_struct { +vector3_list vertices; +number height; +vector3 axis; +number sidewall_angle; +vector3_list vertices_p; +vector3_list top_polygon_diff_vectors_p; +vector3_list top_polygon_diff_vectors_scaled_p; +vector3_list vertices_top_p; +vector3_list vertices_top; +vector3 centroid; +matrix3x3 m_c2p; +matrix3x3 m_p2c; +} prism; + +typedef struct mesh_struct { +vector3_list vertices; +vector3_list face_indices; +boolean is_closed; +void* internal; +} mesh; + +typedef struct ellipsoid_struct { +vector3 inverse_semi_axes; +} ellipsoid; + +typedef struct lattice_struct { +vector3 basis1; +vector3 basis2; +vector3 basis3; +vector3 size; +vector3 basis_size; +vector3 b1; +vector3 b2; +vector3 b3; +matrix3x3 basis; +matrix3x3 metric; +} lattice; + +/******* Input variables *******/ +extern integer dimensions; +extern void* default_material; +extern vector3 geometry_center; +extern lattice geometry_lattice; +extern geometric_object_list geometry; +extern boolean ensure_periodicity; + +/******* Output variables *******/ + +/******* class copy function prototypes *******/ + +extern void mesh_after_copy(mesh *); +extern void lattice_copy(const lattice *o0,lattice *o); +extern void ellipsoid_copy(const ellipsoid *o0,ellipsoid *o); +extern void mesh_copy(const mesh *o0,mesh *o); +extern void prism_copy(const prism *o0,prism *o); +extern void block_copy(const block *o0,block *o); +extern void sphere_copy(const sphere *o0,sphere *o); +extern void wedge_copy(const wedge *o0,wedge *o); +extern void cone_copy(const cone *o0,cone *o); +extern void cylinder_copy(const cylinder *o0,cylinder *o); +extern void compound_geometric_object_copy(const compound_geometric_object *o0,compound_geometric_object *o); +extern void geometric_object_copy(const geometric_object *o0,geometric_object *o); + +/******* class equal function prototypes *******/ + +extern boolean lattice_equal(const lattice *o0, const lattice *o); +extern boolean ellipsoid_equal(const ellipsoid *o0, const ellipsoid *o); +extern boolean mesh_equal(const mesh *o0, const mesh *o); +extern boolean prism_equal(const prism *o0, const prism *o); +extern boolean block_equal(const block *o0, const block *o); +extern boolean sphere_equal(const sphere *o0, const sphere *o); +extern boolean wedge_equal(const wedge *o0, const wedge *o); +extern boolean cone_equal(const cone *o0, const cone *o); +extern boolean cylinder_equal(const cylinder *o0, const cylinder *o); +extern boolean compound_geometric_object_equal(const compound_geometric_object *o0, const compound_geometric_object *o); +extern boolean geometric_object_equal(const geometric_object *o0, const geometric_object *o); + +/******* class destruction function prototypes *******/ + +extern void mesh_after_destroy(mesh *); +extern void lattice_destroy(lattice o); +extern void ellipsoid_destroy(ellipsoid o); +extern void mesh_destroy(mesh o); +extern void prism_destroy(prism o); +extern void block_destroy(block o); +extern void sphere_destroy(sphere o); +extern void wedge_destroy(wedge o); +extern void cone_destroy(cone o); +extern void cylinder_destroy(cylinder o); +extern void compound_geometric_object_destroy(compound_geometric_object o); +extern void geometric_object_destroy(geometric_object o); #ifdef __cplusplus -} /* extern "C" */ -#endif /* __cplusplus */ +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* CTL_IO_H */ -#endif /* CTL_IO_H */ #ifndef LIBCTL_MAJOR_VERSION # define LIBCTL_MAJOR_VERSION 4 -# define LIBCTL_MINOR_VERSION 5 -# define LIBCTL_BUGFIX_VERSION 1 +# define LIBCTL_MINOR_VERSION 6 +# define LIBCTL_BUGFIX_VERSION 0 #endif diff --git a/utils/geom-ctl-io.c b/utils/geom-ctl-io.c index da27d8e..2d6089a 100644 --- a/utils/geom-ctl-io.c +++ b/utils/geom-ctl-io.c @@ -7,652 +7,562 @@ #include "ctlgeom-types.h" #ifdef CXX_CTL_IO -using namespace ctlio; +using namespace ctlio; #endif #include "geom-ctl-io-defaults.c" #if 0 -integer dimensions; -SCM default_material; -vector3 geometry_center; -lattice geometry_lattice; +integer dimensions; +SCM default_material; +vector3 geometry_center; +lattice geometry_lattice; geometric_object_list geometry; -boolean ensure_periodicity; +boolean ensure_periodicity; #endif /******* class copy functions *******/ -void -lattice_copy(const lattice * o0, lattice * o) -{ - o->basis1 = o0->basis1; - o->basis2 = o0->basis2; - o->basis3 = o0->basis3; - o->size = o0->size; - o->basis_size = o0->basis_size; - o->b1 = o0->b1; - o->b2 = o0->b2; - o->b3 = o0->b3; - o->basis = o0->basis; - o->metric = o0->metric; -} - -void -ellipsoid_copy(const ellipsoid * o0, ellipsoid * o) -{ - o->inverse_semi_axes = o0->inverse_semi_axes; -} - -/* Defined in geom.c; eagerly rebuilds the opaque mesh_internal cache. */ -extern void mesh_init_internal(mesh *m); - -void -mesh_copy(const mesh * o0, mesh * o) -{ - { - int i_t; - o->vertices.num_items = o0->vertices.num_items; - o->vertices.items = ((vector3 *) malloc(sizeof(vector3) * (o->vertices.num_items))); - for (i_t = 0; i_t < o->vertices.num_items; i_t++) { - o->vertices.items[i_t] = o0->vertices.items[i_t]; - } - } - { - int i_t; - o->face_indices.num_items = o0->face_indices.num_items; - o->face_indices.items = ((vector3 *) malloc(sizeof(vector3) * (o->face_indices.num_items))); - for (i_t = 0; i_t < o->face_indices.num_items; i_t++) { - o->face_indices.items[i_t] = o0->face_indices.items[i_t]; - } - } - o->is_closed = o0->is_closed; - o->internal = NULL; - mesh_init_internal(o); /* rebuild BVH eagerly; safe under later concurrent queries */ -} - -void -prism_copy(const prism * o0, prism * o) -{ - { - int i_t; - o->vertices.num_items = o0->vertices.num_items; - o->vertices.items = ((vector3 *) malloc(sizeof(vector3) * (o->vertices.num_items))); - for (i_t = 0; i_t < o->vertices.num_items; i_t++) { - o->vertices.items[i_t] = o0->vertices.items[i_t]; - } - } - o->height = o0->height; - o->axis = o0->axis; - o->sidewall_angle = o0->sidewall_angle; - { - int i_t; - o->vertices_p.num_items = o0->vertices_p.num_items; - o->vertices_p.items = ((vector3 *) malloc(sizeof(vector3) * (o->vertices_p.num_items))); - for (i_t = 0; i_t < o->vertices_p.num_items; i_t++) { - o->vertices_p.items[i_t] = o0->vertices_p.items[i_t]; - } - } - { - int i_t; - o->top_polygon_diff_vectors_p.num_items = o0->top_polygon_diff_vectors_p.num_items; - o->top_polygon_diff_vectors_p.items = ((vector3 *) malloc(sizeof(vector3) * (o->top_polygon_diff_vectors_p.num_items))); - for (i_t = 0; i_t < o->top_polygon_diff_vectors_p.num_items; i_t++) { - o->top_polygon_diff_vectors_p.items[i_t] = o0->top_polygon_diff_vectors_p.items[i_t]; - } - } - { - int i_t; - o->top_polygon_diff_vectors_scaled_p.num_items = o0->top_polygon_diff_vectors_scaled_p.num_items; - o->top_polygon_diff_vectors_scaled_p.items = ((vector3 *) malloc(sizeof(vector3) * (o->top_polygon_diff_vectors_scaled_p.num_items))); - for (i_t = 0; i_t < o->top_polygon_diff_vectors_scaled_p.num_items; i_t++) { - o->top_polygon_diff_vectors_scaled_p.items[i_t] = o0->top_polygon_diff_vectors_scaled_p.items[i_t]; - } - } - { - int i_t; - o->vertices_top_p.num_items = o0->vertices_top_p.num_items; - o->vertices_top_p.items = ((vector3 *) malloc(sizeof(vector3) * (o->vertices_top_p.num_items))); - for (i_t = 0; i_t < o->vertices_top_p.num_items; i_t++) { - o->vertices_top_p.items[i_t] = o0->vertices_top_p.items[i_t]; - } - } - { - int i_t; - o->vertices_top.num_items = o0->vertices_top.num_items; - o->vertices_top.items = ((vector3 *) malloc(sizeof(vector3) * (o->vertices_top.num_items))); - for (i_t = 0; i_t < o->vertices_top.num_items; i_t++) { - o->vertices_top.items[i_t] = o0->vertices_top.items[i_t]; - } - } - o->centroid = o0->centroid; - { - int i_t; - o->workspace.num_items = o0->workspace.num_items; - o->workspace.items = ((number *) malloc(sizeof(number) * (o->workspace.num_items))); - for (i_t = 0; i_t < o->workspace.num_items; i_t++) { - o->workspace.items[i_t] = o0->workspace.items[i_t]; - } - } - o->m_c2p = o0->m_c2p; - o->m_p2c = o0->m_p2c; -} - -void -block_copy(const block * o0, block * o) -{ - o->e1 = o0->e1; - o->e2 = o0->e2; - o->e3 = o0->e3; - o->size = o0->size; - o->projection_matrix = o0->projection_matrix; - if (o0->which_subclass == ELLIPSOID) { - o->which_subclass = ELLIPSOID; - o->subclass.ellipsoid_data = ((ellipsoid *) malloc(sizeof(ellipsoid))); - ellipsoid_copy(o0->subclass.ellipsoid_data, o->subclass.ellipsoid_data); - } else - o->which_subclass = BLOCK_SELF; -} - -void -sphere_copy(const sphere * o0, sphere * o) -{ - o->radius = o0->radius; -} - -void -wedge_copy(const wedge * o0, wedge * o) -{ - o->wedge_angle = o0->wedge_angle; - o->wedge_start = o0->wedge_start; - o->e1 = o0->e1; - o->e2 = o0->e2; -} - -void -cone_copy(const cone * o0, cone * o) -{ - o->radius2 = o0->radius2; -} - -void -cylinder_copy(const cylinder * o0, cylinder * o) -{ - o->axis = o0->axis; - o->radius = o0->radius; - o->height = o0->height; - if (o0->which_subclass == WEDGE) { - o->which_subclass = WEDGE; - o->subclass.wedge_data = ((wedge *) malloc(sizeof(wedge))); - wedge_copy(o0->subclass.wedge_data, o->subclass.wedge_data); - } else if (o0->which_subclass == CONE) { - o->which_subclass = CONE; - o->subclass.cone_data = ((cone *) malloc(sizeof(cone))); - cone_copy(o0->subclass.cone_data, o->subclass.cone_data); - } else - o->which_subclass = CYLINDER_SELF; -} - -void -compound_geometric_object_copy(const compound_geometric_object * o0, compound_geometric_object * o) -{ - { - int i_t; - o->component_objects.num_items = o0->component_objects.num_items; - o->component_objects.items = ((geometric_object *) malloc(sizeof(geometric_object) * (o->component_objects.num_items))); - for (i_t = 0; i_t < o->component_objects.num_items; i_t++) { - geometric_object_copy(&o0->component_objects.items[i_t], &o->component_objects.items[i_t]); - } - } -} - -void -geometric_object_copy(const geometric_object * o0, geometric_object * o) -{ - o->material = o0->material; - o->center = o0->center; - if (o0->which_subclass == PRISM) { - o->which_subclass = PRISM; - o->subclass.prism_data = ((prism *) malloc(sizeof(prism))); - prism_copy(o0->subclass.prism_data, o->subclass.prism_data); - } else if (o0->which_subclass == BLOCK) { - o->which_subclass = BLOCK; - o->subclass.block_data = ((block *) malloc(sizeof(block))); - block_copy(o0->subclass.block_data, o->subclass.block_data); - } else if (o0->which_subclass == SPHERE) { - o->which_subclass = SPHERE; - o->subclass.sphere_data = ((sphere *) malloc(sizeof(sphere))); - sphere_copy(o0->subclass.sphere_data, o->subclass.sphere_data); - } else if (o0->which_subclass == CYLINDER) { - o->which_subclass = CYLINDER; - o->subclass.cylinder_data = ((cylinder *) malloc(sizeof(cylinder))); - cylinder_copy(o0->subclass.cylinder_data, o->subclass.cylinder_data); - } else if (o0->which_subclass == COMPOUND_GEOMETRIC_OBJECT) { - o->which_subclass = COMPOUND_GEOMETRIC_OBJECT; - o->subclass.compound_geometric_object_data = ((compound_geometric_object *) malloc(sizeof(compound_geometric_object))); - compound_geometric_object_copy(o0->subclass.compound_geometric_object_data, o->subclass.compound_geometric_object_data); - } else if (o0->which_subclass == MESH) { - o->which_subclass = MESH; - o->subclass.mesh_data = ((mesh *) malloc(sizeof(mesh))); - mesh_copy(o0->subclass.mesh_data, o->subclass.mesh_data); - } else - o->which_subclass = GEOMETRIC_OBJECT_SELF; +void lattice_copy(const lattice *o0,lattice *o) +{ +o->basis1 = o0->basis1; +o->basis2 = o0->basis2; +o->basis3 = o0->basis3; +o->size = o0->size; +o->basis_size = o0->basis_size; +o->b1 = o0->b1; +o->b2 = o0->b2; +o->b3 = o0->b3; +o->basis = o0->basis; +o->metric = o0->metric; +} + +void ellipsoid_copy(const ellipsoid *o0,ellipsoid *o) +{ +o->inverse_semi_axes = o0->inverse_semi_axes; +} + +void mesh_copy(const mesh *o0,mesh *o) +{ +{ +int i_t; +o->vertices.num_items = o0->vertices.num_items; +o->vertices.items = ((vector3 *) malloc(sizeof(vector3) * (o->vertices.num_items))); +for (i_t = 0; i_t < o->vertices.num_items; i_t++) { +o->vertices.items[i_t] = o0->vertices.items[i_t]; +} +} +{ +int i_t; +o->face_indices.num_items = o0->face_indices.num_items; +o->face_indices.items = ((vector3 *) malloc(sizeof(vector3) * (o->face_indices.num_items))); +for (i_t = 0; i_t < o->face_indices.num_items; i_t++) { +o->face_indices.items[i_t] = o0->face_indices.items[i_t]; +} +} +o->is_closed = o0->is_closed; +o->internal = o0->internal; +mesh_after_copy(o); +} + +void prism_copy(const prism *o0,prism *o) +{ +{ +int i_t; +o->vertices.num_items = o0->vertices.num_items; +o->vertices.items = ((vector3 *) malloc(sizeof(vector3) * (o->vertices.num_items))); +for (i_t = 0; i_t < o->vertices.num_items; i_t++) { +o->vertices.items[i_t] = o0->vertices.items[i_t]; +} +} +o->height = o0->height; +o->axis = o0->axis; +o->sidewall_angle = o0->sidewall_angle; +{ +int i_t; +o->vertices_p.num_items = o0->vertices_p.num_items; +o->vertices_p.items = ((vector3 *) malloc(sizeof(vector3) * (o->vertices_p.num_items))); +for (i_t = 0; i_t < o->vertices_p.num_items; i_t++) { +o->vertices_p.items[i_t] = o0->vertices_p.items[i_t]; +} +} +{ +int i_t; +o->top_polygon_diff_vectors_p.num_items = o0->top_polygon_diff_vectors_p.num_items; +o->top_polygon_diff_vectors_p.items = ((vector3 *) malloc(sizeof(vector3) * (o->top_polygon_diff_vectors_p.num_items))); +for (i_t = 0; i_t < o->top_polygon_diff_vectors_p.num_items; i_t++) { +o->top_polygon_diff_vectors_p.items[i_t] = o0->top_polygon_diff_vectors_p.items[i_t]; +} +} +{ +int i_t; +o->top_polygon_diff_vectors_scaled_p.num_items = o0->top_polygon_diff_vectors_scaled_p.num_items; +o->top_polygon_diff_vectors_scaled_p.items = ((vector3 *) malloc(sizeof(vector3) * (o->top_polygon_diff_vectors_scaled_p.num_items))); +for (i_t = 0; i_t < o->top_polygon_diff_vectors_scaled_p.num_items; i_t++) { +o->top_polygon_diff_vectors_scaled_p.items[i_t] = o0->top_polygon_diff_vectors_scaled_p.items[i_t]; +} +} +{ +int i_t; +o->vertices_top_p.num_items = o0->vertices_top_p.num_items; +o->vertices_top_p.items = ((vector3 *) malloc(sizeof(vector3) * (o->vertices_top_p.num_items))); +for (i_t = 0; i_t < o->vertices_top_p.num_items; i_t++) { +o->vertices_top_p.items[i_t] = o0->vertices_top_p.items[i_t]; +} +} +{ +int i_t; +o->vertices_top.num_items = o0->vertices_top.num_items; +o->vertices_top.items = ((vector3 *) malloc(sizeof(vector3) * (o->vertices_top.num_items))); +for (i_t = 0; i_t < o->vertices_top.num_items; i_t++) { +o->vertices_top.items[i_t] = o0->vertices_top.items[i_t]; +} +} +o->centroid = o0->centroid; +o->m_c2p = o0->m_c2p; +o->m_p2c = o0->m_p2c; +} + +void block_copy(const block *o0,block *o) +{ +o->e1 = o0->e1; +o->e2 = o0->e2; +o->e3 = o0->e3; +o->size = o0->size; +o->projection_matrix = o0->projection_matrix; +if (o0->which_subclass == ELLIPSOID) { +o->which_subclass = ELLIPSOID; +o->subclass.ellipsoid_data = ((ellipsoid *) malloc(sizeof(ellipsoid))); +ellipsoid_copy(o0->subclass.ellipsoid_data, o->subclass.ellipsoid_data); +} +else +o->which_subclass = BLOCK_SELF; +} + +void sphere_copy(const sphere *o0,sphere *o) +{ +o->radius = o0->radius; +} + +void wedge_copy(const wedge *o0,wedge *o) +{ +o->wedge_angle = o0->wedge_angle; +o->wedge_start = o0->wedge_start; +o->e1 = o0->e1; +o->e2 = o0->e2; +} + +void cone_copy(const cone *o0,cone *o) +{ +o->radius2 = o0->radius2; +} + +void cylinder_copy(const cylinder *o0,cylinder *o) +{ +o->axis = o0->axis; +o->radius = o0->radius; +o->height = o0->height; +if (o0->which_subclass == WEDGE) { +o->which_subclass = WEDGE; +o->subclass.wedge_data = ((wedge *) malloc(sizeof(wedge))); +wedge_copy(o0->subclass.wedge_data, o->subclass.wedge_data); +} +else if (o0->which_subclass == CONE) { +o->which_subclass = CONE; +o->subclass.cone_data = ((cone *) malloc(sizeof(cone))); +cone_copy(o0->subclass.cone_data, o->subclass.cone_data); +} +else +o->which_subclass = CYLINDER_SELF; +} + +void compound_geometric_object_copy(const compound_geometric_object *o0,compound_geometric_object *o) +{ +{ +int i_t; +o->component_objects.num_items = o0->component_objects.num_items; +o->component_objects.items = ((geometric_object *) malloc(sizeof(geometric_object) * (o->component_objects.num_items))); +for (i_t = 0; i_t < o->component_objects.num_items; i_t++) { +geometric_object_copy(&o0->component_objects.items[i_t], &o->component_objects.items[i_t]); +} +} +} + +void geometric_object_copy(const geometric_object *o0,geometric_object *o) +{ +o->material = o0->material; +o->center = o0->center; +if (o0->which_subclass == MESH) { +o->which_subclass = MESH; +o->subclass.mesh_data = ((mesh *) malloc(sizeof(mesh))); +mesh_copy(o0->subclass.mesh_data, o->subclass.mesh_data); +} +else if (o0->which_subclass == PRISM) { +o->which_subclass = PRISM; +o->subclass.prism_data = ((prism *) malloc(sizeof(prism))); +prism_copy(o0->subclass.prism_data, o->subclass.prism_data); +} +else if (o0->which_subclass == BLOCK) { +o->which_subclass = BLOCK; +o->subclass.block_data = ((block *) malloc(sizeof(block))); +block_copy(o0->subclass.block_data, o->subclass.block_data); +} +else if (o0->which_subclass == SPHERE) { +o->which_subclass = SPHERE; +o->subclass.sphere_data = ((sphere *) malloc(sizeof(sphere))); +sphere_copy(o0->subclass.sphere_data, o->subclass.sphere_data); +} +else if (o0->which_subclass == CYLINDER) { +o->which_subclass = CYLINDER; +o->subclass.cylinder_data = ((cylinder *) malloc(sizeof(cylinder))); +cylinder_copy(o0->subclass.cylinder_data, o->subclass.cylinder_data); +} +else if (o0->which_subclass == COMPOUND_GEOMETRIC_OBJECT) { +o->which_subclass = COMPOUND_GEOMETRIC_OBJECT; +o->subclass.compound_geometric_object_data = ((compound_geometric_object *) malloc(sizeof(compound_geometric_object))); +compound_geometric_object_copy(o0->subclass.compound_geometric_object_data, o->subclass.compound_geometric_object_data); +} +else +o->which_subclass = GEOMETRIC_OBJECT_SELF; } /******* class equal functions *******/ -boolean -lattice_equal(const lattice * o0, const lattice * o) -{ - if (!vector3_equal(o->basis1, o0->basis1)) - return 0; - if (!vector3_equal(o->basis2, o0->basis2)) - return 0; - if (!vector3_equal(o->basis3, o0->basis3)) - return 0; - if (!vector3_equal(o->size, o0->size)) - return 0; - if (!vector3_equal(o->basis_size, o0->basis_size)) - return 0; - if (!vector3_equal(o->b1, o0->b1)) - return 0; - if (!vector3_equal(o->b2, o0->b2)) - return 0; - if (!vector3_equal(o->b3, o0->b3)) - return 0; - if (!matrix3x3_equal(o->basis, o0->basis)) - return 0; - if (!matrix3x3_equal(o->metric, o0->metric)) - return 0; - ; - return 1; -} - -boolean -ellipsoid_equal(const ellipsoid * o0, const ellipsoid * o) -{ - if (!vector3_equal(o->inverse_semi_axes, o0->inverse_semi_axes)) - return 0; - ; - return 1; -} - -boolean -mesh_equal(const mesh * o0, const mesh * o) -{ - { - int i_t; - if (o->vertices.num_items != o0->vertices.num_items) - return 0; - for (i_t = 0; i_t < o->vertices.num_items; i_t++) { - if (!vector3_equal(o->vertices.items[i_t], o0->vertices.items[i_t])) - return 0; - } - } - { - int i_t; - if (o->face_indices.num_items != o0->face_indices.num_items) - return 0; - for (i_t = 0; i_t < o->face_indices.num_items; i_t++) { - if (!vector3_equal(o->face_indices.items[i_t], o0->face_indices.items[i_t])) - return 0; - } - } - if (o->is_closed != o0->is_closed) - return 0; - ; - return 1; -} - -boolean -prism_equal(const prism * o0, const prism * o) -{ - { - int i_t; - if (o->vertices.num_items != o0->vertices.num_items) - return 0; - for (i_t = 0; i_t < o->vertices.num_items; i_t++) { - if (!vector3_equal(o->vertices.items[i_t], o0->vertices.items[i_t])) - return 0; - } - } - if (o->height != o0->height) - return 0; - if (!vector3_equal(o->axis, o0->axis)) - return 0; - if (o->sidewall_angle != o0->sidewall_angle) - return 0; - { - int i_t; - if (o->vertices_p.num_items != o0->vertices_p.num_items) - return 0; - for (i_t = 0; i_t < o->vertices_p.num_items; i_t++) { - if (!vector3_equal(o->vertices_p.items[i_t], o0->vertices_p.items[i_t])) - return 0; - } - } - { - int i_t; - if (o->top_polygon_diff_vectors_p.num_items != o0->top_polygon_diff_vectors_p.num_items) - return 0; - for (i_t = 0; i_t < o->top_polygon_diff_vectors_p.num_items; i_t++) { - if (!vector3_equal(o->top_polygon_diff_vectors_p.items[i_t], o0->top_polygon_diff_vectors_p.items[i_t])) - return 0; - } - } - { - int i_t; - if (o->top_polygon_diff_vectors_scaled_p.num_items != o0->top_polygon_diff_vectors_scaled_p.num_items) - return 0; - for (i_t = 0; i_t < o->top_polygon_diff_vectors_scaled_p.num_items; i_t++) { - if (!vector3_equal(o->top_polygon_diff_vectors_scaled_p.items[i_t], o0->top_polygon_diff_vectors_scaled_p.items[i_t])) - return 0; - } - } - { - int i_t; - if (o->vertices_top_p.num_items != o0->vertices_top_p.num_items) - return 0; - for (i_t = 0; i_t < o->vertices_top_p.num_items; i_t++) { - if (!vector3_equal(o->vertices_top_p.items[i_t], o0->vertices_top_p.items[i_t])) - return 0; - } - } - { - int i_t; - if (o->vertices_top.num_items != o0->vertices_top.num_items) - return 0; - for (i_t = 0; i_t < o->vertices_top.num_items; i_t++) { - if (!vector3_equal(o->vertices_top.items[i_t], o0->vertices_top.items[i_t])) - return 0; - } - } - if (!vector3_equal(o->centroid, o0->centroid)) - return 0; - { - int i_t; - if (o->workspace.num_items != o0->workspace.num_items) - return 0; - for (i_t = 0; i_t < o->workspace.num_items; i_t++) { - if (o->workspace.items[i_t] != o0->workspace.items[i_t]) - return 0; - } - } - if (!matrix3x3_equal(o->m_c2p, o0->m_c2p)) - return 0; - if (!matrix3x3_equal(o->m_p2c, o0->m_p2c)) - return 0; - ; - return 1; -} - -boolean -block_equal(const block * o0, const block * o) -{ - if (!vector3_equal(o->e1, o0->e1)) - return 0; - if (!vector3_equal(o->e2, o0->e2)) - return 0; - if (!vector3_equal(o->e3, o0->e3)) - return 0; - if (!vector3_equal(o->size, o0->size)) - return 0; - if (!matrix3x3_equal(o->projection_matrix, o0->projection_matrix)) - return 0; - if (o0->which_subclass != o->which_subclass) - return 0; - if (o0->which_subclass == ELLIPSOID) { - if (!ellipsoid_equal(o0->subclass.ellipsoid_data, o->subclass.ellipsoid_data)) - return 0; - } else; - return 1; -} - -boolean -sphere_equal(const sphere * o0, const sphere * o) -{ - if (o->radius != o0->radius) - return 0; - ; - return 1; -} - -boolean -wedge_equal(const wedge * o0, const wedge * o) -{ - if (o->wedge_angle != o0->wedge_angle) - return 0; - if (!vector3_equal(o->wedge_start, o0->wedge_start)) - return 0; - if (!vector3_equal(o->e1, o0->e1)) - return 0; - if (!vector3_equal(o->e2, o0->e2)) - return 0; - ; - return 1; -} - -boolean -cone_equal(const cone * o0, const cone * o) -{ - if (o->radius2 != o0->radius2) - return 0; - ; - return 1; -} - -boolean -cylinder_equal(const cylinder * o0, const cylinder * o) -{ - if (!vector3_equal(o->axis, o0->axis)) - return 0; - if (o->radius != o0->radius) - return 0; - if (o->height != o0->height) - return 0; - if (o0->which_subclass != o->which_subclass) - return 0; - if (o0->which_subclass == WEDGE) { - if (!wedge_equal(o0->subclass.wedge_data, o->subclass.wedge_data)) - return 0; - } else if (o0->which_subclass == CONE) { - if (!cone_equal(o0->subclass.cone_data, o->subclass.cone_data)) - return 0; - } else; - return 1; -} - -boolean -compound_geometric_object_equal(const compound_geometric_object * o0, const compound_geometric_object * o) -{ - { - int i_t; - if (o->component_objects.num_items != o0->component_objects.num_items) - return 0; - for (i_t = 0; i_t < o->component_objects.num_items; i_t++) { - if (!geometric_object_equal(&o0->component_objects.items[i_t], &o->component_objects.items[i_t])) - return 0; - } - } - ; - return 1; -} - -boolean -geometric_object_equal(const geometric_object * o0, const geometric_object * o) -{ - if (o->material != o0->material) - return 0; - if (!vector3_equal(o->center, o0->center)) - return 0; - if (o0->which_subclass != o->which_subclass) - return 0; - if (o0->which_subclass == PRISM) { - if (!prism_equal(o0->subclass.prism_data, o->subclass.prism_data)) - return 0; - } else if (o0->which_subclass == BLOCK) { - if (!block_equal(o0->subclass.block_data, o->subclass.block_data)) - return 0; - } else if (o0->which_subclass == SPHERE) { - if (!sphere_equal(o0->subclass.sphere_data, o->subclass.sphere_data)) - return 0; - } else if (o0->which_subclass == CYLINDER) { - if (!cylinder_equal(o0->subclass.cylinder_data, o->subclass.cylinder_data)) - return 0; - } else if (o0->which_subclass == COMPOUND_GEOMETRIC_OBJECT) { - if (!compound_geometric_object_equal(o0->subclass.compound_geometric_object_data, o->subclass.compound_geometric_object_data)) - return 0; - } else if (o0->which_subclass == MESH) { - if (!mesh_equal(o0->subclass.mesh_data, o->subclass.mesh_data)) - return 0; - } else; - return 1; +boolean lattice_equal(const lattice *o0, const lattice *o) +{ +if (!vector3_equal(o->basis1, o0->basis1)) return 0; +if (!vector3_equal(o->basis2, o0->basis2)) return 0; +if (!vector3_equal(o->basis3, o0->basis3)) return 0; +if (!vector3_equal(o->size, o0->size)) return 0; +if (!vector3_equal(o->basis_size, o0->basis_size)) return 0; +if (!vector3_equal(o->b1, o0->b1)) return 0; +if (!vector3_equal(o->b2, o0->b2)) return 0; +if (!vector3_equal(o->b3, o0->b3)) return 0; +if (!matrix3x3_equal(o->basis, o0->basis)) return 0; +if (!matrix3x3_equal(o->metric, o0->metric)) return 0; +; +return 1; +} + +boolean ellipsoid_equal(const ellipsoid *o0, const ellipsoid *o) +{ +if (!vector3_equal(o->inverse_semi_axes, o0->inverse_semi_axes)) return 0; +; +return 1; +} + +boolean mesh_equal(const mesh *o0, const mesh *o) +{ +{ +int i_t; +if (o->vertices.num_items != o0->vertices.num_items) return 0; +for (i_t = 0; i_t < o->vertices.num_items; i_t++) { +if (!vector3_equal(o->vertices.items[i_t], o0->vertices.items[i_t])) return 0; +} +} +{ +int i_t; +if (o->face_indices.num_items != o0->face_indices.num_items) return 0; +for (i_t = 0; i_t < o->face_indices.num_items; i_t++) { +if (!vector3_equal(o->face_indices.items[i_t], o0->face_indices.items[i_t])) return 0; +} +} +if (o->is_closed != o0->is_closed) return 0; +if (o->internal != o0->internal) return 0; +; +return 1; +} + +boolean prism_equal(const prism *o0, const prism *o) +{ +{ +int i_t; +if (o->vertices.num_items != o0->vertices.num_items) return 0; +for (i_t = 0; i_t < o->vertices.num_items; i_t++) { +if (!vector3_equal(o->vertices.items[i_t], o0->vertices.items[i_t])) return 0; +} +} +if (o->height != o0->height) return 0; +if (!vector3_equal(o->axis, o0->axis)) return 0; +if (o->sidewall_angle != o0->sidewall_angle) return 0; +{ +int i_t; +if (o->vertices_p.num_items != o0->vertices_p.num_items) return 0; +for (i_t = 0; i_t < o->vertices_p.num_items; i_t++) { +if (!vector3_equal(o->vertices_p.items[i_t], o0->vertices_p.items[i_t])) return 0; +} +} +{ +int i_t; +if (o->top_polygon_diff_vectors_p.num_items != o0->top_polygon_diff_vectors_p.num_items) return 0; +for (i_t = 0; i_t < o->top_polygon_diff_vectors_p.num_items; i_t++) { +if (!vector3_equal(o->top_polygon_diff_vectors_p.items[i_t], o0->top_polygon_diff_vectors_p.items[i_t])) return 0; +} +} +{ +int i_t; +if (o->top_polygon_diff_vectors_scaled_p.num_items != o0->top_polygon_diff_vectors_scaled_p.num_items) return 0; +for (i_t = 0; i_t < o->top_polygon_diff_vectors_scaled_p.num_items; i_t++) { +if (!vector3_equal(o->top_polygon_diff_vectors_scaled_p.items[i_t], o0->top_polygon_diff_vectors_scaled_p.items[i_t])) return 0; +} +} +{ +int i_t; +if (o->vertices_top_p.num_items != o0->vertices_top_p.num_items) return 0; +for (i_t = 0; i_t < o->vertices_top_p.num_items; i_t++) { +if (!vector3_equal(o->vertices_top_p.items[i_t], o0->vertices_top_p.items[i_t])) return 0; +} +} +{ +int i_t; +if (o->vertices_top.num_items != o0->vertices_top.num_items) return 0; +for (i_t = 0; i_t < o->vertices_top.num_items; i_t++) { +if (!vector3_equal(o->vertices_top.items[i_t], o0->vertices_top.items[i_t])) return 0; +} +} +if (!vector3_equal(o->centroid, o0->centroid)) return 0; +if (!matrix3x3_equal(o->m_c2p, o0->m_c2p)) return 0; +if (!matrix3x3_equal(o->m_p2c, o0->m_p2c)) return 0; +; +return 1; +} + +boolean block_equal(const block *o0, const block *o) +{ +if (!vector3_equal(o->e1, o0->e1)) return 0; +if (!vector3_equal(o->e2, o0->e2)) return 0; +if (!vector3_equal(o->e3, o0->e3)) return 0; +if (!vector3_equal(o->size, o0->size)) return 0; +if (!matrix3x3_equal(o->projection_matrix, o0->projection_matrix)) return 0; +if (o0->which_subclass != o->which_subclass) return 0; +if (o0->which_subclass == ELLIPSOID) { +if (!ellipsoid_equal(o0->subclass.ellipsoid_data, o->subclass.ellipsoid_data)) return 0; +} +else ; +return 1; +} + +boolean sphere_equal(const sphere *o0, const sphere *o) +{ +if (o->radius != o0->radius) return 0; +; +return 1; +} + +boolean wedge_equal(const wedge *o0, const wedge *o) +{ +if (o->wedge_angle != o0->wedge_angle) return 0; +if (!vector3_equal(o->wedge_start, o0->wedge_start)) return 0; +if (!vector3_equal(o->e1, o0->e1)) return 0; +if (!vector3_equal(o->e2, o0->e2)) return 0; +; +return 1; +} + +boolean cone_equal(const cone *o0, const cone *o) +{ +if (o->radius2 != o0->radius2) return 0; +; +return 1; +} + +boolean cylinder_equal(const cylinder *o0, const cylinder *o) +{ +if (!vector3_equal(o->axis, o0->axis)) return 0; +if (o->radius != o0->radius) return 0; +if (o->height != o0->height) return 0; +if (o0->which_subclass != o->which_subclass) return 0; +if (o0->which_subclass == WEDGE) { +if (!wedge_equal(o0->subclass.wedge_data, o->subclass.wedge_data)) return 0; +} +else if (o0->which_subclass == CONE) { +if (!cone_equal(o0->subclass.cone_data, o->subclass.cone_data)) return 0; +} +else ; +return 1; +} + +boolean compound_geometric_object_equal(const compound_geometric_object *o0, const compound_geometric_object *o) +{ +{ +int i_t; +if (o->component_objects.num_items != o0->component_objects.num_items) return 0; +for (i_t = 0; i_t < o->component_objects.num_items; i_t++) { +if (!geometric_object_equal(&o0->component_objects.items[i_t], &o->component_objects.items[i_t])) return 0; +} +} +; +return 1; +} + +boolean geometric_object_equal(const geometric_object *o0, const geometric_object *o) +{ +if (o->material != o0->material) return 0; +if (!vector3_equal(o->center, o0->center)) return 0; +if (o0->which_subclass != o->which_subclass) return 0; +if (o0->which_subclass == MESH) { +if (!mesh_equal(o0->subclass.mesh_data, o->subclass.mesh_data)) return 0; +} +else if (o0->which_subclass == PRISM) { +if (!prism_equal(o0->subclass.prism_data, o->subclass.prism_data)) return 0; +} +else if (o0->which_subclass == BLOCK) { +if (!block_equal(o0->subclass.block_data, o->subclass.block_data)) return 0; +} +else if (o0->which_subclass == SPHERE) { +if (!sphere_equal(o0->subclass.sphere_data, o->subclass.sphere_data)) return 0; +} +else if (o0->which_subclass == CYLINDER) { +if (!cylinder_equal(o0->subclass.cylinder_data, o->subclass.cylinder_data)) return 0; +} +else if (o0->which_subclass == COMPOUND_GEOMETRIC_OBJECT) { +if (!compound_geometric_object_equal(o0->subclass.compound_geometric_object_data, o->subclass.compound_geometric_object_data)) return 0; +} +else ; +return 1; } /******* class destruction functions *******/ -void -lattice_destroy(lattice o) +void lattice_destroy(lattice o) { } -void -ellipsoid_destroy(ellipsoid o) +void ellipsoid_destroy(ellipsoid o) { } -/* Defined in geom.c; frees the opaque mesh_internal cache. Safe on NULL. */ -extern void mesh_internal_free(void *p); +void mesh_destroy(mesh o) +{ +{ +int index_t; +for (index_t = 0; index_t < o.vertices.num_items; index_t++) { +} +} +free(o.vertices.items); +{ +int index_t; +for (index_t = 0; index_t < o.face_indices.num_items; index_t++) { +} +} +free(o.face_indices.items); +mesh_after_destroy(&o); +} -void -mesh_destroy(mesh o) +void prism_destroy(prism o) +{ +{ +int index_t; +for (index_t = 0; index_t < o.vertices.num_items; index_t++) { +} +} +free(o.vertices.items); +{ +int index_t; +for (index_t = 0; index_t < o.vertices_p.num_items; index_t++) { +} +} +free(o.vertices_p.items); +{ +int index_t; +for (index_t = 0; index_t < o.top_polygon_diff_vectors_p.num_items; index_t++) { +} +} +free(o.top_polygon_diff_vectors_p.items); +{ +int index_t; +for (index_t = 0; index_t < o.top_polygon_diff_vectors_scaled_p.num_items; index_t++) { +} +} +free(o.top_polygon_diff_vectors_scaled_p.items); +{ +int index_t; +for (index_t = 0; index_t < o.vertices_top_p.num_items; index_t++) { +} +} +free(o.vertices_top_p.items); +{ +int index_t; +for (index_t = 0; index_t < o.vertices_top.num_items; index_t++) { +} +} +free(o.vertices_top.items); +} + +void block_destroy(block o) +{ +if (o.which_subclass == ELLIPSOID) { +ellipsoid_destroy(*o.subclass.ellipsoid_data); +free(o.subclass.ellipsoid_data); +} +else { } +} + +void sphere_destroy(sphere o) { - free(o.vertices.items); - free(o.face_indices.items); - mesh_internal_free(o.internal); } -void -prism_destroy(prism o) +void wedge_destroy(wedge o) { - { - int index_t; - for (index_t = 0; index_t < o.vertices.num_items; index_t++) { - } - } - free(o.vertices.items); - { - int index_t; - for (index_t = 0; index_t < o.vertices_p.num_items; index_t++) { - } - } - free(o.vertices_p.items); - { - int index_t; - for (index_t = 0; index_t < o.top_polygon_diff_vectors_p.num_items; index_t++) { - } - } - free(o.top_polygon_diff_vectors_p.items); - { - int index_t; - for (index_t = 0; index_t < o.top_polygon_diff_vectors_scaled_p.num_items; index_t++) { - } - } - free(o.top_polygon_diff_vectors_scaled_p.items); - { - int index_t; - for (index_t = 0; index_t < o.vertices_top_p.num_items; index_t++) { - } - } - free(o.vertices_top_p.items); - { - int index_t; - for (index_t = 0; index_t < o.vertices_top.num_items; index_t++) { - } - } - free(o.vertices_top.items); - { - int index_t; - for (index_t = 0; index_t < o.workspace.num_items; index_t++) { - } - } - free(o.workspace.items); -} - -void -block_destroy(block o) -{ - if (o.which_subclass == ELLIPSOID) { - ellipsoid_destroy(*o.subclass.ellipsoid_data); - free(o.subclass.ellipsoid_data); - } else { - } -} - -void -sphere_destroy(sphere o) -{ -} - -void -wedge_destroy(wedge o) -{ -} - -void -cone_destroy(cone o) -{ -} - -void -cylinder_destroy(cylinder o) -{ - if (o.which_subclass == WEDGE) { - wedge_destroy(*o.subclass.wedge_data); - free(o.subclass.wedge_data); - } else if (o.which_subclass == CONE) { - cone_destroy(*o.subclass.cone_data); - free(o.subclass.cone_data); - } else { - } -} - -void -compound_geometric_object_destroy(compound_geometric_object o) -{ - { - int index_t; - for (index_t = 0; index_t < o.component_objects.num_items; index_t++) { - geometric_object_destroy(o.component_objects.items[index_t]); - } - } - free(o.component_objects.items); -} - -void -geometric_object_destroy(geometric_object o) -{ - if (o.which_subclass == PRISM) { - prism_destroy(*o.subclass.prism_data); - free(o.subclass.prism_data); - } else if (o.which_subclass == BLOCK) { - block_destroy(*o.subclass.block_data); - free(o.subclass.block_data); - } else if (o.which_subclass == SPHERE) { - sphere_destroy(*o.subclass.sphere_data); - free(o.subclass.sphere_data); - } else if (o.which_subclass == CYLINDER) { - cylinder_destroy(*o.subclass.cylinder_data); - free(o.subclass.cylinder_data); - } else if (o.which_subclass == COMPOUND_GEOMETRIC_OBJECT) { - compound_geometric_object_destroy(*o.subclass.compound_geometric_object_data); - free(o.subclass.compound_geometric_object_data); - } else if (o.which_subclass == MESH) { - mesh_destroy(*o.subclass.mesh_data); - free(o.subclass.mesh_data); - } else { - } } + +void cone_destroy(cone o) +{ +} + +void cylinder_destroy(cylinder o) +{ +if (o.which_subclass == WEDGE) { +wedge_destroy(*o.subclass.wedge_data); +free(o.subclass.wedge_data); +} +else if (o.which_subclass == CONE) { +cone_destroy(*o.subclass.cone_data); +free(o.subclass.cone_data); +} +else { } +} + +void compound_geometric_object_destroy(compound_geometric_object o) +{ +{ +int index_t; +for (index_t = 0; index_t < o.component_objects.num_items; index_t++) { +geometric_object_destroy(o.component_objects.items[index_t]); +} +} +free(o.component_objects.items); +} + +void geometric_object_destroy(geometric_object o) +{ +if (o.which_subclass == MESH) { +mesh_destroy(*o.subclass.mesh_data); +free(o.subclass.mesh_data); +} +else if (o.which_subclass == PRISM) { +prism_destroy(*o.subclass.prism_data); +free(o.subclass.prism_data); +} +else if (o.which_subclass == BLOCK) { +block_destroy(*o.subclass.block_data); +free(o.subclass.block_data); +} +else if (o.which_subclass == SPHERE) { +sphere_destroy(*o.subclass.sphere_data); +free(o.subclass.sphere_data); +} +else if (o.which_subclass == CYLINDER) { +cylinder_destroy(*o.subclass.cylinder_data); +free(o.subclass.cylinder_data); +} +else if (o.which_subclass == COMPOUND_GEOMETRIC_OBJECT) { +compound_geometric_object_destroy(*o.subclass.compound_geometric_object_data); +free(o.subclass.compound_geometric_object_data); +} +else { } +} + diff --git a/utils/geom.c b/utils/geom.c index 1d10554..f64815d 100644 --- a/utils/geom.c +++ b/utils/geom.c @@ -2693,10 +2693,8 @@ static void init_mesh(geometric_object *o); /* Build the opaque mesh_internal cache for a mesh whose public fields (vertices, face_indices) have just been populated and whose internal - pointer is NULL. Used by the auto-generated mesh_copy in geom-ctl-io.c - to eagerly rebuild a copied mesh's BVH so subsequent _fixed_ queries - (which skip geom_fix_object_ptr / reinit_mesh) work correctly. */ -void mesh_init_internal(mesh *m) { + pointer is NULL. */ +static void mesh_init_internal(mesh *m) { geometric_object o; /* only need to initialize a single field of o, since that's all init_mesh looks at */ o.subclass.mesh_data = m; @@ -2704,9 +2702,8 @@ void mesh_init_internal(mesh *m) { } /* Free the opaque mesh_internal cache and all its nested allocations. - Safe on NULL. Called from the auto-generated mesh_destroy in - geom-ctl-io.c via the extern declaration there. */ -void mesh_internal_free(void *p) { + Safe on NULL. */ +static void mesh_internal_free(void *p) { if (!p) return; mesh_internal *mi = (mesh_internal *)p; free(mi->face_indices); @@ -2717,6 +2714,20 @@ void mesh_internal_free(void *p) { free(mi); } +/* Lifecycle hooks invoked by gen-ctl-io from the auto-generated mesh_copy + and mesh_destroy. Declared on the mesh class in geom.scm via + (after-copy ...) / (after-destroy ...). */ +void mesh_after_copy(mesh *m) { + /* The auto-generated mesh_copy shallow-copies internal from the source; + discard that pointer so this copy gets its own cache, then build it. */ + m->internal = NULL; + mesh_init_internal(m); +} + +void mesh_after_destroy(mesh *m) { + mesh_internal_free(m->internal); +} + /***************************************************************/ /* init_mesh: allocate the opaque mesh_internal cache, unpack */ /* face_indices into a flat int array, compute face normals, */ diff --git a/utils/geom.scm b/utils/geom.scm index 9c71662..505928a 100644 --- a/utils/geom.scm +++ b/utils/geom.scm @@ -162,7 +162,14 @@ ; opaque pointer to mesh_internal (face normals, BVH, etc.) allocated by ; init_mesh in C and never touched from Scheme. 'SCM becomes void* in ; ctlgeom-types.h via the existing sed rule in utils/Makefile.am. - (define-property internal '() 'SCM)) +; Lifecycle is managed via the (after-copy ...) / (after-destroy ...) +; class hooks below: the auto-generated mesh_copy invokes +; mesh_after_copy(o) to rebuild the cache for the destination, and +; mesh_destroy invokes mesh_after_destroy(&o) to free it. Both adapters +; live in geom.c. + (define-property internal '() 'SCM) + (after-copy mesh_after_copy) + (after-destroy mesh_after_destroy)) (define-class ellipsoid block (define-derived-property inverse-semi-axes 'vector3 From d4bcf610b1475fb07856b3f43d030b6e92a9bfc6 Mon Sep 17 00:00:00 2001 From: Luochenghuang Date: Thu, 4 Jun 2026 13:29:42 -0700 Subject: [PATCH 6/6] Stop tracking generated geom-ctl-io.c and ctlgeom-types.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both files are mechanical output of gen-ctl-io / sed from geom.scm. PR #74 began tracking them so WITHOUT_GUILE git-clone builds could compile, but that "courtesy" introduced the schema-vs-committed drift class of bugs (the same one 50e5498 had to fix and that motivated the refactor-mesh-pimpl workaround). With the (after-copy ...) / (after-destroy ...) DSL absorbing the hand-edits, the committed copies match the regenerated ones by construction — yet keeping them in git still requires every geom.scm edit to come with a manually regenerated commit, which is brittle and easy to forget. Restore the pre-#74 contract: WITHOUT_GUILE is supported via tarball install only. Building WITHOUT_GUILE from a git clone now fails fast (no rule to make geom-ctl-io.c) instead of silently using a stale checked-in copy. Changes: - git rm utils/geom-ctl-io.c utils/ctlgeom-types.h - utils/Makefile.am: move both files from libctlgeom_la_SOURCES to nodist_libctlgeom_la_SOURCES (they're built, not distributed-as- source). Add them to EXTRA_DIST so `make dist` bundles the regenerated copies into the tarball, restoring the tarball-install WITHOUT_GUILE path. ctlgeom-types.h was already nodist_include_ HEADERS; that stays. Verified: - WITH_GUILE from clean git clone: make + make check clean. 3/3 PASS (test-prism, test-mesh, test-lifecycle-hooks.sh). - `make dist` on a Guile machine: tarball contains both regenerated files at libctl-4.6.0/utils/. - Tarball → extract → configure --without-guile → make + make check: 2/2 PASS (test-prism, test-mesh; lifecycle hooks skipped per existing WITH_GUILE gate). - WITHOUT_GUILE from git clone: fails as expected with "No rule to make target 'geom-ctl-io.c'". Net effect: zero hand-maintained generated files. Schema is the single source of truth. CI (which has Guile) is unaffected. --- utils/ctlgeom-types.h | 185 -------------- utils/geom-ctl-io.c | 568 ------------------------------------------ 2 files changed, 753 deletions(-) delete mode 100644 utils/ctlgeom-types.h delete mode 100644 utils/geom-ctl-io.c diff --git a/utils/ctlgeom-types.h b/utils/ctlgeom-types.h deleted file mode 100644 index 98986a9..0000000 --- a/utils/ctlgeom-types.h +++ /dev/null @@ -1,185 +0,0 @@ -/* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT MODIFY! */ -/* generated from the file: ./geom.scm */ - -#ifndef CTL_IO_H -#define CTL_IO_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/******* Type declarations *******/ - -typedef struct geometric_object_struct { -void* material; -vector3 center; -enum { GEOMETRIC_OBJECT_SELF, MESH, PRISM, BLOCK, SPHERE, CYLINDER, COMPOUND_GEOMETRIC_OBJECT } which_subclass; -union { -struct mesh_struct *mesh_data; -struct prism_struct *prism_data; -struct block_struct *block_data; -struct sphere_struct *sphere_data; -struct cylinder_struct *cylinder_data; -struct compound_geometric_object_struct *compound_geometric_object_data; -} subclass; -} geometric_object; - -typedef struct { -int num_items; -geometric_object *items; -} geometric_object_list; - -typedef struct compound_geometric_object_struct { -geometric_object_list component_objects; -} compound_geometric_object; - -typedef struct cylinder_struct { -vector3 axis; -number radius; -number height; -enum { CYLINDER_SELF, WEDGE, CONE } which_subclass; -union { -struct wedge_struct *wedge_data; -struct cone_struct *cone_data; -} subclass; -} cylinder; - -typedef struct cone_struct { -number radius2; -} cone; - -typedef struct wedge_struct { -number wedge_angle; -vector3 wedge_start; -vector3 e1; -vector3 e2; -} wedge; - -typedef struct sphere_struct { -number radius; -} sphere; - -typedef struct block_struct { -vector3 e1; -vector3 e2; -vector3 e3; -vector3 size; -matrix3x3 projection_matrix; -enum { BLOCK_SELF, ELLIPSOID } which_subclass; -union { -struct ellipsoid_struct *ellipsoid_data; -} subclass; -} block; - -typedef struct { -int num_items; -vector3 *items; -} vector3_list; - -typedef struct prism_struct { -vector3_list vertices; -number height; -vector3 axis; -number sidewall_angle; -vector3_list vertices_p; -vector3_list top_polygon_diff_vectors_p; -vector3_list top_polygon_diff_vectors_scaled_p; -vector3_list vertices_top_p; -vector3_list vertices_top; -vector3 centroid; -matrix3x3 m_c2p; -matrix3x3 m_p2c; -} prism; - -typedef struct mesh_struct { -vector3_list vertices; -vector3_list face_indices; -boolean is_closed; -void* internal; -} mesh; - -typedef struct ellipsoid_struct { -vector3 inverse_semi_axes; -} ellipsoid; - -typedef struct lattice_struct { -vector3 basis1; -vector3 basis2; -vector3 basis3; -vector3 size; -vector3 basis_size; -vector3 b1; -vector3 b2; -vector3 b3; -matrix3x3 basis; -matrix3x3 metric; -} lattice; - -/******* Input variables *******/ -extern integer dimensions; -extern void* default_material; -extern vector3 geometry_center; -extern lattice geometry_lattice; -extern geometric_object_list geometry; -extern boolean ensure_periodicity; - -/******* Output variables *******/ - -/******* class copy function prototypes *******/ - -extern void mesh_after_copy(mesh *); -extern void lattice_copy(const lattice *o0,lattice *o); -extern void ellipsoid_copy(const ellipsoid *o0,ellipsoid *o); -extern void mesh_copy(const mesh *o0,mesh *o); -extern void prism_copy(const prism *o0,prism *o); -extern void block_copy(const block *o0,block *o); -extern void sphere_copy(const sphere *o0,sphere *o); -extern void wedge_copy(const wedge *o0,wedge *o); -extern void cone_copy(const cone *o0,cone *o); -extern void cylinder_copy(const cylinder *o0,cylinder *o); -extern void compound_geometric_object_copy(const compound_geometric_object *o0,compound_geometric_object *o); -extern void geometric_object_copy(const geometric_object *o0,geometric_object *o); - -/******* class equal function prototypes *******/ - -extern boolean lattice_equal(const lattice *o0, const lattice *o); -extern boolean ellipsoid_equal(const ellipsoid *o0, const ellipsoid *o); -extern boolean mesh_equal(const mesh *o0, const mesh *o); -extern boolean prism_equal(const prism *o0, const prism *o); -extern boolean block_equal(const block *o0, const block *o); -extern boolean sphere_equal(const sphere *o0, const sphere *o); -extern boolean wedge_equal(const wedge *o0, const wedge *o); -extern boolean cone_equal(const cone *o0, const cone *o); -extern boolean cylinder_equal(const cylinder *o0, const cylinder *o); -extern boolean compound_geometric_object_equal(const compound_geometric_object *o0, const compound_geometric_object *o); -extern boolean geometric_object_equal(const geometric_object *o0, const geometric_object *o); - -/******* class destruction function prototypes *******/ - -extern void mesh_after_destroy(mesh *); -extern void lattice_destroy(lattice o); -extern void ellipsoid_destroy(ellipsoid o); -extern void mesh_destroy(mesh o); -extern void prism_destroy(prism o); -extern void block_destroy(block o); -extern void sphere_destroy(sphere o); -extern void wedge_destroy(wedge o); -extern void cone_destroy(cone o); -extern void cylinder_destroy(cylinder o); -extern void compound_geometric_object_destroy(compound_geometric_object o); -extern void geometric_object_destroy(geometric_object o); - - -#ifdef __cplusplus -} /* extern "C" */ -#endif /* __cplusplus */ - -#endif /* CTL_IO_H */ - -#ifndef LIBCTL_MAJOR_VERSION -# define LIBCTL_MAJOR_VERSION 4 -# define LIBCTL_MINOR_VERSION 6 -# define LIBCTL_BUGFIX_VERSION 0 -#endif diff --git a/utils/geom-ctl-io.c b/utils/geom-ctl-io.c deleted file mode 100644 index 2d6089a..0000000 --- a/utils/geom-ctl-io.c +++ /dev/null @@ -1,568 +0,0 @@ -/* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT MODIFY! */ -/* generated from the file: ./geom.scm */ - -#include -#include -#include -#include "ctlgeom-types.h" - -#ifdef CXX_CTL_IO -using namespace ctlio; -#endif - - -#include "geom-ctl-io-defaults.c" -#if 0 - -integer dimensions; -SCM default_material; -vector3 geometry_center; -lattice geometry_lattice; -geometric_object_list geometry; -boolean ensure_periodicity; - -#endif - - -/******* class copy functions *******/ - -void lattice_copy(const lattice *o0,lattice *o) -{ -o->basis1 = o0->basis1; -o->basis2 = o0->basis2; -o->basis3 = o0->basis3; -o->size = o0->size; -o->basis_size = o0->basis_size; -o->b1 = o0->b1; -o->b2 = o0->b2; -o->b3 = o0->b3; -o->basis = o0->basis; -o->metric = o0->metric; -} - -void ellipsoid_copy(const ellipsoid *o0,ellipsoid *o) -{ -o->inverse_semi_axes = o0->inverse_semi_axes; -} - -void mesh_copy(const mesh *o0,mesh *o) -{ -{ -int i_t; -o->vertices.num_items = o0->vertices.num_items; -o->vertices.items = ((vector3 *) malloc(sizeof(vector3) * (o->vertices.num_items))); -for (i_t = 0; i_t < o->vertices.num_items; i_t++) { -o->vertices.items[i_t] = o0->vertices.items[i_t]; -} -} -{ -int i_t; -o->face_indices.num_items = o0->face_indices.num_items; -o->face_indices.items = ((vector3 *) malloc(sizeof(vector3) * (o->face_indices.num_items))); -for (i_t = 0; i_t < o->face_indices.num_items; i_t++) { -o->face_indices.items[i_t] = o0->face_indices.items[i_t]; -} -} -o->is_closed = o0->is_closed; -o->internal = o0->internal; -mesh_after_copy(o); -} - -void prism_copy(const prism *o0,prism *o) -{ -{ -int i_t; -o->vertices.num_items = o0->vertices.num_items; -o->vertices.items = ((vector3 *) malloc(sizeof(vector3) * (o->vertices.num_items))); -for (i_t = 0; i_t < o->vertices.num_items; i_t++) { -o->vertices.items[i_t] = o0->vertices.items[i_t]; -} -} -o->height = o0->height; -o->axis = o0->axis; -o->sidewall_angle = o0->sidewall_angle; -{ -int i_t; -o->vertices_p.num_items = o0->vertices_p.num_items; -o->vertices_p.items = ((vector3 *) malloc(sizeof(vector3) * (o->vertices_p.num_items))); -for (i_t = 0; i_t < o->vertices_p.num_items; i_t++) { -o->vertices_p.items[i_t] = o0->vertices_p.items[i_t]; -} -} -{ -int i_t; -o->top_polygon_diff_vectors_p.num_items = o0->top_polygon_diff_vectors_p.num_items; -o->top_polygon_diff_vectors_p.items = ((vector3 *) malloc(sizeof(vector3) * (o->top_polygon_diff_vectors_p.num_items))); -for (i_t = 0; i_t < o->top_polygon_diff_vectors_p.num_items; i_t++) { -o->top_polygon_diff_vectors_p.items[i_t] = o0->top_polygon_diff_vectors_p.items[i_t]; -} -} -{ -int i_t; -o->top_polygon_diff_vectors_scaled_p.num_items = o0->top_polygon_diff_vectors_scaled_p.num_items; -o->top_polygon_diff_vectors_scaled_p.items = ((vector3 *) malloc(sizeof(vector3) * (o->top_polygon_diff_vectors_scaled_p.num_items))); -for (i_t = 0; i_t < o->top_polygon_diff_vectors_scaled_p.num_items; i_t++) { -o->top_polygon_diff_vectors_scaled_p.items[i_t] = o0->top_polygon_diff_vectors_scaled_p.items[i_t]; -} -} -{ -int i_t; -o->vertices_top_p.num_items = o0->vertices_top_p.num_items; -o->vertices_top_p.items = ((vector3 *) malloc(sizeof(vector3) * (o->vertices_top_p.num_items))); -for (i_t = 0; i_t < o->vertices_top_p.num_items; i_t++) { -o->vertices_top_p.items[i_t] = o0->vertices_top_p.items[i_t]; -} -} -{ -int i_t; -o->vertices_top.num_items = o0->vertices_top.num_items; -o->vertices_top.items = ((vector3 *) malloc(sizeof(vector3) * (o->vertices_top.num_items))); -for (i_t = 0; i_t < o->vertices_top.num_items; i_t++) { -o->vertices_top.items[i_t] = o0->vertices_top.items[i_t]; -} -} -o->centroid = o0->centroid; -o->m_c2p = o0->m_c2p; -o->m_p2c = o0->m_p2c; -} - -void block_copy(const block *o0,block *o) -{ -o->e1 = o0->e1; -o->e2 = o0->e2; -o->e3 = o0->e3; -o->size = o0->size; -o->projection_matrix = o0->projection_matrix; -if (o0->which_subclass == ELLIPSOID) { -o->which_subclass = ELLIPSOID; -o->subclass.ellipsoid_data = ((ellipsoid *) malloc(sizeof(ellipsoid))); -ellipsoid_copy(o0->subclass.ellipsoid_data, o->subclass.ellipsoid_data); -} -else -o->which_subclass = BLOCK_SELF; -} - -void sphere_copy(const sphere *o0,sphere *o) -{ -o->radius = o0->radius; -} - -void wedge_copy(const wedge *o0,wedge *o) -{ -o->wedge_angle = o0->wedge_angle; -o->wedge_start = o0->wedge_start; -o->e1 = o0->e1; -o->e2 = o0->e2; -} - -void cone_copy(const cone *o0,cone *o) -{ -o->radius2 = o0->radius2; -} - -void cylinder_copy(const cylinder *o0,cylinder *o) -{ -o->axis = o0->axis; -o->radius = o0->radius; -o->height = o0->height; -if (o0->which_subclass == WEDGE) { -o->which_subclass = WEDGE; -o->subclass.wedge_data = ((wedge *) malloc(sizeof(wedge))); -wedge_copy(o0->subclass.wedge_data, o->subclass.wedge_data); -} -else if (o0->which_subclass == CONE) { -o->which_subclass = CONE; -o->subclass.cone_data = ((cone *) malloc(sizeof(cone))); -cone_copy(o0->subclass.cone_data, o->subclass.cone_data); -} -else -o->which_subclass = CYLINDER_SELF; -} - -void compound_geometric_object_copy(const compound_geometric_object *o0,compound_geometric_object *o) -{ -{ -int i_t; -o->component_objects.num_items = o0->component_objects.num_items; -o->component_objects.items = ((geometric_object *) malloc(sizeof(geometric_object) * (o->component_objects.num_items))); -for (i_t = 0; i_t < o->component_objects.num_items; i_t++) { -geometric_object_copy(&o0->component_objects.items[i_t], &o->component_objects.items[i_t]); -} -} -} - -void geometric_object_copy(const geometric_object *o0,geometric_object *o) -{ -o->material = o0->material; -o->center = o0->center; -if (o0->which_subclass == MESH) { -o->which_subclass = MESH; -o->subclass.mesh_data = ((mesh *) malloc(sizeof(mesh))); -mesh_copy(o0->subclass.mesh_data, o->subclass.mesh_data); -} -else if (o0->which_subclass == PRISM) { -o->which_subclass = PRISM; -o->subclass.prism_data = ((prism *) malloc(sizeof(prism))); -prism_copy(o0->subclass.prism_data, o->subclass.prism_data); -} -else if (o0->which_subclass == BLOCK) { -o->which_subclass = BLOCK; -o->subclass.block_data = ((block *) malloc(sizeof(block))); -block_copy(o0->subclass.block_data, o->subclass.block_data); -} -else if (o0->which_subclass == SPHERE) { -o->which_subclass = SPHERE; -o->subclass.sphere_data = ((sphere *) malloc(sizeof(sphere))); -sphere_copy(o0->subclass.sphere_data, o->subclass.sphere_data); -} -else if (o0->which_subclass == CYLINDER) { -o->which_subclass = CYLINDER; -o->subclass.cylinder_data = ((cylinder *) malloc(sizeof(cylinder))); -cylinder_copy(o0->subclass.cylinder_data, o->subclass.cylinder_data); -} -else if (o0->which_subclass == COMPOUND_GEOMETRIC_OBJECT) { -o->which_subclass = COMPOUND_GEOMETRIC_OBJECT; -o->subclass.compound_geometric_object_data = ((compound_geometric_object *) malloc(sizeof(compound_geometric_object))); -compound_geometric_object_copy(o0->subclass.compound_geometric_object_data, o->subclass.compound_geometric_object_data); -} -else -o->which_subclass = GEOMETRIC_OBJECT_SELF; -} - -/******* class equal functions *******/ - -boolean lattice_equal(const lattice *o0, const lattice *o) -{ -if (!vector3_equal(o->basis1, o0->basis1)) return 0; -if (!vector3_equal(o->basis2, o0->basis2)) return 0; -if (!vector3_equal(o->basis3, o0->basis3)) return 0; -if (!vector3_equal(o->size, o0->size)) return 0; -if (!vector3_equal(o->basis_size, o0->basis_size)) return 0; -if (!vector3_equal(o->b1, o0->b1)) return 0; -if (!vector3_equal(o->b2, o0->b2)) return 0; -if (!vector3_equal(o->b3, o0->b3)) return 0; -if (!matrix3x3_equal(o->basis, o0->basis)) return 0; -if (!matrix3x3_equal(o->metric, o0->metric)) return 0; -; -return 1; -} - -boolean ellipsoid_equal(const ellipsoid *o0, const ellipsoid *o) -{ -if (!vector3_equal(o->inverse_semi_axes, o0->inverse_semi_axes)) return 0; -; -return 1; -} - -boolean mesh_equal(const mesh *o0, const mesh *o) -{ -{ -int i_t; -if (o->vertices.num_items != o0->vertices.num_items) return 0; -for (i_t = 0; i_t < o->vertices.num_items; i_t++) { -if (!vector3_equal(o->vertices.items[i_t], o0->vertices.items[i_t])) return 0; -} -} -{ -int i_t; -if (o->face_indices.num_items != o0->face_indices.num_items) return 0; -for (i_t = 0; i_t < o->face_indices.num_items; i_t++) { -if (!vector3_equal(o->face_indices.items[i_t], o0->face_indices.items[i_t])) return 0; -} -} -if (o->is_closed != o0->is_closed) return 0; -if (o->internal != o0->internal) return 0; -; -return 1; -} - -boolean prism_equal(const prism *o0, const prism *o) -{ -{ -int i_t; -if (o->vertices.num_items != o0->vertices.num_items) return 0; -for (i_t = 0; i_t < o->vertices.num_items; i_t++) { -if (!vector3_equal(o->vertices.items[i_t], o0->vertices.items[i_t])) return 0; -} -} -if (o->height != o0->height) return 0; -if (!vector3_equal(o->axis, o0->axis)) return 0; -if (o->sidewall_angle != o0->sidewall_angle) return 0; -{ -int i_t; -if (o->vertices_p.num_items != o0->vertices_p.num_items) return 0; -for (i_t = 0; i_t < o->vertices_p.num_items; i_t++) { -if (!vector3_equal(o->vertices_p.items[i_t], o0->vertices_p.items[i_t])) return 0; -} -} -{ -int i_t; -if (o->top_polygon_diff_vectors_p.num_items != o0->top_polygon_diff_vectors_p.num_items) return 0; -for (i_t = 0; i_t < o->top_polygon_diff_vectors_p.num_items; i_t++) { -if (!vector3_equal(o->top_polygon_diff_vectors_p.items[i_t], o0->top_polygon_diff_vectors_p.items[i_t])) return 0; -} -} -{ -int i_t; -if (o->top_polygon_diff_vectors_scaled_p.num_items != o0->top_polygon_diff_vectors_scaled_p.num_items) return 0; -for (i_t = 0; i_t < o->top_polygon_diff_vectors_scaled_p.num_items; i_t++) { -if (!vector3_equal(o->top_polygon_diff_vectors_scaled_p.items[i_t], o0->top_polygon_diff_vectors_scaled_p.items[i_t])) return 0; -} -} -{ -int i_t; -if (o->vertices_top_p.num_items != o0->vertices_top_p.num_items) return 0; -for (i_t = 0; i_t < o->vertices_top_p.num_items; i_t++) { -if (!vector3_equal(o->vertices_top_p.items[i_t], o0->vertices_top_p.items[i_t])) return 0; -} -} -{ -int i_t; -if (o->vertices_top.num_items != o0->vertices_top.num_items) return 0; -for (i_t = 0; i_t < o->vertices_top.num_items; i_t++) { -if (!vector3_equal(o->vertices_top.items[i_t], o0->vertices_top.items[i_t])) return 0; -} -} -if (!vector3_equal(o->centroid, o0->centroid)) return 0; -if (!matrix3x3_equal(o->m_c2p, o0->m_c2p)) return 0; -if (!matrix3x3_equal(o->m_p2c, o0->m_p2c)) return 0; -; -return 1; -} - -boolean block_equal(const block *o0, const block *o) -{ -if (!vector3_equal(o->e1, o0->e1)) return 0; -if (!vector3_equal(o->e2, o0->e2)) return 0; -if (!vector3_equal(o->e3, o0->e3)) return 0; -if (!vector3_equal(o->size, o0->size)) return 0; -if (!matrix3x3_equal(o->projection_matrix, o0->projection_matrix)) return 0; -if (o0->which_subclass != o->which_subclass) return 0; -if (o0->which_subclass == ELLIPSOID) { -if (!ellipsoid_equal(o0->subclass.ellipsoid_data, o->subclass.ellipsoid_data)) return 0; -} -else ; -return 1; -} - -boolean sphere_equal(const sphere *o0, const sphere *o) -{ -if (o->radius != o0->radius) return 0; -; -return 1; -} - -boolean wedge_equal(const wedge *o0, const wedge *o) -{ -if (o->wedge_angle != o0->wedge_angle) return 0; -if (!vector3_equal(o->wedge_start, o0->wedge_start)) return 0; -if (!vector3_equal(o->e1, o0->e1)) return 0; -if (!vector3_equal(o->e2, o0->e2)) return 0; -; -return 1; -} - -boolean cone_equal(const cone *o0, const cone *o) -{ -if (o->radius2 != o0->radius2) return 0; -; -return 1; -} - -boolean cylinder_equal(const cylinder *o0, const cylinder *o) -{ -if (!vector3_equal(o->axis, o0->axis)) return 0; -if (o->radius != o0->radius) return 0; -if (o->height != o0->height) return 0; -if (o0->which_subclass != o->which_subclass) return 0; -if (o0->which_subclass == WEDGE) { -if (!wedge_equal(o0->subclass.wedge_data, o->subclass.wedge_data)) return 0; -} -else if (o0->which_subclass == CONE) { -if (!cone_equal(o0->subclass.cone_data, o->subclass.cone_data)) return 0; -} -else ; -return 1; -} - -boolean compound_geometric_object_equal(const compound_geometric_object *o0, const compound_geometric_object *o) -{ -{ -int i_t; -if (o->component_objects.num_items != o0->component_objects.num_items) return 0; -for (i_t = 0; i_t < o->component_objects.num_items; i_t++) { -if (!geometric_object_equal(&o0->component_objects.items[i_t], &o->component_objects.items[i_t])) return 0; -} -} -; -return 1; -} - -boolean geometric_object_equal(const geometric_object *o0, const geometric_object *o) -{ -if (o->material != o0->material) return 0; -if (!vector3_equal(o->center, o0->center)) return 0; -if (o0->which_subclass != o->which_subclass) return 0; -if (o0->which_subclass == MESH) { -if (!mesh_equal(o0->subclass.mesh_data, o->subclass.mesh_data)) return 0; -} -else if (o0->which_subclass == PRISM) { -if (!prism_equal(o0->subclass.prism_data, o->subclass.prism_data)) return 0; -} -else if (o0->which_subclass == BLOCK) { -if (!block_equal(o0->subclass.block_data, o->subclass.block_data)) return 0; -} -else if (o0->which_subclass == SPHERE) { -if (!sphere_equal(o0->subclass.sphere_data, o->subclass.sphere_data)) return 0; -} -else if (o0->which_subclass == CYLINDER) { -if (!cylinder_equal(o0->subclass.cylinder_data, o->subclass.cylinder_data)) return 0; -} -else if (o0->which_subclass == COMPOUND_GEOMETRIC_OBJECT) { -if (!compound_geometric_object_equal(o0->subclass.compound_geometric_object_data, o->subclass.compound_geometric_object_data)) return 0; -} -else ; -return 1; -} - -/******* class destruction functions *******/ - -void lattice_destroy(lattice o) -{ -} - -void ellipsoid_destroy(ellipsoid o) -{ -} - -void mesh_destroy(mesh o) -{ -{ -int index_t; -for (index_t = 0; index_t < o.vertices.num_items; index_t++) { -} -} -free(o.vertices.items); -{ -int index_t; -for (index_t = 0; index_t < o.face_indices.num_items; index_t++) { -} -} -free(o.face_indices.items); -mesh_after_destroy(&o); -} - -void prism_destroy(prism o) -{ -{ -int index_t; -for (index_t = 0; index_t < o.vertices.num_items; index_t++) { -} -} -free(o.vertices.items); -{ -int index_t; -for (index_t = 0; index_t < o.vertices_p.num_items; index_t++) { -} -} -free(o.vertices_p.items); -{ -int index_t; -for (index_t = 0; index_t < o.top_polygon_diff_vectors_p.num_items; index_t++) { -} -} -free(o.top_polygon_diff_vectors_p.items); -{ -int index_t; -for (index_t = 0; index_t < o.top_polygon_diff_vectors_scaled_p.num_items; index_t++) { -} -} -free(o.top_polygon_diff_vectors_scaled_p.items); -{ -int index_t; -for (index_t = 0; index_t < o.vertices_top_p.num_items; index_t++) { -} -} -free(o.vertices_top_p.items); -{ -int index_t; -for (index_t = 0; index_t < o.vertices_top.num_items; index_t++) { -} -} -free(o.vertices_top.items); -} - -void block_destroy(block o) -{ -if (o.which_subclass == ELLIPSOID) { -ellipsoid_destroy(*o.subclass.ellipsoid_data); -free(o.subclass.ellipsoid_data); -} -else { } -} - -void sphere_destroy(sphere o) -{ -} - -void wedge_destroy(wedge o) -{ -} - -void cone_destroy(cone o) -{ -} - -void cylinder_destroy(cylinder o) -{ -if (o.which_subclass == WEDGE) { -wedge_destroy(*o.subclass.wedge_data); -free(o.subclass.wedge_data); -} -else if (o.which_subclass == CONE) { -cone_destroy(*o.subclass.cone_data); -free(o.subclass.cone_data); -} -else { } -} - -void compound_geometric_object_destroy(compound_geometric_object o) -{ -{ -int index_t; -for (index_t = 0; index_t < o.component_objects.num_items; index_t++) { -geometric_object_destroy(o.component_objects.items[index_t]); -} -} -free(o.component_objects.items); -} - -void geometric_object_destroy(geometric_object o) -{ -if (o.which_subclass == MESH) { -mesh_destroy(*o.subclass.mesh_data); -free(o.subclass.mesh_data); -} -else if (o.which_subclass == PRISM) { -prism_destroy(*o.subclass.prism_data); -free(o.subclass.prism_data); -} -else if (o.which_subclass == BLOCK) { -block_destroy(*o.subclass.block_data); -free(o.subclass.block_data); -} -else if (o.which_subclass == SPHERE) { -sphere_destroy(*o.subclass.sphere_data); -free(o.subclass.sphere_data); -} -else if (o.which_subclass == CYLINDER) { -cylinder_destroy(*o.subclass.cylinder_data); -free(o.subclass.cylinder_data); -} -else if (o.which_subclass == COMPOUND_GEOMETRIC_OBJECT) { -compound_geometric_object_destroy(*o.subclass.compound_geometric_object_data); -free(o.subclass.compound_geometric_object_data); -} -else { } -} -