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}
43fn 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 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 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 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 }
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(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 }
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 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 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, 1 => {
1314 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, _ => 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 , 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 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 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 } 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 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}