1use std::time::Instant;
2
3use honeycomb::{
4 kernels::{
5 grisubal::Clip,
6 remeshing::{
7 EdgeCollapseError, EdgeSwapError, capture_geometry, classify_capture, collapse_edge,
8 cut_inner_edge, cut_outer_edge, move_vertex_to_average, swap_edge,
9 },
10 triangulation::{TriangulateError, earclip_cell_countercw},
11 utils::{EdgeAnchor, FaceAnchor, VertexAnchor, is_orbit_orientation_consistent},
12 },
13 prelude::{CMap2, CoordsFloat, DartIdType, NULL_DART_ID, OrbitPolicy, SewError, Vertex2},
14 stm::{StmClosureResult, Transaction, abort, atomically, atomically_with_err, retry},
15};
16
17use crate::{cli::RemeshArgs, utils::hash_file};
18
19pub fn bench_remesh<T: CoordsFloat>(args: RemeshArgs) -> CMap2<T> {
20 let input_map = args.input.to_str().unwrap();
21 let target_len = T::from(args.target_length).unwrap();
22
23 let n_threads = std::thread::available_parallelism()
24 .map(|n| n.get())
25 .unwrap_or(1);
26
27 let input_hash = hash_file(input_map).expect("E: could not compute input hash"); let mut instant = Instant::now();
32 let mut map: CMap2<T> = capture_geometry(
33 input_map,
34 [T::from(args.lx).unwrap(), T::from(args.ly).unwrap()],
35 Clip::from(args.clip),
36 )
37 .unwrap();
38 let capture_time = instant.elapsed();
39
40 instant = Instant::now();
42 classify_capture(&map).unwrap();
43 let classification_time = instant.elapsed();
44
45 instant = Instant::now();
47 let n_tot = map
48 .iter_faces()
49 .map(|id| (map.orbit(OrbitPolicy::Face, id as DartIdType).count() - 3) * 2)
50 .sum();
51 let start = map.add_free_darts(n_tot);
52 map.iter_faces()
54 .scan(start, |state, f| {
55 let n_d = (map.orbit(OrbitPolicy::Face, f as DartIdType).count() - 3) * 2;
57 *state += n_d as DartIdType;
58 Some((f, n_d, *state - n_d as DartIdType))
59 })
60 .filter(|(_, n_d, _)| *n_d != 0)
61 .for_each(|(f, n_d, start)| {
62 let new_darts = (start..start + n_d as DartIdType).collect::<Vec<_>>();
63 let anchor = map.force_remove_attribute::<FaceAnchor>(f);
64 if let Some(a) = anchor {
66 atomically(|t| {
67 for &d in &new_darts {
68 map.write_attribute(t, d, EdgeAnchor::from(a))?;
69 }
70 Ok(())
71 });
72 }
73 while let Err(e) =
74 atomically_with_err(|t| earclip_cell_countercw(t, &map, f, &new_darts))
75 {
76 match e {
77 TriangulateError::UndefinedFace(_) | TriangulateError::OpFailed(_) => continue,
78 TriangulateError::NoEar => panic!("E: cannot triangulate the geometry capture"),
79 TriangulateError::AlreadyTriangulated
80 | TriangulateError::NonFannable
81 | TriangulateError::NotEnoughDarts(_)
82 | TriangulateError::TooManyDarts(_) => {
83 unreachable!()
84 }
85 }
86 }
87 if let Some(a) = anchor {
89 atomically(|t| {
90 for &d in &new_darts {
91 let fid = map.face_id_transac(t, d)?;
92 map.write_attribute(t, fid, a)?;
93 }
94 Ok(())
95 });
96 }
97 });
98 let triangulation_time = instant.elapsed();
99
100 debug_assert!(
102 map.iter_faces()
103 .all(|f| map.orbit(OrbitPolicy::Face, f as DartIdType).count() == 3)
104 );
105 debug_assert!(map.iter_faces().all(|f| {
106 map.orbit(OrbitPolicy::FaceLinear, f).count() == 3
107 && atomically(|t| check_tri_orientation(t, &map, f as DartIdType))
108 }));
109 debug_assert!(
110 map.iter_vertices()
111 .all(|v| map.force_read_attribute::<VertexAnchor>(v).is_some())
112 );
113 debug_assert!(
114 map.iter_edges()
115 .all(|e| map.force_read_attribute::<EdgeAnchor>(e).is_some())
116 );
117 debug_assert!(
118 map.iter_faces()
119 .all(|f| map.force_read_attribute::<FaceAnchor>(f).is_some())
120 );
121
122 println!("| remesh benchmark");
124 println!("|-> input : {input_map} (hash: {input_hash:#0x})");
125 println!(
126 "|-> backend : {:?} with {n_threads} thread(s)",
127 args.backend
128 );
129 println!("|-> target size: {target_len:?}");
130 println!("|-> capture time : {}ms", capture_time.as_millis());
131 println!(
132 "|-> triangulation time : {}ms",
133 triangulation_time.as_millis()
134 );
135 println!(
136 "|-> classification time : {}ms",
137 classification_time.as_millis()
138 );
139
140 println!(
141 "Round | Relax (tot, s) | Ret cond (s) | Batch compute (s) | Cut/collapse (s) | Swap (s)"
142 );
143 let mut n = 0;
149 let mut r;
150 loop {
151 print!("{:>5}", n);
152
153 instant = Instant::now();
155 r = 0;
156 loop {
157 map.iter_vertices()
158 .filter_map(|v| {
159 let mut neigh = Vec::with_capacity(10);
160 for d in map.orbit(OrbitPolicy::Vertex, v as DartIdType) {
161 let b2d = map.beta::<2>(d);
162 if b2d == NULL_DART_ID {
163 return None; } else {
165 neigh.push(map.vertex_id(b2d));
166 }
167 }
168 Some((v, neigh))
169 })
170 .for_each(|(vid, neighbors)| {
171 let _ = atomically_with_err(|t| {
172 move_vertex_to_average(t, &map, vid, &neighbors)?;
173 if !is_orbit_orientation_consistent(t, &map, vid)? {
174 abort("E: resulting geometry is inverted")?;
175 }
176 Ok(())
177 });
178 });
179
180 r += 1;
181 if r >= args.n_relax_rounds.get() {
182 break;
183 }
184 }
185 print!(" | {:>14.6e}", instant.elapsed().as_secs_f64());
186
187 debug_assert!(
188 map.iter_faces()
189 .all(|f| { atomically(|t| check_tri_orientation(t, &map, f as DartIdType)) })
190 );
191
192 if args.enable_er {
194 instant = Instant::now();
195 let n_e = map.iter_edges().count();
196 let n_e_outside_tol = map
197 .iter_edges()
198 .map(|e| {
199 let (v1, v2) = (
200 map.force_read_vertex(map.vertex_id(e as DartIdType))
201 .unwrap(),
202 map.force_read_vertex(map.vertex_id(map.beta::<1>(e as DartIdType)))
203 .unwrap(),
204 );
205 (v2 - v1).norm()
206 })
207 .filter(|l| {
208 (l.to_f64().unwrap() - args.target_length).abs() / args.target_length
209 > args.target_tolerance
210 })
211 .count();
212 if ((n_e_outside_tol as f32 - n_e as f32).abs() / n_e as f32) < 0.05 {
214 print!(" | {:>12.6e}", instant.elapsed().as_millis());
215 print!(" | {:>17}", "n/a");
216 print!(" | {:>16}", "n/a");
217 println!(" | {:>8}", "n/a");
218 break;
219 }
220 print!(" | {:>12.6e}", instant.elapsed().as_secs_f64());
221 } else {
222 print!(" | {:>12}", "n/a");
223 }
224
225 instant = Instant::now();
227 let edges_to_process = map.iter_edges().collect::<Vec<_>>();
228 print!(" | {:>17.6e}", instant.elapsed().as_secs_f64());
229
230 instant = Instant::now();
232 for e in edges_to_process {
233 if map.is_unused(e as DartIdType) {
234 continue;
236 }
237
238 let (l, r) = (e as DartIdType, map.beta::<1>(e as DartIdType));
240 let diff = atomically(|t| compute_diff_to_target(t, &map, l, r, args.target_length));
241 if diff.abs() < args.target_tolerance {
242 continue;
244 }
245 let e = map.edge_id(e);
246 if diff.is_sign_positive() {
248 if map.is_i_free::<2>(e as DartIdType) {
250 let nd = map.add_free_darts(3);
251 let nds: [DartIdType; 3] = std::array::from_fn(|i| nd + i as DartIdType);
252 while let Err(er) = atomically_with_err(|t| {
253 cut_outer_edge(t, &map, e, nds)?;
254 let new_vid = nds[0];
255 if !is_orbit_orientation_consistent(t, &map, new_vid)? {
256 abort(SewError::BadGeometry(1, nds[0], nds[2]))?;
257 }
258 Ok(())
259 }) {
260 match er {
261 SewError::BadGeometry(1, _, _) => {
262 for d in nds {
263 map.remove_free_dart(d);
264 }
265 break;
266 }
267 SewError::BadGeometry(_, _, _)
268 | SewError::FailedLink(_)
269 | SewError::FailedAttributeOp(_) => continue,
270 }
271 }
272 } else {
273 let nd = map.add_free_darts(6);
274 let nds: [DartIdType; 6] = std::array::from_fn(|i| nd + i as DartIdType);
275 while let Err(er) = atomically_with_err(|t| {
276 cut_inner_edge(t, &map, e, nds)?;
277 let new_vid = nds[0];
278 if !is_orbit_orientation_consistent(t, &map, new_vid)? {
279 abort(SewError::BadGeometry(1, nds[0], nds[3]))?;
280 }
281 Ok(())
282 }) {
283 match er {
284 SewError::BadGeometry(1, _, _) => {
285 for d in nds {
286 map.remove_free_dart(d);
287 }
288 break;
289 }
290 SewError::BadGeometry(_, _, _)
291 | SewError::FailedLink(_)
292 | SewError::FailedAttributeOp(_) => continue,
293 }
294 }
295 }
296 } else {
297 while let Err(er) = atomically_with_err(|t| collapse_edge(t, &map, e)) {
299 match er {
300 EdgeCollapseError::FailedCoreOp(SewError::BadGeometry(_, _, _))
301 | EdgeCollapseError::NonCollapsibleEdge(_)
302 | EdgeCollapseError::InvertedOrientation => break,
303 EdgeCollapseError::FailedCoreOp(_) | EdgeCollapseError::BadTopology => {
304 continue;
305 }
306 EdgeCollapseError::NullEdge => unreachable!(),
307 }
308 }
309 }
310 }
311 print!(" | {:>16.6e}", instant.elapsed().as_secs_f64());
312
313 instant = Instant::now();
315 for (e, diff) in map
316 .iter_edges()
317 .map(|e| {
318 let (l, r) = (e as DartIdType, map.beta::<1>(e as DartIdType));
319 let diff =
320 atomically(|t| compute_diff_to_target(t, &map, l, r, args.target_length));
321 (e, diff)
322 })
323 .filter(|(_, diff)| diff.abs() > args.target_tolerance)
324 {
325 let (l, r) = (e as DartIdType, map.beta::<2>(e as DartIdType));
326 if r != NULL_DART_ID {
327 debug_assert!(
328 map.force_read_attribute::<FaceAnchor>(map.face_id(l))
329 .is_some()
330 );
331 debug_assert!(
332 map.force_read_attribute::<FaceAnchor>(map.face_id(r))
333 .is_some()
334 );
335 if let Err(er) = atomically_with_err(|t| {
336 let (b0l, b0r) = (map.beta_transac::<0>(t, l)?, map.beta_transac::<0>(t, r)?);
337 let new_diff = atomically(|t| {
338 compute_diff_to_target(t, &map, b0l, b0r, args.target_length)
339 });
340
341 if new_diff.abs() < diff.abs() {
343 swap_edge(t, &map, e)?;
344 }
345
346 if !check_tri_orientation(t, &map, l)? || !check_tri_orientation(t, &map, r)? {
348 abort(EdgeSwapError::NotSwappable("swap inverts orientation"))?;
349 }
350
351 Ok(())
352 }) {
353 match er {
354 EdgeSwapError::NotSwappable(_) => {} EdgeSwapError::NullEdge
356 | EdgeSwapError::IncompleteEdge
357 | EdgeSwapError::FailedCoreOp(_)
358 | EdgeSwapError::BadTopology => unreachable!(),
359 }
360 }
361
362 debug_assert!(
363 map.force_read_attribute::<FaceAnchor>(map.face_id(l))
364 .is_some()
365 );
366 debug_assert!(
367 map.force_read_attribute::<FaceAnchor>(map.face_id(r))
368 .is_some()
369 );
370 }
371 }
372 println!(" | {:>8.6e}", instant.elapsed().as_secs_f64());
373
374 debug_assert!(map.iter_faces().all(|f| {
375 map.orbit(OrbitPolicy::FaceLinear, f).count() == 3
376 && atomically(|t| check_tri_orientation(t, &map, f as DartIdType))
377 }));
378
379 n += 1;
380 if n >= args.n_rounds.get() {
381 break;
382 }
383 }
384
385 debug_assert!(map.iter_faces().all(|f| {
386 map.orbit(OrbitPolicy::FaceLinear, f).count() == 3
387 && atomically(|t| check_tri_orientation(t, &map, f as DartIdType))
388 }));
389
390 map
391}
392
393#[inline]
394fn compute_diff_to_target<T: CoordsFloat>(
395 t: &mut Transaction,
396 map: &CMap2<T>,
397 l: DartIdType,
398 r: DartIdType,
399 target: f64,
400) -> StmClosureResult<f64> {
401 let (vid1, vid2) = (map.vertex_id_transac(t, l)?, map.vertex_id_transac(t, r)?);
402 let (v1, v2) =
403 if let (Some(v1), Some(v2)) = (map.read_vertex(t, vid1)?, map.read_vertex(t, vid2)?) {
404 (v1, v2)
405 } else {
406 retry()?
407 };
408 Ok(((v2 - v1).norm().to_f64().unwrap() - target) / target)
409}
410
411#[inline]
412fn check_tri_orientation<T: CoordsFloat>(
413 t: &mut Transaction,
414 map: &CMap2<T>,
415 d: DartIdType,
416) -> StmClosureResult<bool> {
417 let vid1 = map.vertex_id_transac(t, d)?;
418 let b1 = map.beta_transac::<1>(t, d)?;
419 let vid2 = map.vertex_id_transac(t, b1)?;
420 let b1b1 = map.beta_transac::<1>(t, b1)?;
421 let vid3 = map.vertex_id_transac(t, b1b1)?;
422 let v1 = map.read_vertex(t, vid1)?.unwrap();
423 let v2 = map.read_vertex(t, vid2)?.unwrap();
424 let v3 = map.read_vertex(t, vid3)?.unwrap();
425 Ok(Vertex2::cross_product_from_vertices(&v1, &v2, &v3) > T::zero())
426}