1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//! Engine-agnostic isosurface extraction. Field in, triangles out.
//!
//! ```
//! use isomesh::marching_cubes::MarchingCubes;
//! use isomesh::fields::Sphere;
//! use isomesh::{MeshBuffer, RuntimeShape3};
//!
//! let field = Sphere::<f32>::canonical(); // any `Sdf` -- yours, or one of eight here
//! let shape = RuntimeShape3::new([33; 3])?; // 33 samples per axis, so 32 cells
//! let mut mesh = MeshBuffer::<f32>::new(); // reused across calls; never reallocated
//!
//! MarchingCubes::<f32>::new()
//! .extract(&field, &shape, [-2.0; 3], 0.125, &mut mesh)?;
//!
//! assert!(mesh.triangle_count() > 0);
//! # Ok::<(), isomesh::Error>(())
//! ```
//!
//! `mesh.positions` is `Vec<[f32; 3]>` and `mesh.indices` is `Vec<u32>` — hand
//! them to a renderer, a physics engine, or an exporter. There is no mesh type to
//! learn and no math library in the way.
//!
//! # Why it looks like this
//!
//! `isomesh` has to serve both a real-time voxel game and a CAD tool, and that
//! single constraint decides most of its design: no math library appears in a
//! public signature, output buffers are caller-provided and reusable, and the
//! scalar type is generic over `f32` and `f64`.
//!
//! # Which extractor
//!
//! Seven implement [`Extractor`](extractor::Extractor). Two questions decide it,
//! and both answers are measured rather than argued.
//!
//! **Are you chunking?** Then the seam is structural, not a bug someone will fix
//! later: [`MarchingCubes`](marching_cubes::MarchingCubes) and
//! [`SubgridMarchingTetrahedra`](subgrid::extract::SubgridMarchingTetrahedra)
//! tile with zero boundary edges on a shared plane; the dual methods place one
//! vertex per *cell* and are gapped by construction.
//!
//! **Do you need sharp corners?** Then
//! [`DualContouring`](dual_contouring::DualContouring) reaches a box corner to
//! **0.01 cells** where [`SurfaceNets`](surface_nets::SurfaceNets) stops at
//! 0.58, and is 101× closer in Hausdorff distance on a sharp field — and 1.2× on
//! a smooth one, which is why this is a choice rather than an upgrade.
//!
//! You cannot have both today. That is measured and open, not hidden.
//!
//! The others are for narrower jobs:
//! [`MarchingTetrahedra`](marching_tetrahedra::MarchingTetrahedra) trades ~3× the
//! triangles for better sharp-field accuracy,
//! [`ManifoldDualContouring`](manifold_dual_contouring::ManifoldDualContouring)
//! splits a cell by surface component, and
//! [`GreedyQuads`](greedy_quads::GreedyQuads) is the blocky path.
//!
//! # Conventions
//!
//! These hold for every type and every algorithm in this crate. They are stated
//! once here and repeated on the items they constrain, because each one is a
//! convention rather than a fact, and a mismatch across a module boundary
//! produces output that looks plausible and is wrong.
//!
//! - **Sign.** *Negative is inside.* A field is negative strictly inside the
//! solid, positive strictly outside, and the surface is the zero level set.
//! - **Handedness.** Right-handed. `x × y = +z`.
//! - **Winding.** Counter-clockwise viewed from outside the solid.
//! - **Normals.** Point away from the solid, i.e. along increasing field value.
//! - **Index order.** `x` varies fastest: `i = x + y·sx + z·sx·sy`.
//!
//! # Float math
//!
//! Every transcendental goes through [`libm`], unconditionally — there is no
//! `std` fast path. Two reasons. One execution path: a `#[cfg(feature = "std")]`
//! fork would mean two float backends and two sets of results for the same
//! input. And determinism: `std`'s `sin`/`cos` are the platform's, are not
//! correctly rounded, and differ between macOS and Linux, which would make
//! committed golden hashes platform-specific. It costs nothing at run time —
//! `libm::sqrtf` compiles to `fsqrt` on aarch64+neon and `sqrtss` on
//! x86-64+sse2.
//!
//! # `no_std`
//!
//! This crate is `no_std` unconditionally, not `cfg_attr`-conditionally. A
//! conditional attribute would put `std`'s prelude in scope for the default
//! build, so a stray `use std::collections::HashMap` would compile here and
//! break only in some later `no_std` build. Unconditional makes that a compile
//! error at the moment it is typed.
// R-062. `marching_cubes::proofs` const-evaluates 16,384 triangulations so Kani
// can prove properties over a lookup rather than symbolically executing
// `segment_links`' data-dependent walk -- which is what exhausted 32 GB of CBMC.
// That pushes the cumulative const-eval step budget past rustc's default, which
// is tuned to catch accidental infinite loops. `cfg_attr(kani, ...)` so the
// allowance exists only under Kani's compiler: an ordinary `cargo build` still
// gets the lint, and a real runaway const-eval in `segment_links` would still be
// caught there.
extern crate alloc;
// The test harness itself needs `std`; the library never does.
extern crate std;
/// Speculative algorithms, behind the `experimental` feature.
///
/// Off by default, exempt from semver, and exempt from nothing else — see the
/// module's own docs.
pub use ;
pub use ;
pub use Real;
pub use Sdf;
pub use ;
/// Compiles the README's example as a doctest, without putting the README into
/// these docs.
///
/// A code sample on a crate's landing page is the first thing a reader tries and
/// the first thing to rot, because nothing builds it. `cfg(doctest)` means this
/// item exists only when rustdoc is collecting doctests, so the example is
/// checked on every `cargo test` and appears in no rendered documentation.
;