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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
//! Manifold Dual Contouring — one vertex per surface component, not per cell.
//!
//! [`DualContouring`](crate::dual_contouring::DualContouring) places one vertex
//! per cell. Where two sheets of surface pass through the same cell they are
//! forced to share it, and the mesh pinches: `non_manifold_edges` is 128 on the
//! shootout's fields (M-53), and M-29 established that this residue is *all* of
//! what the A-009 cell clamp could not remove. This module removes it.
//!
//! # The construction, and where it comes from
//!
//! Schaefer, Ju & Warren, *Manifold Dual Contouring*, TVCG 13(3) 2007,
//! `10.1109/TVCG.2007.1012`, §2.2 and §3 — read this session:
//!
//! > A problem of DC that has been of common interest in almost all subsequent
//! > work is the restriction of DC in maintaining no more than one contour vertex
//! > within each grid cell. To relax this restriction on a uniform grid, multiple
//! > contour components in a cell can be detected either by identifying
//! > edge-connected components of positive (or negative) cell corners \[9],\[12]
//! > or by utilizing the cycles in the MC lookup table \[7],\[13]. In this paper,
//! > we follow the Dual MC approach of Nielson \[13].
//!
//! > Nielson associates one vertex with each cycle of a modified MC table \[26].
//! > Since each cycle consists of a list of edges on the cubic cell, each vertex
//! > is associated with a set of edges, and each edge is associated with exactly
//! > one vertex. To create polygons, the algorithm constructs one polygon
//! > connecting the vertices associated with that edge in the four adjacent
//! > cells. **This algorithm creates a quadrilateral surface that is the dual of
//! > the surface created using MC** … **this surface is always a manifold because
//! > the original MC algorithm always constructs a manifold and the dual
//! > preserves the topology of the surface.**
//!
//! > To incorporate Hermite data into Nielson's Dual MC algorithm, we simply
//! > construct a QEF for each vertex using the Hermite data on the edges
//! > associated with that vertex. We place this vertex at the location that
//! > minimizes that error function.
//!
//! So the uniform-grid algorithm is Nielson's Dual MC (IEEE Vis 2004,
//! `10.1109/VISUAL.2004.28`) with the QEF from A-007 solved per cycle. **The
//! manifold guarantee is Nielson's, not the 2007 paper's** — the 2007
//! contribution is the octree vertex-clustering criterion, which needs an octree
//! this crate does not have and which G-004 does not want (LOD here mips the
//! *field*, not the mesh). Recorded as a scoping correction on A-010.
//!
//! # Why the cycles are derived, not transcribed
//!
//! Nielson's paper is paywalled, exactly as Doi & Koide was at A-003, and hard
//! rule 5 forbids inventing the table it would have supplied. Nothing is
//! invented: the cycles come from
//! [`crate::marching_cubes::table::segment_links`], the same
//! function the 256-case Marching Cubes table is *built* from. `next[e]` is the
//! cut edge the segment leaving `e` arrives at, so walking it from every unvisited
//! cut edge enumerates precisely the cycles the paper means, for precisely the
//! configuration this crate's Marching Cubes would triangulate.
//!
//! That identity is the whole guarantee. The output is the dual of *this crate's*
//! Marching Cubes — the one measured at zero non-manifold edges and zero
//! self-intersections in M-53 — rather than the dual of some other paper's. And
//! because the dual of a surface has `V' = F`, `E' = E`, `F' = V`, the Euler
//! characteristic is carried across unchanged, which
//! `euler_characteristic_matches_marching_cubes` asserts on all seven fields.
//!
//! # What it does not fix
//!
//! Self-intersection. ✗2 records ODC (2024) measuring Manifold Dual Contouring at
//! **100% of models self-intersecting**, and Manson & Schaefer's within-cell
//! partition argument — the one [`Clamp::ToCell`] rests on — assumes *one* vertex
//! per cell, which is the assumption this module drops. The counts are measured
//! rather than assumed; see A-010's archive entry.
use crate;
use crate;
use crate;
use crateHermiteCell;
use crateFaceAmbiguity;
use cratejoined_mask;
use crate;
use crate::;
/// Manifold Dual Contouring over a sampled grid.
///
/// Sharp features from the same QEF as
/// [`DualContouring`](crate::dual_contouring::DualContouring), and manifold
/// connectivity from splitting a cell's vertex once per Marching Cubes cycle.
///
/// Costs one extra vertex per multi-sheet cell and nothing anywhere else — the
/// cycle walk is over a 12-entry array the table already computes, and cells with
/// one cycle take the identical path they took before.
///
/// # Example
///
/// ```
/// use isomesh::{MeshBuffer, RuntimeShape3};
/// use isomesh::fields::capped_gyroid;
/// use isomesh::manifold_dual_contouring::ManifoldDualContouring;
/// use isomesh::validate::{ValidateConfig, validate};
///
/// let h = 4.0 / 32.0;
/// let mut mdc = ManifoldDualContouring::<f64>::new();
/// let mut out = MeshBuffer::<f64>::new();
/// let shape = RuntimeShape3::new([33; 3])?;
/// mdc.extract(&capped_gyroid::<f64>(), &shape, [-2.0; 3], h, &mut out)?;
///
/// // The gyroid is the field one-vertex-per-cell cannot mesh cleanly.
/// let report = validate(&out, &ValidateConfig::from_cell_size(h)?);
/// assert_eq!(report.non_manifold_edges, 0);
/// # Ok::<(), isomesh::Error>(())
/// ```
/// The Manifold Dual Contouring vertex rule: one QEF solve per Marching Cubes
/// cycle.
///
/// Identical to [`Qef`](crate::dual_contouring::Qef) on any cell the surface
/// passes through once — the single cycle owns every cut edge, the restricted
/// cell is the whole cell, and the solve sees the same crossings. The two differ
/// only where a cell carries more than one sheet, which is the only place the
/// pinch was.