Skip to content

Commit eb8dc72

Browse files
committed
improve metadata handling
1 parent 7fc0048 commit eb8dc72

70 files changed

Lines changed: 1093 additions & 1093 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

PORTING_PLAN.md

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ It should not know about:
7070

7171
`hyperlimit` should own robust predicate-facing geometry:
7272

73-
- minimal `Point2<S>` and `Point3<S>` carrier types
74-
- `Plane3<S>` in implicit form
73+
- minimal `Point2<R>` and `Point3<R>` carrier types
74+
- `Plane3<R>` in implicit form
7575
- predicate case structs for batching, where useful
7676
- orientation and classification results
7777

@@ -92,9 +92,9 @@ rich polygon, mesh, sketch, vertex, toolpath, or CSG types.
9292

9393
`csgrs` should continue to own product-level geometry:
9494

95-
- `Sketch<S>`
96-
- `Mesh<S>`
97-
- `Polygon<S>`
95+
- `Sketch<M>`
96+
- `Mesh<M>`
97+
- `Polygon<M>`
9898
- `Vertex`
9999
- BSP nodes and splitting logic
100100
- optional `BMesh` / manifold-backed representations
@@ -108,6 +108,28 @@ rich polygon, mesh, sketch, vertex, toolpath, or CSG types.
108108
or output file format
109109
- metadata propagation rules
110110

111+
Metadata is moving immediately to a hard-break model: `Mesh<M>`, `Sketch<M>`,
112+
`Polygon<M>`, `BMesh<M>`, and `Nurbs<M>` store `metadata: M`, not
113+
`metadata: Option<M>`. Absence is represented by `M = ()`; optional user
114+
metadata is represented by `M = Option<T>`; required user metadata is represented
115+
by `M = T`. This avoids hidden double optionality, gives required metadata a
116+
compile-time guarantee, and makes boolean provenance rules explicit.
117+
118+
Implementation decision:
119+
120+
- primitive constructors for unannotated geometry should produce or accept
121+
`M = ()` metadata, with `with_metadata` / `map_metadata` helpers used to move
122+
into richer metadata types
123+
- APIs that import dynamic or optional metadata should choose
124+
`M = Option<T>` explicitly
125+
- boolean operations preserve source-face metadata on polygons and use a
126+
documented whole-object metadata policy for the returned shape
127+
- WASM bindings should not expose a generic metadata type; they should use
128+
`Mesh<Option<String>>` / `Sketch<Option<String>>` or
129+
`Mesh<serde_json::Value>` / `Sketch<serde_json::Value>` internally, with
130+
null/undefined represented by `None` when using the `Option<String>` path
131+
- this is an immediate major-version break, not a compatibility shim target
132+
111133
`csgrs` may re-export or alias lower-level point/vector/matrix types for user
112134
convenience, but it should avoid owning a new fundamental numeric geometry
113135
kernel once `hyperlattice` is available.
@@ -118,11 +140,11 @@ kernel once `hyperlattice` is available.
118140

119141
Keep these as `csgrs` types because they encode CSG or product semantics:
120142

121-
- `mesh::Mesh<S>`
122-
- `sketch::Sketch<S>`
123-
- `polygon::Polygon<S>`
143+
- `mesh::Mesh<M>`
144+
- `sketch::Sketch<M>`
145+
- `polygon::Polygon<M>`
124146
- `vertex::Vertex`
125-
- `bmesh::BMesh<S>`
147+
- `bmesh::BMesh<M>`
126148
- `voxels` module types
127149
- `toolpath` module types
128150
- IO module types
@@ -140,7 +162,7 @@ classification and splitting should delegate to `hyperlimit`.
140162

141163
Recommended direction:
142164

143-
- store face-plane data in whichever form is most useful for `Polygon<S>`
165+
- store face-plane data in whichever form is most useful for `Polygon<M>`
144166
- convert to `hyperlimit::Plane3` or oriented `hyperlimit::Point3` triples at
145167
predicate boundaries
146168
- use `hyperlimit` outcomes to classify vertices and polygons
@@ -1837,7 +1859,7 @@ This gives an early robustness win without forcing a broad public API rewrite.
18371859

18381860
## Non-goals
18391861

1840-
- Do not move `Mesh<S>` or `Sketch<S>` into the lower stack.
1862+
- Do not move `Mesh<M>` or `Sketch<M>` into the lower stack.
18411863
- Do not make `hyperlimit` a mesh-processing crate.
18421864
- Do not make `hyperlattice` aware of file formats or CSG operations.
18431865
- Do not list WASM as a geometry file format; it is only a compilation target

examples/adjacency_demo.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ fn main() {
1919

2020
// Create a test mesh - sphere for interesting connectivity
2121
println!("1. Creating test mesh (sphere with 16 segments, 8 rings)...");
22-
let sphere: Mesh<()> = Mesh::sphere(1.0, 16, 8, None);
22+
let sphere: Mesh<()> = Mesh::sphere(1.0, 16, 8, ());
2323
println!(" Original polygons: {}", sphere.polygons.len());
2424

2525
// Build mesh connectivity - this is where adjacency map is created and used

examples/basic2d_shapes_and_offsetting.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ const PATH: &str = "stl/examples/basic2d_shapes";
88
fn main() {
99
fs::create_dir_all(PATH).unwrap();
1010

11-
let square = Sketch::<()>::square(2.0, None);
12-
let circle = Sketch::<()>::circle(1.0, 64, None);
13-
let ring = Sketch::<()>::ring(2.0, 0.25, 64, None);
14-
let keyhole = Sketch::<()>::keyhole(1.0, 0.4, 1.5, 32, None);
11+
let square = Sketch::<()>::square(2.0, ());
12+
let circle = Sketch::<()>::circle(1.0, 64, ());
13+
let ring = Sketch::<()>::ring(2.0, 0.25, 64, ());
14+
let keyhole = Sketch::<()>::keyhole(1.0, 0.4, 1.5, 32, ());
1515

1616
write_sketch(&square, "square");
1717
write_sketch(&circle, "circle");

examples/basic_shapes.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,11 @@ const PATH: &str = "stl/examples/basic_shapes";
88
fn main() {
99
fs::create_dir_all(PATH).unwrap();
1010

11-
write_mesh(&Mesh::<()>::cube(2.0, None), "cube");
12-
write_mesh(&Mesh::<()>::sphere(1.0, 32, 16, None), "sphere");
13-
write_mesh(&Mesh::<()>::cylinder(1.0, 2.0, 32, None), "cylinder");
14-
write_mesh(
15-
&Mesh::<()>::ellipsoid(2.0, 1.0, 3.0, 32, 16, None),
16-
"ellipsoid",
17-
);
18-
write_mesh(&Mesh::<()>::torus(2.0, 0.35, 48, 16, None), "torus");
11+
write_mesh(&Mesh::<()>::cube(2.0, ()), "cube");
12+
write_mesh(&Mesh::<()>::sphere(1.0, 32, 16, ()), "sphere");
13+
write_mesh(&Mesh::<()>::cylinder(1.0, 2.0, 32, ()), "cylinder");
14+
write_mesh(&Mesh::<()>::ellipsoid(2.0, 1.0, 3.0, 32, 16, ()), "ellipsoid");
15+
write_mesh(&Mesh::<()>::torus(2.0, 0.35, 48, 16, ()), "torus");
1916
}
2017

2118
fn write_mesh(mesh: &Mesh<()>, name: &str) {

examples/boolean_operations.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ const PATH: &str = "stl/examples/boolean_operations";
88
fn main() {
99
fs::create_dir_all(PATH).unwrap();
1010

11-
let cube = Mesh::<()>::cube(2.0, None);
12-
let sphere = Mesh::<()>::sphere(1.35, 32, 16, None).translate(0.55, 0.25, 0.2);
11+
let cube = Mesh::<()>::cube(2.0, ());
12+
let sphere = Mesh::<()>::sphere(1.35, 32, 16, ()).translate(0.55, 0.25, 0.2);
1313

1414
write_mesh(&cube.union(&sphere), "union");
1515
write_mesh(&cube.difference(&sphere), "difference");

examples/convex_hull.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ const PATH: &str = "stl/examples/convex_hull";
88
fn main() {
99
fs::create_dir_all(PATH).unwrap();
1010

11-
let cube = Mesh::<()>::cube(2.0, None)
11+
let cube = Mesh::<()>::cube(2.0, ())
1212
.translate(0.8, 0.0, 0.0)
1313
.rotate(0.0, 35.0, 0.0);
14-
let sphere = Mesh::<()>::sphere(1.1, 32, 16, None).translate(-0.5, 0.0, 0.0);
14+
let sphere = Mesh::<()>::sphere(1.1, 32, 16, ()).translate(-0.5, 0.0, 0.0);
1515
let union = cube.union(&sphere);
1616

1717
write_mesh(&union, "union");

examples/extrude.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ const PATH: &str = "stl/examples/extrude";
99
fn main() {
1010
fs::create_dir_all(PATH).unwrap();
1111

12-
let square = Sketch::<()>::square(1.5, None);
13-
let circle = Sketch::<()>::circle(0.5, 32, None).translate(1.5, 0.0, 0.0);
14-
let star = Sketch::<()>::star(5, 1.2, 0.45, None);
12+
let square = Sketch::<()>::square(1.5, ());
13+
let circle = Sketch::<()>::circle(0.5, 32, ()).translate(1.5, 0.0, 0.0);
14+
let star = Sketch::<()>::star(5, 1.2, 0.45, ());
1515

1616
write_mesh(&square.extrude(1.0), "square_extrude");
1717
write_mesh(
@@ -26,7 +26,7 @@ fn main() {
2626
Point3::new(t.cos() * 0.6, t.sin() * 0.6, i as f64 * 0.04)
2727
})
2828
.collect::<Vec<_>>();
29-
write_mesh(&Sketch::<()>::circle(0.08, 12, None).sweep(&path), "sweep");
29+
write_mesh(&Sketch::<()>::circle(0.08, 12, ()).sweep(&path), "sweep");
3030

3131
let bottom = Polygon::new(
3232
vec![
@@ -35,7 +35,7 @@ fn main() {
3535
Vertex::new(Point3::new(0.5, 0.5, 0.0), Vector3::z()),
3636
Vertex::new(Point3::new(-0.5, 0.5, 0.0), Vector3::z()),
3737
],
38-
None,
38+
(),
3939
);
4040
let top = Polygon::new(
4141
vec![
@@ -44,7 +44,7 @@ fn main() {
4444
Vertex::new(Point3::new(0.25, 0.25, 1.0), Vector3::z()),
4545
Vertex::new(Point3::new(-0.25, 0.25, 1.0), Vector3::z()),
4646
],
47-
None,
47+
(),
4848
);
4949
write_mesh(&Sketch::<()>::loft(&bottom, &top, true).unwrap(), "loft");
5050
}

examples/minkowski_sum.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ const PATH: &str = "stl/examples/minkowski_sum";
88
fn main() {
99
fs::create_dir_all(PATH).unwrap();
1010

11-
let cube = Mesh::<()>::cube(1.5, None);
12-
let sphere = Mesh::<()>::sphere(0.4, 16, 8, None);
11+
let cube = Mesh::<()>::cube(1.5, ());
12+
let sphere = Mesh::<()>::sphere(0.4, 16, 8, ());
1313
write_mesh(&cube.minkowski_sum(&sphere), "rounded_cube");
1414
}
1515

examples/multi_format_export.rs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
1414
// Create various Mesh objects to demonstrate OBJ export
1515

1616
// 1. Simple cube
17-
let cube: Mesh<()> = Mesh::cube(20.0, None).center();
17+
let cube: Mesh<()> = Mesh::cube(20.0, ()).center();
1818
export_to_obj(&cube, "cube", "Simple 20x20x20mm cube")?;
1919

2020
// 2. Sphere
21-
let sphere: Mesh<()> = Mesh::sphere(15.0, 32, 16, None);
21+
let sphere: Mesh<()> = Mesh::sphere(15.0, 32, 16, ());
2222
export_to_obj(&sphere, "sphere", "Sphere with 15mm radius")?;
2323

2424
// 3. Cylinder
25-
let cylinder: Mesh<()> = Mesh::cylinder(8.0, 25.0, 24, None);
25+
let cylinder: Mesh<()> = Mesh::cylinder(8.0, 25.0, 24, ());
2626
export_to_obj(&cylinder, "cylinder", "Cylinder: 8mm radius, 25mm height")?;
2727

2828
// 4. Complex boolean operation: cube with spherical cavity
29-
let cube_large: Mesh<()> = Mesh::cube(30.0, None).center();
30-
let sphere_cavity: Mesh<()> = Mesh::sphere(12.0, 24, 12, None).translate(5.0, 5.0, 0.0);
29+
let cube_large: Mesh<()> = Mesh::cube(30.0, ()).center();
30+
let sphere_cavity: Mesh<()> = Mesh::sphere(12.0, 24, 12, ()).translate(5.0, 5.0, 0.0);
3131
let cube_with_cavity = cube_large.difference(&sphere_cavity);
3232
export_to_obj(
3333
&cube_with_cavity,
@@ -36,8 +36,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
3636
)?;
3737

3838
// 5. Union operation: cube + sphere
39-
let cube_small: Mesh<()> = Mesh::cube(16.0, None).center();
40-
let sphere_union: Mesh<()> = Mesh::sphere(10.0, 20, 10, None).translate(8.0, 8.0, 8.0);
39+
let cube_small: Mesh<()> = Mesh::cube(16.0, ()).center();
40+
let sphere_union: Mesh<()> = Mesh::sphere(10.0, 20, 10, ()).translate(8.0, 8.0, 8.0);
4141
let union_object = cube_small.union(&sphere_union);
4242
export_to_obj(
4343
&union_object,
@@ -46,8 +46,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
4646
)?;
4747

4848
// 6. Intersection operation
49-
let cube_intersect: Mesh<()> = Mesh::cube(25.0, None).center();
50-
let sphere_intersect: Mesh<()> = Mesh::sphere(15.0, 24, 12, None).translate(5.0, 5.0, 0.0);
49+
let cube_intersect: Mesh<()> = Mesh::cube(25.0, ()).center();
50+
let sphere_intersect: Mesh<()> = Mesh::sphere(15.0, 24, 12, ()).translate(5.0, 5.0, 0.0);
5151
let intersection_object = cube_intersect.intersection(&sphere_intersect);
5252
export_to_obj(
5353
&intersection_object,
@@ -56,8 +56,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
5656
)?;
5757

5858
// 7. More complex shape: cube with cylindrical hole
59-
let cube_base: Mesh<()> = Mesh::cube(40.0, None).center();
60-
let hole_cylinder: Mesh<()> = Mesh::cylinder(6.0, 50.0, 16, None)
59+
let cube_base: Mesh<()> = Mesh::cube(40.0, ()).center();
60+
let hole_cylinder: Mesh<()> = Mesh::cylinder(6.0, 50.0, 16, ())
6161
.rotate(90.0, 0.0, 0.0) // Rotate to align with X-axis
6262
.translate(0.0, 0.0, 0.0);
6363
let cube_with_hole = cube_base.difference(&hole_cylinder);
@@ -238,7 +238,7 @@ mod tests {
238238
#[test]
239239
fn test_obj_export() {
240240
// Test basic OBJ export functionality
241-
let cube: Mesh<()> = Mesh::cube(10.0, None);
241+
let cube: Mesh<()> = Mesh::cube(10.0, ());
242242

243243
#[cfg(feature = "obj-io")]
244244
{
@@ -258,7 +258,7 @@ mod tests {
258258

259259
#[test]
260260
fn test_obj_content_format() {
261-
let sphere: Mesh<()> = Mesh::sphere(5.0, 8, 4, None); // Low res for testing
261+
let sphere: Mesh<()> = Mesh::sphere(5.0, 8, 4, ()); // Low res for testing
262262

263263
#[cfg(feature = "obj-io")]
264264
{
@@ -314,8 +314,8 @@ mod tests {
314314
#[test]
315315
fn test_boolean_operations_obj_export() {
316316
// Test that boolean operations export correctly
317-
let cube: Mesh<()> = Mesh::cube(10.0, None);
318-
let sphere: Mesh<()> = Mesh::sphere(6.0, 8, 4, None);
317+
let cube: Mesh<()> = Mesh::cube(10.0, ());
318+
let sphere: Mesh<()> = Mesh::sphere(6.0, 8, 4, ());
319319

320320
#[cfg(feature = "obj-io")]
321321
{
@@ -345,7 +345,7 @@ mod tests {
345345
#[test]
346346
fn test_ply_export() {
347347
// Test basic PLY export functionality
348-
let cube: Mesh<()> = Mesh::cube(10.0, None);
348+
let cube: Mesh<()> = Mesh::cube(10.0, ());
349349

350350
#[cfg(feature = "ply-io")]
351351
{
@@ -388,7 +388,7 @@ mod tests {
388388

389389
#[test]
390390
fn test_ply_format_structure() {
391-
let sphere: Mesh<()> = Mesh::sphere(5.0, 8, 4, None); // Low res for testing
391+
let sphere: Mesh<()> = Mesh::sphere(5.0, 8, 4, ()); // Low res for testing
392392

393393
#[cfg(feature = "ply-io")]
394394
{
@@ -452,7 +452,7 @@ mod tests {
452452
#[test]
453453
fn test_amf_export() {
454454
// Test basic AMF export functionality
455-
let cube: Mesh<()> = Mesh::cube(10.0, None);
455+
let cube: Mesh<()> = Mesh::cube(10.0, ());
456456

457457
#[cfg(feature = "amf-io")]
458458
{
@@ -488,7 +488,7 @@ mod tests {
488488

489489
#[test]
490490
fn test_amf_with_color() {
491-
let sphere: Mesh<()> = Mesh::sphere(5.0, 8, 4, None); // Low res for testing
491+
let sphere: Mesh<()> = Mesh::sphere(5.0, 8, 4, ()); // Low res for testing
492492

493493
#[cfg(feature = "amf-io")]
494494
{
@@ -516,7 +516,7 @@ mod tests {
516516

517517
#[test]
518518
fn test_amf_xml_structure() {
519-
let cube: Mesh<()> = Mesh::cube(8.0, None);
519+
let cube: Mesh<()> = Mesh::cube(8.0, ());
520520

521521
#[cfg(feature = "amf-io")]
522522
{

0 commit comments

Comments
 (0)