1use std::collections::BTreeMap;
2
3use i_overlay::core::fill_rule::FillRule as OverlayFill;
4use i_overlay::core::overlay_rule::OverlayRule;
5use i_overlay::float::simplify::SimplifyShape;
6use i_overlay::float::single::SingleFloatOverlay;
7use i_overlay::mesh::float::outline::offset::OutlineOffset;
8use i_overlay::mesh::float::style::{LineJoin, OutlineStyle};
9use lyon_tessellation::math::point;
10use lyon_tessellation::path::Path as LyonPath;
11use lyon_tessellation::{BuffersBuilder, FillOptions, FillTessellator, FillVertex, VertexBuffers};
12
13use crate::field::Surface;
14use crate::icon::join;
15use crate::math::normalize;
16use crate::mesh::Mesh;
17use crate::svg::{Drawing, Element, FillRule, Paint};
18
19pub type Region = Vec<Vec<Vec<[f64; 2]>>>;
20
21pub const DEPTH: f32 = 0.12;
22pub const BEVEL: f32 = 0.05;
23pub const SEGMENTS: u32 = 8;
24pub const LAYER: f32 = 0.25;
25pub const SMOOTH_ANGLE: f32 = 40.0;
26pub const TOLERANCE: f32 = 2e-4;
27pub const BEVEL_SHARE: f32 = 0.2;
28pub const CORE: f32 = 0.15;
29pub const THIN_STROKE: f32 = 0.1;
30pub const VOXEL_SPAN: f32 = 1400.0;
31pub const RELAX: u32 = 2;
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34pub enum Profile {
35 Round,
36 Chamfer,
37}
38
39#[derive(Clone, Debug, PartialEq)]
40pub struct Each {
41 pub all: f32,
42 pub by_element: BTreeMap<usize, f32>,
43}
44
45impl Each {
46 pub fn new(all: f32) -> Self {
47 Self {
48 all,
49 by_element: BTreeMap::new(),
50 }
51 }
52
53 pub fn at(&self, element: usize) -> f32 {
54 self.by_element.get(&element).copied().unwrap_or(self.all)
55 }
56}
57
58#[derive(Clone, Debug, PartialEq)]
59pub struct Form {
60 pub depth: f32,
61 pub bevel: f32,
62 pub segments: u32,
63 pub profile: Profile,
64 pub layer: f32,
65 pub inset: Each,
66 pub element_depth: Each,
67 pub element_z: Each,
68 pub element_material: BTreeMap<usize, String>,
69 pub merge: bool,
70 pub smooth_angle: f32,
71 pub tolerance: f32,
72 pub relax: u32,
73}
74
75impl Default for Form {
76 fn default() -> Self {
77 Self {
78 depth: DEPTH,
79 bevel: BEVEL,
80 segments: SEGMENTS,
81 profile: Profile::Round,
82 layer: LAYER,
83 inset: Each::new(0.0),
84 element_depth: Each::new(1.0),
85 element_z: Each::new(0.0),
86 element_material: BTreeMap::new(),
87 merge: true,
88 smooth_angle: SMOOTH_ANGLE,
89 tolerance: TOLERANCE,
90 relax: RELAX,
91 }
92 }
93}
94
95#[derive(Clone, Debug)]
96pub struct Solid {
97 pub paint: Paint,
98 pub material: Option<String>,
99 pub order: usize,
100 pub elements: Vec<usize>,
101 pub span: Option<[f32; 2]>,
102 pub mesh: Mesh,
103}
104
105#[derive(Clone, Debug)]
106pub struct Mark {
107 pub view: [f32; 4],
108 pub scale: f32,
109 pub ground: Option<Paint>,
110 pub solids: Vec<Solid>,
111}
112
113enum Geometry {
114 Region { region: Region, bevel: f64 },
115 Field(Mesh),
116}
117
118struct Piece {
119 paint: Paint,
120 material: Option<String>,
121 order: usize,
122 element: usize,
123 key: (usize, Option<String>, Option<usize>),
124 mul: f32,
125 base: f32,
126 geometry: Geometry,
127}
128
129struct World {
130 center: [f64; 2],
131 size: f64,
132 view: [f64; 4],
133}
134
135impl World {
136 fn point(&self, p: [f64; 2]) -> [f64; 2] {
137 [
138 (p[0] - self.center[0]) / self.size,
139 (self.center[1] - p[1]) / self.size,
140 ]
141 }
142
143 fn uv(&self, x: f32, y: f32) -> [f32; 2] {
144 let w = self.view[2] / self.size;
145 let h = self.view[3] / self.size;
146 [
147 ((x as f64 + w * 0.5) / w) as f32,
148 ((h * 0.5 - y as f64) / h) as f32,
149 ]
150 }
151}
152
153impl Mark {
154 pub fn build(svg: &str, form: &Form) -> Result<Mark, String> {
155 check(form)?;
156 let drawing = Drawing::parse(svg, form.tolerance as f64)?;
157 let view = drawing.view;
158 let world = World {
159 center: [view[0] + view[2] * 0.5, view[1] + view[3] * 0.5],
160 size: view[2].max(view[3]),
161 view,
162 };
163 let mut ground = None;
164 let mut painted: Vec<Paint> = Vec::new();
165 let mut pieces: Vec<Piece> = Vec::new();
166 let mut n = 0usize;
167 for element in &drawing.elements {
168 if element.ground {
169 ground = element.fill.clone();
170 continue;
171 }
172 let made = element_pieces(element, n, form, &world)?;
173 for (paint, geometry) in made {
174 let order = match painted.iter().position(|p| *p == paint) {
175 Some(order) => order,
176 None => {
177 painted.push(paint.clone());
178 painted.len() - 1
179 }
180 };
181 let material = form.element_material.get(&n).cloned();
182 let key = (
183 order,
184 material.clone(),
185 if form.merge { None } else { Some(n) },
186 );
187 let base =
188 order as f32 * form.layer * form.depth + form.element_z.at(n) * form.depth;
189 pieces.push(Piece {
190 paint,
191 material,
192 order,
193 element: n,
194 key,
195 mul: form.element_depth.at(n).max(1e-3),
196 base,
197 geometry,
198 });
199 }
200 n += 1;
201 }
202 let mut keys: Vec<(usize, Option<String>, Option<usize>)> = Vec::new();
203 for piece in &pieces {
204 if !keys.contains(&piece.key) {
205 keys.push(piece.key.clone());
206 }
207 }
208 let mut solids = Vec::new();
209 for key in keys {
210 let members = pieces.iter().filter(|p| p.key == key).collect::<Vec<_>>();
211 let first = members[0];
212 let mut meshes = Vec::new();
213 let mut merged: Vec<(f32, f32, Vec<&Region>, f64)> = Vec::new();
214 for piece in &members {
215 match &piece.geometry {
216 Geometry::Region { region, bevel } => {
217 match merged.iter_mut().find(|(mul, base, _, own)| {
218 mul.to_bits() == piece.mul.to_bits()
219 && base.to_bits() == piece.base.to_bits()
220 && (*own - *bevel).abs() <= 1e-9
221 }) {
222 Some((_, _, regions, _)) => regions.push(region),
223 None => merged.push((piece.mul, piece.base, vec![region], *bevel)),
224 }
225 }
226 Geometry::Field(mesh) => meshes.push(lift(mesh, piece.mul, piece.base)),
227 }
228 }
229 for (mul, base, regions, bevel) in merged {
230 let region = if regions.len() == 1 {
231 regions[0].clone()
232 } else {
233 let contours = regions
234 .iter()
235 .flat_map(|r| r.iter().flatten().cloned())
236 .collect::<Vec<_>>();
237 contours.simplify_shape_as::<i64>(OverlayFill::NonZero)
238 };
239 meshes.push(extrude(®ion, form, bevel, mul, base, &world));
240 }
241 let mesh = if meshes.len() == 1 {
242 meshes.pop().unwrap()
243 } else {
244 join(&meshes)
245 };
246 if mesh.indices.is_empty() {
247 continue;
248 }
249 let span = match &first.paint {
250 Paint::Gradient(g) => Some([
251 world.point([g.x1 as f64, 0.0])[0] as f32,
252 world.point([g.x2 as f64, 0.0])[0] as f32,
253 ]),
254 Paint::Color(_) => None,
255 };
256 let mut elements = members.iter().map(|p| p.element).collect::<Vec<_>>();
257 elements.dedup();
258 solids.push(Solid {
259 paint: first.paint.clone(),
260 material: first.material.clone(),
261 order: first.order,
262 elements,
263 span,
264 mesh,
265 });
266 }
267 Ok(Mark {
268 view: view.map(|v| v as f32),
269 scale: (1.0 / world.size) as f32,
270 ground,
271 solids,
272 })
273 }
274}
275
276fn check(form: &Form) -> Result<(), String> {
277 let finite = |name: &str, v: f32| {
278 if v.is_finite() && v >= 0.0 {
279 Ok(())
280 } else {
281 Err(format!("form: {name} must be 0 or more, got {v}"))
282 }
283 };
284 finite("depth", form.depth)?;
285 finite("bevel", form.bevel)?;
286 finite("layer", form.layer)?;
287 finite("smooth_angle", form.smooth_angle)?;
288 if !(form.tolerance > 0.0 && form.tolerance.is_finite()) {
289 return Err(format!(
290 "form: tolerance must be above 0, got {}",
291 form.tolerance
292 ));
293 }
294 if form.depth <= 0.0 && form.bevel <= 0.0 {
295 return Err("form: depth and bevel are both 0, so there is no solid".to_string());
296 }
297 Ok(())
298}
299
300fn element_pieces(
301 element: &Element,
302 n: usize,
303 form: &Form,
304 world: &World,
305) -> Result<Vec<(Paint, Geometry)>, String> {
306 let inset = form.inset.at(n) as f64 / world.size;
307 let width = element.stroke_width / world.size;
308 let holes = element
309 .holes
310 .iter()
311 .map(|h| h.iter().map(|p| world.point(*p)).collect::<Vec<_>>())
312 .collect::<Vec<_>>();
313 let rule = match element.fill_rule {
314 FillRule::NonZero => OverlayFill::NonZero,
315 FillRule::EvenOdd => OverlayFill::EvenOdd,
316 };
317 let closed = element
318 .paths
319 .iter()
320 .filter(|p| p.points.len() > 2)
321 .map(|p| p.points.iter().map(|q| world.point(*q)).collect::<Vec<_>>())
322 .collect::<Vec<_>>();
323 let mut out = Vec::new();
324 if element.joined {
325 let fill = element.fill.clone().unwrap();
326 let region = cut(closed.simplify_shape_as::<i64>(rule), &holes);
327 let grow = (width * 0.5 - inset).max(width * 0.5 * 0.2);
328 if !region.is_empty() {
329 out.push((fill, Geometry::Field(pillow(®ion, grow, &holes, form))));
330 }
331 return Ok(out);
332 }
333 if let Some(fill) = &element.fill {
334 let size = extent(&closed);
335 let mut region = closed.simplify_shape_as::<i64>(rule);
336 if inset > 0.0 && !region.is_empty() {
337 let style = OutlineStyle::new(-inset).line_join(LineJoin::Miter(0.05));
338 region = region.outline(&style);
339 }
340 let region = cut(region, &holes);
341 if !region.is_empty() {
342 let bevel = (form.bevel as f64).min(size * BEVEL_SHARE as f64);
343 out.push((fill.clone(), Geometry::Region { region, bevel }));
344 }
345 }
346 if let Some(stroke) = &element.stroke
347 && width > 0.0
348 {
349 let r = (width * 0.5 - inset).max(width * THIN_STROKE as f64);
350 let segments = element
351 .paths
352 .iter()
353 .flat_map(|path| {
354 let points = path
355 .points
356 .iter()
357 .map(|q| world.point(*q))
358 .collect::<Vec<_>>();
359 let mut pairs = points.windows(2).map(|w| [w[0], w[1]]).collect::<Vec<_>>();
360 if path.closed && points.len() > 2 {
361 pairs.push([*points.last().unwrap(), points[0]]);
362 }
363 pairs
364 })
365 .collect::<Vec<_>>();
366 if !segments.is_empty() {
367 out.push((
368 stroke.clone(),
369 Geometry::Field(tube(&segments, r, &holes, form)),
370 ));
371 }
372 }
373 Ok(out)
374}
375
376fn cut(region: Region, holes: &[Vec<[f64; 2]>]) -> Region {
377 if holes.is_empty() || region.is_empty() {
378 return region;
379 }
380 region.overlay_as::<i64>(
381 &holes.to_vec(),
382 OverlayRule::Difference,
383 OverlayFill::NonZero,
384 )
385}
386
387fn extent(contours: &[Vec<[f64; 2]>]) -> f64 {
388 let mut lo = [f64::INFINITY; 2];
389 let mut hi = [f64::NEG_INFINITY; 2];
390 for p in contours.iter().flatten() {
391 for i in 0..2 {
392 lo[i] = lo[i].min(p[i]);
393 hi[i] = hi[i].max(p[i]);
394 }
395 }
396 if !lo[0].is_finite() {
397 return 0.0;
398 }
399 (hi[0] - lo[0]).max(hi[1] - lo[1])
400}
401
402fn lift(mesh: &Mesh, mul: f32, base: f32) -> Mesh {
403 if mul == 1.0 && base == 0.0 {
404 return mesh.clone();
405 }
406 let positions = mesh
407 .positions
408 .iter()
409 .map(|p| [p[0], p[1], p[2] * mul + base])
410 .collect();
411 let normals = mesh
412 .normals
413 .iter()
414 .map(|n| normalize([n[0], n[1], n[2] / mul]))
415 .collect();
416 Mesh::new(
417 positions,
418 normals,
419 Vec::new(),
420 mesh.uvs.clone(),
421 mesh.indices.clone(),
422 )
423}
424
425#[derive(Clone, Copy, Debug)]
426struct Step {
427 o: f32,
428 z: f32,
429 below: [f32; 2],
430 above: [f32; 2],
431 smooth: Option<[f32; 2]>,
432}
433
434fn profile(depth: f32, bevel: f32, form: &Form) -> Vec<Step> {
435 let mut top: Vec<(f32, f32, Option<[f32; 2]>)> = Vec::new();
436 if bevel > 1e-9 {
437 let steps = match form.profile {
438 Profile::Round => form.segments + 1,
439 Profile::Chamfer => 1,
440 };
441 for k in 0..=steps {
442 let g = std::f32::consts::FRAC_PI_2 * k as f32 / steps as f32;
443 let (o, z) = if k == 0 {
444 (bevel, depth)
445 } else if k == steps {
446 (0.0, depth + bevel)
447 } else {
448 (bevel * g.cos(), depth + bevel * g.sin())
449 };
450 let arc = (form.profile == Profile::Round).then_some(if k == steps {
451 [0.0, 1.0]
452 } else {
453 [g.cos(), g.sin()]
454 });
455 top.push((o, z, arc));
456 }
457 } else {
458 top.push((0.0, depth, None));
459 }
460 let mut points: Vec<(f32, f32, Option<[f32; 2]>)> = top
461 .iter()
462 .rev()
463 .map(|(o, z, arc)| (*o, -*z, arc.map(|a| [a[0], -a[1]])))
464 .collect();
465 let skip = usize::from(depth <= 1e-9);
466 points.extend(top.iter().skip(skip).copied());
467 let facets = points
468 .windows(2)
469 .map(|w| {
470 let (to, tz) = (w[1].0 - w[0].0, w[1].1 - w[0].1);
471 let len = (to * to + tz * tz).sqrt().max(1e-12);
472 [tz / len, -to / len]
473 })
474 .collect::<Vec<_>>();
475 let limit = form.smooth_angle.to_radians() + 1e-4;
476 points
477 .iter()
478 .enumerate()
479 .map(|(j, &(o, z, arc))| {
480 let below = if j == 0 { [0.0, -1.0] } else { facets[j - 1] };
481 let above = if j == points.len() - 1 {
482 [0.0, 1.0]
483 } else {
484 facets[j]
485 };
486 let angle = (below[0] * above[0] + below[1] * above[1])
487 .clamp(-1.0, 1.0)
488 .acos();
489 let smooth = (angle <= limit).then(|| {
490 arc.unwrap_or_else(|| {
491 let s = [below[0] + above[0], below[1] + above[1]];
492 let len = (s[0] * s[0] + s[1] * s[1]).sqrt().max(1e-12);
493 [s[0] / len, s[1] / len]
494 })
495 });
496 Step {
497 o,
498 z,
499 below,
500 above,
501 smooth,
502 }
503 })
504 .collect()
505}
506
507struct Column {
508 index: usize,
509 point: [f64; 2],
510 miter: [f64; 2],
511 before: [f64; 2],
512 after: [f64; 2],
513 smooth: bool,
514}
515
516fn columns(contour: &[[f64; 2]], limit: f64) -> Vec<Column> {
517 let mut points = contour.to_vec();
518 points.dedup_by(|a, b| (a[0] - b[0]).abs() <= 1e-12 && (a[1] - b[1]).abs() <= 1e-12);
519 while points.len() > 1 {
520 let (a, b) = (points[0], *points.last().unwrap());
521 if (a[0] - b[0]).abs() <= 1e-12 && (a[1] - b[1]).abs() <= 1e-12 {
522 points.pop();
523 } else {
524 break;
525 }
526 }
527 let count = points.len();
528 if count < 3 {
529 return Vec::new();
530 }
531 let normal = |a: [f64; 2], b: [f64; 2]| {
532 let (dx, dy) = (b[0] - a[0], b[1] - a[1]);
533 let len = (dx * dx + dy * dy).sqrt().max(1e-300);
534 [dy / len, -dx / len]
535 };
536 (0..count)
537 .map(|i| {
538 let prev = points[(i + count - 1) % count];
539 let here = points[i];
540 let next = points[(i + 1) % count];
541 let before = normal(prev, here);
542 let after = normal(here, next);
543 let cos = before[0] * after[0] + before[1] * after[1];
544 let sum = [before[0] + after[0], before[1] + after[1]];
545 let sum_len = (sum[0] * sum[0] + sum[1] * sum[1]).sqrt();
546 let miter = if 1.0 + cos > 0.125 {
547 [sum[0] / (1.0 + cos), sum[1] / (1.0 + cos)]
548 } else if sum_len > 1e-9 {
549 [sum[0] / sum_len * 4.0, sum[1] / sum_len * 4.0]
550 } else {
551 [before[0] * 4.0, before[1] * 4.0]
552 };
553 Column {
554 index: i,
555 point: here,
556 miter,
557 before,
558 after,
559 smooth: cos.clamp(-1.0, 1.0).acos() <= limit,
560 }
561 })
562 .collect()
563}
564
565struct Builder<'a> {
566 world: &'a World,
567 steps: &'a [Step],
568 mul: f32,
569 base: f32,
570 positions: Vec<[f32; 3]>,
571 normals: Vec<[f32; 3]>,
572 uvs: Vec<[f32; 2]>,
573 indices: Vec<u32>,
574 made: BTreeMap<(usize, usize, u8, usize, u8), u32>,
575}
576
577impl Builder<'_> {
578 fn vertex(&mut self, contour: usize, column: &Column, side: u8, j: usize, half: u8) -> u32 {
579 let step = self.steps[j];
580 let (normal2, half) = match step.smooth {
581 Some(n) => (n, 0),
582 None => (if half == 0 { step.below } else { step.above }, half),
583 };
584 let flat = normal2[0].abs() <= 1e-6;
585 let side = if column.smooth || flat { 0 } else { side };
586 let key = (contour, column.index, side, j, half);
587 if let Some(found) = self.made.get(&key) {
588 return *found;
589 }
590 let horizontal = if column.smooth {
591 let s = [
592 column.before[0] + column.after[0],
593 column.before[1] + column.after[1],
594 ];
595 let len = (s[0] * s[0] + s[1] * s[1]).sqrt().max(1e-300);
596 [s[0] / len, s[1] / len]
597 } else if side == 0 {
598 column.before
599 } else {
600 column.after
601 };
602 let x = (column.point[0] + column.miter[0] * step.o as f64) as f32;
603 let y = (column.point[1] + column.miter[1] * step.o as f64) as f32;
604 let z = step.z * self.mul + self.base;
605 let normal = normalize([
606 horizontal[0] as f32 * normal2[0],
607 horizontal[1] as f32 * normal2[0],
608 normal2[1] / self.mul,
609 ]);
610 let index = self.positions.len() as u32;
611 self.positions.push([x, y, z]);
612 self.normals.push(normal);
613 self.uvs.push(self.world.uv(x, y));
614 self.made.insert(key, index);
615 index
616 }
617
618 fn tri(&mut self, a: u32, b: u32, c: u32) {
619 if a == b || b == c || a == c {
620 return;
621 }
622 self.indices.extend([a, b, c]);
623 }
624}
625
626fn extrude(region: &Region, form: &Form, bevel: f64, mul: f32, base: f32, world: &World) -> Mesh {
627 let steps = profile(form.depth, bevel as f32, form);
628 let limit = (form.smooth_angle as f64).to_radians() + 1e-4;
629 let mut builder = Builder {
630 world,
631 steps: &steps,
632 mul,
633 base,
634 positions: Vec::new(),
635 normals: Vec::new(),
636 uvs: Vec::new(),
637 indices: Vec::new(),
638 made: BTreeMap::new(),
639 };
640 let last = steps.len() - 1;
641 let mut contour_id = 0usize;
642 for shape in region {
643 let mut caps: BTreeMap<(u32, u32), (usize, usize)> = BTreeMap::new();
644 let mut rings: Vec<(usize, Vec<Column>)> = Vec::new();
645 for contour in shape {
646 let cols = columns(contour, limit);
647 if cols.is_empty() {
648 continue;
649 }
650 let id = contour_id;
651 contour_id += 1;
652 let count = cols.len();
653 for i in 0..count {
654 let next = (i + 1) % count;
655 for j in 0..last {
656 let a = builder.vertex(id, &cols[i], 1, j, 1);
657 let b = builder.vertex(id, &cols[next], 0, j, 1);
658 let c = builder.vertex(id, &cols[next], 0, j + 1, 0);
659 let d = builder.vertex(id, &cols[i], 1, j + 1, 0);
660 builder.tri(a, b, c);
661 builder.tri(a, c, d);
662 }
663 }
664 for (i, col) in cols.iter().enumerate() {
665 let key = (
666 (col.point[0] as f32).to_bits(),
667 (col.point[1] as f32).to_bits(),
668 );
669 caps.entry(key).or_insert((id, i));
670 }
671 rings.push((id, cols));
672 }
673 if rings.is_empty() {
674 continue;
675 }
676 let mut path = LyonPath::builder();
677 for (_, cols) in &rings {
678 path.begin(point(cols[0].point[0] as f32, cols[0].point[1] as f32));
679 for col in &cols[1..] {
680 path.line_to(point(col.point[0] as f32, col.point[1] as f32));
681 }
682 path.end(true);
683 }
684 let path = path.build();
685 let mut buffers: VertexBuffers<[f32; 2], u32> = VertexBuffers::new();
686 let options = FillOptions::default().with_fill_rule(lyon_tessellation::FillRule::EvenOdd);
687 let result = FillTessellator::new().tessellate_path(
688 &path,
689 &options,
690 &mut BuffersBuilder::new(&mut buffers, |v: FillVertex| v.position().to_array()),
691 );
692 if result.is_err() {
693 continue;
694 }
695 let lookup = |p: [f32; 2]| caps.get(&(p[0].to_bits(), p[1].to_bits())).copied();
696 for (j, half, up) in [(last, 1u8, true), (0usize, 0u8, false)] {
697 let mut map: Vec<u32> = Vec::with_capacity(buffers.vertices.len());
698 for v in &buffers.vertices {
699 let index = match lookup(*v) {
700 Some((id, i)) => {
701 let cols = &rings.iter().find(|(c, _)| *c == id).unwrap().1;
702 builder.vertex(id, &cols[i], 0, j, half)
703 }
704 None => {
705 let z = steps[j].z * mul + base;
706 let index = builder.positions.len() as u32;
707 builder.positions.push([v[0], v[1], z]);
708 builder
709 .normals
710 .push([0.0, 0.0, if up { 1.0 } else { -1.0 }]);
711 builder.uvs.push(world.uv(v[0], v[1]));
712 index
713 }
714 };
715 map.push(index);
716 }
717 for tri in buffers.indices.chunks_exact(3) {
718 let [a, b, c] = [tri[0], tri[1], tri[2]].map(|i| buffers.vertices[i as usize]);
719 let area = (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
720 let (i0, i1, i2) = (
721 map[tri[0] as usize],
722 map[tri[1] as usize],
723 map[tri[2] as usize],
724 );
725 if (area > 0.0) == up {
726 builder.tri(i0, i1, i2);
727 } else {
728 builder.tri(i0, i2, i1);
729 }
730 }
731 }
732 }
733 Mesh::new(
734 builder.positions,
735 builder.normals,
736 Vec::new(),
737 builder.uvs,
738 builder.indices,
739 )
740}
741
742struct Raster {
743 origin: [f64; 2],
744 cell: f64,
745 width: usize,
746 height: usize,
747 values: Vec<f32>,
748}
749
750const TILE: usize = 16;
751
752impl Raster {
753 fn new(lo: [f64; 2], hi: [f64; 2], cell: f64) -> Raster {
754 let width = (((hi[0] - lo[0]) / cell).ceil() as usize + 2).max(2);
755 let height = (((hi[1] - lo[1]) / cell).ceil() as usize + 2).max(2);
756 Raster {
757 origin: lo,
758 cell,
759 width,
760 height,
761 values: vec![0.0; width * height],
762 }
763 }
764
765 fn at(&self, x: usize, y: usize) -> [f64; 2] {
766 [
767 self.origin[0] + x as f64 * self.cell,
768 self.origin[1] + y as f64 * self.cell,
769 ]
770 }
771
772 fn distances(&mut self, segments: &[[[f64; 2]; 2]], band: f64) {
773 let reach = self.cell * TILE as f64 * std::f64::consts::SQRT_2 * 0.5 + band;
774 for ty in (0..self.height).step_by(TILE) {
775 for tx in (0..self.width).step_by(TILE) {
776 let center = self.at(tx + TILE / 2, ty + TILE / 2);
777 let near = segments
778 .iter()
779 .filter(|s| box_distance(s, center) <= reach)
780 .collect::<Vec<_>>();
781 for y in ty..(ty + TILE).min(self.height) {
782 for x in tx..(tx + TILE).min(self.width) {
783 let p = self.at(x, y);
784 let d = near
785 .iter()
786 .map(|s| segment_distance(p, s[0], s[1]))
787 .fold(band, f64::min);
788 self.values[y * self.width + x] = d as f32;
789 }
790 }
791 }
792 }
793 }
794
795 fn sign_inside(&mut self, segments: &[[[f64; 2]; 2]]) {
796 let mut crossings = Vec::new();
797 for y in 0..self.height {
798 let row = self.origin[1] + y as f64 * self.cell;
799 crossings.clear();
800 for [a, b] in segments {
801 if (a[1] <= row) != (b[1] <= row) {
802 crossings.push(a[0] + (row - a[1]) * (b[0] - a[0]) / (b[1] - a[1]));
803 }
804 }
805 crossings.sort_by(f64::total_cmp);
806 for pair in crossings.chunks_exact(2) {
807 let from = ((pair[0] - self.origin[0]) / self.cell).ceil().max(0.0) as usize;
808 let to = ((pair[1] - self.origin[0]) / self.cell).floor();
809 if to < 0.0 {
810 continue;
811 }
812 let to = (to as usize).min(self.width - 1);
813 for x in from..=to {
814 let v = &mut self.values[y * self.width + x];
815 *v = -*v;
816 }
817 }
818 }
819 }
820
821 fn sample(&self, x: f32, y: f32) -> f32 {
822 let fx = ((x as f64 - self.origin[0]) / self.cell).clamp(0.0, (self.width - 1) as f64);
823 let fy = ((y as f64 - self.origin[1]) / self.cell).clamp(0.0, (self.height - 1) as f64);
824 let x0 = (fx.floor() as usize).min(self.width - 2);
825 let y0 = (fy.floor() as usize).min(self.height - 2);
826 let (tx, ty) = ((fx - x0 as f64) as f32, (fy - y0 as f64) as f32);
827 let v = |x: usize, y: usize| self.values[y * self.width + x];
828 let top = v(x0, y0) * (1.0 - tx) + v(x0 + 1, y0) * tx;
829 let bottom = v(x0, y0 + 1) * (1.0 - tx) + v(x0 + 1, y0 + 1) * tx;
830 top * (1.0 - ty) + bottom * ty
831 }
832}
833
834fn box_distance(s: &[[f64; 2]; 2], p: [f64; 2]) -> f64 {
835 let lo = [s[0][0].min(s[1][0]), s[0][1].min(s[1][1])];
836 let hi = [s[0][0].max(s[1][0]), s[0][1].max(s[1][1])];
837 let dx = (lo[0] - p[0]).max(p[0] - hi[0]).max(0.0);
838 let dy = (lo[1] - p[1]).max(p[1] - hi[1]).max(0.0);
839 (dx * dx + dy * dy).sqrt()
840}
841
842fn segment_distance(p: [f64; 2], a: [f64; 2], b: [f64; 2]) -> f64 {
843 let ab = [b[0] - a[0], b[1] - a[1]];
844 let ap = [p[0] - a[0], p[1] - a[1]];
845 let l2 = ab[0] * ab[0] + ab[1] * ab[1];
846 let t = if l2 <= 1e-300 {
847 0.0
848 } else {
849 ((ap[0] * ab[0] + ap[1] * ab[1]) / l2).clamp(0.0, 1.0)
850 };
851 let d = [ap[0] - ab[0] * t, ap[1] - ab[1] * t];
852 (d[0] * d[0] + d[1] * d[1]).sqrt()
853}
854
855fn edges(region: &[Vec<[f64; 2]>]) -> Vec<[[f64; 2]; 2]> {
856 region
857 .iter()
858 .filter(|c| c.len() > 2)
859 .flat_map(|c| (0..c.len()).map(move |i| [c[i], c[(i + 1) % c.len()]]))
860 .collect()
861}
862
863fn bounds2(segments: &[[[f64; 2]; 2]]) -> ([f64; 2], [f64; 2]) {
864 let mut lo = [f64::INFINITY; 2];
865 let mut hi = [f64::NEG_INFINITY; 2];
866 for p in segments.iter().flatten() {
867 for i in 0..2 {
868 lo[i] = lo[i].min(p[i]);
869 hi[i] = hi[i].max(p[i]);
870 }
871 }
872 (lo, hi)
873}
874
875fn signed(segments: &[[[f64; 2]; 2]], lo: [f64; 2], hi: [f64; 2], cell: f64, band: f64) -> Raster {
876 let mut raster = Raster::new(lo, hi, cell);
877 raster.distances(segments, band);
878 raster.sign_inside(segments);
879 raster
880}
881
882fn hole_raster(
883 holes: &[Vec<[f64; 2]>],
884 lo: [f64; 2],
885 hi: [f64; 2],
886 cell: f64,
887 band: f64,
888) -> Option<Raster> {
889 if holes.is_empty() {
890 return None;
891 }
892 let region = holes.simplify_shape_as::<i64>(OverlayFill::NonZero);
893 let contours = region.into_iter().flatten().collect::<Vec<_>>();
894 Some(signed(&edges(&contours), lo, hi, cell, band))
895}
896
897fn tube(segments: &[[[f64; 2]; 2]], r: f64, holes: &[Vec<[f64; 2]>], form: &Form) -> Mesh {
898 let voxel = (r / 10.0).max(1.0 / VOXEL_SPAN as f64);
899 let (lo, hi) = bounds2(segments);
900 let pad = r + 4.0 * voxel;
901 let lo = [lo[0] - pad, lo[1] - pad];
902 let hi = [hi[0] + pad, hi[1] + pad];
903 let cell = voxel * 0.5;
904 let band = r + 6.0 * voxel;
905 let mut raster = Raster::new(lo, hi, cell);
906 raster.distances(segments, band);
907 let cut = hole_raster(holes, lo, hi, cell, band);
908 let r = r as f32;
909 let field = |p: [f32; 3]| {
910 let u = raster.sample(p[0], p[1]);
911 let d = (u * u + p[2] * p[2]).sqrt() - r;
912 match &cut {
913 Some(h) => d.max(-h.sample(p[0], p[1])),
914 None => d,
915 }
916 };
917 let low = [
918 (lo[0] + 3.0 * voxel) as f32,
919 (lo[1] + 3.0 * voxel) as f32,
920 -r,
921 ];
922 let high = [
923 (hi[0] - 3.0 * voxel) as f32,
924 (hi[1] - 3.0 * voxel) as f32,
925 r,
926 ];
927 Surface::new(voxel as f32, form.relax).mesh(&field, low, high)
928}
929
930fn pillow(region: &Region, grow: f64, holes: &[Vec<[f64; 2]>], form: &Form) -> Mesh {
931 let voxel = (grow / 10.0).max(1.0 / VOXEL_SPAN as f64);
932 let contours = region.iter().flatten().cloned().collect::<Vec<_>>();
933 let segments = edges(&contours);
934 let (lo, hi) = bounds2(&segments);
935 let pad = grow + 4.0 * voxel;
936 let lo = [lo[0] - pad, lo[1] - pad];
937 let hi = [hi[0] + pad, hi[1] + pad];
938 let cell = voxel * 0.5;
939 let band = grow + 6.0 * voxel;
940 let raster = signed(&segments, lo, hi, cell, band);
941 let cut = hole_raster(holes, lo, hi, cell, band);
942 let core = (grow * CORE as f64) as f32;
943 let reach = grow as f32 - core;
944 let field = |p: [f32; 3]| {
945 let w = [raster.sample(p[0], p[1]), p[2].abs() - core];
946 let d = w[0].max(w[1]).min(0.0) + (w[0].max(0.0).powi(2) + w[1].max(0.0).powi(2)).sqrt()
947 - reach;
948 match &cut {
949 Some(h) => d.max(-h.sample(p[0], p[1])),
950 None => d,
951 }
952 };
953 let g = grow as f32;
954 let low = [
955 (lo[0] + 3.0 * voxel) as f32,
956 (lo[1] + 3.0 * voxel) as f32,
957 -g,
958 ];
959 let high = [
960 (hi[0] - 3.0 * voxel) as f32,
961 (hi[1] - 3.0 * voxel) as f32,
962 g,
963 ];
964 Surface::new(voxel as f32, form.relax).mesh(&field, low, high)
965}