1use std::{
21 cell::RefCell,
22 cmp::Ordering,
23 fmt::{self, Display, Formatter},
24 io::{self, Write},
25 marker::PhantomData,
26 ops::ControlFlow,
27};
28use rgb::*;
29
30mod approx_priority_queue;
35use approx_priority_queue as pq;
36
37mod linked_list;
38use linked_list as list;
39use list::List;
40
41#[derive(Debug, Clone, Copy, PartialEq)]
47pub struct BoundingBox {
48 pub xmin: f64,
49 pub xmax: f64,
50 pub ymin: f64,
51 pub ymax: f64,
52}
53
54impl BoundingBox {
55 #[inline]
56 pub(crate) fn empty() -> Self {
57 Self { xmin: f64::INFINITY, xmax: f64::NEG_INFINITY,
58 ymin: f64::INFINITY, ymax: f64::NEG_INFINITY }
59 }
60
61 #[inline]
71 pub fn is_empty(&self) -> bool {
72 !(self.xmin < self.xmax && self.ymin < self.ymax) }
74
75 #[inline]
78 fn contains<D>(&self, p: &Point<D>) -> bool {
79 let [x, y] = p.xy;
80 self.xmin <= x && x <= self.xmax && self.ymin <= y && y <= self.ymax
81 }
82
83 #[inline]
86 pub fn hull(&self, other: &Self) -> Self {
87 BoundingBox { xmin: self.xmin.min(other.xmin),
88 xmax: self.xmax.max(other.xmax),
89 ymin: self.ymin.min(other.ymin),
90 ymax: self.ymax.max(other.ymax) }
91 }
92}
93
94
95#[derive(Debug)]
107struct Point<D> {
108 t: f64, xy: [f64; 2],
110 data: D,
111 cost: f64, witness: RefCell<Option<pq::Witness<list::Witness<Point<D>>>>>,
117}
118
119#[derive(Clone, Copy, Debug)]
125struct Coord {
126 t: f64,
127 xy: [f64; 2],
128 cost: f64,
129}
130
131impl<D: Clone> Clone for Point<D> {
132 fn clone(&self) -> Self {
135 Self {
136 t: self.t, xy: self.xy,
137 data: self.data.clone(),
138 cost: self.cost,
139 witness: None.into(),
140 }
141 }
142}
143
144impl<D> Point<D> {
145 #[inline]
147 fn new_unchecked(t: f64, xy: [f64; 2], data: D) -> Self {
148 Point { t, xy, data, cost: 0.,
149 witness: None.into() }
150 }
151
152 #[inline]
154 fn cut(t: f64, data: D) -> Self {
155 Point { t, xy: [f64::NAN; 2], data,
156 cost: 0.,
157 witness: None.into() }
158 }
159
160 #[inline]
162 fn is_valid(&self) -> bool {
163 self.xy.iter().all(|z| z.is_finite())
164 }
165
166 fn cmp_t_incr(p1: &Self, p2: &Self) -> Ordering {
167 p1.t.total_cmp(&p2.t)
168 }
169
170 fn cmp_t_decr(p1: &Self, p2: &Self) -> Ordering {
171 p1.t.total_cmp(&p2.t).reverse()
172 }
173
174 #[inline]
176 fn opt(self) -> Point<Option<D>> {
177 Point {
178 t: self.t, xy: self.xy,
179 data: Some(self.data),
180 cost: self.cost,
181 witness: None.into(),
182 }
183 }
184
185 #[inline]
190 fn coord(&self) -> Coord {
191 Coord { t: self.t, xy: self.xy, cost: self.cost }
192 }
193}
194
195pub struct Sampling<D> {
201 pq: PQ<D>,
207 path: List<Point<D>>,
208 vp: Option<BoundingBox>, }
210
211type PQ<D> = pq::PQ<list::Witness<Point<D>>>;
212
213impl<D: Clone> Clone for Sampling<D> {
214 fn clone(&self) -> Self {
215 Self { pq: PQ::new(),
218 path: self.path.clone(),
219 vp: self.vp }
220 }
221}
222
223#[derive(Debug, Clone, Copy)]
226struct Lengths {
227 t: f64,
228 x: f64,
229 y: f64,
230}
231
232impl<D> Sampling<D> {
233 #[inline]
235 pub fn is_empty(&self) -> bool { self.path.is_empty() }
236
237 #[inline]
239 pub(crate) fn empty() -> Self {
240 Self { pq: PQ::new(),
241 path: List::new(),
242 vp: None }
243 }
244
245 #[inline]
246 pub(crate) fn singleton(p: Point<D>) -> Self {
247 debug_assert!(p.t.is_finite() && p.xy.iter().all(|z| z.is_finite()));
248 let mut path = List::new();
249 path.push_back(p);
250 Self { pq: PQ::new(), path, vp: None }
251 }
252
253 fn from_list(path: List<Point<D>>) -> Self {
254 Self {
255 pq: PQ::new(),
256 path,
257 vp: None,
258 }
259 }
260
261 #[inline]
262 pub(crate) fn set_vp(&mut self, bb: BoundingBox) {
263 self.vp = Some(bb);
264 }
265
266 pub(crate) fn lengths(&self) -> Option<Lengths> {
269 if self.is_empty() { return None }
270 let p0 = self.path.first().unwrap();
271 let p1 = self.path.last().unwrap();
272 let (len_x, len_y) = match self.vp {
273 Some(vp) => (vp.xmax - vp.xmin, vp.ymax - vp.ymin),
274 None => (1., 1.),
275
276 };
277 Some(Lengths { t: (p1.t - p0.t).abs(), x: len_x, y: len_y })
278 }
279
280 pub fn len(&self) -> usize {
282 self.path.len()
283 }
284
285 pub fn iter(&self) -> Iter<'_, D> {
292 Iter { iter: self.iter_data() }
293 }
294
295 pub fn iter_data(&self) -> IterData<'_, D> {
298 IterData {
299 path: self.path.iter(),
300 prev_is_cut: true,
301 }
302 }
303
304 pub fn iter_mut(&mut self) -> IterMut<'_, D> {
310 IterMut { path: self.path.iter_mut() }
311 }
312
313 pub fn into_iter_data(self) -> IntoIterData<D> {
316 IntoIterData { path: self.path.into_iter() }
317 }
318
319 #[inline]
321 pub fn x(&self) -> Vec<f64> {
322 let mut v = Vec::with_capacity(self.path.len());
323 for [x, _] in self.iter() { v.push(x) }
324 v
325 }
326
327 #[inline]
329 pub fn y(&self) -> Vec<f64> {
330 let mut v = Vec::with_capacity(self.path.len());
331 for [_, y] in self.iter() { v.push(y) }
332 v
333 }
334}
335
336
337pub struct IterData<'a, D> {
341 path: list::Iter<'a, Point<D>>,
342 prev_is_cut: bool,
343}
344
345impl<'a, D> Iterator for IterData<'a, D> {
346 type Item = ([f64; 2], &'a D);
347
348 fn next(&mut self) -> Option<Self::Item> {
349 match self.path.next() {
350 None => None,
351 Some(p) => {
352 if p.is_valid() {
353 self.prev_is_cut = false;
354 Some((p.xy, &p.data))
355 } else if self.prev_is_cut {
356 let r = self.path.try_fold((), |_, p| {
359 if p.is_valid() {
360 ControlFlow::Break(((), p))
361 } else {
362 ControlFlow::Continue(())
363 }
364 });
365 match r {
366 ControlFlow::Continue(_) => {
367 None
369 }
370 ControlFlow::Break((_, p)) => {
371 self.prev_is_cut = false;
372 Some((p.xy, &p.data))
373 }
374 }
375 } else {
376 self.prev_is_cut = true;
377 Some(([f64::NAN; 2], &p.data))
378 }
379 }
380 }
381 }
382
383 fn size_hint(&self) -> (usize, Option<usize>) {
384 (0, Some(self.path.len()))
385 }
386}
387
388pub struct Iter<'a, D> {
392 iter: IterData<'a, D>,
393}
394
395impl<'a, D> Iterator for Iter<'a, D> {
396 type Item = [f64; 2];
397
398 #[inline]
399 fn next(&mut self) -> Option<Self::Item> {
400 self.iter.next().map(|(xy, _)| xy)
401 }
402
403 fn size_hint(&self) -> (usize, Option<usize>) {
404 self.iter.size_hint()
405 }
406}
407
408
409pub struct IterMut<'a, D> {
413 path: list::IterMut<'a, Point<D>>,
414}
415
416impl<'a, D> Iterator for IterMut<'a, D> {
417 type Item = &'a mut [f64; 2];
418
419 fn next(&mut self) -> Option<Self::Item> {
420 self.path.next().map(|p| &mut p.xy)
421 }
422
423 fn size_hint(&self) -> (usize, Option<usize>) {
424 (self.path.len(), Some(self.path.len()))
425 }
426}
427
428pub struct IntoIterData<D> {
432 path: list::IntoIter<Point<D>>,
433}
434
435impl<D> Iterator for IntoIterData<D> {
436 type Item = ([f64; 2], D);
437
438 fn next(&mut self) -> Option<Self::Item> {
439 self.path.next().map(|p| (p.xy, p.data))
440 }
441
442 fn size_hint(&self) -> (usize, Option<usize>) {
443 (self.path.len(), Some(self.path.len()))
444 }
445}
446
447
448#[derive(Debug)]
450enum Intersection<D> {
451 Empty,
452 Pt(Point<D>),
453 Seg(Point<D>, Point<D>),
454}
455
456impl<D> Sampling<D> {
457 pub fn bounding_box(&self) -> BoundingBox {
476 let mut points = self.iter().skip_while(|[x,_]| x.is_nan());
477 let mut bb = match &points.next() {
478 Some([x, y]) => BoundingBox { xmin: *x, xmax: *x,
479 ymin: *y, ymax: *y },
480 None => return BoundingBox::empty()
481 };
482 for [x, y] in points {
483 if x < bb.xmin { bb.xmin = x }
484 else if bb.xmax < x { bb.xmax = x };
485 if y < bb.ymin { bb.ymin = y }
486 else if bb.ymax < y { bb.ymax = y };
487 }
488 bb
489 }
490
491 pub fn transpose(&mut self) -> &mut Self {
493 for p in self.path.iter_mut() {
494 let [x, y] = p.xy;
495 p.xy = [y, x];
496 }
497 self
498 }
499
500 #[inline]
504 #[must_use]
505 fn intersect(
506 p0: Coord, p1: Coord, bb: BoundingBox
507 ) -> Option<Point<Option<D>>> {
508 let [x0, y0] = p0.xy;
509 let [x1, y1] = p1.xy;
510 let mut t = 1.; let dx = x1 - x0; let r = (if dx >= 0. {bb.xmax} else {bb.xmin} - x0) / dx;
513 if r < t { t = r } let dy = y1 - y0; let r = (if dy >= 0. {bb.ymax} else {bb.ymin} - y0) / dy;
516 if r < t { t = r };
517 if t <= 1e-14 {
518 None
519 } else {
520 Some(Point::new_unchecked(
521 p0.t + t * (p1.t - p0.t),
522 [x0 + t * dx, y0 + t * dy],
523 None))
524 }
525 }
526
527 #[inline]
532 #[must_use]
533 fn intersect_seg(
534 p0: Coord, p1: Coord, bb: BoundingBox,
535 ) -> Intersection<Option<D>> {
536 let [x0, y0] = p0.xy;
537 let [x1, y1] = p1.xy;
538 let mut t0 = 0.; let mut t1 = 1.; let dx = x1 - x0; let (r0, r1) = if dx >= 0. {
543 ((bb.xmin - x0) / dx, (bb.xmax - x0) / dx)
544 } else {
545 ((bb.xmax - x0) / dx, (bb.xmin - x0) / dx)
546 };
547 if r0 > 1. || r1 < 0. { return Intersection::Empty }
548 if r0 > 0. { t0 = r0 } if r1 < 1. { t1 = r1 }
550 let dy = y1 - y0; let (r0, r1) = if dy >= 0. {
552 ((bb.ymin - y0) / dy, (bb.ymax - y0) / dy)
553 } else {
554 ((bb.ymax - y0) / dy, (bb.ymin - y0) / dy)
555 };
556 if r0 > t1 || r1 < t0 { return Intersection::Empty }
557 if r0 > t0 { t0 = r0 }
558 if r1 < t1 { t1 = r1 }
559 if t0 < t1 { let dt = p1.t - p0.t;
561 let q0 = Point::new_unchecked(p0.t + t0 * dt,
562 [x0 + t0 * dx, y0 + t0 * dy], None);
563 let q1 = Point::new_unchecked(p0.t + t1 * dt,
564 [x0 + t1 * dx, y0 + t1 * dy], None);
565 Intersection::Seg(q0, q1)
566 } else if t0 == t1 {
567 let q0 = Point::new_unchecked(
568 p0.t + t0 * (p1.t - p0.t),
569 [x0 + t0 * dx, y0 + t0 * dy], None);
570 Intersection::Pt(q0)
571 } else {
572 Intersection::Empty
573 }
574 }
575
576 #[must_use]
582 pub fn clip(self, bb: BoundingBox) -> Sampling<Option<D>> {
583 let mut path = Vec::new();
584 let mut p0_opt: Option<Coord> = None;
586 let mut p0_inside = false;
587 let mut prev_cut = true; for p1 in self.path.into_iter() {
589 if prev_cut {
590 match (p0_opt, p1.is_valid()) {
598 (Some(p0), true) => {
599 let p1_inside = bb.contains(&p1);
600 p0_opt = Some(p1.coord());
601 if p0_inside { if p1_inside {
604 path.push(p1.opt());
605 prev_cut = false;
606 } else if
607 let Some(p) = Self::intersect(p0, p1.coord(), bb) {
608 let t = p.t;
609 path.push(p);
610 path.push(Point::cut(t, None));
611 } else {
612 let c = Point::cut(p0.t, None);
613 path.push(c);
614 }
615 } else if p1_inside { if let Some(p) = Self::intersect(p1.coord(), p0, bb) {
617 path.push(p); }
619 path.push(p1.opt());
620 prev_cut = false;
621 } else { match Self::intersect_seg(p0, p1.coord(), bb) {
623 Intersection::Seg(q0, q1) => {
624 let t1 = q1.t;
625 path.push(q0);
626 path.push(q1);
627 path.push(Point::cut(t1, None));
628 }
629 Intersection::Pt(p) => {
630 let t = p.t;
631 path.push(p);
632 path.push(Point::cut(t, None));
633 }
634 Intersection::Empty => (),
635 }
636 }
637 p0_inside = p1_inside;
638 }
639 (None, true) => {
640 p0_inside = bb.contains(&p1);
641 p0_opt = Some(p1.coord());
642 if p0_inside {
643 path.push(p1.opt());
645 }
646 }
647 (Some(p0), false) => {
648 if p0_inside {
649 path.push(Point::cut(p0.t, None));
651 }
652 p0_opt = None;
653 }
654 (None, false) => (), }
656 } else {
657 let p0 = p0_opt.expect("No cut ⟹ previous point");
661 debug_assert!(p0_inside);
662 if p1.is_valid() {
663 p0_opt = Some(p1.coord());
664 p0_inside = bb.contains(&p1); if p0_inside { path.push(p1.opt());
667 } else { if let Some(p) = Self::intersect(p0, p1.coord(), bb) {
669 let t = p.t;
670 path.push(p);
671 path.push(Point::cut(t, None));
672 } else {
673 path.push(Point::cut(p0.t, None));
674 }
675 prev_cut = true;
676 }
677 } else { p0_opt = None;
679 path.push(p1.opt());
680 prev_cut = true
681 }
682 }
683 }
684 if prev_cut { path.pop(); }
685 let mut s = Sampling::from_list(path.into());
686 s.set_vp(bb);
687 s
688 }
689
690 fn from_point_iterator<P>(points: P) -> Self
697 where P: IntoIterator<Item = Point<D>> {
698 let mut s = Sampling::empty();
699 let mut points = points.into_iter();
700 macro_rules! skip_until_last_cut { () => {
701 let mut cut = None;
702 let mut first_pt = None;
703 while let Some(p) = points.next() {
704 if p.is_valid() { first_pt = Some(p); break; }
705 cut = Some(p);
706 }
707 match (cut, first_pt) {
708 (_, None) => {
709 return s
710 }
711 (None, Some(p)) => {
712 s.path.push_back(p);
713 }
714 (Some(c), Some(p)) => {
715 s.path.push_back(Point::cut(c.t, c.data));
716 s.path.push_back(p);
717 }
718 }
719 }}
720 skip_until_last_cut!();
721 while let Some(p) = points.next() {
722 if p.is_valid() {
723 s.path.push_back(p);
724 } else {
725 s.path.push_back(Point::cut(p.t, p.data));
726 skip_until_last_cut!();
727 }
728 }
729 s
730 }
731}
732
733impl<D, Y> FromIterator<Y> for Sampling<D>
734where Y: Img<D> {
735 fn from_iter<P>(points: P) -> Self
740 where P: IntoIterator<Item = Y> {
741 Sampling::from_point_iterator(
742 points.into_iter().enumerate().map(|(i, y)| {
743 let t = i as f64;
744 let p = y.into_point(t);
745 let [x, y] = p.xy;
746 if x.is_finite() && y.is_finite() {
747 p
748 } else {
749 Point::cut(t, p.data)
750 } }))
751 }
752}
753
754struct MergeSortedIterator<I1, I2, CMP>
755where I1: Iterator,
756 I2: Iterator,
757{
758 i1: std::iter::Peekable<I1>,
759 i2: std::iter::Peekable<I2>,
760 cmp: CMP,
761}
762
763impl<I1, I2, CMP> MergeSortedIterator<I1, I2, CMP>
764where I1: Iterator,
765 I2: Iterator,
766 CMP: Fn(&I1::Item, &I2::Item) -> Ordering,
767{
768 fn new(i1: I1, i2: I2, cmp: CMP) -> Self {
775 Self {
776 i1: i1.peekable(),
777 i2: i2.peekable(),
778 cmp,
779 }
780 }
781}
782
783impl<I1, I2, CMP> Iterator for MergeSortedIterator<I1, I2, CMP>
784where I1: Iterator,
785 I2: Iterator<Item = I1::Item>,
786 CMP: Fn(&I1::Item, &I1::Item) -> Ordering,
787{
788 type Item = I1::Item;
789
790 fn next(&mut self) -> Option<Self::Item> {
791 if let Some(p1) = self.i1.peek() {
792 if let Some(p2) = self.i2.peek() {
793 match (self.cmp)(p1, p2) {
794 Ordering::Less => self.i1.next(),
795 Ordering::Greater => self.i2.next(),
796 Ordering::Equal => {
797 drop(self.i2.next());
799 self.i1.next()
800 },
801 }
802 } else {
803 self.i1.next()
804 }
805 } else {
806 self.i2.next()
807 }
808 }
809}
810
811pub trait Img<D> {
824 #[allow(private_interfaces)]
827 fn into_point(self, t: f64) -> Point<D>;
828}
829
830#[derive(Clone, Debug)]
832pub struct NoData {
833 marker: PhantomData<()>,
836}
837
838impl NoData {
839 fn new() -> Self { Self { marker: PhantomData } }
840}
841
842impl Img<NoData> for f64 {
843 #[allow(private_interfaces)]
844 #[inline]
845 fn into_point(self, t: f64) -> Point<NoData> {
846 Point::new_unchecked(t, [t, self], NoData::new())
847 }
848}
849
850impl Img<NoData> for [f64; 2] {
851 #[allow(private_interfaces)]
852 #[inline]
853 fn into_point(self, t: f64) -> Point<NoData> {
854 Point::new_unchecked(t, self, NoData::new())
855 }
856}
857
858impl Img<NoData> for (f64, f64) {
859 #[allow(private_interfaces)]
860 #[inline]
861 fn into_point(self, t: f64) -> Point<NoData> {
862 Point::new_unchecked(t, [self.0, self.1], NoData::new())
863 }
864}
865
866#[cfg(feature = "num-complex")]
867impl Img<num_complex::Complex<f64>> for num_complex::Complex<f64> {
868 #[allow(private_interfaces)]
869 #[inline]
870 fn into_point(self, t: f64) -> Point<num_complex::Complex<f64>> {
871 Point::new_unchecked(t, [self.re, self.im], self)
872 }
873}
874
875pub struct Data<D>(pub D);
879
880impl<D> Img<D> for (f64, Data::<D>) {
882 #[allow(private_interfaces)]
883 #[inline]
884 fn into_point(self, t: f64) -> Point<D> {
885 Point::new_unchecked(t, [t, self.0], self.1.0)
886 }
887}
888
889impl<D> Img<D> for ([f64; 2], Data::<D>) {
891 #[allow(private_interfaces)]
892 #[inline]
893 fn into_point(self, t: f64) -> Point<D> {
894 Point::new_unchecked(t, self.0, self.1.0)
895 }
896}
897
898#[cfg(feature = "num-complex")]
899impl<D> Img<D> for (num_complex::Complex<f64>, Data::<D>) {
900 #[allow(private_interfaces)]
901 #[inline]
902 fn into_point(self, t: f64) -> Point<D> {
903 Point::new_unchecked(t, [self.0.re, self.0.im], self.1.0)
904 }
905}
906
907
908macro_rules! new_sampling_fn {
915 ($(#[$docfn: meta])*, $(#[$docfn_extra: meta])* $fun: ident -> $ft: ty,
917 $(#[$doc: meta])* $struct: ident,
919 $wrap_f: ident
920 ) => {
921 impl<D> Sampling<D> {
922 $(#[$docfn])*
923 $(#[$docfn_extra])*
927 #[must_use]
928 #[allow(deprecated)]
929 pub fn $fun<F, Y>(f: F, a: f64, b: f64) -> $struct<F, D>
930 where F: FnMut(f64) -> Y,
931 Y: Img<D> {
932 if !a.is_finite() {
933 panic!("curve_sampling::{}: a = {} must be finite",
934 stringify!($fun), a);
935 }
936 if !b.is_finite() {
937 panic!("curve_sampling::{}: b = {} must be finite",
938 stringify!($fun), b);
939 }
940 $struct { f,
941 a, b, n: 100,
943 viewport: None,
944 init: vec![],
945 init_pt: vec![],
946 }
947 }
948 }
949
950 $(#[$doc])*
951 pub struct $struct<F, D> {
952 f: F, a: f64, b: f64,
953 n: usize,
954 viewport: Option<BoundingBox>,
955 init: Vec<f64>,
956 init_pt: Vec<Point<D>>,
957 }
958
959 #[allow(deprecated)]
960 impl<F, Y, D> $struct<F, D>
961 where F: FnMut(f64) -> Y,
962 Y: Img<D>,
963 {
964 pub fn n(mut self, n: usize) -> Self {
970 if n < 2 {
971 panic!("curve_sampling: n = {} must at least be 2", n)
972 }
973 self.n = n;
974 self
975 }
976
977 pub fn viewport(mut self, vp: BoundingBox) -> Self {
980 self.viewport = Some(vp);
981 self
982 }
983
984 #[doc = stringify!(Sampling::$fun)]
986 pub fn init<'a, I>(mut self, ts: I) -> Self
990 where I: IntoIterator<Item = &'a f64> {
991 for &t in ts {
992 if self.a <= t && t <= self.b { self.init.push(t);
994 }
995 }
996 self
997 }
998
999 #[doc = stringify!(Sampling::$fun)]
1005 pub fn init_pt<I>(mut self, pts: I) -> Self
1007 where I: IntoIterator<Item = (f64, Y)>,
1008 {
1009 for (t, y) in pts {
1010 if self.a <= t && t <= self.b { self.init_pt.push(y.into_point(t));
1012 }
1013 }
1014 self
1015 }
1016
1017 fn eval_init_and_sort(&mut self) {
1023 self.init_pt.reserve(self.init.len());
1024 for &t in &self.init {
1027 self.init_pt.push((self.f)(t).into_point(t))
1028 }
1029 if self.a <= self.b {
1030 self.init_pt.sort_unstable_by(Point::cmp_t_incr);
1031 } else {
1032 self.init_pt.sort_unstable_by(Point::cmp_t_decr);
1033 }
1034 }
1035 }
1036 }
1037}
1038
1039new_sampling_fn!(
1044 ,
1050 uniform -> f64,
1061 Uniform,
1064 FnPoint);
1065
1066impl<F, Y, D> Uniform<F, D>
1067where F: FnMut(f64) -> Y,
1068 Y: Img<D> {
1069 pub fn build(mut self) -> Sampling<D> {
1071 if self.a == self.b {
1072 let p = (self.f)(self.a).into_point(self.a);
1074 if p.is_valid() {
1075 return Sampling::singleton(p);
1076 } else {
1077 return Sampling::empty()
1078 }
1079 }
1080 self.eval_init_and_sort();
1081 let dt = (self.b - self.a) / (self.n - 1) as f64;
1082 let uniform = (0 .. self.n).into_iter().map(|i| {
1083 let t = self.a + i as f64 * dt;
1084 (self.f)(t).into_point(t)
1085 });
1086 let cmp = if self.a <= self.b {
1087 Point::cmp_t_incr
1088 } else {
1089 Point::cmp_t_decr
1090 };
1091 let points = MergeSortedIterator::new(
1092 self.init_pt.into_iter(),
1093 uniform,
1094 cmp);
1095 Sampling::from_point_iterator(points)
1096 }
1097}
1098
1099mod cost {
1104 use super::{Point, Coord, Lengths};
1105
1106 pub const HANGING_NODE: f64 = 5e5;
1132
1133 #[inline]
1136 pub(crate) fn set_middle<D>(
1137 p0: Coord, pm: &mut Point<D>, p1: Coord, len: Lengths
1138 ) {
1139 let [x0, y0] = p0.xy;
1140 let [xm, ym] = pm.xy;
1141 let [x1, y1] = p1.xy;
1142 let dx0m = (x0 - xm) / len.x;
1143 let dy0m = (y0 - ym) / len.y;
1144 let dx1m = (x1 - xm) / len.x;
1145 let dy1m = (y1 - ym) / len.y;
1146 let len0m = dx0m.hypot(dy0m);
1147 let len1m = dx1m.hypot(dy1m);
1148 if len0m == 0. || len1m == 0. {
1149 pm.cost = 0.; } else {
1151 let dx = - dx0m * dx1m - dy0m * dy1m;
1152 let dy = dy0m * dx1m - dx0m * dy1m;
1153 pm.cost = dy.atan2(dx); debug_assert!(!pm.cost.is_nan());
1155 }
1156 }
1157
1158 #[inline]
1161 pub(crate) fn segment_vp(
1162 p0: Coord,
1163 p1: Coord,
1164 len: Lengths,
1165 in_vp: bool
1166 ) -> f64 {
1167 if ! in_vp { return f64::NEG_INFINITY }
1168 segment(p0, p1, len)
1169 }
1170
1171 #[inline]
1174 pub(crate) fn segment(p0: Coord, p1: Coord, len: Lengths) -> f64 {
1175 let dt = (p1.t - p0.t).abs() / len.t; debug_assert!((0. ..=1.).contains(&dt), "dt = {dt} ∉ [0,1]");
1177 let [x0, y0] = p0.xy;
1181 let [x1, y1] = p1.xy;
1182 let dx = ((x1 - x0) / len.x).abs();
1183 let dy = ((y1 - y0) / len.y).abs();
1184 let mut cost = p0.cost.abs() + p1.cost.abs(); if p0.cost * p1.cost < 0. {
1186 if dx <= 0.01 && dy <= 0.01 { cost *= 0.5 }
1189 else if !(dx <= 0.05 && dy <= 0.05) { cost *= 8. }
1190 }
1191 if dt >= 0.8 { cost }
1192 else {
1193 let dt = dt / 0.8;
1194 dt * dt * (6. + (-8. + 3. * dt) * dt) * cost
1195 }
1196 }
1197
1198}
1199
1200fn push_segment<D>(
1205 pq: &mut PQ<D>,
1206 p0: &Point<D>, w0: &list::Witness<Point<D>>, p1: Coord,
1207 len: Lengths, in_vp: bool
1208) {
1209 let cost_segment = cost::segment_vp(p0.coord(), p1, len, in_vp);
1213 let w = pq.push(cost_segment, w0.clone());
1215 *p0.witness.borrow_mut() = Some(w);
1216}
1217
1218unsafe fn update_segment<D>(
1224 pq: &mut PQ<D>,
1225 p0: &Point<D>,
1226 p1: Coord,
1227 len: Lengths
1228) {
1229 match &*p0.witness.borrow() {
1230 Some(w) => {
1231 let priority = cost::segment(p0.coord(), p1, len);
1232 pq.update_priority(w, priority)
1233 }
1234 None => panic!("Sampling::update_segment: unset witness"),
1235 }
1236}
1237
1238fn compute<D>(s: &mut Sampling<D>, in_vp: impl Fn(&Point<D>) -> bool) {
1245 if let Some(len) = s.lengths() {
1246 s.path.first_mut().unwrap().cost = 0.;
1247 s.path.last_mut().unwrap().cost = 0.;
1248 let segments = unsafe { s.path.iter_segments_mut() };
1249 for (p0, w0, pm, p1) in segments {
1250 let p0_is_valid = p0.is_valid();
1251 let p0_in_vp = p0_is_valid && in_vp(p0);
1252 let pm_is_valid = pm.is_valid();
1253 let pm_in_vp;
1254 if let Some(p1) = p1 {
1255 if pm_is_valid {
1256 pm_in_vp = in_vp(pm);
1257 if p0_is_valid && p1.is_valid() {
1258 let c0 = p0.coord();
1259 let c1 = p1.coord();
1260 cost::set_middle(c0, pm, c1, len);
1261 } else {
1262 pm.cost = cost::HANGING_NODE;
1263 }
1264 } else { pm_in_vp = false;
1266 pm.cost = 0.;
1267 }
1268 if p0_is_valid || pm_is_valid {
1269 push_segment(&mut s.pq, p0, &w0, pm.coord(), len,
1271 p0_in_vp || pm_in_vp);
1272 }
1273 } else { if p0_is_valid || pm_is_valid {
1275 let mut vp = p0_in_vp;
1276 if pm.is_valid() { vp = vp || in_vp(pm) };
1277 push_segment(&mut s.pq, p0, &w0, pm.coord(), len, vp);
1278 }
1279 }
1280 }
1281 }
1282}
1283
1284fn refine_gen<D>(
1285 s: &mut Sampling<D>, n: usize,
1286 mut f: impl FnMut(f64) -> Point<D>,
1287 in_vp: impl Fn(&Point<D>) -> bool
1288) {
1289 let len = match s.lengths() {
1290 Some(lengths) => lengths,
1291 None => return };
1292 macro_rules! r { ($x: ident) => { unsafe { s.path.get(&$x) } } }
1293 macro_rules! m { ($x: ident) => { unsafe { s.path.get_mut(&$x) } } }
1294 macro_rules! prev { ($x: ident) => { unsafe { s.path.prev(& $x) } } }
1295 macro_rules! next { ($x: ident) => { unsafe { s.path.next(& $x) } } }
1296 for _ in 0 .. n {
1297 let mut p0: list::Witness<Point<D>> = match s.pq.pop() {
1298 None => break,
1299 Some(p) => p };
1300 *r!(p0).witness.borrow_mut() = None; let p1 = next!(p0).unwrap();
1302 let t = (r!(p0).t + r!(p1).t) * 0.5;
1304 let mut pm = f(t);
1305 if r!(p0).is_valid() {
1306 if r!(p1).is_valid() {
1307 let pm = unsafe { s.path.insert_after(&mut p0, pm) };
1308 let mut pm_in_vp = false;
1309 if r!(pm).is_valid() {
1310 pm_in_vp = in_vp(r!(pm));
1311 let c0 = r!(p0).coord();
1312 let c1 = r!(p1).coord();
1313 cost::set_middle(c0, m!(pm), c1, len);
1314 let cm = r!(pm).coord();
1315 if let Some(p_1) = prev!(p0) && r!(p_1).is_valid() {
1316 let c_1 = r!(p_1).coord();
1317 cost::set_middle(c_1, m!(p0), cm, len);
1318 let p_1 = r!(p_1);
1319 let p0 = r!(p0).coord();
1320 unsafe { update_segment(&mut s.pq, p_1, p0, len) }
1321 }
1322 if let Some(p2) = next!(p1) && r!(p2).is_valid() {
1323 let c2 = r!(p2).coord();
1324 cost::set_middle(cm, m!(p1), c2, len);
1325 let p1 = r!(p1);
1326 unsafe { update_segment(&mut s.pq, p1, c2, len) }
1327 }
1328 } else { m!(p0).cost = cost::HANGING_NODE;
1330 m!(pm).cost = 0.;
1331 m!(p1).cost = cost::HANGING_NODE;
1332 if let Some(p_1) = prev!(p0) {
1333 let p_1 = r!(p_1);
1334 let p0 = r!(p0).coord();
1335 unsafe { update_segment(&mut s.pq, p_1, p0, len) }
1336 }
1337 if let Some(p2) = next!(p1) {
1338 let p1 = r!(p1);
1339 let p2 = r!(p2).coord();
1340 unsafe { update_segment(&mut s.pq, p1, p2, len) }
1341 }
1342 }
1343 let vp = pm_in_vp || in_vp(r!(p0));
1344 let cm = r!(pm).coord();
1345 push_segment(&mut s.pq, r!(p0), &p0, cm, len, vp);
1346 let vp = pm_in_vp || in_vp(r!(p1));
1347 let c1 = r!(p1).coord();
1348 push_segment(&mut s.pq, r!(pm), &pm, c1, len, vp);
1349 } else { if pm.is_valid() {
1352 pm.cost = cost::HANGING_NODE;
1353 let pm = unsafe { s.path.insert_after(&mut p0, pm) };
1354 if let Some(p_1) = prev!(p0) && r!(p_1).is_valid() {
1355 let c_1 = r!(p_1).coord();
1356 let pm = r!(pm).coord();
1357 cost::set_middle(c_1, m!(p0), pm, len);
1358 let p_1 = r!(p_1);
1359 let p0 = r!(p0).coord();
1360 unsafe{ update_segment(&mut s.pq, p_1, p0, len) }
1361 }
1362 let pm_in_vp = in_vp(r!(pm));
1363 let vp = pm_in_vp || in_vp(r!(p0));
1364 let cm = r!(pm).coord();
1365 let c1 = r!(p1).coord();
1366 push_segment(&mut s.pq, r!(p0), &p0, cm, len, vp);
1367 push_segment(&mut s.pq, r!(pm), &pm, c1, len, pm_in_vp)
1368 } else { pm.cost = 0.;
1375 let pm = {
1376 if r!(p1).witness.borrow().is_none() {
1377 unsafe { *s.path.get_mut(&p1) = pm } ;
1380 p1 } else {
1382 unsafe { s.path.insert_after(&mut p0, pm) }
1383 }
1384 };
1385 let vp = in_vp(r!(p0));
1386 let cm = r!(pm).coord();
1387 push_segment(&mut s.pq, r!(p0), &p0, cm, len, vp)
1388 }
1389 }
1390 } else { debug_assert!(r!(p1).is_valid());
1392 if pm.is_valid() {
1393 pm.cost = cost::HANGING_NODE;
1394 let pm = unsafe { s.path.insert_after(&mut p0, pm) };
1395 if let Some(p2) = next!(p1) && r!(p2).is_valid() {
1396 let pm = r!(pm).coord();
1397 let p2 = r!(p2).coord();
1398 cost::set_middle(pm, m!(p1), p2, len);
1399 let p1 = r!(p1);
1400 unsafe{ update_segment(&mut s.pq, p1, p2, len) }
1401 }
1402 let pm_in_vp = in_vp(r!(pm));
1403 let cm = r!(pm).coord();
1404 let c1 = r!(p1).coord();
1405 push_segment(&mut s.pq, r!(p0), &p0, cm, len, pm_in_vp);
1406 let in_vp = pm_in_vp || in_vp(r!(p1));
1407 push_segment(&mut s.pq, r!(pm), &pm, c1, len, in_vp);
1408 } else { pm.cost = 0.;
1411 let pm = {
1412 if let Some(p_1) = prev!(p0) {
1413 if r!(p_1).is_valid() {
1414 unsafe { s.path.insert_after(&mut p0, pm) }
1415 } else {
1416 unsafe { *s.path.get_mut(&p0) = pm };
1418 p0
1419 }
1420 } else {
1421 unsafe { s.path.insert_after(&mut p0, pm) }
1422 }
1423 };
1424 let vp = in_vp(r!(p1));
1425 let c1 = r!(p1).coord();
1426 push_segment(&mut s.pq, r!(pm), &pm, c1, len, vp)
1427 }
1428 }
1429 }
1430}
1431
1432struct AlmostUniformSampling<D, F>
1433where F: FnMut(f64) -> Point<D> {
1434 f: F,
1435 a: f64,
1436 b: f64,
1437 n: usize,
1438 i: usize,
1439 dt: f64,
1440 random: u32,
1441}
1442
1443fn almost_uniform_sampling<D, F: FnMut(f64) -> Point<D>>(
1449 f: F,
1450 a: f64, b: f64, n: usize
1451) -> AlmostUniformSampling<D, F> {
1452 let n = n.max(4);
1453 let dt = (b - a) / (n - 3) as f64;
1454 let random = (n as u32).wrapping_mul(1_000_000);
1455 AlmostUniformSampling { f, a, b, n, i: 0, dt, random }
1456}
1457
1458impl<D, F: FnMut(f64) -> Point<D>> Iterator for AlmostUniformSampling<D, F> {
1459 type Item = Point<D>;
1460
1461 fn next(&mut self) -> Option<Point<D>> {
1462 const NORMALIZE_01: f64 = 1. / u32::MAX as f64;
1463
1464 if self.i >= self.n { return None }
1465 let f = &mut self.f;
1466 let v =
1467 if self.i == 0 { f(self.a) }
1468 else if self.i == 1 { f(self.a + 0.0625 * self.dt) }
1469 else if self.i == self.n - 2 { f(self.b - 0.0625 * self.dt) }
1470 else if self.i == self.n - 1 { f(self.b) }
1471 else { self.random ^= self.random << 13;
1478 self.random ^= self.random >> 17;
1479 self.random ^= self.random << 5;
1480 let rand = self.random as f64 * NORMALIZE_01;
1481 let j = (self.i - 1) as f64 + rand * 0.125 - 0.0625;
1482 f(self.a + j * self.dt)
1483 };
1484 self.i += 1;
1485 Some(v)
1486 }
1487}
1488
1489impl<D> Sampling<D> {
1490 fn build(
1497 points: Vec<Point<D>>,
1498 mut f: impl FnMut(f64) -> Point<D>,
1499 a: f64, b: f64, n: usize,
1500 viewport: Option<BoundingBox>
1501 ) -> Self {
1502 if a == b {
1503 let p = f(a);
1504 if p.is_valid() {
1505 return Sampling::singleton(p);
1506 } else {
1507 return Sampling::empty()
1508 }
1509 }
1510 let n0 = (n / 10).max(10);
1515 let uniform = almost_uniform_sampling(&mut f, a, b, n0);
1516 let points = MergeSortedIterator::new(
1517 points.into_iter(),
1518 uniform,
1519 if a <= b { Point::cmp_t_incr } else { Point::cmp_t_decr });
1520 let mut s = Sampling::from_point_iterator(points);
1521 match viewport {
1522 Some(vp) => {
1523 let in_vp = |p: &Point<_>| vp.contains(p);
1524 compute(&mut s, in_vp);
1525 refine_gen(&mut s, n.saturating_sub(n0), &mut f, in_vp)
1526 }
1527 None => {
1528 compute(&mut s, |_| true);
1529 refine_gen(&mut s, n.saturating_sub(n0), &mut f, |_| true)
1530 }
1531 }
1532 s
1533 }
1534}
1535
1536
1537new_sampling_fn!(
1538 ,
1549 #[cfg_attr(feature = "num-complex", doc = "
1581With the feature `num-complex`, complex-valued functions may be used directly.
1582
1583```
1584use num_complex::Complex64 as C64;
1585use curve_sampling::{Sampling, Data};
1586# fn main() -> Result<(), Box<dyn std::error::Error>> {
1587fn f(t: f64) -> C64 {
1588 C64::i().powf(t)
1589}
1590let s = Sampling::fun(f, 0., 1.).build();
1591// ...
1592# Ok(()) }
1593```
1594")]
1595 fun -> f64,
1596 Fun,
1599 FnPoint);
1600
1601impl<F, Y, D> Fun<F, D>
1602where F: FnMut(f64) -> Y,
1603 Y: Img<D> {
1604 pub fn build(mut self) -> Sampling<D> {
1606 self.eval_init_and_sort();
1607 Sampling::build(
1608 self.init_pt,
1609 |x| (self.f)(x).into_point(x),
1610 self.a, self.b, self.n, self.viewport)
1611 }
1612}
1613
1614
1615new_sampling_fn!(
1616 #[deprecated(note = "Use Sampling::fun instead")]
1617 ,
1620 param -> [f64; 2],
1631 #[deprecated(note = "See curve_sampling::Fun instead")]
1632 Param,
1635 ParamPoint);
1636
1637#[allow(deprecated)]
1638impl<F, Y, D> Param<F, D>
1639where F: FnMut(f64) -> Y,
1640 Y: Img<D> {
1641 pub fn build(mut self) -> Sampling<D> {
1643 self.eval_init_and_sort();
1644 Sampling::build(
1645 self.init_pt,
1646 |t| (self.f)(t).into_point(t),
1647 self.a, self.b, self.n, self.viewport)
1648 }
1649}
1650
1651
1652pub struct LaTeX<'a, D> {
1669 sampling: &'a Sampling<D>,
1670 n: usize,
1671 color: Option<RGB8>,
1672 arrow: Option<&'a str>,
1673 arrow_pos: Option<f64>,
1674}
1675
1676impl<'a, D> LaTeX<'a, D> {
1677 #[inline]
1678 fn new(s: &'a Sampling<D>) -> Self {
1679 Self { sampling: s, n: 20_000, color: None,
1680 arrow: None, arrow_pos: None }
1681 }
1682
1683 pub fn n(&mut self, n: usize) -> &mut Self {
1687 self.n = n;
1688 self
1689 }
1690
1691 pub fn color(&mut self, color: RGB8) -> &mut Self {
1694 self.color = Some(color);
1695 self
1696 }
1697
1698 pub fn arrow(&mut self, arrow: &'a str) -> &mut Self {
1702 self.arrow = Some(arrow);
1703 self
1704 }
1705
1706 pub fn arrow_pos(&mut self, arrow_pos: f64) -> &mut Self {
1710 if ! arrow_pos.is_finite() {
1711 panic!("curve_sampling::LaTeX::arrow_pos: \
1712 position must be finite");
1713 }
1714 self.arrow_pos = Some(arrow_pos.clamp(0., 1.));
1715 self
1716 }
1717
1718 fn write_with_lines(&self, f: &mut impl Write) -> Result<(), io::Error> {
1720 let mut n = 0;
1721 let mut new_path = true;
1722 for [x,y] in self.sampling.iter() {
1723 if x.is_nan() {
1724 writeln!(f, "\\pgfusepath{{stroke}}")?;
1725 n = 0;
1726 new_path = true;
1727 } else {
1728 n += 1;
1729 if new_path {
1730 writeln!(f, "\\pgfpathmoveto{{\\pgfpointxy\
1731 {{{x}}}{{{y}}}}}")?
1732 } else if n >= self.n {
1733 write!(f, "\\pgfpathlineto{{\\pgfpointxy\
1734 {{{x}}}{{{y}}}}}\n\
1735 \\pgfusepath{{stroke}}\n\
1736 \\pgfpathmoveto{{\\pgfpointxy\
1737 {{{x}}}{{{y}}}}}\n")?;
1738 n = 0;
1739 } else {
1740 writeln!(f, "\\pgfpathlineto{{\\pgfpointxy\
1741 {{{x}}}{{{y}}}}}")?
1742 }
1743 new_path = false;
1744 }
1745 }
1746 Ok(())
1747 }
1748
1749 fn write_with_arrows(&self, f: &mut impl Write, arrow: &str,
1751 arrow_pos: f64) -> Result<(), io::Error> {
1752 let mut lens = vec![];
1754 let mut prev_pt: Option<[f64; 2]> = None;
1755 let mut cur_len = 0.;
1756 for p @ [x, y] in self.sampling.iter() {
1757 if x.is_nan() {
1758 lens.push(arrow_pos * cur_len);
1759 prev_pt = None;
1760 cur_len = 0.;
1761 } else {
1762 if let Some([x0, y0]) = prev_pt {
1763 cur_len += (x - x0).hypot(y - y0);
1764 }
1765 prev_pt = Some(p);
1766 }
1767 }
1768 lens.push(arrow_pos * cur_len);
1769 if lens.is_empty() { return Ok(()) }
1770 let mut lens = lens.iter();
1771 let mut rem_len = *lens.next().unwrap(); prev_pt = None;
1773 let mut n = 0;
1774 for p @ [x, y] in self.sampling.iter() {
1775 if x.is_nan() {
1776 writeln!(f, "\\pgfusepath{{stroke}}")?;
1777 rem_len = *lens.next().unwrap();
1778 prev_pt = None;
1779 } else {
1780 n += 1;
1781 if let Some([x0, y0]) = prev_pt {
1782 let dx = x - x0;
1783 let dy = y - y0;
1784 let l = dx.hypot(dy);
1785 if rem_len <= l {
1786 writeln!(f, "\\pgfusepath{{stroke}}")?;
1787 let pct = rem_len / l;
1790 if pct <= 1e-14 {
1791 write!(f, "\\pgfsetarrowsstart{{{arrow}}}\n\
1792 \\pgfpathmoveto{{\\pgfpointxy
1793 {{{x0}}}{{{y0}}}}}\n\
1794 \\pgfpathlineto{{\\pgfpointxy\
1795 {{{x}}}{{{y}}}}}\n\
1796 \\pgfusepath{{stroke}}\n\
1797 \\pgfsetarrowsend{{}}\n\
1798 \\pgfpathmoveto{{\\pgfpointxy\
1799 {{{x}}}{{{y}}}}}\n")?;
1800 } else {
1801 let xm = x0 + pct * dx;
1802 let ym = y0 + pct * dy;
1803 write!(f, "\\pgfsetarrowsend{{{arrow}}}\n\
1804 \\pgfpathmoveto{{\\pgfpointxy\
1805 {{{x0}}}{{{y0}}}}}\n\
1806 \\pgfpathlineto{{\\pgfpointxy\
1807 {{{xm}}}{{{ym}}}}}\n\
1808 \\pgfusepath{{stroke}}\n\
1809 \\pgfsetarrowsend{{}}\n\
1810 \\pgfpathmoveto{{\\pgfpointxy\
1811 {{{xm}}}{{{ym}}}}}\n\
1812 \\pgfpathlineto{{\\pgfpointxy\
1813 {{{x}}}{{{y}}}}}\n")?;
1814 n = 2;
1815 }
1816 rem_len = f64::INFINITY; } else if n >= self.n {
1818 write!(f, "\\pgfpathlineto{{\\pgfpointxy\
1819 {{{x}}}{{{y}}}}}\n\
1820 \\pgfusepath{{stroke}}\n\
1821 \\pgfpathmoveto{{\\pgfpointxy\
1822 {{{x}}}{{{y}}}}}\n")?;
1823 n = 0;
1824 } else {
1825 writeln!(f, "\\pgfpathlineto{{\\pgfpointxy\
1826 {{{x}}}{{{y}}}}}")?
1827 }
1828 rem_len -= l;
1829 } else {
1830 writeln!(f, "\\pgfpathmoveto{{\\pgfpointxy\
1832 {{{x}}}{{{y}}}}}")?
1833 }
1834 prev_pt = Some(p);
1835 }
1836 }
1837 Ok(())
1838 }
1839
1840 pub fn write(&self, f: &mut impl Write) -> Result<(), io::Error> {
1842 writeln!(f, "% Written by the Rust curve-sampling crate.")?;
1843 writeln!(f, "\\begin{{pgfscope}}")?;
1844 if let Some(RGB8 {r, g, b}) = self.color {
1845 write!(f, "\\definecolor{{RustCurveSamplingColor}}{{RGB}}\
1846 {{{r},{g},{b}}}\n\
1847 \\pgfsetstrokecolor{{RustCurveSamplingColor}}\n")?
1848 }
1849 match (self.arrow, self.arrow_pos) {
1850 (None, None) => self.write_with_lines(f)?,
1851 (Some(arrow), None) =>
1852 self.write_with_arrows(f, arrow, 0.5)?,
1853 (None, Some(arrow_pos)) =>
1854 self.write_with_arrows(f, ">",arrow_pos)?,
1855 (Some(arrow), Some(arrow_pos)) =>
1856 self.write_with_arrows(f, arrow, arrow_pos)?,
1857 }
1858 write!(f, "\\pgfusepath{{stroke}}\n\\end{{pgfscope}}\n")
1859 }
1860}
1861
1862impl<D> Sampling<D> {
1864 pub fn latex(&self) -> LaTeX<'_, D> { LaTeX::new(self) }
1866
1867 pub fn write(&self, f: &mut impl Write) -> Result<(), io::Error> {
1872 for [x, y] in self.iter() {
1873 if x.is_nan() {
1874 writeln!(f)?
1875 } else {
1876 writeln!(f, "{x:e} {y:e}")?
1877 }
1878 }
1879 Ok(())
1880 }
1881}
1882
1883impl<D> Display for Sampling<D> {
1884 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
1889 for [x, y] in self.iter() {
1890 if x.is_nan() {
1891 writeln!(f)?
1892 } else {
1893 writeln!(f, "{x:e} {y:e}")?
1894 }
1895 }
1896 Ok(())
1897 }
1898}
1899
1900#[cfg(test)]
1905mod tests {
1906 use std::{error::Error,
1907 fs::File,
1908 io::Write, path::Path};
1909 use crate::{BoundingBox, Data, MergeSortedIterator, NoData, Point, Sampling};
1910
1911 #[test]
1912 fn merge_sorted_iterator() {
1913 let i1 = [1, 3, 5, 7].into_iter();
1914 let i2 = [1, 2, 5, 8].into_iter();
1915 let i = MergeSortedIterator::new(i1, i2, |i1, i2| i1.cmp(i2));
1916 assert_eq!(i.collect::<Vec<_>>(), [1, 2, 3, 5, 7, 8])
1917 }
1918
1919 type R<T> = Result<T, Box<dyn Error>>;
1920
1921 fn xy_of_sampling<D>(s: &Sampling<D>) -> Vec<Option<(f64, f64)>> {
1922 s.path.iter().map(|p| {
1923 if p.is_valid() { Some((p.xy[0], p.xy[1])) } else { None }
1924 })
1925 .collect()
1926 }
1927
1928 #[test]
1929 fn clone_sampling() {
1930 let s = Sampling::from_iter([[0.,0.], [1.,1.]]);
1931 let xy0: Vec<_> = s.iter().collect();
1932 let xy1: Vec<_> = s.clone().iter().collect();
1933 assert_eq!(xy0, xy1)
1934 }
1935
1936 #[test]
1937 fn io() {
1938 let s = Sampling::from_iter([[0.,0.], [1.,1.]]);
1939 assert_eq!(xy_of_sampling(&s), vec![Some((0.,0.)), Some((1.,1.))]);
1940 let s = Sampling::from_iter([[0.,0.], [1.,1.], [f64::NAN, 1.],
1941 [2.,2.]]);
1942 assert_eq!(xy_of_sampling(&s),
1943 vec![Some((0.,0.)), Some((1.,1.)), None, Some((2.,2.))]);
1944 }
1945
1946 #[test]
1947 fn bounding_box_singleton() {
1948 let s = Sampling::singleton(
1949 Point::new_unchecked(0., [1., 2.], NoData::new()));
1950 let bb = BoundingBox {xmin: 1., xmax: 1., ymin: 2., ymax: 2.};
1951 assert_eq!(s.bounding_box(), bb);
1952 }
1953
1954 #[test]
1955 fn singleton_has_empty_bounding_box() {
1956 let s = Sampling::singleton(
1957 Point::new_unchecked(0., [1., 2.], NoData::new()));
1958 assert!(s.bounding_box().is_empty());
1959 }
1960
1961 fn test_clip(bb: BoundingBox,
1962 path: Vec<[f64;2]>,
1963 expected: Vec<Option<(f64,f64)>>) {
1964 let s = Sampling::from_iter(path).clip(bb);
1965 assert_eq!(xy_of_sampling(&s), expected);
1966 }
1967
1968 #[test]
1969 fn clip_base () {
1970 let bb = BoundingBox { xmin: 0., xmax: 3., ymin: 0., ymax: 2.};
1971 test_clip(bb, vec![[0.,1.], [2.,3.]],
1972 vec![Some((0.,1.)), Some((1.,2.))]);
1973 test_clip(bb,
1974 vec![[-1.,0.], [2.,3.], [4.,1.]],
1975 vec![Some((0.,1.)), Some((1.,2.)), None, Some((3., 2.))]);
1976 test_clip(bb,
1977 vec![[0.,1.], [2.,3.], [4.,1.], [2.,1.], [2., -1.],
1978 [0., -1.], [0., 1.] ],
1979 vec![Some((0.,1.)), Some((1.,2.)), None,
1980 Some((3., 2.)), None,
1981 Some((3., 1.)), Some((2., 1.)), Some((2., 0.)), None,
1982 Some((0., 0.)), Some((0., 1.))]);
1983 }
1984
1985 #[test]
1986 fn clip_empty() {
1987 let bb = BoundingBox {xmin: 0., xmax: 1., ymin: 0., ymax: 1.};
1988 let path = xy_of_sampling(&Sampling::<()>::empty().clip(bb));
1989 assert_eq!(path, vec![]);
1990 }
1991
1992 #[test]
1993 fn clip_double_cut() {
1994 let bb = BoundingBox { xmin: 0., xmax: 4., ymin: 0., ymax: 2.};
1995 test_clip(bb,
1996 vec![[1., 2.], [2.,3.], [5.,0.], [-1., 0.] ],
1997 vec![Some((1., 2.)), None,
1998 Some((3., 2.)), Some((4., 1.)), None,
1999 Some((4., 0.)), Some((0., 0.)) ]);
2000 }
2001
2002 #[test]
2003 fn clip_almost_horiz() {
2004 let bb = BoundingBox { xmin: 0., xmax: 2., ymin: -1., ymax: 1.};
2005 test_clip(bb,
2006 vec![[1., 1e-100], [3., -1e-100] ],
2007 vec![Some((1., 1e-100)), Some((2., 0.))]);
2008 }
2009
2010 #[test]
2011 fn clip_touches_1pt_cut() {
2012 let bb = BoundingBox {xmin: 0., xmax: 2., ymax: 0., ymin: -1.};
2013 let cut = [f64::NAN, f64::NAN];
2014 test_clip(bb,
2015 vec![[0.,1.], cut, [1., 0.], cut, cut, [2., 1.]],
2016 vec![Some((1., 0.))])
2017 }
2018
2019 #[test]
2020 fn clip_final_cut() {
2021 let bb = BoundingBox {xmin: 0., xmax: 1., ymin: 0., ymax: 2.};
2022 test_clip(bb,
2023 vec![[0., 0.], [2., 2.]],
2024 vec![Some((0., 0.)), Some((1., 1.))])
2025 }
2026
2027 #[test]
2028 fn small_n() {
2029 for n in 2 ..= 12 {
2034 let mut evals = 0;
2035 let s = Sampling::fun(|x: f64| { evals += 1; x.sin() }, 0., 3.)
2036 .n(n).build();
2037 assert_eq!(evals, n.max(10), "n = {n}");
2038 assert_eq!(s.len(), evals);
2039 }
2040 }
2041
2042 #[test]
2043 fn uniform1() {
2044 let s = Sampling::uniform(|x| x, 0., 4.).n(3)
2045 .init(&[1.]).init_pt([(3., 0.)]).build();
2046 assert_eq!(xy_of_sampling(&s),
2047 vec![Some((0.,0.)), Some((1.,1.)), Some((2.,2.)),
2048 Some((3., 0.)), Some((4.,4.))]);
2049 }
2050
2051 #[test]
2052 fn uniform2() {
2053 let s = Sampling::uniform(|x| (4. - x).sqrt(), 0., 6.).n(4).build();
2054 assert_eq!(xy_of_sampling(&s),
2055 vec![Some((0.,2.)), Some((2., 2f64.sqrt())),
2056 Some((4., 0.)), None]);
2057 }
2058
2059 #[test]
2060 fn iter_data() {
2061 let s = Sampling::uniform(|x| (x, Data(x as i32)), 0., 4.).n(3)
2062 .init(&[1.]).init_pt([(3., (0., Data(-1)))]).build();
2063 let expected = vec![
2064 ([0.,0.], &0), ([1.,1.], &1), ([2.,2.], &2), ([3., 0.], &-1),
2065 ([4.,4.], &4)];
2066 let (_, size_hint) = s.iter_data().size_hint();
2067 assert_eq!(size_hint, Some(expected.len()));
2068 for (i, d) in s.iter_data().enumerate() {
2069 assert_eq!(d, expected[i]);
2070 }
2071 }
2072
2073 #[test]
2074 fn into_iter_data() {
2075 let s = Sampling::uniform(|x| (x, Data(x as i32)), 0., 4.).n(3)
2076 .init(&[1.]).init_pt([(3., (0., Data(-1)))]).build();
2077 let expected = vec![
2078 ([0.,0.], 0), ([1.,1.], 1), ([2.,2.], 2), ([3., 0.], -1),
2079 ([4.,4.], 4)];
2080 let (_, size_hint) = s.iter_data().size_hint();
2081 assert_eq!(size_hint, Some(expected.len()));
2082 for (i, d) in s.into_iter_data().enumerate() {
2083 assert_eq!(d, expected[i]);
2084 }
2085 }
2086
2087 fn write_with_point_costs<D>(
2090 s: &Sampling<D>,
2091 fname: impl AsRef<Path>
2092 ) -> R<()> {
2093 let mut fh = File::create(fname)?;
2094 for p in s.path.iter() {
2095 if p.is_valid() {
2096 let [x, y] = p.xy;
2097 writeln!(fh, "{x} {y} {}", p.cost)?;
2098 } else {
2099 writeln!(fh)?;
2100 }
2101 }
2102 Ok(())
2103 }
2104
2105 fn write_segments<D>(mut s: Sampling<D>, fname: impl AsRef<Path>) -> R<()> {
2106 let mut fh = File::create(fname)?;
2107 let mut seg = vec![];
2108 loop {
2109 let priority = s.pq.max_priority();
2110 if let Some(p0) = s.pq.pop() {
2111 let p1 = unsafe { s.path.next(&p0).unwrap() };
2112 let p1 = unsafe { s.path.get(&p1) };
2113 let p0 = unsafe { s.path.get(&p0) };
2114 let tm = (p0.t + p1.t) / 2.;
2115 seg.push((tm, p0.coord(), p1.coord(), priority))
2116 } else {
2117 break;
2118 }
2119 }
2120 seg.sort_by(|(t1,_,_,_), (t2,_,_,_)| t1.partial_cmp(t2).unwrap());
2121 for (tm, p0, p1, priority) in seg {
2122 writeln!(fh, "{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}",
2123 tm, p0.t, p0.xy[0], p0.xy[1],
2124 p1.t, p1.xy[0], p1.xy[1], priority)?;
2125 }
2126 Ok(())
2127 }
2128
2129 #[derive(Clone)]
2130 struct Plot {
2131 xmin: f64, xmax: f64,
2132 ymin: f64, ymax: f64,
2133 n: usize,
2134 init: Vec<f64>,
2135 }
2136
2137 impl Plot {
2138 fn plot<F>(&self, f: F, title: &str) -> R<String>
2140 where F: FnMut(f64) -> f64 {
2141 let vp = BoundingBox { xmin: self.xmin, xmax: self.xmax,
2142 ymin: self.ymin, ymax: self.ymax };
2143 let s = Sampling::fun(f, self.xmin, self.xmax)
2144 .n(self.n).init(self.init.iter()).viewport(vp).build();
2145 static mut NDAT: usize = 0;
2146 let ndat = unsafe { NDAT += 1; NDAT };
2147 println!("Horror {ndat}");
2148 s.pq.print_stats();
2149 let dir = Path::new("target");
2150 let fname = format!("horror{}.dat", ndat);
2151 s.write(&mut File::create(dir.join(&fname))?)?;
2152 let fname_p = format!("horror{}_p.dat", ndat);
2153 write_with_point_costs(&s, dir.join(&fname_p))?;
2154 let fname_s = format!("horror{}_s.dat", ndat);
2155 write_segments(s, dir.join(&fname_s))?;
2156 Ok(format!(
2157 "unset title\n\
2158 unset y2tics\n\
2159 plot [{}:{}] \"{}\" with l lt 5 title \"{}\", \
2160 \"{}\" with p lt 3 pt 6 ps 0.2 title \"n={}\"\n\
2161 set title \"#{ndat} Restricted to viewport [{}:{}]×[{}:{}]\"\n\
2162 set y2tics\n\
2163 set y2range [-1e-6:]\n\
2164 plot [{}:{}] [{}:{}] \"{}\" with l lt 5 title \"{}\", \
2165 \"{}\" with p lt 3 pt 7 ps 0.2 title \"n={}\", \
2166 \"{}\" using 1:3 with lp ps 0.2 lt rgb \"#760b0b\" \
2167 title \"cost points\", \
2168 \"{}\" using 1:8 with lp ps 0.2 lt rgb \"#909090\" \
2169 axes x1y2 title \"cost segments\"\n",
2170 self.xmin, self.xmax, &fname, title, &fname, self.n,
2171 self.xmin, self.xmax, self.ymin, self.ymax,
2172 self.xmin, self.xmax, self.ymin, self.ymax, &fname, title,
2173 &fname, self.n, &fname_p, &fname_s))
2174 }
2175 }
2176
2177 #[test]
2178 #[cfg_attr(miri, ignore)] fn horror() -> R<()> {
2180 let d = Plot {
2181 xmin: -5., xmax: 5., ymin: -5., ymax: 5., n: 100, init: vec![] };
2182 macro_rules! p {
2183 ($($id:ident $l:tt $e:expr),*) => {
2184 Plot{ $($id: $e,)* ..d.clone() } };
2185 }
2186 let s = [
2187 p!(n: 10).plot(|_| 2., "x ↦ 2")?,
2188 p!().plot(|x| x, "x ↦ x")?,
2189 p!().plot(|x| 5. * x, "x ↦ 5x")?,
2190 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(
2194 |x| 1. / x, "x ↦ 1/x")?, p!(xmin: -0.4, xmax: 2., ymin: 0., ymax: 1.6, n: 50).plot(
2196 |x| x.sqrt(), "x ↦ √x")?,
2197 p!(xmin: -2., xmax: 1., ymin: 0., ymax: 1.6, n: 50).plot(
2199 |x| (-x).sqrt(), "x ↦ √(-x)")?,
2200 p!(n: 200).plot(|x| x.tan(), "tan")?,
2201 p!().plot(|x| 1. / x.abs(), "x ↦ 1/|x|")?,
2202 p!(xmin: -6., xmax: 6., ymin: -2., ymax: 2.).plot(
2203 |x| (1. + x.cos().sin()).ln(), "1 + sin(cos x)")?,
2204 p!(xmin: 0., xmax: 6.28, ymin: -1.5, ymax: 1.5, n: 400).plot(
2205 |x| x.powi(3).sin() + x.powi(3).cos(), "sin x³ + cos x³")?,
2206 p!(xmin: -5., xmax:200., ymin: -1., ymax: 1., n: 400).plot(
2207 |x| x.sin(), "sin")?,
2208 p!(xmin: -4., xmax: 4., ymin: -1., ymax: 1.).plot(
2212 |x| (300. * x).sin(), "sin(300 x)")?,
2213 p!(xmin: -4., xmax: 4., ymin: -1., ymax: 1., n: 1000).plot(
2214 |x| (300. * x).sin(), "sin(300 x)")?,
2215 p!(xmin: -2., xmax: 2., ymin: 0., ymax: 3.).plot(
2216 |x| 1. + x * x + 0.0125 * (1. - 3. * (x - 1.)).abs().ln(),
2217 "1 + x² + 0.0125 ln|1 - 3(x-1)|")?,
2218 p!(xmin: -2., xmax: 2., ymin: 0., ymax: 3., n: 300,
2219 init:vec![4. / 3.]).plot(
2220 |x| 1. + x * x + 0.0125 * (1. - 3. * (x - 1.)).abs().ln(),
2221 "1 + x² + 0.0125 ln|1 - 3(x-1)| (specifying x:4/3)")?,
2222 p!(xmin: -0.5, xmax: 0.5, ymin: -1., ymax: 1.).plot(
2223 |x| x * (1. / x).sin(), "x sin(1/x)")?,
2224 p!(xmin: -0.5, xmax: 0.5, ymin: -1., ymax: 1., n:200).plot(
2225 |x| x * (1. / x).sin(), "x sin(1/x)")?,
2226 p!(xmin: -2., xmax: 2., ymin: -1., ymax: 1.).plot(
2227 |x| (1. / x).sin(), "sin(1/x)")?,
2228 p!(xmin: -2., xmax: 2., ymin: -1., ymax: 1., n: 400).plot(
2229 |x| (1. / x).sin(), "sin(1/x)")?,
2230 p!(xmin: -4., xmax: 4., ymin: -1., ymax: 1.).plot(
2231 |x| x.powi(4).sin(), "sin(x⁴)")?,
2232 p!(xmin: -4., xmax: 4., ymin: -1., ymax: 1., n: 600).plot(
2233 |x| x.powi(4).sin(), "sin(x⁴)")?,
2234 p!(xmin: -6., xmax: 6., ymin: -1., ymax: 1.).plot(
2235 |x| x.exp().sin(), "sin(exp x)")?,
2236 p!(xmin: -6., xmax: 6., ymin: -1., ymax: 1., n: 500).plot(
2237 |x| x.exp().sin(), "sin(exp x)")?,
2238 p!(xmin: -10., xmax: 10., ymin: 0., ymax: 10.).plot(
2239 |x| 1. / x.sin(), "1 / sin x")?,
2240 p!(xmin: -6., xmax: 6., ymin: -1., ymax: 1.).plot(
2241 |x| x.sin() / x, "(sin x)/x")?,
2242 p!(xmin: -2., xmax: 2., ymin: -15., ymax: 15.).plot(
2243 |x| (x.powi(3) - x + 1.).tan() + 1. / (x + 3. * x.exp()),
2244 "tan(x³ - x + 1) + 1/(x + 3 eˣ)")?,
2245 p!(xmin: 0., xmax: 17., ymin: 0., ymax: 2.).plot(
2246 |x| (1. + x.cos()) * (-0.1 * x).exp(),
2247 "(1 + cos x) exp(-x/10)")?,
2248 p!(xmin: -2., xmax: 17., ymin: 0., ymax: 2.).plot(
2249 |x| (1. + x.cos()) * (-0.1 * x).exp(),
2250 "(1 + cos x) exp(-x/10)")?,
2251 p!(xmin: 0., xmax: 17., ymin: 0., ymax: 2.).plot(
2252 |x| (1. + x.cos()) * (-0.01 * x * x).exp(),
2253 "(1 + cos x) exp(-x²/100)")?,
2254 ].join("");
2255 let mut fh = File::create("target/horror.gp")?;
2256 write!(fh, "set terminal pdfcairo\n\
2257 set output \"horror.pdf\"\n\
2258 set grid\n\
2259 {s}\n")?;
2260 Ok(())
2261 }
2262}