1use crate::custom_rand::{noise, rand};
2use std::{
3 collections::HashMap,
4 f64::consts::{E, PI},
5 iter::successors,
6 rc::Rc,
7};
8
9pub type Point = (f64, f64);
10pub type Polyline = Vec<Point>;
11
12pub trait PolylineOps {
13 fn rev(&self) -> Polyline;
14 fn concat(&self, other: &Polyline) -> Polyline;
15}
16
17impl PolylineOps for Polyline {
18 fn rev(&self) -> Polyline {
19 self.iter().rev().map(|p| *p).collect()
20 }
21
22 fn concat(&self, other: &Polyline) -> Polyline {
23 let mut out = self.clone();
24 out.extend(other.iter());
25 out
26 }
27}
28
29pub fn flat(polylines: &Vec<Polyline>) -> Polyline {
30 polylines
31 .iter()
32 .flat_map(|p| p.iter())
33 .map(|p| *p)
34 .collect()
35}
36
37pub fn dist((x0, y0): Point, (x1, y1): Point) -> f64 {
38 ((x1 - x0).powi(2) + (y1 - y0).powi(2)).sqrt()
39}
40pub fn lerp(a: f64, b: f64, t: f64) -> f64 {
41 a * (1. - t) + b * t
42}
43pub fn lerp2d((x0, y0): Point, (x1, y1): Point, t: f64) -> Point {
44 (x0 * (1. - t) + x1 * t, y0 * (1. - t) + y1 * t)
45}
46
47#[derive(Debug, Clone)]
48pub struct BoundingBox {
49 pub x: f64,
50 pub y: f64,
51 pub w: f64,
52 pub h: f64,
53}
54
55pub fn get_boundingbox(points: &Polyline) -> BoundingBox {
56 let mut xmin = f64::INFINITY;
57 let mut ymin = f64::INFINITY;
58 let mut xmax = -f64::INFINITY;
59 let mut ymax = -f64::INFINITY;
60
61 for &(x, y) in points {
62 xmin = xmin.min(x);
63 ymin = ymin.min(y);
64 xmax = xmax.max(x);
65 ymax = ymax.max(y);
66 }
67
68 BoundingBox {
69 x: xmin,
70 y: ymin,
71 w: xmax - xmin,
72 h: ymax - ymin,
73 }
74}
75
76#[derive(Debug, Clone)]
77pub struct Intersection {
78 t: f64,
79 s: f64,
80 side: i64,
81 other: Option<usize>,
82 xy: Option<Point>,
83 jump: Option<bool>,
84}
85
86pub fn sort_intersections(intersections: &mut Vec<Intersection>) {
87 intersections.sort_by(|a, b| a.t.total_cmp(&b.t));
88}
89
90pub fn pt_in_pl((x, y): Point, (x0, y0): Point, (x1, y1): Point) -> f64 {
91 let dx = x1 - x0;
92 let dy = y1 - y0;
93 (x - x0) * dy - (y - y0) * dx
94}
95
96pub fn get_side((x, y): Point, (x0, y0): Point, (x1, y1): Point) -> i64 {
97 if pt_in_pl((x, y), (x0, y0), (x1, y1)) < 0. {
98 1
99 } else {
100 -1
101 }
102}
103
104pub fn seg_isect(
105 (p0x, p0y): Point,
106 (p1x, p1y): Point,
107 (q0x, q0y): Point,
108 (q1x, q1y): Point,
109 is_ray_opt: Option<bool>,
110) -> Option<Intersection> {
111 let is_ray = is_ray_opt.unwrap_or(false);
112 let d0x = p1x - p0x;
113 let d0y = p1y - p0y;
114 let d1x = q1x - q0x;
115 let d1y = q1y - q0y;
116 let vc = d0x * d1y - d0y * d1x;
117 if vc == 0. {
118 return None;
119 }
120 let vcn = vc * vc;
121 let q0x_p0x = q0x - p0x;
122 let q0y_p0y = q0y - p0y;
123 let vc_vcn = vc / vcn;
124 let t = (q0x_p0x * d1y - q0y_p0y * d1x) * vc_vcn;
125 let s = (q0x_p0x * d0y - q0y_p0y * d0x) * vc_vcn;
126 if 0. <= t && (is_ray || t < 1.) && 0. <= s && s < 1. {
127 return Some(Intersection {
128 t,
129 s,
130 side: get_side((p0x, p0y), (p1x, p1y), (q0x, q0y)),
131 other: None,
132 xy: Some((p1x * t + p0x * (1. - t), p1y * t + p0y * (1. - t))),
133 jump: None,
134 });
135 }
136 return None;
137}
138
139pub fn poly_bridge(poly0: &Polyline, poly1: &Polyline) -> Polyline {
140 let mut dmin = f64::INFINITY;
141 let mut imin = (0, 0);
142 for i in 0..poly0.len() {
143 for j in 0..poly1.len() {
144 let (x0, y0) = poly0[i];
145 let (x1, y1) = poly1[j];
146 let dx = x0 - x1;
147 let dy = y0 - y1;
148 let d2 = dx * dx + dy * dy;
149 if d2 < dmin {
150 dmin = d2;
151 imin = (i, j);
152 }
153 }
154 }
155 poly0[0..imin.0]
156 .iter()
157 .chain(poly1[imin.1..poly1.len()].iter())
158 .chain(poly1[0..imin.1].iter())
159 .chain(poly0[imin.0..poly0.len()].iter())
160 .map(|p| *p)
161 .collect()
162}
163#[derive(Debug, Clone)]
164pub struct Vertex {
165 xy: Point,
166 isects: Vec<Intersection>,
167 isects_map: HashMap<(usize, usize), Intersection>,
168}
169
170pub fn build_vertices(
171 poly: &Polyline,
172 other: &Polyline,
173 out: &mut Vec<Vertex>,
174 oout: &mut Vec<Vertex>,
175 idx: usize,
176 self_isect: bool,
177 has_isect: &mut bool,
178) {
179 let n = poly.len();
180 let m = other.len();
181 if self_isect {
182 for i in 0..n {
183 let id = (idx, i);
184 let i1 = (i + 1 + n) % n;
185 let a = poly[i];
186 let b = poly[i1];
187 for j in 0..n {
188 let jd = (idx, j);
189 let j1 = (j + 1 + n) % n;
190 if i == j || i == j1 || i1 == j || i1 == j1 {
191 continue;
192 }
193 let c = poly[j];
194 let d = poly[j1];
195 let xx_opt = if let Some(ox) = out[j].isects_map.get(&id) {
196 Some(Intersection {
197 t: ox.s,
198 s: ox.t,
199 xy: ox.xy,
200 other: None,
201 side: get_side(a, b, c),
202 jump: None,
203 })
204 } else {
205 seg_isect(a, b, c, d, None)
206 };
207 if let Some(mut xx) = xx_opt {
208 xx.other = Some(j);
209 xx.jump = Some(false);
210 let p = out.get_mut(i).unwrap();
211 p.isects.push(xx.clone());
212 p.isects_map.insert(jd, xx);
213 }
214 }
215 }
216 }
217
218 for i in 0..n {
219 let id = (idx, i);
220 let p = out.get_mut(i).unwrap();
221 let i1 = (i + 1 + n) % n;
222 let a = poly[i];
223 let b = poly[i1];
224 for j in 0..m {
225 let jd = (1 - idx, j);
226 let j1 = (j + 1 + m) % m;
227 let c = other[j];
228 let d = other[j1];
229 let xx_opt = if let Some(ox) = oout[j].isects_map.get(&id) {
230 Some(Intersection {
231 t: ox.s,
232 s: ox.t,
233 xy: ox.xy,
234 other: None,
235 side: get_side(a, b, c),
236 jump: None,
237 })
238 } else {
239 seg_isect(a, b, c, d, None)
240 };
241 if let Some(mut xx) = xx_opt {
242 *has_isect = true;
243 xx.other = Some(j);
244 xx.jump = Some(true);
245 p.isects.push(xx.clone());
246 p.isects_map.insert(jd, xx);
247 }
248 }
249 sort_intersections(&mut p.isects);
250 }
251}
252
253pub fn poly_union(poly0: &Polyline, poly1: &Polyline, self_isect_opt: Option<bool>) -> Polyline {
254 let self_isect = self_isect_opt.unwrap_or(false);
255 let mut verts0 = poly0
256 .iter()
257 .map(|&xy| Vertex {
258 xy,
259 isects: Vec::new(),
260 isects_map: HashMap::new(),
261 })
262 .collect();
263 let mut verts1 = poly1
264 .iter()
265 .map(|&xy| Vertex {
266 xy,
267 isects: Vec::new(),
268 isects_map: HashMap::new(),
269 })
270 .collect();
271
272 let mut has_isect = false;
273
274 build_vertices(
275 poly0,
276 poly1,
277 &mut verts0,
278 &mut verts1,
279 0,
280 self_isect,
281 &mut has_isect,
282 );
283 build_vertices(
284 poly1,
285 poly0,
286 &mut verts1,
287 &mut verts0,
288 1,
289 self_isect,
290 &mut has_isect,
291 );
292
293 if !has_isect {
294 if !self_isect {
295 return poly_bridge(poly0, poly1);
296 } else {
297 return poly_union(&poly_bridge(poly0, poly1), &Vec::new(), Some(true));
298 }
299 }
300
301 let mut isect_mir = HashMap::new();
302 pub fn mirror_isects(
303 verts0: &mut Vec<Vertex>,
304 verts1: &mut Vec<Vertex>,
305 idx: usize,
306 isect_mir: &mut HashMap<(usize, usize, i64), (usize, usize, i64)>,
307 ) {
308 let n = verts0.len();
309 for i in 0..n {
310 let m = verts0[i].isects.len();
311 for j in 0..m {
312 let id = (idx, i, j as i64);
313 let jump = verts0[i].isects[j].jump.unwrap_or(false);
314 let jd = if jump { 1 - idx } else { idx };
315 let k = verts0[i].isects[j].other.unwrap();
316 let z = (if jump { &verts1 } else { &verts0 })[k]
317 .isects
318 .iter()
319 .position(|x| (x.jump == Some(jump) && x.other == Some(i)))
320 .unwrap();
321 isect_mir.insert(id, (jd, k, z as i64));
322 }
323 }
324 }
325 mirror_isects(&mut verts0, &mut verts1, 0, &mut isect_mir);
326 mirror_isects(&mut verts1, &mut verts0, 1, &mut isect_mir);
327
328 pub fn trace_outline(
329 idx: usize,
330 i0: usize,
331 j0: i64,
332 dir: i64,
333 verts0: &Vec<Vertex>,
334 verts1: &Vec<Vertex>,
335 isect_mir: &HashMap<(usize, usize, i64), (usize, usize, i64)>,
336 ) -> Option<Polyline> {
337 pub fn trace_from(
338 mut zero: Option<(usize, usize, i64)>,
339 verts0: &Vec<Vertex>,
340 verts1: &Vec<Vertex>,
341 idx: usize,
342 i0: usize,
343 j0: i64,
344 dir: i64,
345 out: &mut Polyline,
346 isect_mir: &HashMap<(usize, usize, i64), (usize, usize, i64)>,
347 ) -> bool {
348 if zero == None {
349 zero = Some((idx, i0, j0));
350 } else if idx == zero.unwrap().0 && i0 == zero.unwrap().1 && j0 == zero.unwrap().2 {
351 return true;
352 }
353 let verts = if idx > 0 { verts1 } else { verts0 };
354 let n = verts.len();
355 let p = &verts[i0];
356 let i1 = (((i0 + n) as i64) + dir) as usize % n;
357 if j0 == -1 {
358 out.push(p.xy);
359 if dir < 0 {
360 return trace_from(
361 zero,
362 verts0,
363 verts1,
364 idx,
365 i1,
366 verts[i1].isects.len() as i64 - 1,
367 dir,
368 out,
369 isect_mir,
370 );
371 } else if verts[i0].isects.is_empty() {
372 return trace_from(zero, verts0, verts1, idx, i1, -1, dir, out, isect_mir);
373 } else {
374 return trace_from(zero, verts0, verts1, idx, i0, 0, dir, out, isect_mir);
375 }
376 } else if j0 >= p.isects.len() as i64 {
377 return trace_from(zero, verts0, verts1, idx, i1, -1, dir, out, isect_mir);
378 } else {
379 out.push(p.isects[j0 as usize].xy.unwrap());
380
381 let q = &p.isects[j0 as usize];
382 let (jdx, k, z) = isect_mir[&(idx, i0, j0)];
383 if q.side * dir < 0 {
384 return trace_from(
385 zero,
386 verts0,
387 verts1,
388 jdx,
389 k,
390 (z - 1) as i64,
391 -1,
392 out,
393 isect_mir,
394 );
395 }
396 return trace_from(
397 zero,
398 verts0,
399 verts1,
400 jdx,
401 k,
402 (z + 1) as i64,
403 1,
404 out,
405 isect_mir,
406 );
407 }
408 }
409 let zero = None;
410 let mut out = Vec::new();
411 let success = trace_from(zero, verts0, verts1, idx, i0, j0, dir, &mut out, isect_mir);
412 if !success || out.len() < 3 {
413 return None;
414 }
415 Some(out)
416 }
417
418 let mut xmin = f64::INFINITY;
419 let mut amin = (0, 0);
420 for i in 0..poly0.len() {
421 if poly0[i].0 < xmin {
422 xmin = poly0[i].0;
423 amin = (0, i);
424 }
425 }
426 for i in 0..poly1.len() {
427 if poly1[i].0 < xmin {
428 xmin = poly1[i].0;
429 amin = (1, i);
430 }
431 }
432
433 pub fn check_concavity(poly: &Polyline, idx: usize) -> i64 {
434 let n = poly.len();
435 let a = poly[(idx as i32 - 1 + n as i32) as usize % n];
436 let b = poly[idx];
437 let c = poly[(idx + 1) % n];
438 let cw = get_side(a, b, c);
439 return cw;
440 }
441
442 let cw = check_concavity(if amin.0 != 0 { &poly1 } else { &poly0 }, amin.1);
443 let outline = trace_outline(amin.0, amin.1, -1, cw, &verts0, &verts1, &isect_mir);
444 outline.unwrap_or_else(|| Vec::new())
445}
446
447pub fn seg_isect_poly(
448 p0: Point,
449 p1: Point,
450 poly: &Polyline,
451 is_ray_opt: Option<bool>,
452) -> Vec<Intersection> {
453 let is_ray = is_ray_opt.unwrap_or(false);
454 let n = poly.len();
455 let mut isects = Vec::new();
456 for i in 0..poly.len() {
457 let a = poly[i];
458 let b = poly[(i + 1) % n];
459
460 if let Some(xx) = seg_isect(p0, p1, a, b, Some(is_ray)) {
461 isects.push(xx);
462 }
463 }
464 sort_intersections(&mut isects);
465 isects
466}
467
468#[derive(Default)]
469pub struct ClipSegments {
470 pub clip: Vec<Polyline>,
471 pub dont_clip: Vec<Polyline>,
472}
473
474impl ClipSegments {
475 pub fn new_with_empty() -> Self {
476 ClipSegments {
477 clip: vec![vec![]],
478 dont_clip: vec![vec![]],
479 }
480 }
481 pub fn extend(&mut self, other: Self) {
482 self.clip.extend(other.clip.into_iter());
483 self.dont_clip.extend(other.dont_clip.into_iter());
484 }
485 pub fn filter_empty(mut self) -> Self {
486 self.clip = self.clip.into_iter().filter(|l| !l.is_empty()).collect();
487 self.dont_clip = self
488 .dont_clip
489 .into_iter()
490 .filter(|l| !l.is_empty())
491 .collect();
492 self
493 }
494 pub fn get(&self, clip: bool) -> &Vec<Polyline> {
495 if clip {
496 &self.clip
497 } else {
498 &self.dont_clip
499 }
500 }
501
502 pub fn get_mut(&mut self, clip: bool) -> &mut Vec<Polyline> {
503 if clip {
504 &mut self.clip
505 } else {
506 &mut self.dont_clip
507 }
508 }
509}
510
511pub fn clip(polyline: &Polyline, polygon: &Polyline) -> ClipSegments {
512 if polyline.is_empty() {
513 return ClipSegments::default();
514 }
515 let zero = seg_isect_poly(
516 polyline[0],
517 (polyline[0].0 + E, polyline[0].1 + PI),
518 polygon,
519 Some(true),
520 )
521 .len()
522 % 2
523 != 0;
524 let mut out = ClipSegments::new_with_empty();
525 let mut io = zero;
526 for i in 0..polyline.len() {
527 let a = polyline[i];
528 let b_opt = polyline.get(i + 1);
529 let idx = out.get(io).len() - 1;
530 out.get_mut(io)[idx].push(a);
531 let Some(&b) = b_opt else {
532 break;
533 };
534
535 let isects = seg_isect_poly(a, b, polygon, Some(false));
536 for j in 0..isects.len() {
537 let idx = out.get(io).len() - 1;
538 out.get_mut(io)[idx].push(isects[j].xy.unwrap());
539 io = !io;
540 out.get_mut(io).push(vec![isects[j].xy.unwrap()]);
541 }
542 }
543 out.filter_empty()
544}
545
546pub fn clip_multi(polylines: &Vec<Polyline>, polygon: &Polyline) -> ClipSegments {
547 let mut out = ClipSegments::default();
548 for polyline in polylines {
549 out.extend(clip(polyline, polygon));
550 }
551 return out;
552}
553
554pub fn binclip(polyline: &Polyline, func: impl Fn(Point, usize) -> bool) -> ClipSegments {
555 if polyline.is_empty() {
556 return ClipSegments::default();
557 }
558 let mut bins = Vec::new();
559 for i in 0..polyline.len() {
560 let t = i / (polyline.len() - 1);
561 bins.push(func(polyline[i], t));
562 }
563 let zero = bins[0];
564 let mut out = ClipSegments::new_with_empty();
565 let mut io = zero;
566 for i in 0..polyline.len() {
567 let a = polyline[i];
568 let b_opt = polyline.get(i + 1);
569 let idx = out.get(io).len() - 1;
570 out.get_mut(io)[idx].push(a);
571 let Some(&b) = b_opt else {
572 break;
573 };
574
575 let do_isect = bins[i] != bins[i + 1];
576
577 if do_isect {
578 let pt = lerp2d(a, b, 0.5);
579 let idx = out.get(io).len() - 1;
580 out.get_mut(io)[idx].push(pt);
581 io = !io;
582 out.get_mut(io).push(vec![pt]);
583 }
584 }
585 out.filter_empty()
586}
587
588pub fn binclip_multi(
589 polylines: &Vec<Polyline>,
590 f: Rc<dyn Fn(Point, usize) -> bool>,
591) -> ClipSegments {
592 let mut out = ClipSegments::default();
593 for polyline in polylines {
594 out.extend(binclip(polyline, f.as_ref()));
595 }
596 return out;
597}
598
599pub fn trsl_poly(poly: &Polyline, x: f64, y: f64) -> Polyline {
600 return poly.iter().map(|(x0, y0)| (x0 + x, y0 + y)).collect();
601}
602
603pub fn shade_shape(
604 poly: &Polyline,
605 step_opt: Option<f64>,
606 dx_opt: Option<f64>,
607 dy_opt: Option<f64>,
608) -> Vec<Polyline> {
609 let step = step_opt.unwrap_or(5.);
610 let dx = dx_opt.unwrap_or(10.);
611 let dy = dy_opt.unwrap_or(20.);
612 let mut bbox = get_boundingbox(poly);
613 bbox.x -= step;
614 bbox.y -= step;
615 bbox.w += step * 2.;
616 bbox.h += step * 2.;
617 let mut lines: Vec<_> = successors(Some(-bbox.h), |i| {
618 let next = i + step;
619 (next < bbox.w).then_some(next)
620 })
621 .map(|i| vec![(bbox.x + i, bbox.y), (bbox.x + i + bbox.h, bbox.y + bbox.h)])
622 .collect();
623
624 lines = clip_multi(&lines, poly).clip;
625
626 let carve = trsl_poly(poly, -dx, -dy);
627
628 lines = clip_multi(&lines, &carve).dont_clip;
629
630 for i in 0..lines.len() {
631 let line = &lines[i];
632 let mut a = line[0];
633 let mut b = line[1];
634 let s = (rand()) * 0.5;
635 if dy > 0. {
636 a = lerp2d(a, b, s);
637 lines[i][0] = a;
638 } else {
639 b = lerp2d(b, a, s);
640 lines[i][1] = b;
641 }
642 }
643
644 lines
645}
646
647pub fn fill_shape(poly: &Polyline, step_opt: Option<f64>) -> Vec<Vec<(f64, f64)>> {
648 let step = step_opt.unwrap_or(5.);
649 let mut bbox = get_boundingbox(poly);
650 bbox.x -= step as f64;
651 bbox.y -= step as f64;
652 bbox.w += step as f64 * 2.;
653 bbox.h += step as f64 * 2.;
654 let mut lines = vec![];
655
656 for i in successors(Some(0.), |i| {
657 let next = i + step;
658 (next < bbox.w + bbox.h / 2.).then_some(next)
659 }) {
660 let x0 = bbox.x + i;
661 let y0 = bbox.y;
662 let x1 = bbox.x + i - bbox.h / 2.;
663 let y1 = bbox.y + bbox.h;
664 lines.push(vec![(x0, y0), (x1, y1)]);
665 }
666 lines = clip_multi(&lines, &poly).clip;
667
668 return lines;
669}
670
671pub fn patternshade_shape(
672 poly: &Polyline,
673 step: f64,
674 pattern_func: Rc<dyn Fn((f64, f64)) -> bool>,
675) -> Vec<Polyline> {
676 let mut bbox = get_boundingbox(poly);
677 bbox.x -= step;
678 bbox.y -= step;
679 bbox.w += step * 2.;
680 bbox.h += step * 2.;
681 let mut lines = vec![];
682 for i in successors(Some(-bbox.h / 2.), |i| {
683 let next = i + step;
684 (next < bbox.w).then_some(next)
685 }) {
686 let x0 = bbox.x + i;
687 let y0 = bbox.y;
688 let x1 = bbox.x + i + bbox.h / 2.;
689 let y1 = bbox.y + bbox.h;
690 lines.push(vec![(x0, y0), (x1, y1)]);
691 }
692 lines = clip_multi(&lines, poly).clip;
693
694 for i in 0..lines.len() {
695 lines[i] = resample(&lines[i], 2.);
696 }
697
698 binclip_multi(&lines, Rc::new(move |p, _| pattern_func(p))).clip
699}
700
701pub fn vein_shape(poly: &Polyline, n_opt: Option<i64>) -> Vec<Polyline> {
702 let n = n_opt.unwrap_or(50);
703 let bbox = get_boundingbox(poly);
704 let mut out = vec![];
705 for _ in 0..n {
706 let mut x = bbox.x + rand() * bbox.w;
707 let mut y = bbox.y + rand() * bbox.h;
708 let mut o = vec![(x, y)];
709 for _ in 0..15 {
710 let dx = (noise(x * 0.1, Some(y * 0.1), Some(7.)) - 0.5) * 4.;
711 let dy = (noise(x * 0.1, Some(y * 0.1), Some(6.)) - 0.5) * 4.;
712 x += dx;
713 y += dy;
714 o.push((x, y));
715 }
716 out.push(o);
717 }
718 out = clip_multi(&out, poly).clip;
719 return out;
720}
721pub fn smalldot_shape(poly: &Polyline, scale: f64) -> Vec<Polyline> {
722 let mut samples = vec![];
723 let bbox = get_boundingbox(poly);
724 poissondisk(bbox.w, bbox.h, 5. * scale, &mut samples);
725 for i in 0..samples.len() {
726 samples[i].0 += bbox.x;
727 samples[i].1 += bbox.y;
728 }
729 let mut out = vec![];
730 let n = 7;
731 for (x, y) in samples {
732 let t = if (y > 0.) { (y / 300.) } else { 0.5 };
733 if ((t > 0.4 || y < 0.) && t > rand()) {
735 continue;
736 }
737 for k in 0..2 {
738 let mut o = vec![];
739 for j in 0..n {
740 let t = j / (n - 1);
741 let a = t as f64 * PI * 2.;
742 o.push((
743 f64::cos(a) * 1. - k as f64 * 0.3,
744 f64::sin(a) * 0.5 - k as f64 * 0.3,
745 ))
746 }
747 out.push(trsl_poly(&rot_poly(&o, rand() * PI * 2.), x, y));
748 }
749 }
750 clip_multi(&out, poly).clip
751}
752
753pub fn isect_circ_line((cx, cy): Point, r: f64, (x0, y0): Point, (x1, y1): Point) -> Option<f64> {
754 let dx = x1 - x0;
756 let dy = y1 - y0;
757 let fx = x0 - cx;
758 let fy = y0 - cy;
759 let a = dx * dx + dy * dy;
760 let b = 2. * (fx * dx + fy * dy);
761 let c = (fx * fx + fy * fy) - r * r;
762 let mut discriminant = b * b - 4. * a * c;
763 if discriminant < 0. {
764 return None;
765 }
766 discriminant = discriminant.sqrt();
767 let t0 = (-b - discriminant) / (2. * a);
768 if 0. <= t0 && t0 <= 1. {
769 return Some(t0);
770 }
771 let t = (-b + discriminant) / (2. * a);
772 if t > 1. || t < 0. {
773 return None;
774 }
775 return Some(t);
776}
777
778pub fn resample(polyline_slice: &[Point], step: f64) -> Vec<Point> {
779 let mut polyline = polyline_slice.to_vec();
780 if polyline_slice.len() < 2 {
781 return polyline;
782 }
783 let mut out = vec![polyline[0]];
784 let mut next;
785 let mut i = 0;
786 while i < polyline.len() - 1 {
787 let a = polyline[i];
788 let b = polyline[i + 1];
789 let dx = b.0 - a.0;
790 let dy = b.1 - a.1;
791 let d = f64::sqrt(dx * dx + dy * dy);
792 if d == 0. {
793 i += 1;
794 continue;
795 }
796 let n = (d / step).trunc();
797 let rest = (n as f64 * step) / d;
798 let rpx = a.0 * (1. - rest) + b.0 * rest;
799 let rpy = a.1 * (1. - rest) + b.1 * rest;
800 for j in 1..n as i64 {
801 let t = j as f64 / n;
802 let x = a.0 * (1. - t) + rpx * t;
803 let y = a.1 * (1. - t) + rpy * t;
804 out.push((x, y));
809 }
810
811 next = None;
812 for j in i + 2..polyline.len() {
813 let b = polyline[j - 1];
814 let c = polyline[j];
815 if b.0 == c.0 && b.1 == c.1 {
816 continue;
817 }
818 let t_opt: Option<f64> = isect_circ_line((rpx, rpy), step, b, c);
819 let Some(t) = t_opt else {
820 continue;
821 };
822
823 let q = (b.0 * (1. - t) + c.0 * t, b.1 * (1. - t) + c.1 * t);
824 out.push(q);
828 polyline[j - 1] = q;
829 next = Some(j - 1);
830 break;
831 }
832 let Some(nxt) = next else {
833 break;
834 };
835 i = nxt;
836 }
837
838 if out.len() > 1 {
839 let lx = out[out.len() - 1].0;
840 let ly = out[out.len() - 1].1;
841 let mx = polyline[polyline.len() - 1].0;
842 let my = polyline[polyline.len() - 1].1;
843 let d = f64::sqrt((mx - lx).powi(2) + (my - ly).powi(2));
844 if d < step * 0.5 {
845 out.pop();
846 }
847 }
848 out.push(polyline[polyline.len() - 1]);
849 return out;
850}
851
852pub fn pt_seg_dist((x, y): Point, (x1, y1): Point, (x2, y2): Point) -> f64 {
853 let a = x - x1;
855 let b = y - y1;
856 let c = x2 - x1;
857 let d = y2 - y1;
858 let dot = a * c + b * d;
859 let len_sq = c * c + d * d;
860 let mut param = -1.;
861 if len_sq != 0. {
862 param = dot / len_sq;
863 }
864 let xx;
865 let yy;
866 if param < 0. {
867 xx = x1;
868 yy = y1;
869 } else if param > 1. {
870 xx = x2;
871 yy = y2;
872 } else {
873 xx = x1 + param * c;
874 yy = y1 + param * d;
875 }
876 let dx = x - xx;
877 let dy = y - yy;
878 return f64::sqrt(dx * dx + dy * dy);
879}
880pub fn distsq((x0, y0): Point, (x1, y1): Point) -> f64 {
912 let dx = x0 - x1;
913 let dy = y0 - y1;
914 dx * dx + dy * dy
915}
916
917pub fn poissondisk(width: f64, height: f64, radius: f64, samples: &mut Polyline) {
918 let mut active = vec![];
919 let radius_over_root_2 = radius / 2.0f64.sqrt();
920 let r2 = radius.powi(2);
921 let cols = (width / radius_over_root_2) as usize;
922 let rows = (height / radius_over_root_2) as usize;
923 let mut grid: Vec<i32> = vec![-1; (cols) * (rows)];
924 let mut pos = (width / 2., height / 2.);
925 samples.push(pos);
926 for i in 0..samples.len() {
927 let col = (samples[i].0 / radius_over_root_2) as usize;
928 let row = (samples[i].1 / radius_over_root_2) as usize;
929 grid[col + (row * cols)] = i as i32;
930 active.push(samples[i]);
931 }
932 while !active.is_empty() {
933 let ridx = (rand() * active.len() as f64) as usize;
934 pos = active[ridx];
935 let mut found = false;
936 for _ in 0..30 {
937 let sr = radius + (rand() * radius);
938 let sa = 6.2831853072 * rand();
939 let sx = pos.0 + (sr * sa.cos());
940 let sy = pos.1 + (sr * sa.sin());
941 let col = (sx / radius_over_root_2) as i32;
942 let row = (sy / radius_over_root_2) as i32;
943 if col > 0
944 && row > 0
945 && col < cols as i32 - 1
946 && row < rows as i32 - 1
947 && grid[(col + (row * cols as i32)) as usize] == -1
948 {
949 let mut ok = true;
950 for i in -1..=1 {
951 for j in -1..=1 {
952 let idx = (((row + i) * cols as i32) + col) + j;
953 let nbr = grid[idx as usize];
954 if -1 != nbr {
955 let d = distsq((sx, sy), samples[nbr as usize]);
956 if d < r2 {
957 ok = false;
958 };
959 };
960 }
961 }
962 if ok {
963 found = true;
964 grid[((row * (cols as i32)) + col) as usize] = samples.len() as i32;
965 let sample = (sx, sy);
966 active.push(sample);
967 samples.push(sample);
968 };
969 };
970 }
971 if !found {
972 active.remove(ridx);
973 };
974 }
975}
976
977pub fn pow(a: f64, b: f64) -> f64 {
978 return a.abs().powf(b).copysign(a);
979}
980
981pub fn gauss2d(x: f64, y: f64) -> f64 {
982 let z0 = f64::exp(-0.5 * x * x);
983 let z1 = f64::exp(-0.5 * y * y);
984 return z0 * z1;
985}
986
987pub fn scl_poly(poly: &Polyline, sx: f64, sy_opt: Option<f64>) -> Polyline {
988 let sy = sy_opt.unwrap_or(sx);
989 poly.iter().map(|xy| (xy.0 * sx, xy.1 * sy)).collect()
990}
991pub fn shr_poly(poly: &Polyline, sx: f64) -> Polyline {
992 poly.iter().map(|xy| (xy.0 + xy.1 * sx, xy.1)).collect()
993}
994pub fn rot_poly(poly: &Polyline, th: f64) -> Polyline {
995 let costh = f64::cos(th);
996 let sinth = f64::sin(th);
997 poly.iter()
998 .map(|(x0, y0)| (x0 * costh - y0 * sinth, x0 * sinth + y0 * costh))
999 .collect()
1000}
1001
1002pub fn pattern_dot(scale: f64) -> Rc<dyn Fn((f64, f64)) -> bool> {
1003 let mut samples = vec![];
1004 poissondisk(500., 300., 20. * scale, &mut samples);
1005 let mut rs = vec![];
1006 for _ in 0..samples.len() {
1007 rs.push((rand() * 5. + 10.) * scale)
1008 }
1009 Rc::new(move |(x, y)| {
1010 for i in 0..samples.len() {
1011 let r = rs[i];
1012 if dist((x, y), samples[i]) < r {
1013 let (x0, y0) = samples[i];
1014 let dx = x - x0;
1015 let dy = y - y0;
1016 if gauss2d(dx / r * 2., dy / r * 2.) * noise(x, Some(y), Some(999.)) > 0.2 {
1017 return true;
1018 }
1019 }
1020 }
1021 return false;
1022 })
1023}