Skip to content

Commit dc7fefb

Browse files
committed
Correct SDF preview examples
1 parent 019ab69 commit dc7fefb

4 files changed

Lines changed: 79 additions & 32 deletions

File tree

examples/readme_renders.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -264,19 +264,19 @@ fn render_readme_meshes() {
264264
render_mesh(
265265
"sdf",
266266
&Mesh::<()>::sdf(
267-
|p| p.coords.norm() - 1.0,
268-
(16, 16, 16),
269-
Point3::new(-1.2, -1.2, -1.2),
270-
Point3::new(1.2, 1.2, 1.2),
267+
|p| p.coords.norm() - 0.75,
268+
(20, 20, 20),
269+
Point3::new(-1.1, -1.1, -1.1),
270+
Point3::new(1.1, 1.1, 1.1),
271271
0.0,
272272
(),
273273
),
274274
);
275275

276276
let tpms_box = Mesh::<()>::cube(2.0, ());
277-
render_mesh("gyroid", &tpms_box.gyroid(14, 0.75, 0.0, ()));
278-
render_mesh("schwarz_p", &tpms_box.schwarz_p(14, 0.75, 0.0, ()));
279-
render_mesh("schwarz_d", &tpms_box.schwarz_d(14, 0.75, 0.0, ()));
277+
render_mesh("gyroid", &tpms_box.gyroid(20, 2.0, 0.0, ()));
278+
render_mesh("schwarz_p", &tpms_box.schwarz_p(20, 2.0, 0.0, ()));
279+
render_mesh("schwarz_d", &tpms_box.schwarz_d(20, 2.0, 0.0, ()));
280280
render_mesh(
281281
"spur_gear_involute",
282282
&Mesh::<()>::spur_gear_involute(0.18, 22, 20.0, 0.0, 0.0, 8, 0.35, ()),

readme.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ let faces = vec![
270270
vec![2, 3, 4],
271271
vec![3, 0, 4],
272272
];
273-
let pyramid = Mesh::polyhedron(points, &faces, None);
273+
let pyramid = Mesh::<()>::polyhedron(points, &faces, ());
274274

275275
// Metaballs https://en.wikipedia.org/wiki/Metaballs
276276
use csgrs::mesh::metaballs::MetaBall;
@@ -283,12 +283,12 @@ let resolution = (60, 60, 60);
283283
let iso_value = 1.0;
284284
let padding = 1.0;
285285

286-
let metaball_csg = CSG::from_metaballs(
286+
let metaball_csg = Mesh::<()>::metaballs(
287287
&balls,
288288
resolution,
289289
iso_value,
290290
padding,
291-
None,
291+
(),
292292
);
293293

294294
// Example Signed Distance Field for a sphere of radius 1.5 centered at (0,0,0)
@@ -299,7 +299,7 @@ let min_pt = Point3::new(-2.0, -2.0, -2.0);
299299
let max_pt = Point3::new( 2.0, 2.0, 2.0);
300300
let iso_value = 0.0; // Typically zero for SDF-based surfaces
301301

302-
let csg_shape = Mesh::from_sdf(my_sdf, resolution, min_pt, max_pt, iso_value, None);
302+
let csg_shape = Mesh::<()>::sdf(my_sdf, resolution, min_pt, max_pt, iso_value, ());
303303
```
304304

305305
### CSG Boolean Operations

src/mesh/tpms.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@ impl<M: Clone + Debug + Send + Sync> Mesh<M> {
3737

3838
// ------------ Specific minimal‑surface flavours --------------------
3939

40-
/// Gyroid surface: `sin x cos y + sin y cos z + sin z cos x = iso`
41-
/// (`period` rescales the spatial frequency; larger => slower repeat)
40+
/// Gyroid surface: `sin x cos y + sin y cos z + sin z cos x = iso`
41+
/// after scaling coordinates by `2π / period`.
42+
///
43+
/// `period` is a spatial wavelength in model units; larger values repeat
44+
/// more slowly.
4245
/// **Mathematical Foundation**: Gyroid is a triply periodic minimal surface with zero mean curvature.
4346
/// **Optimization**: Pre-compute trigonometric values for better performance.
4447
pub fn gyroid(
@@ -49,13 +52,13 @@ impl<M: Clone + Debug + Send + Sync> Mesh<M> {
4952
metadata: M,
5053
) -> Mesh<M> {
5154
let res = (resolution.max(2), resolution.max(2), resolution.max(2));
52-
let period_inv = 1.0 / period;
55+
let scale = std::f64::consts::TAU as Real / period;
5356
self.tpms_from_sdf(
5457
move |p: &Point3<Real>| {
5558
// Pre-compute scaled coordinates for efficiency
56-
let x_scaled = p.x * period_inv;
57-
let y_scaled = p.y * period_inv;
58-
let z_scaled = p.z * period_inv;
59+
let x_scaled = p.x * scale;
60+
let y_scaled = p.y * scale;
61+
let z_scaled = p.z * scale;
5962

6063
// Pre-compute trigonometric values to avoid redundant calculations
6164
let (sin_x, cos_x) = x_scaled.sin_cos();
@@ -73,6 +76,7 @@ impl<M: Clone + Debug + Send + Sync> Mesh<M> {
7376
}
7477

7578
/// Schwarz‑P surface: `cos x + cos y + cos z = iso` (default iso = 0)
79+
/// after scaling coordinates by `2π / period`.
7680
/// **Mathematical Foundation**: Schwarz P-surface has constant mean curvature and cubic symmetry.
7781
/// **Optimization**: Use direct cosine computation for this simpler surface equation.
7882
pub fn schwarz_p(
@@ -83,13 +87,13 @@ impl<M: Clone + Debug + Send + Sync> Mesh<M> {
8387
metadata: M,
8488
) -> Mesh<M> {
8589
let res = (resolution.max(2), resolution.max(2), resolution.max(2));
86-
let period_inv = 1.0 / period;
90+
let scale = std::f64::consts::TAU as Real / period;
8791
self.tpms_from_sdf(
8892
move |p: &Point3<Real>| {
8993
// Pre-compute scaled coordinates
90-
let x_scaled = p.x * period_inv;
91-
let y_scaled = p.y * period_inv;
92-
let z_scaled = p.z * period_inv;
94+
let x_scaled = p.x * scale;
95+
let y_scaled = p.y * scale;
96+
let z_scaled = p.z * scale;
9397

9498
// **Mathematical Formula**: Schwarz P-surface equation
9599
// P(x,y,z) = cos(x) + cos(y) + cos(z)
@@ -102,6 +106,7 @@ impl<M: Clone + Debug + Send + Sync> Mesh<M> {
102106
}
103107

104108
/// Schwarz‑D (Diamond) surface: `sin x sin y sin z + sin x cos y cos z + ... = iso`
109+
/// after scaling coordinates by `2π / period`.
105110
/// **Mathematical Foundation**: Diamond surface exhibits tetrahedral symmetry and is self-intersecting.
106111
/// **Optimization**: Pre-compute all trigonometric values for maximum efficiency.
107112
pub fn schwarz_d(
@@ -112,13 +117,13 @@ impl<M: Clone + Debug + Send + Sync> Mesh<M> {
112117
metadata: M,
113118
) -> Mesh<M> {
114119
let res = (resolution.max(2), resolution.max(2), resolution.max(2));
115-
let period_inv = 1.0 / period;
120+
let scale = std::f64::consts::TAU as Real / period;
116121
self.tpms_from_sdf(
117122
move |p: &Point3<Real>| {
118123
// Pre-compute scaled coordinates
119-
let x_scaled = p.x * period_inv;
120-
let y_scaled = p.y * period_inv;
121-
let z_scaled = p.z * period_inv;
124+
let x_scaled = p.x * scale;
125+
let y_scaled = p.y * scale;
126+
let z_scaled = p.z * scale;
122127

123128
// Pre-compute all trigonometric values once
124129
let (sin_x, cos_x) = x_scaled.sin_cos();

tests/sdf_tpms_diagnostics.rs

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,20 +91,23 @@ fn assert_sdf_diagnostics_consistent(mesh: &Mesh<&'static str>, diagnostics: &Sd
9191
}
9292

9393
fn gyroid_value(point: &Point3<Real>, period: Real) -> Real {
94-
let x = point.x / period;
95-
let y = point.y / period;
96-
let z = point.z / period;
94+
let scale = std::f64::consts::TAU as Real / period;
95+
let x = point.x * scale;
96+
let y = point.y * scale;
97+
let z = point.z * scale;
9798
x.sin() * y.cos() + y.sin() * z.cos() + z.sin() * x.cos()
9899
}
99100

100101
fn schwarz_p_value(point: &Point3<Real>, period: Real) -> Real {
101-
(point.x / period).cos() + (point.y / period).cos() + (point.z / period).cos()
102+
let scale = std::f64::consts::TAU as Real / period;
103+
(point.x * scale).cos() + (point.y * scale).cos() + (point.z * scale).cos()
102104
}
103105

104106
fn schwarz_d_value(point: &Point3<Real>, period: Real) -> Real {
105-
let (sx, cx) = (point.x / period).sin_cos();
106-
let (sy, cy) = (point.y / period).sin_cos();
107-
let (sz, cz) = (point.z / period).sin_cos();
107+
let scale = std::f64::consts::TAU as Real / period;
108+
let (sx, cx) = (point.x * scale).sin_cos();
109+
let (sy, cy) = (point.y * scale).sin_cos();
110+
let (sz, cz) = (point.z * scale).sin_cos();
108111
sx * sy * sz + sx * cy * cz + cx * sy * cz + cx * cy * sz
109112
}
110113

@@ -345,3 +348,42 @@ fn closed_sdf_sphere_has_no_boundary_edges_or_degenerate_triangles() {
345348
"sphere SDF emitted degenerate triangles: {diagnostics:#?}"
346349
);
347350
}
351+
352+
#[test]
353+
fn readme_sdf_examples_mesh_non_empty_surfaces() {
354+
let (sphere, sphere_diagnostics) = Mesh::<&'static str>::sdf_with_diagnostics(
355+
|p| p.coords.norm() - 0.75,
356+
(20, 20, 20),
357+
Point3::new(-1.1, -1.1, -1.1),
358+
Point3::new(1.1, 1.1, 1.1),
359+
0.0,
360+
"readme_sdf",
361+
);
362+
assert_sdf_diagnostics_consistent(&sphere, &sphere_diagnostics);
363+
assert_mesh_vertices_finite(&sphere);
364+
assert_sdf_mesh_is_triangular(&sphere);
365+
assert!(
366+
sphere_diagnostics.emitted_triangle_count > 0,
367+
"README SDF sphere emitted no triangles: {sphere_diagnostics:#?}"
368+
);
369+
assert_eq!(
370+
boundary_edge_count(&sphere),
371+
0,
372+
"README SDF sphere should be closed: {sphere_diagnostics:#?}"
373+
);
374+
375+
let box_mesh = Mesh::<&'static str>::cube(2.0, "tpms");
376+
for (name, mesh) in [
377+
("gyroid", box_mesh.gyroid(20, 2.0, 0.0, "gyroid")),
378+
("schwarz_p", box_mesh.schwarz_p(20, 2.0, 0.0, "schwarz_p")),
379+
("schwarz_d", box_mesh.schwarz_d(20, 2.0, 0.0, "schwarz_d")),
380+
] {
381+
assert_mesh_vertices_finite(&mesh);
382+
assert_sdf_mesh_is_triangular(&mesh);
383+
assert!(
384+
mesh.polygons.len() > 100,
385+
"{name} README TPMS example emitted too few triangles: {}",
386+
mesh.polygons.len()
387+
);
388+
}
389+
}

0 commit comments

Comments
 (0)