1pub mod process_status;
2mod processing_change;
3
4use crate::{
5 coord::Coord,
6 generator::{process_status::ProcessStatus, processing_change::ProcessingChange},
7 map::{DensityMap, DensityMapError},
8 mesh::{
9 points_separation::PointsSeparation, settings::GenerateDensityMeshSettings, DensityMesh,
10 GenerateDensityMeshError,
11 },
12 triangle::Triangle,
13 Scalar,
14};
15#[cfg(feature = "parallel")]
16use rayon::prelude::*;
17use serde::{Deserialize, Serialize};
18use std::{
19 collections::VecDeque,
20 time::{Duration, Instant},
21};
22use triangulation::{Delaunay, Point};
23
24#[cfg(feature = "parallel")]
25macro_rules! into_iter {
26 ($v:expr) => {
27 $v.into_par_iter()
28 };
29}
30
31#[cfg(not(feature = "parallel"))]
32macro_rules! into_iter {
33 ($v:expr) => {
34 $v.into_iter()
35 };
36}
37
38#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
41pub struct DensityMeshGenerator {
42 map: DensityMap,
43 mesh: Option<DensityMesh>,
44 queue: VecDeque<(Vec<Coord>, GenerateDensityMeshSettings)>,
46 current: Option<ProcessingChange>,
47}
48
49impl DensityMeshGenerator {
50 pub fn new(points: Vec<Coord>, map: DensityMap, settings: GenerateDensityMeshSettings) -> Self {
60 let mut queue = VecDeque::with_capacity(1);
61 queue.push_back((points, settings));
62 Self {
63 map,
64 mesh: None,
65 queue,
66 current: None,
67 }
68 }
69
70 pub fn map(&self) -> &DensityMap {
72 &self.map
73 }
74
75 pub fn mesh(&self) -> Option<&DensityMesh> {
77 self.mesh.as_ref()
78 }
79
80 pub fn into_mesh(self) -> Option<DensityMesh> {
81 self.mesh
82 }
83
84 pub fn in_progress(&self) -> bool {
86 self.current.is_some() || !self.queue.is_empty()
87 }
88
89 pub fn progress(&self) -> (usize, usize, Scalar) {
94 match &self.current {
95 Some(ProcessingChange::FindingPoints {
96 progress_current,
97 progress_limit,
98 ..
99 }) => (
100 *progress_current,
101 *progress_limit,
102 *progress_current as Scalar / *progress_limit as Scalar,
103 ),
104 Some(ProcessingChange::Triangulate { progress_limit, .. }) => {
105 (*progress_limit, *progress_limit, 1.0)
106 }
107 Some(ProcessingChange::RemoveInvisibleTriangles { progress_limit, .. }) => {
108 (*progress_limit, *progress_limit, 1.0)
109 }
110 Some(ProcessingChange::Extrude { progress_limit, .. }) => {
111 (*progress_limit, *progress_limit, 1.0)
112 }
113 _ => (0, 0, 0.0),
114 }
115 }
116
117 pub fn change_map(
130 &mut self,
131 col: usize,
132 row: usize,
133 width: usize,
134 height: usize,
135 data: Vec<u8>,
136 settings: GenerateDensityMeshSettings,
137 ) -> Result<(), DensityMapError> {
138 self.map.change(col, row, width, height, data)?;
139 self.queue.push_back((vec![], settings));
140 Ok(())
141 }
142
143 #[allow(clippy::many_single_char_names)]
148 pub fn process(&mut self) -> Result<ProcessStatus, GenerateDensityMeshError> {
149 if let Some(current) = std::mem::replace(&mut self.current, None) {
150 match current {
151 ProcessingChange::FindingPoints {
152 settings,
153 mut tries,
154 mut remaining,
155 mut points,
156 mut progress_current,
157 progress_limit,
158 } => {
159 if !points.is_empty() {
160 remaining = into_iter!(remaining)
161 .filter(|(p1, _, _, lpss)| {
162 points.iter().all(|p2| (*p2 - *p1).sqr_magnitude() > *lpss)
163 })
164 .collect::<Vec<_>>();
165 if remaining.is_empty() {
166 self.current = Some(ProcessingChange::Triangulate {
167 settings,
168 points,
169 progress_limit,
170 });
171 return Ok(ProcessStatus::InProgress);
172 }
173 }
174 if let Some((point, _, _, _)) = remaining
175 .iter()
176 .max_by(|a, b| a.2.partial_cmp(&b.2).unwrap())
177 {
178 points.push(*point);
179 tries = settings.max_iterations;
180 } else if tries > 0 {
181 tries -= 1;
182 self.current = Some(ProcessingChange::FindingPoints {
183 settings,
184 tries,
185 remaining,
186 points,
187 progress_current,
188 progress_limit,
189 });
190 return Ok(ProcessStatus::InProgress);
191 } else {
192 self.current = Some(ProcessingChange::Triangulate {
193 settings,
194 points,
195 progress_limit,
196 });
197 return Ok(ProcessStatus::InProgress);
198 }
199 progress_current = progress_limit - remaining.len();
200 self.current = Some(ProcessingChange::FindingPoints {
201 settings,
202 tries,
203 remaining,
204 points,
205 progress_current,
206 progress_limit,
207 });
208 Ok(ProcessStatus::InProgress)
209 }
210 ProcessingChange::Triangulate {
211 settings,
212 points,
213 progress_limit,
214 } => {
215 let dpoints = points
216 .iter()
217 .map(|v| Point::new(v.x, v.y))
218 .collect::<Vec<_>>();
219 let triangulation = if let Some(triangulation) = Delaunay::new(&dpoints) {
220 triangulation
221 } else {
222 return Err(GenerateDensityMeshError::FailedTriangulation);
223 };
224 let triangles = triangulation
225 .dcel
226 .vertices
227 .chunks(3)
228 .map(|t| Triangle {
229 a: t[0],
230 b: t[1],
231 c: t[2],
232 })
233 .collect::<Vec<_>>();
234 if !settings.keep_invisible_triangles {
235 self.current = Some(ProcessingChange::RemoveInvisibleTriangles {
236 settings,
237 points,
238 triangles,
239 progress_limit,
240 });
241 Ok(ProcessStatus::InProgress)
242 } else if let Some(size) = settings.extrude_size {
243 self.current = Some(ProcessingChange::Extrude {
244 points,
245 triangles,
246 size,
247 progress_limit,
248 });
249 Ok(ProcessStatus::InProgress)
250 } else {
251 self.mesh = Some(DensityMesh { points, triangles });
252 Ok(ProcessStatus::MeshChanged)
253 }
254 }
255 ProcessingChange::RemoveInvisibleTriangles {
256 settings,
257 points,
258 mut triangles,
259 progress_limit,
260 } => {
261 triangles = triangles
262 .into_iter()
263 .filter(|t| {
264 Self::is_triangle_visible(
265 points[t.a],
266 points[t.b],
267 points[t.c],
268 &self.map,
269 &settings,
270 )
271 })
272 .collect::<Vec<_>>();
273 if let Some(size) = settings.extrude_size {
274 self.current = Some(ProcessingChange::Extrude {
275 points,
276 triangles,
277 size,
278 progress_limit,
279 });
280 Ok(ProcessStatus::InProgress)
281 } else {
282 self.mesh = Some(DensityMesh { points, triangles });
283 Ok(ProcessStatus::MeshChanged)
284 }
285 }
286 ProcessingChange::Extrude {
287 mut points,
288 mut triangles,
289 size,
290 ..
291 } => {
292 let (p, t) = Self::extrude(&points, &triangles, size);
293 points.extend(p);
294 triangles.extend(t);
295 self.mesh = Some(DensityMesh { points, triangles });
296 Ok(ProcessStatus::MeshChanged)
297 }
298 }
299 } else if let Some((points, settings)) = self.queue.pop_front() {
300 let scale = self.map.scale();
301 let remaining = self
302 .map
303 .value_steepness_iter()
304 .filter_map(|(x, y, v, s)| {
305 if v > settings.visibility_threshold && s > settings.steepness_threshold {
306 let x = (x * scale) as Scalar;
307 let y = (y * scale) as Scalar;
308 let lpss = match settings.points_separation {
309 PointsSeparation::Constant(v) => v * v,
310 PointsSeparation::SteepnessMapping(f, t) => {
311 let v = Self::lerp(s, t, f);
312 v * v
313 }
314 };
315 Some((Coord::new(x, y), v, s, lpss))
316 } else {
317 None
318 }
319 })
320 .collect::<Vec<_>>();
321 let progress_limit = remaining.len();
322 let tries = settings.max_iterations;
323 self.current = Some(ProcessingChange::FindingPoints {
324 settings,
325 tries,
326 remaining,
327 points,
328 progress_current: 0,
329 progress_limit,
330 });
331 Ok(ProcessStatus::InProgress)
332 } else {
333 Ok(ProcessStatus::Idle)
334 }
335 }
336
337 pub fn process_wait(&mut self) -> Result<(), GenerateDensityMeshError> {
342 while self.process()? == ProcessStatus::InProgress {}
343 Ok(())
344 }
345
346 pub fn process_wait_timeout(
354 &mut self,
355 timeout: Duration,
356 ) -> Result<ProcessStatus, GenerateDensityMeshError> {
357 let timer = Instant::now();
358 loop {
359 let status = self.process()?;
360 if status != ProcessStatus::InProgress || timer.elapsed() > timeout {
361 return Ok(status);
362 }
363 }
364 }
365
366 pub fn process_wait_tracked<F>(&mut self, mut f: F) -> Result<(), GenerateDensityMeshError>
374 where
375 F: FnMut(usize, usize, Scalar),
376 {
377 let (c, l, p) = self.progress();
378 f(c, l, p);
379 while self.process()? == ProcessStatus::InProgress {
380 let (c, l, p) = self.progress();
381 f(c, l, p);
382 }
383 Ok(())
384 }
385
386 pub fn process_wait_timeout_tracked<F>(
395 &mut self,
396 mut f: F,
397 timeout: Duration,
398 ) -> Result<ProcessStatus, GenerateDensityMeshError>
399 where
400 F: FnMut(usize, usize, Scalar),
401 {
402 let timer = Instant::now();
403 let (c, l, p) = self.progress();
404 f(c, l, p);
405 loop {
406 let status = self.process()?;
407 let (c, l, p) = self.progress();
408 f(c, l, p);
409 if status != ProcessStatus::InProgress || timer.elapsed() > timeout {
410 return Ok(status);
411 }
412 }
413 }
414
415 fn extrude(
416 points: &[Coord],
417 triangles: &[Triangle],
418 size: Scalar,
419 ) -> (Vec<Coord>, Vec<Triangle>) {
420 let edges = triangles
421 .iter()
422 .enumerate()
423 .flat_map(|(i, t)| vec![(i, t.a, t.b), (i, t.b, t.c), (i, t.c, t.a)])
424 .collect::<Vec<_>>();
425 let outline = edges
426 .iter()
427 .filter(|e1| {
428 !edges
429 .iter()
430 .any(|e2| e1.0 != e2.0 && Self::are_edges_connected(e1.1, e1.2, e2.1, e2.2))
431 })
432 .collect::<Vec<_>>();
433 let offsets = outline
434 .iter()
435 .map(|(_, m, n)| {
436 let i = *m;
437 let p = outline.iter().find(|(_, _, p)| p == m).unwrap().1;
438 let p = points[p];
439 let m = points[*m];
440 let n = points[*n];
441 let pm = -(m - p).normalized().right();
442 let mn = -(n - m).normalized().right();
443 (i, m + (pm + mn).normalized() * size)
444 })
445 .collect::<Vec<_>>();
446 let triangles = outline
447 .into_iter()
448 .flat_map(|(_, a, b)| {
449 let ea = offsets.iter().position(|(ea, _)| ea == a).unwrap() + points.len();
450 let eb = offsets.iter().position(|(eb, _)| eb == b).unwrap() + points.len();
451 vec![[*b, *a, ea].into(), [ea, eb, *b].into()]
452 })
453 .collect::<Vec<_>>();
454 (
455 offsets.into_iter().map(|(_, p)| p).collect::<Vec<_>>(),
456 triangles,
457 )
458 }
459
460 fn are_edges_connected(a_from: usize, a_to: usize, b_from: usize, b_to: usize) -> bool {
461 (a_from == b_from && a_to == b_to) || (a_from == b_to && a_to == b_from)
462 }
463
464 fn is_triangle_visible(
465 a: Coord,
466 b: Coord,
467 c: Coord,
468 map: &DensityMap,
469 settings: &GenerateDensityMeshSettings,
470 ) -> bool {
471 let fx = (a.x as isize).min(b.x as isize).min(c.x as isize);
472 let fy = (a.y as isize).min(b.y as isize).min(c.y as isize);
473 let tx = (a.x as isize).max(b.x as isize).max(c.x as isize);
474 let ty = (a.y as isize).max(b.y as isize).max(c.y as isize);
475 let nab = (b - a).right();
476 let nbc = (c - b).right();
477 let nca = (a - c).right();
478 let mut count = 0;
479 let mut samples = 0;
480 for y in fy..=ty {
481 for x in fx..=tx {
482 let p = Coord::new(x as _, y as _);
483 if (p - a).dot(nab) >= 0.0 && (p - b).dot(nbc) >= 0.0 && (p - c).dot(nca) >= 0.0 {
484 samples += 1;
485 if Self::is_point_visible((x, y), map, settings) {
486 count += 1;
487 }
488 }
489 }
490 }
491 count as Scalar / samples as Scalar > 0.5
492 }
493
494 #[inline]
495 fn is_point_visible(
496 pos: (isize, isize),
497 map: &DensityMap,
498 settings: &GenerateDensityMeshSettings,
499 ) -> bool {
500 map.value_at_point(pos) > settings.visibility_threshold
501 }
502
503 #[inline]
504 fn lerp(value: Scalar, from: Scalar, to: Scalar) -> Scalar {
505 from + (to - from) * value.max(0.0).min(1.0)
506 }
507}