Skip to main content

fish_oxide/
geometry.rs

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        // console.log(y,t);
734        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    //https://stackoverflow.com/a/1084899
755    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            // let xy = [x, y];
805            // for k in 2..a.len() {
806            //     xy.push(a[k] * (1 - t) + (a[k] * (1 - rest) + b[k] * rest) * t);
807            // }
808            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            // for k in 2..b.len() {
825            //     q.push(b[k] * (1 - t) + c[k] * t);
826            // }
827            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    // https://stackoverflow.com/a/6853926
854    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}
880/*
881
882
883pub fn approx_poly_dp(polyline, epsilon){
884  if (polyline.len() <= 2){
885    return polyline;
886  }
887  let dmax   = 0;
888  let argmax = -1;
889  for i in 1; i < polyline.len()-1 {
890    let d = pt_seg_dist(polyline[i] ,
891                        polyline[0] ,
892                        polyline[polyline.len()-1] );
893    if (d > dmax){
894      dmax = d;
895      argmax = i;
896    }
897  }
898  let ret = [];
899  if (dmax > epsilon){
900    let L = approx_poly_dp(polyline.slice(0,argmax+1),epsilon);
901    let R = approx_poly_dp(polyline.slice(argmax,polyline.len()),epsilon);
902    ret = ret.concat(L.slice(0,L.len()-1)).concat(R);
903  }else{
904    ret.push(polyline[0].slice());
905    ret.push(polyline[polyline.len()-1].slice());
906  }
907  return ret;
908}
909*/
910
911pub 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}