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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
//! Surface Nets — the dual method.
//!
//! Marching Cubes puts vertices *on grid edges* and triangulates within each
//! cell. Surface Nets does the opposite: one vertex per cell that the surface
//! passes through, and one quad per grid edge that the surface crosses, joining
//! the four cells around that edge. Hence "dual" — vertices and faces swap roles.
//!
//! The consequences are worth stating precisely, because two of the three
//! things usually claimed for Surface Nets turn out to be measurably false:
//!
//! - **It does *not* produce fewer triangles.** Measured against Marching Cubes
//! on four fields, the counts differ by exactly `2χ` — four triangles on a
//! sphere, zero on a torus. That is forced by Euler, not luck: Marching Cubes
//! places one vertex per crossed grid edge and Surface Nets emits two
//! triangles per crossed grid edge, and any closed triangulated surface has
//! `F = 2V − 2χ`. See `triangle_counts_track_marching_cubes_up_to_two_chi`.
//! - **Quads, so the connectivity is regular** — but not uniformly degree four
//! once triangulated. Measured max degree on a sphere: 10, against Marching
//! Cubes' 9.
//! - **Rounded corners, genuinely.** A vertex placed at the centroid of its
//! cell's edge crossings cannot sit on a sharp feature, because an average of
//! points on a corner's two faces lands between them. Measured on `box_exact`
//! at 33³: the nearest vertex to the corner `(1,1,1)` is 1.15 cells away. That
//! is not a defect to fix here — it is exactly what dual contouring changes,
//! and it is the whole point of E-104.
//!
//! Worth knowing before drawing conclusions from that number: Marching Cubes
//! does *worse* on the same measurement (1.41 cells), because `box_exact` is
//! exactly zero across its entire boundary and a grid plane on a box face
//! therefore classifies as outside. See
//! `neither_method_reaches_a_box_corner_and_the_reason_is_the_grid`.
//!
//! # The structural limit
//!
//! One vertex per cell means that where two sheets of the surface pass through
//! the same cell, they are forced to share a vertex and the result is
//! **non-manifold**. Measured: 48 non-manifold edges on the capped gyroid at
//! 49³, 15 on `fbm_terrain` at 33³. The literature review calls this out as "DC's
//! actual structural defect", fixed architecturally by vertex splitting rather
//! than by patching — which is A-010.
//!
//! # On the absence of published timings
//!
//! `docs/research/2026-08-11-meshing-speed-analysis.md` records that Surface
//! Nets and greedy meshing **have no credible published timings at all**,
//! despite being the two things game engines actually ship. Every number this
//! crate measures for it is therefore a reference rather than a comparison, and
//! should be reported with its hardware and grid attached.
use crate;
use crate;
use crate::;
/// Surface Nets over a sampled grid.
///
/// Owns its scratch buffers, for the same reason
/// [`MarchingCubes`](crate::marching_cubes::MarchingCubes) does: the real workload re-meshes
/// thousands of chunks and allocation dominates.
///
/// The topology — one vertex per crossed cell, one quad per crossed edge — is
/// shared with [`DualContouring`](crate::dual_contouring::DualContouring) and lives in
/// `crate::dual`. This type is that engine plus [`Centroid`], and the centroid
/// is the whole of what makes it Surface Nets rather than dual contouring.
///
/// # Example
///
/// ```
/// use isomesh::{MeshBuffer, RuntimeShape3};
/// use isomesh::fields::Sphere;
/// use isomesh::surface_nets::SurfaceNets;
///
/// let mut sn = SurfaceNets::<f32>::new();
/// let mut out = MeshBuffer::<f32>::new();
/// let shape = RuntimeShape3::new([33; 3])?;
/// sn.extract(&Sphere::<f32>::canonical(), &shape, [-2.0; 3], 0.125, &mut out)?;
///
/// assert!(out.triangle_count() > 0);
/// # Ok::<(), isomesh::Error>(())
/// ```
/// The Surface Nets vertex rule: the centroid of the cell's edge crossings.
///
/// Cheap, and **always inside the cell** — so the mesh cannot self-intersect
/// through bad placement, which is a guarantee dual contouring gives up and
/// A-009 is the ticket that measures the cost of getting back.
///
/// It cannot represent a sharp feature, and not by accident: an average of
/// points lying on a corner's two faces lands between them, never on the corner.
/// Measured on `box_exact` at 33³, the nearest vertex to `(1,1,1)` is 1.15 cells
/// away. That is the gap [`DualContouring`](crate::dual_contouring::DualContouring) closes.
;