1use std::{fmt::{self, Display, Formatter},
23 cell::Cell,
24 io::{self, Write},
25 iter::Iterator,
26 ops::ControlFlow};
27use rgb::*;
28
29mod approx_priority_queue;
34use approx_priority_queue as pq;
35
36mod list;
37use list::List;
38
39#[derive(Debug, Clone, Copy, PartialEq)]
45pub struct BoundingBox {
46 pub xmin: f64,
47 pub xmax: f64,
48 pub ymin: f64,
49 pub ymax: f64,
50}
51
52impl BoundingBox {
53 #[inline]
54 pub(crate) fn empty() -> Self {
55 Self { xmin: f64::INFINITY, xmax: f64::NEG_INFINITY,
56 ymin: f64::INFINITY, ymax: f64::NEG_INFINITY }
57 }
58
59 #[inline]
61 pub fn is_empty(&self) -> bool {
62 !(self.xmin < self.xmax && self.ymin < self.ymax) }
64
65 #[inline]
68 fn contains(&self, p: &Point) -> bool {
69 let [x, y] = p.xy;
70 self.xmin <= x && x <= self.xmax && self.ymin <= y && y <= self.ymax
71 }
72
73 #[inline]
76 pub fn hull(&self, other: &Self) -> Self {
77 BoundingBox { xmin: self.xmin.min(other.xmin),
78 xmax: self.xmax.max(other.xmax),
79 ymin: self.ymin.min(other.ymin),
80 ymax: self.ymax.max(other.ymax) }
81 }
82}
83
84
85#[derive(Debug)]
97struct Point {
98 t: f64, xy: [f64; 2],
100 cost: f64, witness: Option<pq::Witness<list::Witness<Point>>>,
106}
107
108impl Clone for Point {
109 fn clone(&self) -> Self {
112 Self { witness: None, .. *self }
113 }
114}
115
116impl Point {
117 #[inline]
119 fn new_unchecked(t: f64, xy: [f64; 2]) -> Self {
120 Point { t, xy, cost: 0.,
121 witness: None }
122 }
123
124 #[inline]
125 fn cut(t: f64) -> Self {
126 Point { t, xy: [f64::NAN; 2],
127 cost: 0.,
128 witness: None }
129 }
130
131 #[inline]
133 fn is_valid(&self) -> bool {
134 self.xy.iter().all(|z| z.is_finite())
135 }
136
137}
138
139pub struct Sampling {
143 pq: PQ,
149 path: List<Point>,
150 guess_len: Cell<usize>,
153 vp: Option<BoundingBox>, }
155
156type PQ = pq::PQ<list::Witness<Point>>;
157
158impl Clone for Sampling {
159 fn clone(&self) -> Self {
160 Self { pq: PQ::new(),
163 path: self.path.clone(),
164 guess_len: self.guess_len.get().into(),
165 vp: self.vp }
166 }
167}
168
169#[derive(Debug, Clone, Copy)]
172struct Lengths {
173 t: f64,
174 x: f64,
175 y: f64,
176}
177
178impl Sampling {
179 #[inline]
181 pub fn is_empty(&self) -> bool { self.path.is_empty() }
182
183 #[inline]
185 pub(crate) fn empty() -> Self {
186 Self { pq: PQ::new(),
187 path: List::new(),
188 guess_len: 0.into(),
189 vp: None }
190 }
191
192 #[inline]
193 pub(crate) fn singleton(p: Point) -> Self {
194 debug_assert!(p.t.is_finite() && p.xy.iter().all(|z| z.is_finite()));
195 let mut path = List::new();
196 path.push_back(p);
197 Self { pq: PQ::new(), path, guess_len: 1.into(), vp: None }
198 }
199
200 #[inline]
201 pub(crate) fn set_vp(&mut self, bb: BoundingBox) {
202 self.vp = Some(bb);
203 }
204
205 pub(crate) fn len_txy(&self) -> Option<Lengths> {
208 if self.is_empty() { return None }
209 let p0 = self.path.first().unwrap();
210 let p1 = self.path.last().unwrap();
211 let len_x;
212 let len_y;
213 match self.vp {
214 Some(vp) => {
215 len_x = vp.xmax - vp.xmin;
216 len_y = vp.ymax - vp.ymin;
217 }
218 None => {
219 len_x = 1.;
220 len_y = 1.;
221 }
222 }
223 Some(Lengths { t: (p1.t - p0.t).abs(), x: len_x, y: len_y })
224 }
225
226 pub fn iter(&self) -> SamplingIter<'_> {
233 SamplingIter {
234 path: self.path.iter(),
235 prev_is_cut: true,
236 guess_len: self.guess_len.get(),
237 }
238 }
239
240 pub fn iter_mut(&mut self) -> SamplingIterMut<'_> {
245 SamplingIterMut {
246 path: self.path.iter_mut(),
247 guess_len: self.guess_len.get(),
248 }
249 }
250
251 #[inline]
254 pub fn x(&self) -> Vec<f64> {
255 let mut v = Vec::with_capacity(self.guess_len.get());
256 for [x, _] in self.iter() { v.push(x) }
257 v
258 }
259
260 #[inline]
263 pub fn y(&self) -> Vec<f64> {
264 let mut v = Vec::with_capacity(self.guess_len.get());
265 for [_, y] in self.iter() { v.push(y) }
266 v
267 }
268}
269
270pub struct SamplingIter<'a> {
274 path: list::Iter<'a, Point>,
275 prev_is_cut: bool,
276 guess_len: usize,
277}
278
279impl<'a> Iterator for SamplingIter<'a> {
280 type Item = [f64; 2];
281
282 fn next(&mut self) -> Option<Self::Item> {
283 match self.path.next() {
284 None => None,
285 Some(p) => {
286 self.guess_len -= 1;
287 if p.is_valid() {
288 self.prev_is_cut = false;
289 Some(p.xy)
290 } else if self.prev_is_cut {
291 let r = self.path.try_fold(0, |n, p| {
293 if p.is_valid() {
294 ControlFlow::Break((n, p))
295 } else {
296 ControlFlow::Continue(n+1)
297 }
298 });
299 match r {
300 ControlFlow::Continue(_) => {
301 self.guess_len = 0;
303 None
304 }
305 ControlFlow::Break((n, p)) => {
306 self.guess_len -= n;
307 self.prev_is_cut = false;
308 Some(p.xy)
309 }
310 }
311 } else {
312 self.prev_is_cut = true;
313 Some([f64::NAN; 2])
314 }
315 }
316 }
317 }
318
319 fn size_hint(&self) -> (usize, Option<usize>) {
320 (0, Some(self.guess_len))
321 }
322}
323
324pub struct SamplingIterMut<'a> {
328 path: list::IterMut<'a, Point>,
329 guess_len: usize,
330}
331
332impl<'a> Iterator for SamplingIterMut<'a> {
333 type Item = &'a mut [f64; 2];
334
335 fn next(&mut self) -> Option<Self::Item> {
336 match self.path.next() {
337 None => None,
338 Some(p) => {
339 self.guess_len -= 1;
340 Some(&mut p.xy)
341 }
342 }
343 }
344}
345
346#[derive(Debug)]
348enum Intersection {
349 Empty,
350 Pt(Point),
351 Seg(Point, Point),
352}
353
354impl Sampling {
355 pub fn bounding_box(&self) -> BoundingBox {
359 let mut points = self.iter().skip_while(|[x,_]| x.is_nan());
360 let mut bb = match &points.next() {
361 Some([x, y]) => BoundingBox { xmin: *x, xmax: *x,
362 ymin: *y, ymax: *y },
363 None => return BoundingBox::empty()
364 };
365 for [x, y] in points {
366 if x < bb.xmin { bb.xmin = x }
367 else if bb.xmax < x { bb.xmax = x };
368 if y < bb.ymin { bb.ymin = y }
369 else if bb.ymax < y { bb.ymax = y };
370 }
371 bb
372 }
373
374 pub fn transpose(&mut self) -> &mut Self {
376 for p in self.path.iter_mut() {
377 let [x, y] = p.xy;
378 p.xy = [y, x];
379 }
380 self
381 }
382
383 #[inline]
387 #[must_use]
388 fn intersect(p0: &Point, p1: &Point, bb: BoundingBox) -> Option<Point> {
389 let mut t = 1.; let [x0, y0] = p0.xy;
391 let [x1, y1] = p1.xy;
392 let dx = x1 - x0; let r = (if dx >= 0. {bb.xmax} else {bb.xmin} - x0) / dx;
394 if r < t { t = r } let dy = y1 - y0; let r = (if dy >= 0. {bb.ymax} else {bb.ymin} - y0) / dy;
397 if r < t { t = r };
398 if t <= 1e-14 {
399 None
400 } else {
401 Some(Point::new_unchecked(p0.t + t * (p1.t - p0.t),
402 [x0 + t * dx, y0 + t * dy]))
403 }
404 }
405
406 #[inline]
411 #[must_use]
412 fn intersect_seg(p0: &Point, p1: &Point, bb: BoundingBox) -> Intersection {
413 let mut t0 = 0.; let mut t1 = 1.; let [x0, y0] = p0.xy;
416 let [x1, y1] = p1.xy;
417 let dx = x1 - x0; let r0;
419 let r1; if dx >= 0. {
421 r0 = (bb.xmin - x0) / dx;
422 r1 = (bb.xmax - x0) / dx;
423 } else {
424 r0 = (bb.xmax - x0) / dx;
425 r1 = (bb.xmin - x0) / dx;
426 }
427 if r0 > 1. || r1 < 0. { return Intersection::Empty }
428 if r0 > 0. { t0 = r0 } if r1 < 1. { t1 = r1 }
430 let dy = y1 - y0; let r0;
432 let r1;
433 if dy >= 0. {
434 r0 = (bb.ymin - y0) / dy;
435 r1 = (bb.ymax - y0) / dy;
436 } else {
437 r0 = (bb.ymax - y0) / dy;
438 r1 = (bb.ymin - y0) / dy;
439 }
440 if r0 > t1 || r1 < t0 { return Intersection::Empty }
441 if r0 > t0 { t0 = r0 }
442 if r1 < t1 { t1 = r1 }
443 if t0 < t1 { let dt = p1.t - p0.t;
445 let q0 = Point::new_unchecked(p0.t + t0 * dt,
446 [x0 + t0 * dx, y0 + t0 * dy]);
447 let q1 = Point::new_unchecked(p0.t + t1 * dt,
448 [x0 + t1 * dx, y0 + t1 * dy]);
449 Intersection::Seg(q0, q1)
450 } else if t0 == t1 {
451 let q0 = Point::new_unchecked(
452 p0.t + t0 * (p1.t - p0.t),
453 [x0 + t0 * dx, y0 + t0 * dy]);
454 Intersection::Pt(q0)
455 } else {
456 Intersection::Empty
457 }
458 }
459
460 #[must_use]
466 pub fn clip(&self, bb: BoundingBox) -> Self {
467 let mut s = Sampling::empty();
468 let mut new_len: usize = 0;
469 let mut p0_opt: Option<&Point> = None;
471 let mut p0_inside = false;
472 let mut prev_cut = true; for p1 in self.path.iter() {
474 if prev_cut {
475 match (p0_opt, p1.is_valid()) {
483 (Some(p0), true) => {
484 let p1_inside = bb.contains(p1);
485 if p0_inside { s.path.push_back(p0.clone());
487 if p1_inside {
488 s.path.push_back(p1.clone());
489 new_len += 2;
490 prev_cut = false;
491 } else if
492 let Some(p) = Self::intersect(p0, p1, bb) {
493 let t = p.t;
494 s.path.push_back(p);
495 s.path.push_back(Point::cut(t));
496 new_len += 3;
497 } else {
498 s.path.push_back(Point::cut(p0.t));
499 new_len += 2;
500 }
501 } else if p1_inside { if let Some(p) = Self::intersect(p1, p0, bb) {
503 s.path.push_back(p); new_len += 1;
505 }
506 s.path.push_back(p1.clone());
507 new_len += 1;
508 prev_cut = false;
509 } else { match Self::intersect_seg(p0, p1, bb) {
511 Intersection::Seg(q0, q1) => {
512 let t1 = q1.t;
513 s.path.push_back(q0);
514 s.path.push_back(q1);
515 s.path.push_back(Point::cut(t1));
516 new_len += 3;
517 }
518 Intersection::Pt(p) => {
519 let t = p.t;
520 s.path.push_back(p);
521 s.path.push_back(Point::cut(t));
522 new_len += 2;
523 }
524 Intersection::Empty => (),
525 }
526 }
527 p0_opt = Some(p1);
528 p0_inside = p1_inside;
529 }
530 (None, true) => {
531 p0_opt = Some(p1);
532 p0_inside = bb.contains(p1);
533 }
534 (Some(p0), false) => {
535 if p0_inside {
536 s.path.push_back(p0.clone());
537 s.path.push_back(Point::cut(p0.t));
538 new_len += 2;
539 }
540 p0_opt = None;
541 }
542 (None, false) => (), }
544 } else {
545 let p0 = p0_opt.expect("No cut ⟹ previous point");
549 debug_assert!(p0_inside);
550 if p1.is_valid() {
551 p0_opt = Some(p1);
552 p0_inside = bb.contains(p1); if p0_inside { s.path.push_back(p1.clone());
555 new_len += 1;
556 } else { if let Some(p) = Self::intersect(p0, p1, bb) {
558 let t = p.t;
559 s.path.push_back(p);
560 s.path.push_back(Point::cut(t));
561 new_len += 2;
562 } else {
563 s.path.push_back(Point::cut(p0.t));
564 new_len += 1;
565 }
566 prev_cut = true;
567 }
568 } else { p0_opt = None;
570 s.path.push_back(p1.clone());
571 new_len += 1;
572 prev_cut = true
573 }
574 }
575 }
576 if prev_cut { s.path.pop_back(); }
577 s.set_vp(bb);
578 s.guess_len.set(new_len);
579 s
580 }
581
582 fn from_point_iterator<P>(points: P) -> Self
585 where P: IntoIterator<Item = Point> {
586 let mut s = Sampling::empty();
587 let mut points = points.into_iter();
588 let mut len: usize = 0;
589 macro_rules! skip_until_last_cut { () => {
590 let mut cut = None;
591 let mut first_pt = None;
592 for p in &mut points {
593 if p.is_valid() { first_pt = Some(p); break; }
594 cut = Some(p);
595 }
596 match (cut, first_pt) {
597 (_, None) => {
598 s.guess_len.set(len);
599 return s
600 }
601 (None, Some(p)) => {
602 s.path.push_back(p);
603 len += 1;
604 }
605 (Some(c), Some(p)) => {
606 s.path.push_back(Point::cut(c.t));
607 s.path.push_back(p);
608 len += 2;
609 }
610 }
611 }}
612 skip_until_last_cut!();
613 while let Some(p) = points.next() {
614 if p.is_valid() {
615 s.path.push_back(p);
616 len += 1;
617 } else {
618 s.path.push_back(Point::cut(p.t));
619 len += 1;
620 skip_until_last_cut!();
621 }
622 }
623 s.guess_len.set(len);
624 s
625 }
626
627 fn from_vec(mut points: Vec<Point>, incr: bool) -> Self {
632 if incr {
633 points.sort_unstable_by(|p1, p2| {
634 p1.t.partial_cmp(&p2.t).unwrap() });
636 } else {
637 points.sort_unstable_by(|p1, p2| {
638 p2.t.partial_cmp(&p1.t).unwrap() });
639 }
640 Self::from_point_iterator(points)
641 }
642}
643
644
645impl FromIterator<[f64; 2]> for Sampling {
646 fn from_iter<T>(points: T) -> Self
649 where T: IntoIterator<Item = [f64; 2]> {
650 Sampling::from_point_iterator(
651 points.into_iter().enumerate().map(|(i, xy @ [x, y])| {
652 let t = i as f64;
653 if x.is_finite() && y.is_finite() {
654 Point::new_unchecked(t, xy)
655 } else {
656 Point::cut(t)
657 } }))
658 }
659}
660
661impl From<(f64, f64)> for Point {
669 #[inline]
670 fn from((x, y): (f64, f64)) -> Self {
671 Point::new_unchecked(x, [x, y])
673 }
674}
675
676impl From<(f64, [f64; 2])> for Point {
677 #[inline]
679 fn from((t, xy): (f64, [f64;2])) -> Self {
680 if xy[0].is_finite() { Point::new_unchecked(t, xy) }
682 else { Point::cut(t) }
683 }
684}
685
686trait IntoFnPoint {
688 fn eval(&mut self, t: f64) -> Point;
689}
690
691struct FnPoint<T>(T);
696
697impl<T> IntoFnPoint for FnPoint<T> where T: FnMut(f64) -> f64 {
698 #[inline]
699 fn eval(&mut self, t: f64) -> Point {
700 Point::new_unchecked(t, [t, self.0(t)])
701 }
702}
703
704struct ParamPoint<T>(T);
705
706impl<T> IntoFnPoint for ParamPoint<T> where T: FnMut(f64) -> [f64; 2] {
707 #[inline]
708 fn eval(&mut self, t: f64) -> Point {
709 let xy @ [x, y] = self.0(t);
710 if x.is_finite() && y.is_finite() {
711 Point::new_unchecked(t, xy)
712 } else {
713 Point::new_unchecked(t, [xy[0], f64::NAN])
714 }
715 }
716}
717
718macro_rules! new_sampling_fn {
725 ($(#[$docfn: meta])*, $(#[$docfn_extra: meta])* $fun: ident -> $ft: ty,
727 $(#[$doc: meta])* $struct: ident,
729 $wrap_f: ident
730 ) => {
731 impl Sampling {
732 $(#[$docfn])*
733 $(#[$docfn_extra])*
737 #[must_use]
738 pub fn $fun<F>(f: F, a: f64, b: f64) -> $struct<F>
739 where F: FnMut(f64) -> $ft {
740 if !a.is_finite() {
741 panic!("curve_sampling::{}: a = {} must be finite",
742 stringify!($fun), a);
743 }
744 if !b.is_finite() {
745 panic!("curve_sampling::{}: b = {} must be finite",
746 stringify!($fun), b);
747 }
748 $struct { f: $wrap_f(f),
749 a, b, n: 100,
751 viewport: None,
752 init: vec![],
753 init_pt: vec![],
754 }
755 }
756 }
757
758 $(#[$doc])*
759 pub struct $struct<F> {
760 f: $wrap_f<F>, a: f64, b: f64,
761 n: usize,
762 viewport: Option<BoundingBox>,
763 init: Vec<f64>,
764 init_pt: Vec<Point>,
765 }
766
767 impl<F> $struct<F>
768 where F: FnMut(f64) -> $ft {
769 pub fn n(mut self, n: usize) -> Self {
772 if n < 2 {
773 panic!("curve_sampling: n = {} must at least be 2", n)
774 }
775 self.n = n;
776 self
777 }
778
779 pub fn viewport(mut self, vp: BoundingBox) -> Self {
782 self.viewport = Some(vp);
783 self
784 }
785
786 #[doc = stringify!(Sampling::$fun)]
788 pub fn init<'a, I>(mut self, ts: I) -> Self
792 where I: IntoIterator<Item = &'a f64> {
793 for &t in ts {
794 if self.a <= t && t <= self.b { self.init.push(t);
796 }
797 }
798 self
799 }
800
801 #[doc = stringify!(Sampling::$fun)]
807 pub fn init_pt<'a, I>(mut self, pts: I) -> Self
809 where I: IntoIterator<Item = &'a (f64, $ft)> {
810 for &p in pts {
811 if self.a <= p.0 && p.0 <= self.b { self.init_pt.push(Point::from(p));
813 }
814 }
815 self
816 }
817
818 fn eval_init(&mut self) {
821 for &t in &self.init {
824 self.init_pt.push(self.f.eval(t))
825 }
826 self.init.clear()
827 }
828 }
829 }
830}
831
832new_sampling_fn!(
837 ,
840 uniform -> f64,
851 Uniform,
854 FnPoint);
855
856impl<F> Uniform<F>
857where F: FnMut(f64) -> f64 {
858 pub fn build(mut self) -> Sampling {
860 if self.a == self.b {
861 let p = self.f.eval(self.a); if p.is_valid() {
863 return Sampling::singleton(p);
864 } else {
865 return Sampling::empty()
866 }
867 }
868 self.eval_init();
869 let mut points = self.init_pt;
873 let dt = (self.b - self.a) / (self.n - 1) as f64;
874 for i in 0 .. self.n {
875 let t = self.a + i as f64 * dt;
876 points.push(self.f.eval(t));
877 }
878 Sampling::from_vec(points, self.a < self.b)
879 }
880}
881
882mod cost {
887 use super::{Point, Lengths};
888
889 pub const HANGING_NODE: f64 = 5e5;
915
916 #[inline]
919 pub(crate) fn set_middle(p0: &Point, pm: &mut Point, p1: &Point,
920 len: Lengths)
921 {
922 let [x0, y0] = p0.xy;
923 let [xm, ym] = pm.xy;
924 let [x1, y1] = p1.xy;
925 let dx0m = (x0 - xm) / len.x;
926 let dy0m = (y0 - ym) / len.y;
927 let dx1m = (x1 - xm) / len.x;
928 let dy1m = (y1 - ym) / len.y;
929 let len0m = dx0m.hypot(dy0m);
930 let len1m = dx1m.hypot(dy1m);
931 if len0m == 0. || len1m == 0. {
932 pm.cost = 0.; } else {
934 let dx = - dx0m * dx1m - dy0m * dy1m;
935 let dy = dy0m * dx1m - dx0m * dy1m;
936 pm.cost = dy.atan2(dx); debug_assert!(!pm.cost.is_nan());
938 }
939 }
940
941 #[inline]
944 pub(crate) fn segment_vp(p0: &Point, p1: &Point, len: Lengths,
945 in_vp: bool) -> f64 {
946 if ! in_vp { return f64::NEG_INFINITY }
947 segment(p0, p1, len)
948 }
949
950 #[inline]
953 pub(crate) fn segment(p0: &Point, p1: &Point, len: Lengths) -> f64 {
954 let dt = (p1.t - p0.t).abs() / len.t; debug_assert!((0. ..=1.).contains(&dt), "dt = {dt} ∉ [0,1]");
956 let [x0, y0] = p0.xy;
960 let [x1, y1] = p1.xy;
961 let dx = ((x1 - x0) / len.x).abs();
962 let dy = ((y1 - y0) / len.y).abs();
963 let mut cost = p0.cost.abs() + p1.cost.abs(); if p0.cost * p1.cost < 0. {
965 if dx <= 0.01 && dy <= 0.01 { cost *= 0.5 }
968 else if !(dx <= 0.05 && dy <= 0.05) { cost *= 8. }
969 }
970 if dt >= 0.8 { cost }
971 else {
972 let dt = dt / 0.8;
973 dt * dt * (6. + (-8. + 3. * dt) * dt) * cost
974 }
975 }
976
977}
978
979fn push_segment(pq: &mut PQ,
984 p0: &mut list::Witness<Point>, p1: &Point,
985 len: Lengths, in_vp: bool) {
986 let cost_segment = cost::segment_vp(unsafe { p0.as_ref() },
990 p1, len, in_vp);
991 let w = pq.push(cost_segment, p0.clone());
993 unsafe { p0.as_mut().witness = Some(w) }
994}
995
996unsafe fn update_segment(pq: &mut PQ, p0: &Point, p1: &Point, len: Lengths) {
1002 match &p0.witness {
1003 Some(w) => {
1004 let priority = cost::segment(p0, p1, len);
1005 pq.increase_priority(w, priority)
1006 }
1007 None => panic!("Sampling::update_segment: unset witness"),
1008 }
1009}
1010
1011fn compute(s: &mut Sampling, in_vp: impl Fn(&Point) -> bool) {
1018 if let Some(len) = s.len_txy() {
1019 macro_rules! r { ($x: ident) => { unsafe { $x.as_ref() } } }
1020 macro_rules! m { ($x: ident) => { unsafe { $x.as_mut() } } }
1021 let mut pts = s.path.iter_witness_mut();
1023 let mut p0 = pts.next().unwrap();
1024 m!(p0).cost = 0.;
1025 let mut p0_is_valid = r!(p0).is_valid();
1026 let mut p0_in_vp = p0_is_valid && in_vp(r!(p0));
1027 let mut pm = match pts.next() {
1028 Some(p) => p,
1029 None => return };
1030 for p1 in pts {
1031 let pm_is_valid = r!(pm).is_valid();
1032 let pm_in_vp;
1033 if pm_is_valid {
1034 pm_in_vp = in_vp(r!(pm));
1035 if p0_is_valid && r!(p1).is_valid() {
1036 cost::set_middle(r!(p0), m!(pm), r!(p1), len);
1037 } else {
1038 m!(pm).cost = cost::HANGING_NODE;
1039 }
1040 } else { pm_in_vp = false;
1042 m!(pm).cost = 0.;
1043 }
1044 if p0_is_valid || pm_is_valid {
1045 push_segment(&mut s.pq, &mut p0, r!(pm), len,
1047 p0_in_vp || pm_in_vp);
1048 }
1049 p0 = pm;
1050 p0_is_valid = pm_is_valid;
1051 p0_in_vp = pm_in_vp;
1052 pm = p1;
1053 }
1054 m!(pm).cost = 0.; if p0_is_valid || r!(pm).is_valid() {
1056 let mut vp = p0_in_vp;
1057 if r!(pm).is_valid() { vp = vp || in_vp(r!(pm)) };
1058 push_segment(&mut s.pq, &mut p0, r!(pm), len, vp);
1059 }
1060 }
1061}
1062
1063fn refine_gen(s: &mut Sampling, n: usize,
1064 mut f: impl FnMut(f64) -> Point,
1065 in_vp: impl Fn(&Point) -> bool) {
1066 let len = match s.len_txy() {
1067 Some(txy) => txy,
1068 None => return };
1069 s.guess_len.set(s.guess_len.get() + n);
1070 macro_rules! r { ($x: ident) => { unsafe { $x.as_ref() } } }
1071 macro_rules! m { ($x: ident) => { unsafe { $x.as_mut() } } }
1072 for _ in 0 .. n {
1073 let mut p0: list::Witness<Point> = match s.pq.pop() {
1074 None => break,
1075 Some(p) => p };
1076 m!(p0).witness = None; let mut p1 = unsafe { p0.next().unwrap() };
1078 let t = (r!(p0).t + r!(p1).t) * 0.5;
1080 let mut pm = f(t);
1081 if r!(p0).is_valid() {
1082 if r!(p1).is_valid() {
1083 let mut pm = unsafe { s.path.insert_after(&mut p0, pm) };
1084 let mut pm_in_vp = false;
1085 if r!(pm).is_valid() {
1086 pm_in_vp = in_vp(r!(pm));
1087 cost::set_middle(r!(p0), m!(pm), r!(p1), len);
1088 if let Some(p_1) = unsafe { p0.prev() } {
1089 if r!(p_1).is_valid() {
1090 cost::set_middle(r!(p_1), m!(p0), r!(pm), len);
1091 unsafe {
1092 update_segment(&mut s.pq, p_1.as_ref(),
1093 p0.as_ref(), len) }
1094 }
1095 }
1096 if let Some(p2) = unsafe { p1.next() } {
1097 if r!(p2).is_valid() {
1098 cost::set_middle(r!(pm), m!(p1), r!(p2), len);
1099 unsafe {
1100 update_segment(&mut s.pq, p1.as_ref(),
1101 p2.as_ref(), len) }
1102 }
1103 }
1104 } else { m!(p0).cost = cost::HANGING_NODE;
1106 m!(pm).cost = 0.;
1107 m!(p1).cost = cost::HANGING_NODE;
1108 unsafe {
1109 if let Some(p_1) = p0.prev() {
1110 update_segment(&mut s.pq, p_1.as_ref(),
1111 p0.as_ref(), len)
1112 }
1113 if let Some(p2) = p1.next() {
1114 update_segment(&mut s.pq, p1.as_ref(),
1115 p2.as_ref(), len);
1116 }
1117 }
1118 }
1119 let vp = pm_in_vp || in_vp(r!(p0));
1120 push_segment(&mut s.pq, &mut p0, r!(pm), len, vp);
1121 let vp = pm_in_vp || in_vp(r!(p1));
1122 push_segment(&mut s.pq, &mut pm, r!(p1), len, vp);
1123 } else { if pm.is_valid() {
1126 pm.cost = cost::HANGING_NODE;
1127 let mut pm = unsafe { s.path.insert_after(&mut p0, pm) };
1128 if let Some(p_1) = unsafe { p0.prev() } {
1129 if r!(p_1).is_valid() {
1130 cost::set_middle(r!(p_1), m!(p0), r!(pm), len);
1131 unsafe{update_segment(&mut s.pq, p_1.as_ref(),
1132 p0.as_ref(), len)}
1133 }
1134 }
1135 let pm_in_vp = in_vp(r!(pm));
1136 let vp = pm_in_vp || in_vp(r!(p0));
1137 push_segment(&mut s.pq, &mut p0, r!(pm), len, vp);
1138 push_segment(&mut s.pq, &mut pm, r!(p1), len, pm_in_vp)
1139 } else { pm.cost = 0.;
1146 let pm = unsafe {
1147 if p1.as_ref().witness.is_none() {
1148 s.path.replace(&mut p1, pm);
1151 p1 } else {
1153 s.path.insert_after(&mut p0, pm)
1154 } };
1155 let vp = in_vp(r!(p0));
1156 push_segment(&mut s.pq, &mut p0, r!(pm), len, vp)
1157 }
1158 }
1159 } else { debug_assert!(r!(p1).is_valid());
1161 if pm.is_valid() {
1162 pm.cost = cost::HANGING_NODE;
1163 let mut pm = unsafe { s.path.insert_after(&mut p0, pm) };
1164 if let Some(p2) = unsafe { p1.next() } {
1165 if r!(p2).is_valid() {
1166 cost::set_middle(r!(pm), m!(p1), r!(p2), len);
1167 unsafe{update_segment(&mut s.pq, p1.as_ref(),
1168 p2.as_ref(), len)}
1169 }
1170 }
1171 let pm_in_vp = in_vp(r!(pm));
1172 push_segment(&mut s.pq, &mut p0, r!(pm), len, pm_in_vp);
1173 push_segment(&mut s.pq, &mut pm, r!(p1), len,
1174 pm_in_vp || in_vp(r!(p1)))
1175 } else { pm.cost = 0.;
1178 let mut pm = unsafe {
1179 if let Some(p_1) = p0.prev() {
1180 if p_1.as_ref().is_valid() {
1181 s.path.insert_after(&mut p0, pm)
1182 } else {
1183 s.path.replace(&mut p0, pm);
1185 p0
1186 }
1187 } else {
1188 s.path.insert_after(&mut p0, pm)
1189 } };
1190 let vp = in_vp(r!(p1));
1191 push_segment(&mut s.pq, &mut pm, r!(p1), len, vp)
1192 }
1193 }
1194 }
1195}
1196
1197fn push_almost_uniform_sampling(points: &mut Vec<Point>,
1198 f: &mut impl FnMut(f64) -> Point,
1199 a: f64, b: f64, n: usize) {
1200 debug_assert!(n >= 4);
1201 let dt = (b - a) / (n - 3) as f64;
1202 let mut random = (n as u32).wrapping_mul(1_000_000);
1207 const NORMALIZE_01: f64 = 1. / u32::MAX as f64;
1208 let mut rand = move || {
1209 random ^= random << 13;
1210 random ^= random >> 17;
1211 random ^= random << 5;
1212 random as f64 * NORMALIZE_01
1213 };
1214 points.push(f(a));
1215 points.push(f(a + 0.0625 * dt));
1216 for i in 1 ..= n - 4 {
1217 let j = i as f64 + rand() * 0.125 - 0.0625;
1218 points.push(f(a + j * dt));
1219 }
1220 points.push(f(b - 0.0625 * dt));
1221 points.push(f(b));
1222}
1223
1224impl Sampling {
1225 fn build(mut points: Vec<Point>,
1227 mut f: impl FnMut(f64) -> Point,
1228 a: f64, b: f64, n: usize,
1229 viewport: Option<BoundingBox>) -> Sampling {
1230 if a == b {
1231 let p = f(a);
1232 if p.is_valid() {
1233 return Sampling::singleton(p);
1234 } else {
1235 return Sampling::empty()
1236 }
1237 }
1238 let n0 = (n / 10).max(10);
1241 push_almost_uniform_sampling(&mut points, &mut f, a, b, n0);
1243 let mut s = Sampling::from_vec(points, a < b);
1244 match viewport {
1245 Some(vp) => {
1246 let in_vp = |p: &Point| vp.contains(p);
1247 compute(&mut s, in_vp);
1248 refine_gen(&mut s, n - n0, &mut f, in_vp)
1249 }
1250 None => {
1251 compute(&mut s, |_| true);
1252 refine_gen(&mut s, n - n0, &mut f, |_| true)
1253 }
1254 }
1255 s
1256 }
1257}
1258
1259
1260new_sampling_fn!(
1261 ,
1264 fun -> f64,
1275 Fun,
1278 FnPoint);
1279
1280impl<F> Fun<F>
1281where F: FnMut(f64) -> f64 {
1282 pub fn build(mut self) -> Sampling {
1284 self.eval_init();
1285 Sampling::build(self.init_pt, |x| self.f.eval(x),
1286 self.a, self.b, self.n, self.viewport)
1287 }
1288}
1289
1290
1291new_sampling_fn!(
1292 ,
1295 param -> [f64; 2],
1306 Param,
1309 ParamPoint);
1310
1311impl<F> Param<F>
1312where F: FnMut(f64) -> [f64; 2] {
1313 pub fn build(mut self) -> Sampling {
1315 self.eval_init();
1316 Sampling::build(self.init_pt, |t| self.f.eval(t),
1317 self.a, self.b, self.n, self.viewport)
1318 }
1319}
1320
1321
1322pub struct LaTeX<'a> {
1339 sampling: &'a Sampling,
1340 n: usize,
1341 color: Option<RGB8>,
1342 arrow: Option<&'a str>,
1343 arrow_pos: Option<f64>,
1344}
1345
1346impl<'a> LaTeX<'a> {
1347 #[inline]
1348 fn new(s: &'a Sampling) -> Self {
1349 Self { sampling: s, n: 20_000, color: None,
1350 arrow: None, arrow_pos: None }
1351 }
1352
1353 pub fn n(&mut self, n: usize) -> &mut Self {
1357 self.n = n;
1358 self
1359 }
1360
1361 pub fn color(&mut self, color: RGB8) -> &mut Self {
1364 self.color = Some(color);
1365 self
1366 }
1367
1368 pub fn arrow(&mut self, arrow: &'a str) -> &mut Self {
1372 self.arrow = Some(arrow);
1373 self
1374 }
1375
1376 pub fn arrow_pos(&mut self, arrow_pos: f64) -> &mut Self {
1380 if ! arrow_pos.is_finite() {
1381 panic!("curve_sampling::LaTeX::arrow_pos: \
1382 position must be finite");
1383 }
1384 self.arrow_pos = Some(arrow_pos.clamp(0., 1.));
1385 self
1386 }
1387
1388 fn write_with_lines(&self, f: &mut impl Write) -> Result<(), io::Error> {
1390 let mut n = 0;
1391 let mut new_path = true;
1392 for [x,y] in self.sampling.iter() {
1393 if x.is_nan() {
1394 writeln!(f, "\\pgfusepath{{stroke}}")?;
1395 n = 0;
1396 new_path = true;
1397 } else {
1398 n += 1;
1399 if new_path {
1400 writeln!(f, "\\pgfpathmoveto{{\\pgfpointxy\
1401 {{{:.16}}}{{{:.16}}}}}", x, y)?
1402 } else if n >= self.n {
1403 write!(f, "\\pgfpathlineto{{\\pgfpointxy\
1404 {{{:.16}}}{{{:.16}}}}}\n\
1405 \\pgfusepath{{stroke}}\n\
1406 \\pgfpathmoveto{{\\pgfpointxy\
1407 {{{:.16}}}{{{:.16}}}}}\n", x, y, x, y)?;
1408 n = 0;
1409 } else {
1410 writeln!(f, "\\pgfpathlineto{{\\pgfpointxy\
1411 {{{:.16}}}{{{:.16}}}}}", x, y)?
1412 }
1413 new_path = false;
1414 }
1415 }
1416 Ok(())
1417 }
1418
1419 fn write_with_arrows(&self, f: &mut impl Write, arrow: &str,
1421 arrow_pos: f64) -> Result<(), io::Error> {
1422 let mut lens = vec![];
1424 let mut prev_pt: Option<[f64; 2]> = None;
1425 let mut cur_len = 0.;
1426 for p @ [x, y] in self.sampling.iter() {
1427 if x.is_nan() {
1428 lens.push(arrow_pos * cur_len);
1429 prev_pt = None;
1430 cur_len = 0.;
1431 } else {
1432 if let Some([x0, y0]) = prev_pt {
1433 cur_len += (x - x0).hypot(y - y0);
1434 }
1435 prev_pt = Some(p);
1436 }
1437 }
1438 lens.push(arrow_pos * cur_len);
1439 if lens.is_empty() { return Ok(()) }
1440 let mut lens = lens.iter();
1441 let mut rem_len = *lens.next().unwrap(); prev_pt = None;
1443 let mut n = 0;
1444 for p @ [x, y] in self.sampling.iter() {
1445 if x.is_nan() {
1446 writeln!(f, "\\pgfusepath{{stroke}}")?;
1447 rem_len = *lens.next().unwrap();
1448 prev_pt = None;
1449 } else {
1450 n += 1;
1451 if let Some([x0, y0]) = prev_pt {
1452 let dx = x - x0;
1453 let dy = y - y0;
1454 let l = dx.hypot(dy);
1455 if rem_len <= l {
1456 writeln!(f, "\\pgfusepath{{stroke}}")?;
1457 let pct = rem_len / l;
1460 if pct <= 1e-14 {
1461 write!(f, "\\pgfsetarrowsstart{{{}}}\n\
1462 \\pgfpathmoveto{{\\pgfpointxy
1463 {{{:.16}}}{{{:.16}}}}}\n\
1464 \\pgfpathlineto{{\\pgfpointxy\
1465 {{{:.16}}}{{{:.16}}}}}\n\
1466 \\pgfusepath{{stroke}}\n\
1467 \\pgfsetarrowsend{{}}\n\
1468 \\pgfpathmoveto{{\\pgfpointxy\
1469 {{{:.16}}}{{{:.16}}}}}\n",
1470 arrow, x0, y0, x, y, x, y)?;
1471 } else {
1472 let xm = x0 + pct * dx;
1473 let ym = y0 + pct * dy;
1474 write!(f, "\\pgfsetarrowsend{{{}}}\n\
1475 \\pgfpathmoveto{{\\pgfpointxy\
1476 {{{:.16}}}{{{:.16}}}}}\n\
1477 \\pgfpathlineto{{\\pgfpointxy\
1478 {{{:.16}}}{{{:.16}}}}}\n\
1479 \\pgfusepath{{stroke}}\n\
1480 \\pgfsetarrowsend{{}}\n\
1481 \\pgfpathmoveto{{\\pgfpointxy\
1482 {{{:.16}}}{{{:.16}}}}}\n\
1483 \\pgfpathlineto{{\\pgfpointxy\
1484 {{{:.16}}}{{{:.16}}}}}\n",
1485 arrow, x0, y0, xm, ym, xm, ym, x, y)?;
1486 n = 2;
1487 }
1488 rem_len = f64::INFINITY; } else if n >= self.n {
1490 write!(f, "\\pgfpathlineto{{\\pgfpointxy\
1491 {{{:.16}}}{{{:.16}}}}}\n\
1492 \\pgfusepath{{stroke}}\n\
1493 \\pgfpathmoveto{{\\pgfpointxy\
1494 {{{:.16}}}{{{:.16}}}}}\n", x, y, x, y)?;
1495 n = 0;
1496 } else {
1497 writeln!(f, "\\pgfpathlineto{{\\pgfpointxy\
1498 {{{:.16}}}{{{:.16}}}}}", x, y)?
1499 }
1500 rem_len -= l;
1501 } else {
1502 writeln!(f, "\\pgfpathmoveto{{\\pgfpointxy\
1504 {{{:.16}}}{{{:.16}}}}}", x, y)?
1505 }
1506 prev_pt = Some(p);
1507 }
1508 }
1509 Ok(())
1510 }
1511
1512 pub fn write(&self, f: &mut impl Write) -> Result<(), io::Error> {
1514 writeln!(f, "% Written by the Rust curve-sampling crate.")?;
1515 writeln!(f, "\\begin{{pgfscope}}")?;
1516 if let Some(RGB8 {r, g, b}) = self.color {
1517 write!(f, "\\definecolor{{RustCurveSamplingColor}}{{RGB}}\
1518 {{{},{},{}}}\n\
1519 \\pgfsetstrokecolor{{RustCurveSamplingColor}}\n",
1520 r, g, b)?
1521 }
1522 match (self.arrow, self.arrow_pos) {
1523 (None, None) => self.write_with_lines(f)?,
1524 (Some(arrow), None) =>
1525 self.write_with_arrows(f, arrow, 0.5)?,
1526 (None, Some(arrow_pos)) =>
1527 self.write_with_arrows(f, ">",arrow_pos)?,
1528 (Some(arrow), Some(arrow_pos)) =>
1529 self.write_with_arrows(f, arrow, arrow_pos)?,
1530 }
1531 write!(f, "\\pgfusepath{{stroke}}\n\\end{{pgfscope}}\n")
1532 }
1533}
1534
1535impl Sampling {
1537 pub fn latex(&self) -> LaTeX<'_> { LaTeX::new(self) }
1539
1540 pub fn write(&self, f: &mut impl Write) -> Result<(), io::Error> {
1545 for [x, y] in self.iter() {
1546 if x.is_nan() {
1547 writeln!(f)?
1548 } else {
1549 writeln!(f, "{:e} {:e}", x, y)?
1550 }
1551 }
1552 Ok(())
1553 }
1554}
1555
1556impl Display for Sampling {
1557 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
1562 for [x, y] in self.iter() {
1563 if x.is_nan() {
1564 writeln!(f)?
1565 } else {
1566 writeln!(f, "{:e} {:e}", x, y)?
1567 }
1568 }
1569 Ok(())
1570 }
1571}
1572
1573#[cfg(test)]
1578mod tests {
1579 use std::{error::Error,
1580 fs::File,
1581 io::Write, path::Path};
1582 use crate::{Sampling, BoundingBox, Point};
1583
1584 type R<T> = Result<T, Box<dyn Error>>;
1585
1586 fn xy_of_sampling(s: &Sampling) -> Vec<Option<(f64, f64)>> {
1587 s.path.iter().map(|p| {
1588 if p.is_valid() { Some((p.xy[0], p.xy[1])) } else { None }
1589 })
1590 .collect()
1591 }
1592
1593 #[test]
1594 fn clone_sampling() {
1595 let s = Sampling::from_iter([[0.,0.], [1.,1.]]);
1596 let xy0: Vec<_> = s.iter().collect();
1597 let xy1: Vec<_> = s.clone().iter().collect();
1598 assert_eq!(xy0, xy1)
1599 }
1600
1601 #[test]
1602 fn io() {
1603 let s = Sampling::from_iter([[0.,0.], [1.,1.]]);
1604 assert_eq!(xy_of_sampling(&s), vec![Some((0.,0.)), Some((1.,1.))]);
1605 let s = Sampling::from_iter([[0.,0.], [1.,1.], [f64::NAN, 1.],
1606 [2.,2.]]);
1607 assert_eq!(xy_of_sampling(&s),
1608 vec![Some((0.,0.)), Some((1.,1.)), None, Some((2.,2.))]);
1609 }
1610
1611 #[test]
1612 fn bounding_box_singleton() {
1613 let s = Sampling::singleton(
1614 Point::new_unchecked(0., [1., 2.]));
1615 let bb = BoundingBox {xmin: 1., xmax: 1., ymin: 2., ymax: 2.};
1616 assert_eq!(s.bounding_box(), bb);
1617 }
1618
1619 fn test_clip(bb: BoundingBox,
1620 path: Vec<[f64;2]>,
1621 expected: Vec<Option<(f64,f64)>>) {
1622 let s = Sampling::from_iter(path).clip(bb);
1623 assert_eq!(xy_of_sampling(&s), expected);
1624 }
1625
1626 #[test]
1627 fn clip_base () {
1628 let bb = BoundingBox { xmin: 0., xmax: 3., ymin: 0., ymax: 2.};
1629 test_clip(bb, vec![[0.,1.], [2.,3.]],
1630 vec![Some((0.,1.)), Some((1.,2.))]);
1631 test_clip(bb,
1632 vec![[-1.,0.], [2.,3.], [4.,1.]],
1633 vec![Some((0.,1.)), Some((1.,2.)), None, Some((3., 2.))]);
1634 test_clip(bb,
1635 vec![[0.,1.], [2.,3.], [4.,1.], [2.,1.], [2., -1.],
1636 [0., -1.], [0., 1.] ],
1637 vec![Some((0.,1.)), Some((1.,2.)), None,
1638 Some((3., 2.)), None,
1639 Some((3., 1.)), Some((2., 1.)), Some((2., 0.)), None,
1640 Some((0., 0.)), Some((0., 1.))]);
1641 }
1642
1643 #[test]
1644 fn clip_empty() {
1645 let bb = BoundingBox {xmin: 0., xmax: 1., ymin: 0., ymax: 1.};
1646 let path = xy_of_sampling(&Sampling::empty().clip(bb));
1647 assert_eq!(path, vec![]);
1648 }
1649
1650 #[test]
1651 fn clip_double_cut() {
1652 let bb = BoundingBox { xmin: 0., xmax: 4., ymin: 0., ymax: 2.};
1653 test_clip(bb,
1654 vec![[1., 2.], [2.,3.], [5.,0.], [-1., 0.] ],
1655 vec![Some((1., 2.)), None,
1656 Some((3., 2.)), Some((4., 1.)), None,
1657 Some((4., 0.)), Some((0., 0.)) ]);
1658 }
1659
1660 #[test]
1661 fn clip_almost_horiz() {
1662 let bb = BoundingBox { xmin: 0., xmax: 2., ymin: -1., ymax: 1.};
1663 test_clip(bb,
1664 vec![[1., 1e-100], [3., -1e-100] ],
1665 vec![Some((1., 1e-100)), Some((2., 0.))]);
1666 }
1667
1668 #[test]
1669 fn clip_touches_1pt_cut() {
1670 let bb = BoundingBox {xmin: 0., xmax: 2., ymax: 0., ymin: -1.};
1671 let cut = [f64::NAN, f64::NAN];
1672 test_clip(bb,
1673 vec![[0.,1.], cut, [1., 0.], cut, cut, [2., 1.]],
1674 vec![Some((1., 0.))])
1675 }
1676
1677 #[test]
1678 fn clip_final_cut() {
1679 let bb = BoundingBox {xmin: 0., xmax: 1., ymin: 0., ymax: 2.};
1680 test_clip(bb,
1681 vec![[0., 0.], [2., 2.]],
1682 vec![Some((0., 0.)), Some((1., 1.))])
1683 }
1684
1685 #[test]
1686 fn uniform1() {
1687 let s = Sampling::uniform(|x| x, 0., 4.).n(3)
1688 .init(&[1.]).init_pt(&[(3., 0.)]).build();
1689 assert_eq!(xy_of_sampling(&s),
1690 vec![Some((0.,0.)), Some((1.,1.)), Some((2.,2.)),
1691 Some((3., 0.)), Some((4.,4.))]);
1692 }
1693
1694 #[test]
1695 fn uniform2() {
1696 let s = Sampling::uniform(|x| (4. - x).sqrt(), 0., 6.).n(4).build();
1697 assert_eq!(xy_of_sampling(&s),
1698 vec![Some((0.,2.)), Some((2., 2f64.sqrt())),
1699 Some((4., 0.)), None]);
1700 }
1701
1702 fn write_with_point_costs(s: &Sampling, fname: impl AsRef<Path>) -> R<()> {
1705 let mut fh = File::create(fname)?;
1706 for p in s.path.iter() {
1707 if p.is_valid() {
1708 let [x, y] = p.xy;
1709 writeln!(fh, "{x} {y} {}", p.cost)?;
1710 } else {
1711 writeln!(fh)?;
1712 }
1713 }
1714 Ok(())
1715 }
1716
1717 fn write_segments(mut s: Sampling, fname: impl AsRef<Path>) -> R<()> {
1718 let mut fh = File::create(fname)?;
1719 let mut seg: Vec<(f64, Point, Point, f64)> = vec![];
1720 loop {
1721 let priority = s.pq.max_priority();
1722 if let Some(p0) = s.pq.pop() {
1723 let p1 = unsafe { p0.next().unwrap() };
1724 let p1 = unsafe { p1.as_ref() };
1725 let p0 = unsafe { p0.as_ref() };
1726 let tm = (p0.t + p1.t) / 2.;
1727 seg.push((tm, p0.clone(), p1.clone(), priority))
1728 } else {
1729 break;
1730 }
1731 }
1732 seg.sort_by(|(t1,_,_,_), (t2,_,_,_)| t1.partial_cmp(t2).unwrap());
1733 for (tm, p0, p1, priority) in seg {
1734 let [x0, y0] = p0.xy;
1735 let [x1, y1] = p1.xy;
1736 writeln!(fh, "{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}",
1737 tm, p0.t, x0, y0, p1.t, x1, y1, priority)?;
1738 }
1739 Ok(())
1740 }
1741
1742 #[derive(Clone)]
1743 struct Plot {
1744 xmin: f64, xmax: f64,
1745 ymin: f64, ymax: f64,
1746 n: usize,
1747 init: Vec<f64>,
1748 }
1749
1750 impl Plot {
1751 fn plot<F>(&self, f: F, title: &str) -> R<String>
1753 where F: FnMut(f64) -> f64 {
1754 let vp = BoundingBox { xmin: self.xmin, xmax: self.xmax,
1755 ymin: self.ymin, ymax: self.ymax };
1756 let s = Sampling::fun(f, self.xmin, self.xmax)
1757 .n(self.n).init(self.init.iter()).viewport(vp).build();
1758 static mut NDAT: usize = 0;
1759 let ndat = unsafe { NDAT += 1; NDAT };
1760 let dir = Path::new("target");
1761 let fname = format!("horror{}.dat", ndat);
1762 s.write(&mut File::create(dir.join(&fname))?)?;
1763 let fname_p = format!("horror{}_p.dat", ndat);
1764 write_with_point_costs(&s, dir.join(&fname_p))?;
1765 let fname_s = format!("horror{}_s.dat", ndat);
1766 write_segments(s, dir.join(&fname_s))?;
1767 Ok(format!(
1768 "unset title\n\
1769 unset y2tics\n\
1770 plot [{}:{}] \"{}\" with l lt 5 title \"{}\", \
1771 \"{}\" with p lt 3 pt 6 ps 0.2 title \"n={}\"\n\
1772 set title \"Restricted to viewport [{}:{}]×[{}:{}]\"\n\
1773 set y2tics\n\
1774 set y2range [-1e-6:]\n\
1775 plot [{}:{}] [{}:{}] \"{}\" with l lt 5 title \"{}\", \
1776 \"{}\" with p lt 3 pt 7 ps 0.2 title \"n={}\", \
1777 \"{}\" using 1:3 with lp ps 0.2 lt rgb \"#760b0b\" \
1778 title \"cost points\", \
1779 \"{}\" using 1:8 with lp ps 0.2 lt rgb \"#909090\" \
1780 axes x1y2 title \"cost segments\"\n",
1781 self.xmin, self.xmax, &fname, title, &fname, self.n,
1782 self.xmin, self.xmax, self.ymin, self.ymax,
1783 self.xmin, self.xmax, self.ymin, self.ymax, &fname, title,
1784 &fname, self.n, &fname_p, &fname_s))
1785 }
1786 }
1787
1788 #[test]
1789 #[cfg_attr(miri, ignore)] fn horror() -> R<()> {
1791 let d = Plot {
1792 xmin: -5., xmax: 5., ymin: -5., ymax: 5., n: 100, init: vec![] };
1793 macro_rules! p {
1794 ($($id:ident $l:tt $e:expr),*) => {
1795 Plot{ $($id: $e,)* ..d.clone() } };
1796 }
1797 let s = [
1798 p!(n: 10).plot(|_| 2., "x ↦ 2")?,
1799 p!().plot(|x| x, "x ↦ x")?,
1800 p!().plot(|x| 5. * x, "x ↦ 5x")?,
1801 p!().plot(|x| 1e6 * x, "x ↦ 10⁶ x")?, p!().plot(|x| 1e50 * x, "x ↦ 10⁵⁰ x")?, p!().plot(|x| 1. / x, "x ↦ 1/x")?, p!(xmin: 0., xmax: 5., ymax: 100.).plot(
1805 |x| 1. / x, "x ↦ 1/x")?, p!(xmin: -0.4, xmax: 2., ymin: 0., ymax: 1.6, n: 50).plot(
1807 |x| x.sqrt(), "x ↦ √x")?,
1808 p!(xmin: -2., xmax: 1., ymin: 0., ymax: 1.6, n: 50).plot(
1810 |x| (-x).sqrt(), "x ↦ √(-x)")?,
1811 p!(n: 200).plot(|x| x.tan(), "tan")?,
1812 p!().plot(|x| 1. / x.abs(), "x ↦ 1/|x|")?,
1813 p!(xmin: -6., xmax: 6., ymin: -2., ymax: 2.).plot(
1814 |x| (1. + x.cos().sin()).ln(), "1 + sin(cos x)")?,
1815 p!(xmin: 0., xmax: 6.28, ymin: -1.5, ymax: 1.5, n: 400).plot(
1816 |x| x.powi(3).sin() + x.powi(3).cos(), "sin x³ + cos x³")?,
1817 p!(xmin: -5., xmax:200., ymin: -1., ymax: 1., n: 400).plot(
1818 |x| x.sin(), "sin")?,
1819 p!(xmin: -4., xmax: 4., ymin: -1., ymax: 1.).plot(
1823 |x| (300. * x).sin(), "sin(300 x)")?,
1824 p!(xmin: -4., xmax: 4., ymin: -1., ymax: 1., n: 1000).plot(
1825 |x| (300. * x).sin(), "sin(300 x)")?,
1826 p!(xmin: -2., xmax: 2., ymin: 0., ymax: 3.).plot(
1827 |x| 1. + x * x + 0.0125 * (1. - 3. * (x - 1.)).abs().ln(),
1828 "1 + x² + 0.0125 ln|1 - 3(x-1)|")?,
1829 p!(xmin: -2., xmax: 2., ymin: 0., ymax: 3., n: 300,
1830 init:vec![4. / 3.]).plot(
1831 |x| 1. + x * x + 0.0125 * (1. - 3. * (x - 1.)).abs().ln(),
1832 "1 + x² + 0.0125 ln|1 - 3(x-1)| (specifying x:4/3")?,
1833 p!(xmin: -0.5, xmax: 0.5, ymin: -1., ymax: 1.).plot(
1834 |x| x * (1. / x).sin(), "x sin(1/x)")?,
1835 p!(xmin: -0.5, xmax: 0.5, ymin: -1., ymax: 1., n:200).plot(
1836 |x| x * (1. / x).sin(), "x sin(1/x)")?,
1837 p!(xmin: -2., xmax: 2., ymin: -1., ymax: 1.).plot(
1838 |x| (1. / x).sin(), "sin(1/x)")?,
1839 p!(xmin: -2., xmax: 2., ymin: -1., ymax: 1., n: 400).plot(
1840 |x| (1. / x).sin(), "sin(1/x)")?,
1841 p!(xmin: -4., xmax: 4., ymin: -1., ymax: 1.).plot(
1842 |x| x.powi(4).sin(), "sin(x⁴)")?,
1843 p!(xmin: -4., xmax: 4., ymin: -1., ymax: 1., n: 600).plot(
1844 |x| x.powi(4).sin(), "sin(x⁴)")?,
1845 p!(xmin: -6., xmax: 6., ymin: -1., ymax: 1.).plot(
1846 |x| x.exp().sin(), "sin(exp x)")?,
1847 p!(xmin: -6., xmax: 6., ymin: -1., ymax: 1., n: 500).plot(
1848 |x| x.exp().sin(), "sin(exp x)")?,
1849 p!(xmin: -10., xmax: 10., ymin: 0., ymax: 10.).plot(
1850 |x| 1. / x.sin(), "1 / sin x")?,
1851 p!(xmin: -6., xmax: 6., ymin: 0., ymax: 2.).plot(
1852 |x| x.sin() / x, "(sin x)/x")?,
1853 p!(xmin: -2., xmax: 2., ymin: -15., ymax: 15.).plot(
1854 |x| (x.powi(3) - x + 1.).tan() + 1. / (x + 3. * x.exp()),
1855 "tan(x³ - x + 1) + 1/(x + 3 eˣ)")?,
1856 p!(xmin: 0., xmax: 17., ymin: 0., ymax: 2.).plot(
1857 |x| (1. + x.cos()) * (-0.1 * x).exp(),
1858 "(1 + cos x) exp(-x/10)")?,
1859 p!(xmin: -2., xmax: 17., ymin: 0., ymax: 2.).plot(
1860 |x| (1. + x.cos()) * (-0.1 * x).exp(),
1861 "(1 + cos x) exp(-x/10)")?,
1862 p!(xmin: 0., xmax: 17., ymin: 0., ymax: 2.).plot(
1863 |x| (1. + x.cos()) * (-0.01 * x * x).exp(),
1864 "(1 + cos x) exp(-x²/100)")?,
1865 ].join("");
1866 let mut fh = File::create("target/horror.gp")?;
1867 write!(fh, "set terminal pdfcairo\n\
1868 set output \"horror.pdf\"\n\
1869 set grid\n\
1870 {}\n", s)?;
1871 Ok(())
1872 }
1873
1874
1875}