Skip to main content

fish_oxide/
draw.rs

1use std::{
2    f64::consts::{E, PI},
3    iter::successors,
4    rc::Rc,
5};
6
7use crate::{
8    custom_rand::{deviate, noise, rand},
9    geometry::{
10        binclip, binclip_multi, clip, clip_multi, dist, fill_shape, flat, get_boundingbox, lerp,
11        lerp2d, pattern_dot, patternshade_shape, poly_union, pow, pt_seg_dist, resample, scl_poly,
12        shade_shape, shr_poly, smalldot_shape, trsl_poly, vein_shape, Point, Polyline, PolylineOps,
13    },
14    hershey::compile_hershey,
15    params::Params,
16};
17
18const SVG_PREAMBLE: &str = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"520\" height=\"320\">
19<rect x=\"0\" y=\"0\" width=\"520\" height=\"320\" fill=\"floralwhite\"/>
20<rect x=\"10\" y=\"10\" width=\"500\" height=\"300\" stroke=\"black\" stroke-width=\"1\" fill=\"none\"/>
21<path stroke=\"black\" stroke-width=\"1\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"";
22
23pub fn draw_svg(polylines: Vec<Polyline>) -> String {
24    SVG_PREAMBLE.to_string()
25        + &polylines
26            .iter()
27            .map(|line| {
28                "\nM ".to_string()
29                    + &line
30                        .iter()
31                        .map(|(x, y)| {
32                            format!(
33                                "{} {} ",
34                                (((x + 10.) * 100.).trunc()) / 100.,
35                                ((y + 10.) * 100.).trunc() / 100.
36                            )
37                        })
38                        .collect::<String>()
39            })
40            .collect::<String>()
41        + "\n\"/></svg>"
42}
43/*
44
45pub pub fn draw_svg_anim(polylines,speed){
46  let o = `<svg xmlns="http://www.w3.org/2000/svg" width="520" height="320">`;
47  o += `<rect x="0" y="0" width="520" height="320" fill="floralwhite"/><rect x="10" y="10" width="500" height="300" stroke="black" stroke-width="1" fill="none"/>`
48  let lengths = [];
49  let acc_lengths = [];
50  let total_l = 0;
51  for (let i = 0; i < polylines.length; i++){
52    let l = 0;
53    for (let j = 1; j < polylines[i].length; j++){
54      l += f64::hypot(
55        polylines[i][j-1].0-polylines[i][j].0,
56        polylines[i][j-1].1-polylines[i][j].1
57      );
58    }
59    lengths.push(l);
60    acc_lengths.push(total_l);
61    total_l+=l;
62  }
63  for (let i = 0; i < polylines.length; i++){
64    let l = lengths[i];
65    o += `
66    <path
67      stroke="black"
68      stroke-width="1"
69      fill="none"
70      stroke-dasharray="${l}"
71      stroke-dashoffset="${l}"
72      d="M`;
73    for (let j = 0; j < polylines[i].length; j++){
74      o += polylines[i][j] + ' ';
75    }
76    let t = speed*l;
77    o += `">
78    <animate id="a${i}"
79      attributeName="stroke-dashoffset"
80      fill="freeze"
81      from="${l}" to="${0}" dur="${t}s"
82      begin="${(acc_lengths[i])*speed}s;a${i}.end+${8+speed*total_l-t}s"/>
83    />
84    <animate id="b${i}"
85      attributeName="stroke-dashoffset"
86      fill="freeze"
87      from="${0}" to="${l}" dur="${3}s"
88      begin="${5+speed*total_l}s;b${i}.end+${5+speed*total_l}s"/>
89    />
90    </path>`;
91  }
92  o += `</svg>`;
93  return o;
94}
95
96pub pub fn draw_ps(polylines){
97  let o = `%!PS-Adobe-3.0 EPSF-3.0
98%%BoundingBox: 0 0 520 320
991 setlinewidth
1000.5 0.5 translate
101/m /moveto load def
102/l /lineto load def
103/F /stroke load def
104%%EndPageSetup
10510 10 m
106510 10 l
107510 310 l
10810 310 l
109closepath
110F
111`;
112  for (let i = 0; i < polylines.length; i++){
113    for (let j = 0; j < polylines[i].length; j++){
114      let [x,y] = polylines[i][j];
115      o += `${(~~((x+10)*100)) /100} ${(~~((310-y)*100)) /100} `;
116      if (j == 0) {
117        o += `m\n`;
118      } else {
119        o += `l\n`;
120      }
121    }
122    o += `F\n\n`;
123  }
124  return o;
125}
126*/
127fn squama_mask(w: f64, h: f64) -> Polyline {
128    let mut p = vec![];
129    let n = 7;
130    for i in 0..n {
131        let t = i as f64 / n as f64;
132        let a = t * PI * 2.;
133        let x = -pow(a.cos(), 1.3) * w;
134        let y = pow(a.sin(), 1.3) * h;
135        p.push((x, y));
136    }
137    return p;
138}
139
140fn squama(w: f64, h: f64, m_opt: Option<usize>) -> Vec<Polyline> {
141    let m = m_opt.unwrap_or(3);
142    let mut p = vec![];
143    let n = 8;
144    for i in 0..n {
145        let t = i as f64 / (n - 1) as f64;
146        let a = t * PI + PI / 2.;
147        let cos = a.cos();
148        let sin = a.sin();
149        let x = cos.abs().powf(1.4).copysign(-cos) * w;
150        let y = sin.abs().powf(1.4).copysign(sin) * h;
151        p.push((x, y));
152    }
153    let mut q = vec![p];
154    for i in 0..m {
155        let t = i as f64 / (m - 1) as f64;
156        q.push(vec![
157            (
158                -w * 0.3 + (rand() - 0.5),
159                -h * 0.2 + t * h * 0.4 + (rand() - 0.5),
160            ),
161            (
162                w * 0.5 + (rand() - 0.5),
163                -h * 0.3 + t * h * 0.6 + (rand() - 0.5),
164            ),
165        ]);
166    }
167    return q;
168}
169
170pub fn squama_mesh(
171    m: usize,
172    n: usize,
173    (uw, uh): Point,
174    squama_func: Box<dyn Fn(Point, Point) -> Vec<Polyline>>,
175    (noise_x, noise_y): Point,
176    interclip_opt: Option<bool>,
177) -> Vec<Polyline> {
178    let interclip = interclip_opt.unwrap_or(true);
179    let mut clipper: Option<Polyline> = None;
180
181    let mut pts = vec![];
182    for i in 0..n {
183        for j in 0..m {
184            let x = j as f64 * uw;
185            let y = (n as f64 * uh / 2.)
186                - f64::cos(i as f64 / (n as f64 - 1.) * PI) * (n as f64 * uh / 2.);
187            let a = noise(x * 0.005, Some(y * 0.005), None) * PI * 2. - PI;
188            let r = noise(x * 0.005, Some(y * 0.005), None);
189            let dx = f64::cos(a) * r * noise_x;
190            let dy = f64::cos(a) * r * noise_y;
191            pts.push((x + dx, y + dy));
192        }
193    }
194    let mut out = vec![];
195
196    let mut whs = vec![];
197    for i in 0..n {
198        for j in 0..m {
199            if i == 0 || j == 0 || i == n - 1 || j == m - 1 {
200                whs.push((uw / 2., uh / 2.));
201                continue;
202            }
203            let a = pts[i * m + j];
204            let b = pts[i * m + j + 1];
205            let c = pts[i * m + j - 1];
206            let d = pts[(i - 1) * m + j];
207            let e = pts[(i + 1) * m + j];
208
209            let dw = (dist(a, b) + dist(a, c)) / 4.;
210            let dh = (dist(a, d) + dist(a, e)) / 4.;
211            whs.push((dw, dh));
212        }
213    }
214    let mut j = 1;
215    while j < m as i64 - 1 {
216        for i in 1..n - 1 {
217            let (x, y) = pts[i * m + j as usize];
218            let (dw, dh) = whs[i * m + j as usize];
219            let q = trsl_poly(&squama_mask(dw, dh), x, y);
220            let p = squama_func((x, y), (dw, dh));
221            let p: Vec<Polyline> = p.into_iter().map(|a| trsl_poly(&a, x, y)).collect();
222            if !interclip {
223                out.extend(p.into_iter());
224            } else {
225                if let Some(c) = clipper {
226                    out.extend(clip_multi(&p, &c).dont_clip.into_iter());
227                    clipper = Some(poly_union(&c, &q, None));
228                } else {
229                    out.extend(p.into_iter());
230                    clipper = Some(q);
231                }
232            }
233        }
234        for i in 1..n - 1 {
235            let a = pts[i * m + j as usize];
236            let b = pts[i * m + j as usize + 1];
237            let c = pts[(i + 1) * m + j as usize];
238            let d = pts[(i + 1) * m + j as usize + 1];
239
240            let (dwa, dha) = whs[i * m + j as usize];
241            let (dwb, dhb) = whs[i * m + j as usize + 1];
242            let (dwc, dhc) = whs[(i + 1) * m + j as usize];
243            let (dwd, dhd) = whs[(i + 1) * m + j as usize + 1];
244
245            let (x, y) = ((a.0 + b.0 + c.0 + d.0) / 4., (a.1 + b.1 + c.1 + d.1) / 4.);
246            let (mut dw, dh) = ((dwa + dwb + dwc + dwd) / 4., (dha + dhb + dhc + dhd) / 4.);
247            dw *= 1.2;
248            let q = trsl_poly(&squama_mask(dw, dh), x, y);
249
250            let p: Vec<Polyline> = squama_func((x, y), (dw, dh))
251                .into_iter()
252                .map(|a| trsl_poly(&a, x, y))
253                .collect();
254            if !interclip {
255                out.extend(p.into_iter());
256            } else {
257                if let Some(c) = clipper {
258                    out.extend(clip_multi(&p, &c).dont_clip.into_iter());
259                    clipper = Some(poly_union(&c, &q, None));
260                } else {
261                    out.extend(p.into_iter());
262                    clipper = Some(q);
263                }
264            }
265        }
266        j += 1;
267    }
268    // for i in 0..n-1 {
269    //   for j in 0..m-1; j++){
270    //     let a= pts[i*m+j];
271    //     let b= pts[i*m+j+1];
272    //     let c = pts[(i+1)*m+j];
273    //     out.push([a,b]);
274    //     out.push([a,c]);
275    //   }
276    // }
277    return out;
278}
279
280fn fish_body_a(
281    curve0: &Polyline,
282    curve1: &Polyline,
283    scale_scale: f64,
284    pattern_func: Option<Rc<dyn Fn(Point) -> bool>>,
285) -> Vec<Polyline> {
286    let mut curve2 = vec![];
287    let mut curve3 = vec![];
288    for i in 0..curve0.len() {
289        curve2.push(lerp2d(curve0[i], curve1[i], 0.95));
290        curve3.push(lerp2d(curve0[i], curve1[i], 0.85));
291    }
292
293    let outline1 = curve0.concat(&curve1.rev());
294    let outline2 = curve0.concat(&curve2.rev());
295    let outline3 = curve0.concat(&curve3.rev());
296
297    let bbox = get_boundingbox(&curve0.concat(curve1));
298    let m = (bbox.w / (scale_scale * 15.)).trunc() as usize;
299    let n = (bbox.h / (scale_scale * 15.)).trunc() as usize;
300    let uw = bbox.w / m as f64;
301    let uh = bbox.h / n as f64;
302    let funky: Box<dyn Fn(Point, Point) -> Vec<Polyline>> = if let Some(f) = pattern_func {
303        Box::new(move |(x, y), (w, h)| squama(w, h, Some(f((x, y)) as usize * 3)))
304    } else {
305        Box::new(|(x, y), (w, h)| squama(w, h, None))
306    };
307
308    let mut sq = squama_mesh(m, n + 3, (uw, uh), funky, (uw * 3., uh * 3.), Some(true));
309
310    sq = sq
311        .into_iter()
312        .map(|a| trsl_poly(&a, bbox.x, bbox.y - uh * 1.5))
313        .collect();
314    let o0 = clip_multi(&sq, &outline2).clip;
315    let mut o1 = clip_multi(&o0, &outline3);
316    o1.dont_clip = o1.dont_clip.into_iter().filter(|x| rand() < 0.6).collect();
317
318    [curve0.clone(), curve1.rev()]
319        .into_iter()
320        .chain(o1.clip)
321        .chain(o1.dont_clip)
322        .collect()
323}
324
325pub fn fish_body_b(
326    curve0: &Polyline,
327    curve1: &Polyline,
328    scale_scale: f64,
329    pattern_func: Option<Rc<dyn Fn((f64, f64)) -> bool>>,
330) -> Vec<Polyline> {
331    let mut curve2 = vec![];
332    for i in 0..curve0.len() {
333        curve2.push(lerp2d(curve0[i], curve1[i], 0.95));
334    }
335    let outline1 = curve0.concat(&curve1.rev());
336    let outline2 = curve0.concat(&curve2.rev());
337
338    let bbox = get_boundingbox(&curve0.concat(curve1));
339    let m = (bbox.w / (scale_scale * 5.)).trunc();
340    let n = (bbox.h / (scale_scale * 5.)).trunc();
341    let uw = bbox.w / m;
342    let uh = bbox.h / n;
343
344    let sq = squama_mesh(
345        m as usize,
346        n as usize + 16,
347        (uw, uh),
348        Box::new(|(x, y), (w, h)| squama(w * 0.7, h * 0.6, Some(0))),
349        (uw * 8., uh * 8.),
350        Some(false),
351    )
352    .iter()
353    .map(|a| trsl_poly(a, bbox.x, bbox.y - uh * 8.))
354    .collect();
355    let o0 = clip_multi(&sq, &outline2).clip;
356
357    let mut o1 = vec![];
358    for line in o0 {
359        let (x, y) = line[0];
360        let t = (y - bbox.y) / bbox.h;
361        // if (rand() > t){
362        //   o1.push(line);
363        // }
364        // if ((!!(x/30))%2 || (rand() > t && rand()>t)){
365        //   o1.push(line);
366        // }
367        if let Some(funky) = &pattern_func {
368            if funky((x, y)) || (rand() > t && rand() > t) {
369                o1.push(line);
370            }
371        } else {
372            if rand() > t {
373                o1.push(line);
374            }
375        }
376    }
377
378    [curve0.clone(), curve1.rev()]
379        .into_iter()
380        .chain(o1)
381        .collect()
382}
383
384pub fn fish_body_c(curve0: &Polyline, curve1: &Polyline, scale_scale: f64) -> Vec<Polyline> {
385    let step = 6. * scale_scale;
386
387    let mut curve2 = vec![];
388    let mut curve3 = vec![];
389
390    for i in 0..curve0.len() {
391        curve2.push(lerp2d(curve0[i], curve1[i], 0.95));
392        curve3.push(lerp2d(curve0[i], curve1[i], 0.4));
393    }
394    let outline1 = curve0.concat(&curve1.rev());
395    let outline2 = curve0.concat(&curve2.rev());
396
397    let mut bbox = get_boundingbox(&curve0.concat(&curve1));
398    bbox.x -= step;
399    bbox.y -= step;
400    bbox.w += step * 2.;
401    bbox.h += step * 2.;
402
403    let mut lines = vec![curve3.rev()];
404
405    for i in successors(Some(-bbox.h), |i| {
406        let next = i + step;
407        (next < bbox.w).then_some(next)
408    }) {
409        lines.push(vec![
410            (bbox.x + i, bbox.y),
411            (bbox.x + i + bbox.h, bbox.y + bbox.h),
412        ]);
413    }
414
415    for i in successors(Some(0.), |i| {
416        let next = i + step;
417        (next < bbox.w + bbox.h).then_some(next)
418    }) {
419        lines.push(vec![
420            (bbox.x + i, bbox.y),
421            (bbox.x + i - bbox.h, bbox.y + bbox.h),
422        ]);
423    }
424    for i in 0..lines.len() {
425        lines[i] = resample(&lines[i], 4.);
426        for j in 0..lines[i].len() {
427            let (x, y) = lines[i][j];
428            let t = (y - bbox.y) / bbox.h;
429            let y1 = -f64::cos(t * PI) * bbox.h / 2. + bbox.y + bbox.h / 2.;
430
431            let dx = (noise(x * 0.005, Some(y1 * 0.005), Some(0.1)) - 0.5) * 50.;
432            let dy = (noise(x * 0.005, Some(y1 * 0.005), Some(1.2)) - 0.5) * 50.;
433
434            lines[i][j].0 += dx;
435            lines[i][j].1 = y1 + dy;
436        }
437    }
438
439    let mut o0 = clip_multi(&lines, &outline2).clip;
440
441    o0 = binclip_multi(
442        &o0,
443        Rc::new(|(x, y), t| (rand() > t as f64 || rand() > t as f64)),
444    )
445    .clip;
446
447    [curve0.clone(), curve1.rev()]
448        .into_iter()
449        .chain(o0.into_iter())
450        .collect()
451}
452
453pub fn fish_body_d(curve0: &Polyline, curve1: &Polyline, scale_scale: f64) -> Vec<Polyline> {
454    let mut curve2 = vec![];
455    for i in 0..curve0.len() {
456        curve2.push(lerp2d(curve0[i], curve1[i], 0.4));
457    }
458    let curve0 = resample(curve0, 10. * scale_scale);
459    let curve1 = resample(curve1, 10. * scale_scale);
460    curve2 = resample(&curve2, 10. * scale_scale);
461
462    let outline1 = curve0.concat(&curve1.rev());
463    let outline2 = curve0.concat(&curve2.rev());
464
465    let mut o0 = vec![curve2.clone()];
466    for i in 3..curve0.len().min(curve1.len()).min(curve2.len()) {
467        o0.push(vec![curve0[i], curve2[i - 3]]);
468        o0.push(vec![curve2[i - 3], curve1[i]]);
469    }
470
471    let mut o1 = vec![];
472    for i in 0..o0.len() {
473        o0[i] = resample(&o0[i], 4.);
474        for j in 0..o0[i].len() {
475            let (x, y) = o0[i][j];
476            let dx = 30. * (noise(x * 0.01, Some(y * 0.01), Some(-1.)) - 0.5);
477            let dy = 30. * (noise(x * 0.01, Some(y * 0.01), Some(9.)) - 0.5);
478            o0[i][j].0 += dx;
479            o0[i][j].1 += dy;
480        }
481
482        o1.extend(
483            binclip(&o0[i], |(x, y), t| {
484                (rand() > (t as f64 * PI).cos() && rand() < x / 500.)
485                    || (rand() > (t as f64 * PI).cos() && rand() < x / 500.)
486            })
487            .clip,
488        );
489    }
490    o1 = clip_multi(&o1, &outline1).clip;
491
492    let sh = vein_shape(&outline1, None);
493
494    [curve0, curve1.rev()]
495        .into_iter()
496        .chain(o1)
497        .chain(sh)
498        .collect()
499}
500
501pub fn fin_a(
502    curve: &Polyline,
503    ang0: f64,
504    ang1: f64,
505    func: Box<dyn Fn(f64) -> f64>,
506    clip_root_opt: Option<bool>,
507    curvature0_opt: Option<f64>,
508    curvature1_opt: Option<f64>,
509    softness_opt: Option<f64>,
510) -> (Polyline, Vec<Polyline>) {
511    let clip_root = clip_root_opt.unwrap_or(false);
512    let curvature0 = curvature0_opt.unwrap_or(0.);
513    let curvature1 = curvature1_opt.unwrap_or(0.);
514    let softness = softness_opt.unwrap_or(10.);
515    let mut angs = vec![];
516    for i in 0..curve.len() {
517        if i == 0 {
518            angs.push(
519                f64::atan2(curve[i + 1].1 - curve[i].1, curve[i + 1].0 - curve[i].0) - PI / 2.,
520            );
521        } else if i == curve.len() - 1 {
522            angs.push(
523                f64::atan2(curve[i].1 - curve[i - 1].1, curve[i].0 - curve[i - 1].0) - PI / 2.,
524            );
525        } else {
526            let a0 = f64::atan2(curve[i - 1].1 - curve[i].1, curve[i - 1].0 - curve[i].0);
527            let mut a1 = f64::atan2(curve[i + 1].1 - curve[i].1, curve[i + 1].0 - curve[i].0);
528            while a1 > a0 {
529                a1 -= PI * 2.;
530            }
531            a1 += PI * 2.;
532            let a = (a0 + a1) / 2.;
533            angs.push(a);
534        }
535    }
536    let mut out0 = vec![];
537    let mut out1 = vec![];
538    let mut out2 = vec![];
539    let mut out3 = vec![];
540    for i in 0..curve.len() {
541        let t = i as f64 / (curve.len() - 1) as f64;
542        let aa = lerp(ang0, ang1, t);
543        let a = angs[i] + aa;
544        let w = func(t);
545
546        let (x0, y0) = curve[i];
547        let x1 = x0 + f64::cos(a) * w;
548        let y1 = y0 + f64::sin(a) * w;
549
550        let mut p = resample(&[(x0, y0), (x1, y1)], 3.);
551        for j in 0..p.len() {
552            let s = j as f64 / (p.len() - 1) as f64;
553            let ss = f64::sqrt(s);
554            let (x, y) = p[j];
555            let cv = lerp(curvature0, curvature1, t) * f64::sin(s * PI);
556            p[j].0 += noise(x * 0.1, Some(y * 0.1), Some(3.)) * ss * softness
557                + f64::cos(a - PI / 2.) * cv;
558            p[j].1 += noise(x * 0.1, Some(y * 0.1), Some(4.)) * ss * softness
559                + f64::sin(a - PI / 2.) * cv;
560        }
561        if i == 0 {
562            out2 = p;
563        } else if i == curve.len() - 1 {
564            out3 = p.rev();
565        } else {
566            out0.push(p[p.len() - 1]);
567            // if (i % 2){
568            let q = &p[(if clip_root { (rand() * 4.) as usize } else { 0 })
569                ..(2.max((p.len() as f64 * (rand() * 0.5 + 0.5)) as usize))];
570            if !q.is_empty() {
571                out1.push(q.iter().map(|x| *x).collect());
572            }
573            // }
574        }
575    }
576    out0 = resample(&out0, 3.);
577    for i in 0..out0.len() {
578        let (x, y) = out0[i];
579        out0[i].0 += (noise(x * 0.1, Some(y * 0.1), None) * 6. - 3.) * (softness / 10.);
580        out0[i].1 += (noise(x * 0.1, Some(y * 0.1), None) * 6. - 3.) * (softness / 10.);
581    }
582    let o: Polyline = [out2, out0, out3].into_iter().flatten().collect();
583    out1.insert(0, o.clone());
584    return (o.concat(&curve.rev()), out1);
585}
586
587pub fn fin_b(
588    curve: &Polyline,
589    ang0: f64,
590    ang1: f64,
591    func: impl Fn(f64) -> f64,
592    dark_opt: Option<f64>,
593) -> (Polyline, Vec<Polyline>) {
594    let dark = dark_opt.unwrap_or(1.);
595    let mut angs = vec![];
596    for i in 0..curve.len() {
597        if (i == 0) {
598            angs.push(
599                f64::atan2(curve[i + 1].1 - curve[i].1, curve[i + 1].0 - curve[i].0) - (PI / 2.),
600            );
601        } else if (i == curve.len() - 1) {
602            angs.push(
603                f64::atan2(curve[i].1 - curve[i - 1].1, curve[i].0 - curve[i - 1].0) - (PI / 2.),
604            );
605        } else {
606            let a0 = f64::atan2(curve[i - 1].1 - curve[i].1, curve[i - 1].0 - curve[i].0);
607            let mut a1 = f64::atan2(curve[i + 1].1 - curve[i].1, curve[i + 1].0 - curve[i].0);
608            while (a1 > a0) {
609                a1 -= PI * 2.;
610            }
611            a1 += PI * 2.;
612            let a = (a0 + a1) / 2.;
613            angs.push(a);
614        }
615    }
616
617    let mut out0 = vec![];
618    let mut out1 = vec![];
619    let mut out2 = vec![];
620    let mut out3 = vec![];
621    for i in 0..curve.len() {
622        let t = (i as f64 / (curve.len() - 1) as f64);
623        let aa = lerp(ang0, ang1, t);
624        let a = angs[i] + aa;
625        let w = func(t);
626
627        let (x0, y0) = curve[i];
628        let x1 = x0 + f64::cos(a) * w;
629        let y1 = y0 + f64::sin(a) * w;
630
631        let b = (
632            x1 + 0.5 * f64::cos(a - (PI / 2.)),
633            y1 + 0.5 * f64::sin(a - (PI / 2.)),
634        );
635        let c = (
636            x1 + 0.5 * f64::cos(a + (PI / 2.)),
637            y1 + 0.5 * f64::sin(a + (PI / 2.)),
638        );
639
640        let p = (
641            curve[i].0 + 1.8 * f64::cos(a - (PI / 2.)),
642            curve[i].1 + 1.8 * f64::sin(a - (PI / 2.)),
643        );
644        let q = (
645            curve[i].0 + 1.8 * f64::cos(a + (PI / 2.)),
646            curve[i].1 + 1.8 * f64::sin(a + (PI / 2.)),
647        );
648        out1.push((x1, y1));
649        out0.push(vec![p, b, c, q]);
650    }
651
652    let n = 10;
653    for i in 0..curve.len() - 1 {
654        let (a0, q0) = (out0[i][2], out0[i][3]);
655        let (p1, a1) = (out0[i + 1][0], out0[i + 1][1]);
656
657        let b = lerp2d(a0, q0, 0.1);
658        let c = lerp2d(a1, p1, 0.1);
659
660        let mut o = vec![];
661        let ang = f64::atan2(c.1 - b.1, c.0 - b.0);
662
663        for j in 0..n {
664            let t = (j as f64 / (n - 1) as f64);
665            let d = f64::sin(t * PI) * 2.;
666            let a = lerp2d(b, c, t);
667            o.push((
668                a.0 + f64::cos(ang + (PI / 2.)) * d,
669                a.1 + f64::sin(ang + (PI / 2.)) * d,
670            ))
671        }
672
673        // out2.push([b,c]);
674        out2.push(o.clone());
675
676        let m = (f64::min(dist(a0, q0), dist(a1, p1)) / 10. * dark) as u64;
677        let e = lerp2d(curve[i], curve[i + 1], 0.5);
678        for k in 0..m {
679            let mut p = vec![];
680            let s = k as f64 / m as f64 * 0.7;
681            for j in 1..n - 1 {
682                p.push(lerp2d(o[j], e, s));
683            }
684            out3.push(p);
685        }
686    }
687
688    let mut out4 = vec![];
689    if (out0.len() > 1) {
690        let mut clipper = out0[0].clone();
691        out4.push(out0[0].clone());
692        for i in 1..out0.len() {
693            out4.extend(clip(&out0[i], &clipper).dont_clip);
694            clipper = poly_union(&clipper, &out0[i], None);
695        }
696    }
697
698    return (
699        out2.clone()
700            .into_iter()
701            .flatten()
702            .chain(curve.clone().into_iter().rev())
703            .collect(),
704        out4.into_iter().chain(out2).chain(out3).collect(),
705    );
706}
707
708pub fn finlet(curve: &Polyline, h: f64, dir_opt: Option<i32>) -> (Polyline, Vec<Polyline>) {
709    let dir = dir_opt.unwrap_or(1);
710    let mut angs = vec![];
711    for i in 0..curve.len() {
712        if i == 0 {
713            angs.push(
714                f64::atan2(curve[i + 1].1 - curve[i].1, curve[i + 1].0 - curve[i].0) - PI / 2.,
715            );
716        } else if i == curve.len() - 1 {
717            angs.push(
718                f64::atan2(curve[i].1 - curve[i - 1].1, curve[i].0 - curve[i - 1].0) - PI / 2.,
719            );
720        } else {
721            let a0 = f64::atan2(curve[i - 1].1 - curve[i].1, curve[i - 1].0 - curve[i].0);
722            let mut a1 = f64::atan2(curve[i + 1].1 - curve[i].1, curve[i + 1].0 - curve[i].0);
723            while a1 > a0 {
724                a1 -= PI * 2.;
725            }
726            a1 += PI * 2.;
727            let a = (a0 + a1) / 2.;
728            angs.push(a);
729        }
730    }
731    let mut out0 = vec![];
732    for i in 0..curve.len() {
733        let t = i as f64 / (curve.len() - 1) as f64;
734        let a = angs[i];
735        let mut w = if (i + 1) % 3 != 0 { 0. } else { h };
736        if dir > 0 {
737            w *= 1. - t * 0.5;
738        } else {
739            w *= 0.5 + t * 0.5
740        }
741
742        let (x0, y0) = curve[i];
743        let x1 = x0 + f64::cos(a) * w;
744        let y1 = y0 + f64::sin(a) * w;
745        out0.push((x1, y1));
746    }
747    out0 = resample(&out0, 2.);
748    for i in 0..out0.len() {
749        let (x, y) = out0[i];
750        out0[i].0 += noise(x * 0.1, Some(y * 0.1), None) * 2. - 3.;
751        out0[i].1 += noise(x * 0.1, Some(y * 0.1), None) * 2. - 3.;
752    }
753    out0.push(curve[curve.len() - 1]);
754    return (out0.concat(&curve.rev()), vec![out0]);
755}
756
757pub fn fin_adipose(curve: &Polyline, dx: f64, dy: f64, r: f64) -> (Polyline, Vec<Polyline>) {
758    let n = 20;
759    let (x0, y0) = curve[curve.len() / 2];
760    let (x, y) = (x0 + dx, y0 + dy);
761    let (x1, y1) = curve[0];
762    let (x2, y2) = curve[curve.len() - 1];
763    let d1 = dist((x, y), (x1, y1));
764    let d2 = dist((x, y), (x2, y2));
765    let a1 = f64::acos(r / d1);
766    let a2 = f64::acos(r / d2);
767    let a01 = f64::atan2(y1 - y, x1 - x) + a1;
768    let mut a02 = f64::atan2(y2 - y, x2 - x) - a2;
769    a02 -= PI * 2.;
770    while a02 < a01 {
771        a02 += PI * 2.;
772    }
773    let mut out0 = vec![(x1, y1)];
774    for i in 0..n {
775        let t = i as f64 / (n - 1) as f64;
776        let a = lerp(a01, a02, t);
777        let p = (x + f64::cos(a) * r, y + f64::sin(a) * r);
778        out0.push(p);
779    }
780    out0.push((x2, y2));
781    out0 = resample(&out0, 3.);
782    for i in 0..out0.len() {
783        let t = i as f64 / (out0.len() - 1) as f64;
784        let s = f64::sin(t * PI);
785        let (x, y) = out0[i];
786        out0[i].0 += (noise(x * 0.01, Some(y * 0.01), None) - 0.5) * s * 50.;
787        out0[i].1 += (noise(x * 0.01, Some(y * 0.01), None) - 0.5) * s * 50.;
788    }
789    let cc = out0.concat(&curve.rev());
790    let mut out1 = clip(&trsl_poly(&out0, 0., 4.), &cc).clip;
791    fn shape((x, y): Point, t: usize) -> bool {
792        rand() < (t as f64 * PI).sin()
793    }
794    out1 = binclip_multi(&out1, Rc::new(shape)).clip;
795    return (cc, vec![out0].into_iter().chain(out1).collect());
796}
797
798pub fn fish_lip((mut x0, mut y0): Point, (mut x1, mut y1): Point, w: f64) -> Polyline {
799    x0 += rand() * 0.001 - 0.0005;
800    y0 += rand() * 0.001 - 0.0005;
801    x1 += rand() * 0.001 - 0.0005;
802    y1 += rand() * 0.001 - 0.0005;
803    let h = dist((x0, y0), (x1, y1));
804    let a0 = f64::atan2(y1 - y0, x1 - x0);
805    let n = 10;
806    let ang = f64::acos(w / h);
807    let dx = f64::cos(a0 + PI / 2.) * 0.5;
808    let dy = f64::sin(a0 + PI / 2.) * 0.5;
809    let mut o = vec![(x0 - dx, y0 - dy)];
810    for i in 0..n {
811        let t = i as f64 / (n - 1) as f64;
812        let a = lerp(ang, PI * 2. - ang, t) + a0;
813        let x = -f64::cos(a) * w + x1;
814        let y = -f64::sin(a) * w + y1;
815        o.push((x, y));
816    }
817    o.push((x0 + dx, y0 + dy));
818    o = resample(&o, 2.5);
819    for i in 0..o.len() {
820        let (x, y) = o[i];
821        o[i].0 += noise(x * 0.05, Some(y * 0.05), Some(-1.)) * 2. - 1.;
822        o[i].1 += noise(x * 0.05, Some(y * 0.05), Some(-2.)) * 2. - 1.;
823    }
824    return o;
825}
826
827pub fn fish_teeth(
828    (x0, y0): Point,
829    (x1, y1): Point,
830    h: f64,
831    dir: i64,
832    sep_opt: Option<f64>,
833) -> Vec<Polyline> {
834    let sep = sep_opt.unwrap_or(3.5);
835    let n = f64::max(2., dist((x0, y0), (x1, y1)).trunc() / sep) as i64;
836    let ang = f64::atan2(y1 - y0, x1 - x0);
837    let mut out = vec![];
838    for i in 0..n {
839        let t = i as f64 / (n as f64 - 1.);
840        let a = lerp2d((x0, y0), (x1, y1), t);
841        let w = h * t;
842        let b = (
843            a.0 + f64::cos(ang + dir as f64 * PI / 2.) * w,
844            a.1 + f64::sin(ang + dir as f64 * PI / 2.) * w,
845        );
846        let c = (a.0 + 1. * f64::cos(ang), a.1 + 1. * f64::sin(ang));
847        let d = (a.0 + 1. * f64::cos(ang + PI), a.1 + 1. * f64::sin(ang + PI));
848        let e = lerp2d(c, b, 0.7);
849        let f = lerp2d(d, b, 0.7);
850        let g = (
851            a.0 + f64::cos(ang + dir as f64 * (PI / 2. + 0.15)) * w,
852            a.1 + f64::sin(ang + dir as f64 * (PI / 2. + 0.15)) * w,
853        );
854        out.push(vec![c, e, g, f, d])
855        // out.push(barbel(...a,10,ang+dir*PI/2.))
856    }
857    return out;
858}
859
860pub fn fish_jaw((x0, y0): Point, (x1, y1): Point, (x2, y2): Point) -> (Polyline, Vec<Polyline>) {
861    let n = 10;
862    let ang = f64::atan2(y2 - y0, x2 - x0);
863    let d = dist((x0, y0), (x2, y2));
864    let mut o = vec![];
865    for i in 0..n {
866        let t = i as f64 / (n as f64 - 1.);
867        let s = f64::sin(t * PI);
868        let w = s * d / 20.;
869        let p = lerp2d((x2, y2), (x0, y0), t);
870        let q = (
871            p.0 + f64::cos(ang - PI / 2.) * w,
872            p.1 + f64::sin(ang - PI / 2.) * w,
873        );
874        let qq = (
875            q.0 + (noise(q.0 * 0.01, Some(q.1 * 0.01), Some(1.)) - 0.5) * 4. * s,
876            q.1 + (noise(q.0 * 0.01, Some(q.1 * 0.01), Some(4.)) - 0.5) * 4. * s,
877        );
878        o.push(qq);
879    }
880    return (
881        vec![(x2, y2), (x1, y1), (x0, y0)],
882        [o.clone()]
883            .into_iter()
884            .chain(vein_shape(&o, Some(5)))
885            .collect(),
886    );
887}
888
889pub fn fish_eye_a(ex: f64, ey: f64, rad: f64) -> (Polyline, Vec<Polyline>) {
890    let n = 20;
891    let mut eye0 = vec![];
892    let mut eye1 = vec![];
893    let mut eye2 = vec![];
894    for i in 0..n {
895        let t = i as f64 / (n as f64 - 1.);
896        let a = t * PI * 2. + PI / 4. * 3.;
897        eye0.push((ex + f64::cos(a) * rad, ey + f64::sin(a) * rad));
898        if t > 0.5 {
899            eye1.push((
900                ex + f64::cos(a) * (rad * 0.8),
901                ey + f64::sin(a) * (rad * 0.8),
902            ));
903        }
904        eye2.push((
905            ex + f64::cos(a) * (rad * 0.4) - 0.75,
906            ey + f64::sin(a) * (rad * 0.4) - 0.75,
907        ));
908    }
909
910    let ef = shade_shape(&eye2, Some(2.7), Some(10.), Some(10.));
911    return (
912        eye0.clone(),
913        [eye0, eye1, eye2].into_iter().chain(ef).collect(),
914    );
915}
916
917pub fn fish_eye_b(ex: f64, ey: f64, rad: f64) -> (Polyline, Vec<Polyline>) {
918    let n = 20;
919    let mut eye0 = vec![];
920    let mut eye1 = vec![];
921    let mut eye2 = vec![];
922    for i in 0..n {
923        let t = i as f64 / (n as f64 - 1.);
924        let a = t * PI * 2. + E;
925        eye0.push((ex + f64::cos(a) * rad, ey + f64::sin(a) * rad));
926        eye2.push((
927            ex + f64::cos(a) * (rad * 0.4),
928            ey + f64::sin(a) * (rad * 0.4),
929        ));
930    }
931    let m = ((rad * 0.6) / 2.).trunc() as usize;
932    for i in 0..m {
933        let r = rad - i as f64 * 2.;
934        let mut e = vec![];
935        for i in 0..n {
936            let t = i as f64 / (n as f64 - 1.);
937            let a = lerp(PI * 7. / 8., PI * 13. / 8., t);
938            e.push((ex + f64::cos(a) * r, ey + f64::sin(a) * r));
939        }
940        eye1.push(e);
941    }
942    let mut trig = vec![
943        (
944            ex + f64::cos(-PI * 3. / 4.) * (rad * 0.9),
945            ey + f64::sin(-PI * 3. / 4.) * (rad * 0.9),
946        ),
947        (ex + 1., ey + 1.),
948        (
949            ex + f64::cos(-PI * 11. / 12.) * (rad * 0.9),
950            ey + f64::sin(-PI * 11. / 12.) * (rad * 0.9),
951        ),
952    ];
953    trig = resample(&trig, 3.);
954    for i in 0..trig.len() {
955        let (mut x, mut y) = trig[i];
956        x += noise(x * 0.1, Some(y * 0.1), Some(22.)) * 4. - 2.;
957        y += noise(x * 0.1, Some(y * 0.1), Some(33.)) * 4. - 2.;
958        trig[i] = (x, y);
959    }
960
961    let mut ef = fill_shape(&eye2, Some(1.5));
962
963    ef = clip_multi(&ef, &trig).dont_clip;
964    eye1 = clip_multi(&eye1, &trig).dont_clip;
965    let eye2_clip = clip(&eye2, &trig).dont_clip;
966
967    return (
968        eye0.clone(),
969        vec![eye0]
970            .into_iter()
971            .chain(eye1)
972            .chain(eye2_clip)
973            .chain(ef)
974            .collect(),
975    );
976}
977
978pub fn barbel((mut x, mut y): Point, n: usize, mut ang: f64, dd_opt: Option<f64>) -> Polyline {
979    let dd = dd_opt.unwrap_or(3.);
980    let mut curve = vec![(x, y)];
981    let sd = rand() * PI * 2.;
982    let mut ar = 1.;
983    for i in 0..n {
984        x += f64::cos(ang) * dd;
985        y += f64::sin(ang) * dd;
986        ang += (noise(i as f64 * 0.1, Some(sd), None) - 0.5) * ar;
987        if i < n / 2 {
988            ar *= 1.02;
989        } else {
990            ar *= 0.92;
991        }
992        curve.push((x, y));
993    }
994    let mut o0 = vec![];
995    let mut o1 = vec![];
996    for i in 0..n - 1 {
997        let t = i as f64 / (n - 1) as f64;
998        let w = 1.5 * (1. - t);
999
1000        let b = curve[i];
1001        let c = curve[i + 1];
1002
1003        let mut a1 = f64::atan2(c.1 - b.1, c.0 - b.0);
1004        let a2;
1005
1006        if let Some(a) = if i == 0 { None } else { curve.get(i - 1) } {
1007            let a0 = f64::atan2(a.1 - b.1, a.0 - b.0);
1008
1009            a1 -= PI * 2.;
1010            while a1 < a0 {
1011                a1 += PI * 2.;
1012            }
1013            a2 = (a0 + a1) / 2.;
1014        } else {
1015            a2 = a1 - PI / 2.;
1016        }
1017
1018        o0.push((b.0 + f64::cos(a2) * w, b.1 + f64::sin(a2) * w));
1019        o1.push((b.0 + f64::cos(a2 + PI) * w, b.1 + f64::sin(a2 + PI) * w));
1020    }
1021    o0.push(curve[curve.len() - 1]);
1022    o1.reverse();
1023    o0.extend(o1);
1024    return o0;
1025}
1026
1027pub fn fish_head(
1028    (x0, y0): Point,
1029    (x1, y1): Point,
1030    (x2, y2): Point,
1031    mut arg: Params,
1032) -> (Polyline, Vec<Polyline>) {
1033    let n = 20;
1034    let mut curve0 = vec![];
1035    let mut curve1 = vec![];
1036    let mut curve2 = vec![];
1037    for i in 0..n {
1038        let t = i as f64 / (n as f64 - 1.);
1039        let a = PI / 2. * t;
1040        let x = x1 - pow(f64::cos(a), 1.5) * (x1 - x0);
1041        let y = y0 - pow(f64::sin(a), 1.5) * (y0 - y1);
1042        // let x = lerp(x0,x1,t);
1043        // let y = lerp(y0,y1,t);
1044
1045        let dx = (noise(x * 0.01, Some(y * 0.01), Some(9.)) * 40. - 20.) * (1.01 - t);
1046        let dy = (noise(x * 0.01, Some(y * 0.01), Some(8.)) * 40. - 20.) * (1.01 - t);
1047        curve0.push((x + dx, y + dy));
1048    }
1049    for i in 0..n {
1050        let t = i as f64 / (n as f64 - 1.);
1051        let a = PI / 2. * t;
1052        let x = x2 - pow(f64::cos(a), 0.8) * (x2 - x0);
1053        let y = y0 + pow(f64::sin(a), 1.5) * (y2 - y0);
1054
1055        let dx = (noise(x * 0.01, Some(y * 0.01), Some(9.)) * 40. - 20.) * (1.01 - t);
1056        let dy = (noise(x * 0.01, Some(y * 0.01), Some(8.)) * 40. - 20.) * (1.01 - t);
1057        curve1.insert(0, (x + dx, y + dy));
1058    }
1059    let ang = f64::atan2(y2 - y1, x2 - x1);
1060    for i in 1..n - 1 {
1061        let t = i as f64 / (n as f64 - 1.);
1062        let p = lerp2d((x1, y1), (x2, y2), t);
1063        let s = pow(f64::sin(t * PI), 0.5);
1064        let r = noise(t * 2., Some(1.2), None) * s * 20.;
1065
1066        let dx = f64::cos(ang - PI / 2.) * r;
1067        let dy = f64::sin(ang - PI / 2.) * r;
1068        curve2.push((p.0 + dx, p.1 + dy));
1069    }
1070    let outline = curve0
1071        .iter()
1072        .chain(curve2.iter())
1073        .chain(curve1.iter())
1074        .map(|p| *p)
1075        .collect();
1076
1077    let mut inline: Polyline = curve2[(curve2.len() / 3)..]
1078        .iter()
1079        .chain(curve1[0..curve1.len() / 2].iter())
1080        .take(curve0.len())
1081        .map(|p| *p)
1082        .collect();
1083    for i in 0..inline.len() {
1084        let t = i as f64 / (inline.len() - 1) as f64;
1085        let s = f64::sin(t * PI).powi(2) * 0.1 + 0.12;
1086        inline[i] = lerp2d(inline[i], curve0[i], s);
1087    }
1088    let dix = (x0 - inline[inline.len() - 1].0) * 0.3;
1089    let diy = (y0 - inline[inline.len() - 1].1) * 0.2;
1090    for i in 0..inline.len() {
1091        inline[i] = (inline[i].0 + dix, inline[i].1 + diy);
1092    }
1093
1094    let par = (0.475, 0.375);
1095    let mut ex = x0 * par.0 + x1 * par.1 + x2 * (1. - par.0 - par.1);
1096    let mut ey = y0 * par.0 + y1 * par.1 + y2 * (1. - par.0 - par.1);
1097    let d0 = pt_seg_dist((ex, ey), (x0, y0), (x1, y1));
1098    let d1 = pt_seg_dist((ex, ey), (x0, y0), (x2, y2));
1099    if d0 < arg.eye_size && d1 < arg.eye_size {
1100        arg.eye_size = f64::min(d0, d1);
1101    } else if d0 < arg.eye_size {
1102        let ang = f64::atan2(y1 - y0, x1 - x0) + PI / 2.;
1103        ex = x0 * 0.5 + x1 * 0.5 + f64::cos(ang) * arg.eye_size;
1104        ey = y0 * 0.5 + y1 * 0.5 + f64::sin(ang) * arg.eye_size;
1105    }
1106
1107    let jaw_pt0 = curve1[18 - arg.mouth_size];
1108    let jaw_l = dist(jaw_pt0, curve1[18]) * arg.jaw_size;
1109    let jaw_ang0 = f64::atan2(curve1[18].1 - jaw_pt0.1, curve1[18].0 - jaw_pt0.0);
1110    let jaw_ang = jaw_ang0 - (arg.has_teeth as f64 * 0.5 + 0.5) * arg.jaw_open as f64 * PI / 4.;
1111    let jaw_pt1 = (
1112        jaw_pt0.0 + f64::cos(jaw_ang) * jaw_l,
1113        jaw_pt0.1 + f64::sin(jaw_ang) * jaw_l,
1114    );
1115
1116    let (eye0, mut ef) = if arg.eye_type != 0 {
1117        fish_eye_b(ex, ey, arg.eye_size)
1118    } else {
1119        fish_eye_a(ex, ey, arg.eye_size)
1120    };
1121    ef = clip_multi(&ef, &outline).clip;
1122
1123    let inlines = clip(&inline, &eye0).dont_clip;
1124
1125    let lip0 = fish_lip(jaw_pt0, curve1[18], 3.);
1126
1127    let lip1 = fish_lip(jaw_pt0, jaw_pt1, 3.);
1128
1129    let (jc, mut jaw) = fish_jaw(curve1[15 - arg.mouth_size], jaw_pt0, jaw_pt1);
1130
1131    jaw = clip_multi(&jaw, &lip1).dont_clip;
1132    jaw = clip_multi(&jaw, &outline).dont_clip;
1133
1134    let mut teeth0s = vec![];
1135    let mut teeth1s = vec![];
1136    if arg.has_teeth != 0 {
1137        let teeth0 = fish_teeth(
1138            jaw_pt0,
1139            curve1[18],
1140            arg.teeth_length as f64,
1141            -1,
1142            Some(arg.teeth_space),
1143        );
1144        let teeth1 = fish_teeth(
1145            jaw_pt0,
1146            jaw_pt1,
1147            arg.teeth_length as f64,
1148            1,
1149            Some(arg.teeth_space),
1150        );
1151
1152        teeth0s = clip_multi(&teeth0, &lip0).dont_clip;
1153        teeth1s = clip_multi(&teeth1, &lip1).dont_clip;
1154    }
1155
1156    let olines = clip(&outline, &lip0).dont_clip;
1157
1158    let lip0s = clip(&lip0, &lip1).dont_clip;
1159
1160    let mut sh = shade_shape(&outline, Some(6.), Some(-6.), Some(-6.));
1161    sh = clip_multi(&sh, &lip0).dont_clip;
1162    sh = clip_multi(&sh, &eye0).dont_clip;
1163
1164    let mut sh2 = vein_shape(&outline, Some(arg.head_texture_amount));
1165
1166    // let sh2 = patternshade_shape(outline,3,(x,y)=>{
1167    //   return noise(x*0.1,y*0.1)>0.6;
1168    // })
1169
1170    sh2 = clip_multi(&sh2, &lip0).dont_clip;
1171    sh2 = clip_multi(&sh2, &eye0).dont_clip;
1172
1173    let mut bbs = vec![];
1174
1175    let mut lip1s = vec![lip1.clone()];
1176
1177    if arg.has_moustache != 0 {
1178        let bb0 = barbel(jaw_pt0, arg.moustache_length, PI * 3. / 4., Some(1.5));
1179        lip1s = clip(&lip1, &bb0).dont_clip;
1180        jaw = clip_multi(&jaw, &bb0).dont_clip;
1181        bbs.push(bb0);
1182    }
1183
1184    if arg.has_beard != 0 {
1185        let jaw_pt;
1186        if !jaw.is_empty() && !jaw[0].is_empty() {
1187            jaw_pt = jaw[0][!!(jaw[0].len() / 2)];
1188        } else {
1189            jaw_pt = curve1[8];
1190        }
1191        let bb1 = trsl_poly(
1192            &barbel(
1193                jaw_pt,
1194                arg.beard_length,
1195                PI * 0.6 + rand() * 0.4 - 0.2,
1196                None,
1197            ),
1198            rand() * 1. - 0.5,
1199            rand() * 1. - 0.5,
1200        );
1201        let bb2 = trsl_poly(
1202            &barbel(
1203                jaw_pt,
1204                arg.beard_length,
1205                PI * 0.6 + rand() * 0.4 - 0.2,
1206                None,
1207            ),
1208            rand() * 1. - 0.5,
1209            rand() * 1. - 0.5,
1210        );
1211        let bb3 = trsl_poly(
1212            &barbel(
1213                jaw_pt,
1214                arg.beard_length,
1215                PI * 0.6 + rand() * 0.4 - 0.2,
1216                None,
1217            ),
1218            rand() * 1. - 0.5,
1219            rand() * 1. - 0.5,
1220        );
1221
1222        let mut bb3c = clip_multi(&vec![bb3], &bb2).dont_clip;
1223        bb3c = clip_multi(&bb3c, &bb1).dont_clip;
1224        let bb2c = clip_multi(&vec![bb2], &bb1).dont_clip;
1225        bbs.push(bb1);
1226        bbs.extend(bb2c.into_iter().chain(bb3c));
1227    }
1228
1229    let mut outline_l = vec![
1230        (0., 0.),
1231        (curve0.last().unwrap().0, 0.),
1232        curve0[curve0.len() - 1],
1233    ];
1234    outline_l.extend(curve2);
1235    outline_l.extend([curve1[0], (curve1[0].0, 300.), (0., 300.)]);
1236
1237    return (
1238        outline_l,
1239        [
1240            olines, inlines, lip0s, lip1s, ef, sh, sh2, bbs, teeth0s, teeth1s, jaw,
1241        ]
1242        .into_iter()
1243        .flatten()
1244        .collect(),
1245    );
1246}
1247
1248pub fn bean(x: f64) -> f64 {
1249    f64::powf(0.25 - f64::powf(x - 0.5, 2.), 0.5) * (2.6 + 2.4 * f64::powf(x, 1.5)) * 0.542
1250}
1251
1252fn rev_slice<T: Copy>(v: &Vec<T>, start: usize, end: usize) -> Vec<T> {
1253    let mut out = vec![];
1254    for i in (start..end).rev() {
1255        out.push(v[i]);
1256    }
1257    out
1258}
1259
1260pub fn fish(arg: Params) -> Vec<Polyline> {
1261    let n = 32;
1262    let mut curve0 = vec![];
1263    let mut curve1 = vec![];
1264    if arg.body_curve_type == 0 {
1265        let s = arg.body_curve_amount;
1266        for i in 0..n {
1267            let t = i as f64 / ((n as f64) - 1.);
1268
1269            let x = 225. + (t - 0.5) * arg.body_length;
1270            let y = 150.
1271                - ((t * PI).sin() * lerp(0.5, 1., noise(t * 2., Some(1.), None)) * s + (1. - s))
1272                    * arg.body_height as f64;
1273            curve0.push((x, y));
1274        }
1275        for i in 0..n {
1276            let t = i as f64 / ((n as f64) - 1.);
1277            let x = 225. + (t - 0.5) * arg.body_length;
1278            let y = 150.
1279                + ((t * PI).sin() * lerp(0.5, 1., noise(t * 2., Some(2.), None)) * s + (1. - s))
1280                    * arg.body_height;
1281            curve1.push((x, y));
1282        }
1283    } else if arg.body_curve_type == 1 {
1284        for i in 0..n {
1285            let t = i as f64 / ((n as f64) - 1.);
1286
1287            let x = 225. + (t - 0.5) * arg.body_length;
1288            let y = 150.
1289                - lerp(
1290                    1. - arg.body_curve_amount,
1291                    1.,
1292                    lerp(0., 1., noise(t * 1.2, Some(1.), None)) * bean(1. - t),
1293                ) * arg.body_height;
1294            curve0.push((x, y));
1295        }
1296        for i in 0..n {
1297            let t = i as f64 / ((n as f64) - 1.);
1298            let x = 225. + (t - 0.5) * arg.body_length;
1299            let y = 150.
1300                + lerp(
1301                    1. - arg.body_curve_amount,
1302                    1.,
1303                    lerp(0., 1., noise(t * 1.2, Some(2.), None)) * bean(1. - t),
1304                ) * arg.body_height;
1305            curve1.push((x, y));
1306        }
1307    }
1308    let mut outline = curve0.concat(&curve1.rev());
1309    let mut sh = shade_shape(&outline, Some(8.), Some(-12.), Some(-12.));
1310
1311    let pattern_func: Option<Rc<dyn Fn((f64, f64)) -> bool>> = match arg.pattern_type {
1312        0 => None, // none
1313        1 => {
1314            // (x,y)=>{
1315            //   return noise(x*0.1,y*0.1)>0.55;
1316            // }
1317            Some(pattern_dot(arg.pattern_scale))
1318        }
1319        2 => Some(Rc::new(|(x, y)| {
1320            (noise(x * 0.1, Some(y * 0.1), None) * f64::max(0.35, (y - 10.) / 280.)) < 0.2
1321        })),
1322        3 => Some(Rc::new(move |(x, y)| {
1323            let dx = noise(x * 0.01, Some(y * 0.01), None) * 30.;
1324            ((x + dx) / (30. * arg.pattern_scale)).trunc() as i64 % 2 == 1
1325        })),
1326        4 => None, //small dot;
1327        _ => panic!("invalid pattern type: {}", arg.pattern_type),
1328    };
1329
1330    let mut bd = match arg.scale_type {
1331        0 => fish_body_a(&curve0, &curve1, arg.scale_scale, pattern_func.clone()),
1332        1 => fish_body_b(&curve0, &curve1, arg.scale_scale, pattern_func.clone()),
1333        2 => fish_body_c(&curve0, &curve1, arg.scale_scale),
1334        3 => fish_body_d(&curve0, &curve1, arg.scale_scale),
1335        _ => unreachable!(),
1336    };
1337
1338    let f0_func: Box<dyn Fn(f64) -> f64>;
1339    let f0_a0;
1340    let f0_a1;
1341    let f0_cv;
1342    let dl = arg.dorsal_length;
1343    if arg.dorsal_type == 0 {
1344        f0_a0 = 0.2 + deviate(0.05);
1345        f0_a1 = 0.3 + deviate(0.05);
1346        f0_cv = 0.;
1347
1348        f0_func = Box::new(move |t| {
1349            (0.3 + noise(t * 3., None, None) * 0.7) * dl * f64::sin(t * PI).powf(0.5)
1350        });
1351    } else if arg.dorsal_type == 1 {
1352        f0_a0 = 0.6 + deviate(0.05);
1353        f0_a1 = 0.3 + deviate(0.05);
1354        f0_cv = arg.dorsal_length / 8.;
1355        f0_func = Box::new(move |t| dl * ((f64::powi(t - 1., 2)) * 0.5 + (1. - t) * 0.5));
1356    } else {
1357        unreachable!();
1358    }
1359    let f0_curve;
1360    let c0: Polyline;
1361    let mut f0: Vec<Polyline>;
1362    if arg.dorsal_texture_type == 0 {
1363        f0_curve = resample(&curve0[arg.dorsal_start..arg.dorsal_end], 5.);
1364        (c0, f0) = fin_a(
1365            &f0_curve,
1366            f0_a0,
1367            f0_a1,
1368            f0_func,
1369            Some(false),
1370            Some(f0_cv),
1371            Some(0.),
1372            None,
1373        );
1374    } else {
1375        f0_curve = resample(&curve0[arg.dorsal_start..arg.dorsal_end], 15.);
1376        (c0, f0) = fin_b(&f0_curve, f0_a0, f0_a1, f0_func, None);
1377    }
1378    f0 = clip_multi(&f0, &trsl_poly(&outline, 0., 0.001)).dont_clip;
1379
1380    let mut f1_curve = vec![];
1381    let f1_func: Box<dyn Fn(f64) -> f64>;
1382    let f1_a0;
1383    let f1_a1;
1384    let f1_soft;
1385    let f1_cv;
1386    let f1_pt = lerp2d(curve0[arg.wing_start], curve1[arg.wing_end], arg.wing_y);
1387
1388    for i in 0..10 {
1389        let t = i as f64 / 9.;
1390        let y = lerp(
1391            f1_pt.1 - arg.wing_width / 2.,
1392            f1_pt.1 + arg.wing_width / 2.,
1393            t,
1394        );
1395        f1_curve.push((f1_pt.0 /*+ f64::sin(t*PI)*2*/, y));
1396    }
1397    if arg.wing_type == 0 {
1398        f1_a0 = -0.4 + deviate(0.05);
1399        f1_a1 = 0.4 + deviate(0.05);
1400        f1_soft = 10.;
1401        f1_cv = 0.;
1402        f1_func = Box::new(move |t| {
1403            (40. + (20. + noise(t * 3., None, None) * 70.) * f64::sin(t * PI).powf(0.5)) / 130.
1404                * arg.wing_length
1405        });
1406    } else {
1407        f1_a0 = 0. + deviate(0.05);
1408        f1_a1 = 0.4 + deviate(0.05);
1409        f1_soft = 5.;
1410        f1_cv = arg.wing_length / 25.;
1411        f1_func = Box::new(move |t| (arg.wing_length * (1. - t * 0.95)));
1412    }
1413
1414    let c1;
1415    let mut f1;
1416    if arg.wing_texture_type == 0 {
1417        f1_curve = resample(&f1_curve, 1.5);
1418        (c1, f1) = fin_a(
1419            &f1_curve,
1420            f1_a0,
1421            f1_a1,
1422            f1_func,
1423            Some(true),
1424            Some(f1_cv),
1425            Some(0.),
1426            Some(f1_soft),
1427        );
1428    } else {
1429        f1_curve = resample(&f1_curve, 4.);
1430        (c1, f1) = fin_b(&f1_curve, f1_a0, f1_a1, f1_func, Some(0.3));
1431    }
1432    bd = clip_multi(&bd, &c1).dont_clip;
1433
1434    let f2_curve;
1435    let f2_func: Box<dyn Fn(f64) -> f64>;
1436    let f2_a0;
1437    let f2_a1;
1438    if arg.pelvic_type == 0 {
1439        f2_a0 = -0.8 + deviate(0.05);
1440        f2_a1 = -0.5 + deviate(0.05);
1441        f2_func = Box::new(move |t| {
1442            (10. + (15. + noise(t * 3., None, None) * 60.) * f64::sin(t * PI).powf(0.5)) / 85.
1443                * arg.pelvic_length
1444        });
1445    } else {
1446        f2_a0 = -0.9 + deviate(0.05);
1447        f2_a1 = -0.3 + deviate(0.05);
1448        f2_func = Box::new(move |t| (t * 0.5 + 0.5) * arg.pelvic_length);
1449    }
1450
1451    let mut f2;
1452    if arg.pelvic_texture_type == 0 {
1453        f2_curve = resample(
1454            &rev_slice(&curve1, arg.pelvic_start, arg.pelvic_end),
1455            if arg.pelvic_type != 0 { 2. } else { 5. },
1456        );
1457        (_, f2) = fin_a(&f2_curve, f2_a0, f2_a1, f2_func, None, None, None, None);
1458    } else {
1459        f2_curve = resample(
1460            &rev_slice(&curve1, arg.pelvic_start, arg.pelvic_end),
1461            if arg.pelvic_type != 0 { 2. } else { 15. },
1462        );
1463        (_, f2) = fin_b(&f2_curve, f2_a0, f2_a1, f2_func, None);
1464    }
1465    f2 = clip_multi(&f2, &c1).dont_clip;
1466
1467    let f3_curve;
1468    let f3_func: Box<dyn Fn(f64) -> f64>;
1469    let f3_a0;
1470    let f3_a1;
1471    if arg.anal_type == 0 {
1472        f3_a0 = -0.4 + deviate(0.05);
1473        f3_a1 = -0.4 + deviate(0.05);
1474        f3_func = Box::new(move |t| {
1475            (10. + (10. + noise(t * 3., None, None) * 30.) * f64::sin(t * PI).powf(0.5)) / 50.
1476                * arg.anal_length
1477        });
1478    } else {
1479        f3_a0 = -0.4 + deviate(0.05);
1480        f3_a1 = -0.4 + deviate(0.05);
1481        f3_func = Box::new(move |t| arg.anal_length * (t * t * 0.8 + 0.2));
1482    }
1483    let mut f3;
1484    if arg.anal_texture_type == 0 {
1485        f3_curve = resample(&rev_slice(&curve1, arg.anal_start, arg.anal_end), 5.);
1486        (_, f3) = fin_a(&f3_curve, f3_a0, f3_a1, f3_func, None, None, None, None);
1487    } else {
1488        f3_curve = resample(&rev_slice(&curve1, arg.anal_start, arg.anal_end), 15.);
1489        (_, f3) = fin_b(&f3_curve, f3_a0, f3_a1, f3_func, None);
1490    }
1491    f3 = clip_multi(&f3, &c1).dont_clip;
1492
1493    let mut f4_curve;
1494    let c4;
1495    let mut f4;
1496    let f4_r = dist(curve0[curve0.len() - 2], curve1[curve1.len() - 2]);
1497    let mut f4_n = (f4_r / 1.5).trunc();
1498    f4_n = f64::max(f64::min(f4_n, 20.), 8.);
1499    let f4_d = f4_r / f4_n;
1500    // console.log(f4_n,f4_d);
1501    if arg.tail_type == 0 {
1502        f4_curve = vec![curve0[curve0.len() - 1], curve1[curve1.len() - 1]];
1503        f4_curve = resample(&f4_curve, f4_d);
1504        (c4, f4) = fin_a(
1505            &f4_curve,
1506            -0.6,
1507            0.6,
1508            Box::new(move |t| {
1509                (75. - (10. + noise(t * 3., None, None) * 10.) * f64::sin(3. * t * PI - PI)) / 75.
1510                    * arg.tail_length
1511            }),
1512            Some(true),
1513            None,
1514            None,
1515            None,
1516        );
1517    } else if arg.tail_type == 1 {
1518        f4_curve = vec![curve0[curve0.len() - 2], curve1[curve1.len() - 2]];
1519        f4_curve = resample(&f4_curve, f4_d);
1520        (c4, f4) = fin_a(
1521            &f4_curve,
1522            -0.6,
1523            0.6,
1524            Box::new(move |t| arg.tail_length * (f64::sin(t * PI) * 0.5 + 0.5)),
1525            Some(true),
1526            None,
1527            None,
1528            None,
1529        );
1530    } else if arg.tail_type == 2 {
1531        f4_curve = vec![curve0[curve0.len() - 1], curve1[curve1.len() - 1]];
1532        f4_curve = resample(&f4_curve, f4_d * 0.7);
1533        let cv = arg.tail_length / 8.;
1534        (c4, f4) = fin_a(
1535            &f4_curve,
1536            -0.6,
1537            0.6,
1538            Box::new(move |t| (f64::abs(f64::cos(PI * t)) * 0.8 + 0.2) * arg.tail_length),
1539            Some(true),
1540            Some(cv),
1541            Some(-cv),
1542            None,
1543        );
1544    } else if arg.tail_type == 3 {
1545        f4_curve = vec![curve0[curve0.len() - 2], curve1[curve1.len() - 2]];
1546        f4_curve = resample(&f4_curve, f4_d);
1547
1548        (c4, f4) = fin_a(
1549            &f4_curve,
1550            -0.6,
1551            0.6,
1552            Box::new(move |t| (1. - f64::sin(t * PI) * 0.3) * arg.tail_length),
1553            Some(true),
1554            None,
1555            None,
1556            None,
1557        );
1558    } else if arg.tail_type == 4 {
1559        f4_curve = vec![curve0[curve0.len() - 2], curve1[curve1.len() - 2]];
1560        f4_curve = resample(&f4_curve, f4_d);
1561        (c4, f4) = fin_a(
1562            &f4_curve,
1563            -0.6,
1564            0.6,
1565            Box::new(move |t| (1. - f64::sin(t * PI) * 0.6) * (1. - t * 0.45) * arg.tail_length),
1566            Some(true),
1567            None,
1568            None,
1569            None,
1570        );
1571    } else if arg.tail_type == 5 {
1572        f4_curve = vec![curve0[curve0.len() - 2], curve1[curve1.len() - 2]];
1573        f4_curve = resample(&f4_curve, f4_d);
1574        (c4, f4) = fin_a(
1575            &f4_curve,
1576            -0.6,
1577            0.6,
1578            Box::new(move |t| (1. - f64::sin(t * PI).powf(0.4) * 0.55) * arg.tail_length),
1579            Some(true),
1580            None,
1581            None,
1582            None,
1583        );
1584    } else {
1585        unreachable!();
1586    }
1587    // f4 = clip_multi(f4,trsl_poly(outline,-1,0)).dont_clip;
1588    bd = clip_multi(&bd, &trsl_poly(&c4, 1., 0.)).dont_clip;
1589
1590    f4 = clip_multi(&f4, &c1).dont_clip;
1591
1592    let mut f5_curve = vec![];
1593    let mut c5 = vec![];
1594    let mut f5 = vec![];
1595    if arg.finlet_type == 0 {
1596        //pass
1597    } else if arg.finlet_type == 1 {
1598        f5_curve = resample(&curve0[arg.dorsal_end..curve0.len() - 2], 5.);
1599        (c5, f5) = finlet(&f5_curve, 5., None);
1600        f5_curve = resample(&rev_slice(&curve1, arg.anal_end, curve1.len() - 2), 5.);
1601        if f5_curve.len() > 1 {
1602            (c5, f5) = finlet(&f5_curve, 5., None);
1603        }
1604    } else if arg.finlet_type == 2 {
1605        f5_curve = resample(&curve0[27..30], 5.);
1606        (c5, f5) = fin_adipose(&f5_curve, 20., -5., 6.);
1607        outline = poly_union(&outline, &trsl_poly(&c5, 0., -1.), None);
1608    } else {
1609        f5_curve = resample(&curve0[arg.dorsal_end + 2..curve0.len() - 3], 5.);
1610        if f5_curve.len() > 2 {
1611            (c5, f5) = fin_a(
1612                &f5_curve,
1613                0.2,
1614                0.3,
1615                Box::new(move |t| {
1616                    (0.3 + noise(t * 3., None, None) * 0.7)
1617                        * arg.dorsal_length
1618                        * 0.6
1619                        * f64::sin(t * PI).powf(0.5)
1620                }),
1621                None,
1622                None,
1623                None,
1624                None,
1625            );
1626        }
1627    }
1628    let (cf, fh) = if arg.neck_type == 0 {
1629        fish_head(
1630            (50. - arg.head_length, 150. + arg.nose_height),
1631            curve0[6],
1632            curve1[5],
1633            arg,
1634        )
1635    } else {
1636        fish_head(
1637            (50. - arg.head_length, 150. + arg.nose_height),
1638            curve0[5],
1639            curve1[6],
1640            arg,
1641        )
1642    };
1643    bd = clip_multi(&bd, &cf).dont_clip;
1644
1645    sh = clip_multi(&sh, &cf).dont_clip;
1646    sh = clip_multi(&sh, &c1).dont_clip;
1647
1648    f1 = clip_multi(&f1, &cf).dont_clip;
1649
1650    f0 = clip_multi(&f0, &c1).dont_clip;
1651
1652    let mut sh2 = vec![];
1653    if let Some(func) = pattern_func {
1654        if arg.scale_type > 1 {
1655            sh2 = patternshade_shape(
1656                &poly_union(&outline, &trsl_poly(&c0, 0., 3.), None),
1657                3.5,
1658                func,
1659            );
1660        } else {
1661            sh2 = patternshade_shape(&c0, 4.5, func);
1662        }
1663        sh2 = clip_multi(&sh2, &cf).dont_clip;
1664        sh2 = clip_multi(&sh2, &c1).dont_clip;
1665    }
1666
1667    let mut sh3 = vec![];
1668    if arg.pattern_type == 4 {
1669        sh3 = smalldot_shape(
1670            &poly_union(&outline, &trsl_poly(&c0, 0., 5.), None),
1671            arg.pattern_scale,
1672        );
1673        sh3 = clip_multi(&sh3, &c1).dont_clip;
1674        sh3 = clip_multi(&sh3, &cf).dont_clip;
1675    }
1676    bd.extend(f0);
1677    bd.extend(f1);
1678    bd.extend(f2);
1679    bd.extend(f3);
1680    bd.extend(f4);
1681    bd.extend(f5);
1682    bd.extend(fh);
1683    bd.extend(sh);
1684    bd.extend(sh2);
1685    bd.extend(sh3);
1686    bd
1687}
1688
1689fn put_text(txt: String) -> (f64, Vec<Vec<(f64, f64)>>) {
1690    let base = 500;
1691    let mut x = 0.;
1692    let mut o = vec![];
1693    for c in txt.chars() {
1694        let ord = c as i64;
1695        let idx;
1696        if 65 <= ord && ord <= 90 {
1697            idx = base + 1 + (ord - 65);
1698        } else if 97 <= ord && ord <= 122 {
1699            idx = base + 101 + (ord - 97);
1700        } else if ord == 46 {
1701            idx = 710;
1702        } else if ord == 32 {
1703            x += 10.;
1704            continue;
1705        } else {
1706            continue;
1707        }
1708        let (xmin, xmax, polylines) = compile_hershey(idx);
1709        o.extend(polylines.iter().map(|p| trsl_poly(p, x - xmin as f64, 0.)));
1710        x += (xmax - xmin) as f64;
1711    }
1712    return (x, o);
1713}
1714
1715pub fn reframe(
1716    mut polylines: Vec<Polyline>,
1717    pad_opt: Option<f64>,
1718    text: Option<String>,
1719) -> Vec<Polyline> {
1720    let pad = pad_opt.unwrap_or(20.);
1721
1722    let w = 500. - pad * 2.;
1723    let h = (300. - pad * 2.) - (if text.is_some() { 10. } else { 0. });
1724    let bbox = get_boundingbox(&flat(&polylines));
1725    let sw = w / bbox.w;
1726    let sh = h / bbox.h;
1727    let s = sw.min(sh);
1728    let px = (w - bbox.w * s) / 2.;
1729    let py = (h - bbox.h * s) / 2.;
1730    for i in 0..polylines.len() {
1731        for j in 0..polylines[i].len() {
1732            let (mut x, mut y) = polylines[i][j];
1733            x = (x - bbox.x) * s + px + pad;
1734            y = (y - bbox.y) * s + py + pad;
1735            polylines[i][j] = (x, y);
1736        }
1737    }
1738    let (mut tw, tp) = put_text(text.unwrap_or(String::new()));
1739    tw *= 0.3;
1740    polylines.extend(
1741        tp.into_iter()
1742            .map(|p| scl_poly(&shr_poly(&p, -0.3), 0.3, Some(0.3)))
1743            .map(|p| trsl_poly(&p, 250. - tw / 2., 300. - pad + 5.)),
1744    );
1745    return polylines;
1746}
1747
1748pub fn cleanup(mut polylines: Vec<Polyline>) -> Vec<Vec<(f64, f64)>> {
1749    for i in (polylines.len() - 1)..=0 {
1750        // polylines[i] = approx_poly_dp(polylines[i], 0.1);
1751        for j in 0..polylines[i].len() {
1752            polylines[i][j] = (
1753                (polylines[i][j].0 * 10000.).trunc() / 10000.,
1754                (polylines[i][j].1 * 10000.).trunc() / 10000.,
1755            );
1756        }
1757        if polylines[i].len() < 2 {
1758            polylines.splice(i..1, []);
1759            continue;
1760        }
1761        if polylines[i].len() == 2 {
1762            if dist(polylines[i][0], polylines[i][1]) < 0.9 {
1763                polylines.splice(i..1, []);
1764                continue;
1765            }
1766        }
1767    }
1768    return polylines;
1769}