use std::{
cell::RefCell,
fmt::{self, Display, Formatter},
io::{self, Write},
marker::PhantomData,
ops::ControlFlow,
};
use rgb::*;
mod approx_priority_queue;
use approx_priority_queue as pq;
mod linked_list;
use linked_list as list;
use list::List;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct BoundingBox {
pub xmin: f64,
pub xmax: f64,
pub ymin: f64,
pub ymax: f64,
}
impl BoundingBox {
#[inline]
pub(crate) fn empty() -> Self {
Self { xmin: f64::INFINITY, xmax: f64::NEG_INFINITY,
ymin: f64::INFINITY, ymax: f64::NEG_INFINITY }
}
#[inline]
pub fn is_empty(&self) -> bool {
!(self.xmin < self.xmax && self.ymin < self.ymax) }
#[inline]
fn contains<D>(&self, p: &Point<D>) -> bool {
let [x, y] = p.xy;
self.xmin <= x && x <= self.xmax && self.ymin <= y && y <= self.ymax
}
#[inline]
pub fn hull(&self, other: &Self) -> Self {
BoundingBox { xmin: self.xmin.min(other.xmin),
xmax: self.xmax.max(other.xmax),
ymin: self.ymin.min(other.ymin),
ymax: self.ymax.max(other.ymax) }
}
}
#[derive(Debug)]
struct Point<D> {
t: f64, xy: [f64; 2],
data: D,
cost: f64,
witness: RefCell<Option<pq::Witness<list::Witness<Point<D>>>>>,
}
#[derive(Clone, Copy, Debug)]
struct Coord {
t: f64,
xy: [f64; 2],
cost: f64,
}
impl<D: Clone> Clone for Point<D> {
fn clone(&self) -> Self {
Self {
t: self.t, xy: self.xy,
data: self.data.clone(),
cost: self.cost,
witness: None.into(),
}
}
}
impl<D> Point<D> {
#[inline]
fn new_unchecked(t: f64, xy: [f64; 2], data: D) -> Self {
Point { t, xy, data, cost: 0.,
witness: None.into() }
}
#[inline]
fn cut(t: f64, data: D) -> Self {
Point { t, xy: [f64::NAN; 2], data,
cost: 0.,
witness: None.into() }
}
#[inline]
fn is_valid(&self) -> bool {
self.xy.iter().all(|z| z.is_finite())
}
#[inline]
fn opt(self) -> Point<Option<D>> {
Point {
t: self.t, xy: self.xy,
data: Some(self.data),
cost: self.cost,
witness: None.into(),
}
}
#[inline]
fn coord(&self) -> Coord {
Coord { t: self.t, xy: self.xy, cost: self.cost }
}
}
pub struct Sampling<D> {
pq: PQ<D>,
path: List<Point<D>>,
vp: Option<BoundingBox>, }
type PQ<D> = pq::PQ<list::Witness<Point<D>>>;
impl<D: Clone> Clone for Sampling<D> {
fn clone(&self) -> Self {
Self { pq: PQ::new(),
path: self.path.clone(),
vp: self.vp }
}
}
#[derive(Debug, Clone, Copy)]
struct Lengths {
t: f64,
x: f64,
y: f64,
}
impl<D> Sampling<D> {
#[inline]
pub fn is_empty(&self) -> bool { self.path.is_empty() }
#[inline]
pub(crate) fn empty() -> Self {
Self { pq: PQ::new(),
path: List::new(),
vp: None }
}
#[inline]
pub(crate) fn singleton(p: Point<D>) -> Self {
debug_assert!(p.t.is_finite() && p.xy.iter().all(|z| z.is_finite()));
let mut path = List::new();
path.push_back(p);
Self { pq: PQ::new(), path, vp: None }
}
fn from_list(path: List<Point<D>>) -> Self {
Self {
pq: PQ::new(),
path,
vp: None,
}
}
#[inline]
pub(crate) fn set_vp(&mut self, bb: BoundingBox) {
self.vp = Some(bb);
}
pub(crate) fn lengths(&self) -> Option<Lengths> {
if self.is_empty() { return None }
let p0 = self.path.first().unwrap();
let p1 = self.path.last().unwrap();
let len_x;
let len_y;
match self.vp {
Some(vp) => {
len_x = vp.xmax - vp.xmin;
len_y = vp.ymax - vp.ymin;
}
None => {
len_x = 1.;
len_y = 1.;
}
}
Some(Lengths { t: (p1.t - p0.t).abs(), x: len_x, y: len_y })
}
pub fn len(&self) -> usize {
self.path.len()
}
pub fn iter(&self) -> Iter<'_, D> {
Iter { iter: self.iter_data() }
}
pub fn iter_data(&self) -> IterData<'_, D> {
IterData {
path: self.path.iter(),
prev_is_cut: true,
}
}
pub fn iter_mut(&mut self) -> IterMut<'_, D> {
IterMut { path: self.path.iter_mut() }
}
pub fn into_iter_data(self) -> IntoIterData<D> {
IntoIterData { path: self.path.into_iter() }
}
#[inline]
pub fn x(&self) -> Vec<f64> {
let mut v = Vec::with_capacity(self.path.len());
for [x, _] in self.iter() { v.push(x) }
v
}
#[inline]
pub fn y(&self) -> Vec<f64> {
let mut v = Vec::with_capacity(self.path.len());
for [_, y] in self.iter() { v.push(y) }
v
}
}
pub struct IterData<'a, D> {
path: list::Iter<'a, Point<D>>,
prev_is_cut: bool,
}
impl<'a, D> Iterator for IterData<'a, D> {
type Item = ([f64; 2], &'a D);
fn next(&mut self) -> Option<Self::Item> {
match self.path.next() {
None => None,
Some(p) => {
if p.is_valid() {
self.prev_is_cut = false;
Some((p.xy, &p.data))
} else if self.prev_is_cut {
let r = self.path.try_fold((), |_, p| {
if p.is_valid() {
ControlFlow::Break(((), p))
} else {
ControlFlow::Continue(())
}
});
match r {
ControlFlow::Continue(_) => {
None
}
ControlFlow::Break((_, p)) => {
self.prev_is_cut = false;
Some((p.xy, &p.data))
}
}
} else {
self.prev_is_cut = true;
Some(([f64::NAN; 2], &p.data))
}
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.path.len()))
}
}
pub struct Iter<'a, D> {
iter: IterData<'a, D>,
}
impl<'a, D> Iterator for Iter<'a, D> {
type Item = [f64; 2];
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|(xy, _)| xy)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
pub struct IterMut<'a, D> {
path: list::IterMut<'a, Point<D>>,
}
impl<'a, D> Iterator for IterMut<'a, D> {
type Item = &'a mut [f64; 2];
fn next(&mut self) -> Option<Self::Item> {
self.path.next().map(|p| &mut p.xy)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.path.len(), Some(self.path.len()))
}
}
pub struct IntoIterData<D> {
path: list::IntoIter<Point<D>>,
}
impl<D> Iterator for IntoIterData<D> {
type Item = ([f64; 2], D);
fn next(&mut self) -> Option<Self::Item> {
self.path.next().map(|p| (p.xy, p.data))
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.path.len(), Some(self.path.len()))
}
}
#[derive(Debug)]
enum Intersection<D> {
Empty,
Pt(Point<D>),
Seg(Point<D>, Point<D>),
}
impl<D> Sampling<D> {
pub fn bounding_box(&self) -> BoundingBox {
let mut points = self.iter().skip_while(|[x,_]| x.is_nan());
let mut bb = match &points.next() {
Some([x, y]) => BoundingBox { xmin: *x, xmax: *x,
ymin: *y, ymax: *y },
None => return BoundingBox::empty()
};
for [x, y] in points {
if x < bb.xmin { bb.xmin = x }
else if bb.xmax < x { bb.xmax = x };
if y < bb.ymin { bb.ymin = y }
else if bb.ymax < y { bb.ymax = y };
}
bb
}
pub fn transpose(&mut self) -> &mut Self {
for p in self.path.iter_mut() {
let [x, y] = p.xy;
p.xy = [y, x];
}
self
}
#[inline]
#[must_use]
fn intersect(
p0: Coord, p1: Coord, bb: BoundingBox
) -> Option<Point<Option<D>>> {
let [x0, y0] = p0.xy;
let [x1, y1] = p1.xy;
let mut t = 1.; let dx = x1 - x0; let r = (if dx >= 0. {bb.xmax} else {bb.xmin} - x0) / dx;
if r < t { t = r } let dy = y1 - y0; let r = (if dy >= 0. {bb.ymax} else {bb.ymin} - y0) / dy;
if r < t { t = r };
if t <= 1e-14 {
None
} else {
Some(Point::new_unchecked(
p0.t + t * (p1.t - p0.t),
[x0 + t * dx, y0 + t * dy],
None))
}
}
#[inline]
#[must_use]
fn intersect_seg(
p0: Coord, p1: Coord, bb: BoundingBox,
) -> Intersection<Option<D>> {
let [x0, y0] = p0.xy;
let [x1, y1] = p1.xy;
let mut t0 = 0.; let mut t1 = 1.; let dx = x1 - x0; let r0;
let r1; if dx >= 0. {
r0 = (bb.xmin - x0) / dx;
r1 = (bb.xmax - x0) / dx;
} else {
r0 = (bb.xmax - x0) / dx;
r1 = (bb.xmin - x0) / dx;
}
if r0 > 1. || r1 < 0. { return Intersection::Empty }
if r0 > 0. { t0 = r0 } if r1 < 1. { t1 = r1 }
let dy = y1 - y0; let r0;
let r1;
if dy >= 0. {
r0 = (bb.ymin - y0) / dy;
r1 = (bb.ymax - y0) / dy;
} else {
r0 = (bb.ymax - y0) / dy;
r1 = (bb.ymin - y0) / dy;
}
if r0 > t1 || r1 < t0 { return Intersection::Empty }
if r0 > t0 { t0 = r0 }
if r1 < t1 { t1 = r1 }
if t0 < t1 { let dt = p1.t - p0.t;
let q0 = Point::new_unchecked(p0.t + t0 * dt,
[x0 + t0 * dx, y0 + t0 * dy], None);
let q1 = Point::new_unchecked(p0.t + t1 * dt,
[x0 + t1 * dx, y0 + t1 * dy], None);
Intersection::Seg(q0, q1)
} else if t0 == t1 {
let q0 = Point::new_unchecked(
p0.t + t0 * (p1.t - p0.t),
[x0 + t0 * dx, y0 + t0 * dy], None);
Intersection::Pt(q0)
} else {
Intersection::Empty
}
}
#[must_use]
pub fn clip(self, bb: BoundingBox) -> Sampling<Option<D>> {
let mut path = Vec::new();
let mut p0_opt: Option<Coord> = None;
let mut p0_inside = false;
let mut prev_cut = true; for p1 in self.path.into_iter() {
if prev_cut {
match (p0_opt, p1.is_valid()) {
(Some(p0), true) => {
let p1_inside = bb.contains(&p1);
p0_opt = Some(p1.coord());
if p0_inside { if p1_inside {
path.push(p1.opt());
prev_cut = false;
} else if
let Some(p) = Self::intersect(p0, p1.coord(), bb) {
let t = p.t;
path.push(p);
path.push(Point::cut(t, None));
} else {
let c = Point::cut(p0.t, None);
path.push(c);
}
} else if p1_inside { if let Some(p) = Self::intersect(p1.coord(), p0, bb) {
path.push(p); }
path.push(p1.opt());
prev_cut = false;
} else { match Self::intersect_seg(p0, p1.coord(), bb) {
Intersection::Seg(q0, q1) => {
let t1 = q1.t;
path.push(q0);
path.push(q1);
path.push(Point::cut(t1, None));
}
Intersection::Pt(p) => {
let t = p.t;
path.push(p);
path.push(Point::cut(t, None));
}
Intersection::Empty => (),
}
}
p0_inside = p1_inside;
}
(None, true) => {
p0_inside = bb.contains(&p1);
p0_opt = Some(p1.coord());
if p0_inside {
path.push(p1.opt());
}
}
(Some(p0), false) => {
if p0_inside {
path.push(Point::cut(p0.t, None));
}
p0_opt = None;
}
(None, false) => (), }
} else {
let p0 = p0_opt.expect("No cut ⟹ previous point");
debug_assert!(p0_inside);
if p1.is_valid() {
p0_opt = Some(p1.coord());
p0_inside = bb.contains(&p1); if p0_inside { path.push(p1.opt());
} else { if let Some(p) = Self::intersect(p0, p1.coord(), bb) {
let t = p.t;
path.push(p);
path.push(Point::cut(t, None));
} else {
path.push(Point::cut(p0.t, None));
}
prev_cut = true;
}
} else { p0_opt = None;
path.push(p1.opt());
prev_cut = true
}
}
}
if prev_cut { path.pop(); }
let mut s = Sampling::from_list(path.into());
s.set_vp(bb);
s
}
fn from_point_iterator<P>(points: P) -> Self
where P: IntoIterator<Item = Point<D>> {
let mut s = Sampling::empty();
let mut points = points.into_iter();
macro_rules! skip_until_last_cut { () => {
let mut cut = None;
let mut first_pt = None;
while let Some(p) = points.next() {
if p.is_valid() { first_pt = Some(p); break; }
cut = Some(p);
}
match (cut, first_pt) {
(_, None) => {
return s
}
(None, Some(p)) => {
s.path.push_back(p);
}
(Some(c), Some(p)) => {
s.path.push_back(Point::cut(c.t, c.data));
s.path.push_back(p);
}
}
}}
skip_until_last_cut!();
while let Some(p) = points.next() {
if p.is_valid() {
s.path.push_back(p);
} else {
s.path.push_back(Point::cut(p.t, p.data));
skip_until_last_cut!();
}
}
s
}
fn from_vec(mut points: Vec<Point<D>>, incr: bool) -> Self {
if incr {
points.sort_unstable_by(|p1, p2| {
p1.t.partial_cmp(&p2.t).unwrap() });
} else {
points.sort_unstable_by(|p1, p2| {
p2.t.partial_cmp(&p1.t).unwrap() });
}
Self::from_point_iterator(points)
}
}
impl<D, Y> FromIterator<Y> for Sampling<D>
where Y: Img<D> {
fn from_iter<P>(points: P) -> Self
where P: IntoIterator<Item = Y> {
Sampling::from_point_iterator(
points.into_iter().enumerate().map(|(i, y)| {
let t = i as f64;
let p = y.into_point(t);
let [x, y] = p.xy;
if x.is_finite() && y.is_finite() {
p
} else {
Point::cut(t, p.data)
} }))
}
}
pub trait Img<D> {
#[allow(private_interfaces)]
fn into_point(self, t: f64) -> Point<D>;
}
#[derive(Clone, Debug)]
pub struct NoData {
marker: PhantomData<()>,
}
impl NoData {
fn new() -> Self { Self { marker: PhantomData } }
}
impl Img<NoData> for f64 {
#[allow(private_interfaces)]
#[inline]
fn into_point(self, t: f64) -> Point<NoData> {
Point::new_unchecked(t, [t, self], NoData::new())
}
}
impl Img<NoData> for [f64; 2] {
#[allow(private_interfaces)]
#[inline]
fn into_point(self, t: f64) -> Point<NoData> {
Point::new_unchecked(t, self, NoData::new())
}
}
impl Img<NoData> for (f64, f64) {
#[allow(private_interfaces)]
#[inline]
fn into_point(self, t: f64) -> Point<NoData> {
Point::new_unchecked(t, [self.0, self.1], NoData::new())
}
}
#[cfg(feature = "num-complex")]
impl Img<num_complex::Complex<f64>> for num_complex::Complex<f64> {
#[allow(private_interfaces)]
#[inline]
fn into_point(self, t: f64) -> Point<num_complex::Complex<f64>> {
Point::new_unchecked(t, [self.re, self.im], self)
}
}
pub struct Data<D>(pub D);
impl<D> Img<D> for (f64, Data::<D>) {
#[allow(private_interfaces)]
#[inline]
fn into_point(self, t: f64) -> Point<D> {
Point::new_unchecked(t, [t, self.0], self.1.0)
}
}
impl<D> Img<D> for ([f64; 2], Data::<D>) {
#[allow(private_interfaces)]
#[inline]
fn into_point(self, t: f64) -> Point<D> {
Point::new_unchecked(t, self.0, self.1.0)
}
}
#[cfg(feature = "num-complex")]
impl<D> Img<D> for (num_complex::Complex<f64>, Data::<D>) {
#[allow(private_interfaces)]
#[inline]
fn into_point(self, t: f64) -> Point<D> {
Point::new_unchecked(t, [self.0.re, self.0.im], self.1.0)
}
}
macro_rules! new_sampling_fn {
($(#[$docfn: meta])*, $(#[$docfn_extra: meta])* $fun: ident -> $ft: ty,
// The structure to hold the options (and other fields).
$(#[$doc: meta])* $struct: ident,
$wrap_f: ident
) => {
impl<D> Sampling<D> {
$(#[$docfn])*
$(#[$docfn_extra])*
#[must_use]
#[allow(deprecated)]
pub fn $fun<F, Y>(f: F, a: f64, b: f64) -> $struct<F, D>
where F: FnMut(f64) -> Y,
Y: Img<D> {
if !a.is_finite() {
panic!("curve_sampling::{}: a = {} must be finite",
stringify!($fun), a);
}
if !b.is_finite() {
panic!("curve_sampling::{}: b = {} must be finite",
stringify!($fun), b);
}
$struct { f,
a, b, n: 100,
viewport: None,
init: vec![],
init_pt: vec![],
}
}
}
$(#[$doc])*
pub struct $struct<F, D> {
f: F, a: f64, b: f64,
n: usize,
viewport: Option<BoundingBox>,
init: Vec<f64>,
init_pt: Vec<Point<D>>,
}
#[allow(deprecated)]
impl<F, Y, D> $struct<F, D>
where F: FnMut(f64) -> Y,
Y: Img<D>,
{
pub fn n(mut self, n: usize) -> Self {
if n < 2 {
panic!("curve_sampling: n = {} must at least be 2", n)
}
self.n = n;
self
}
pub fn viewport(mut self, vp: BoundingBox) -> Self {
self.viewport = Some(vp);
self
}
#[doc = stringify!(Sampling::$fun)]
pub fn init<'a, I>(mut self, ts: I) -> Self
where I: IntoIterator<Item = &'a f64> {
for &t in ts {
if self.a <= t && t <= self.b { self.init.push(t);
}
}
self
}
#[doc = stringify!(Sampling::$fun)]
pub fn init_pt<I>(mut self, pts: I) -> Self
where I: IntoIterator<Item = (f64, Y)>,
{
for (t, y) in pts {
if self.a <= t && t <= self.b { self.init_pt.push(y.into_point(t));
}
}
self
}
fn eval_init(&mut self) {
for &t in &self.init {
self.init_pt.push((self.f)(t).into_point(t))
}
self.init.clear()
}
}
}
}
new_sampling_fn!(
,
uniform -> f64,
Uniform,
FnPoint);
impl<F, Y, D> Uniform<F, D>
where F: FnMut(f64) -> Y,
Y: Img<D> {
pub fn build(mut self) -> Sampling<D> {
if self.a == self.b {
let p = (self.f)(self.a).into_point(self.a);
if p.is_valid() {
return Sampling::singleton(p);
} else {
return Sampling::empty()
}
}
self.eval_init();
let mut points = self.init_pt;
let dt = (self.b - self.a) / (self.n - 1) as f64;
for i in 0 .. self.n {
let t = self.a + i as f64 * dt;
points.push((self.f)(t).into_point(t));
}
Sampling::from_vec(points, self.a < self.b)
}
}
mod cost {
use super::{Point, Coord, Lengths};
pub const HANGING_NODE: f64 = 5e5;
#[inline]
pub(crate) fn set_middle<D>(
p0: Coord, pm: &mut Point<D>, p1: Coord, len: Lengths
) {
let [x0, y0] = p0.xy;
let [xm, ym] = pm.xy;
let [x1, y1] = p1.xy;
let dx0m = (x0 - xm) / len.x;
let dy0m = (y0 - ym) / len.y;
let dx1m = (x1 - xm) / len.x;
let dy1m = (y1 - ym) / len.y;
let len0m = dx0m.hypot(dy0m);
let len1m = dx1m.hypot(dy1m);
if len0m == 0. || len1m == 0. {
pm.cost = 0.; } else {
let dx = - dx0m * dx1m - dy0m * dy1m;
let dy = dy0m * dx1m - dx0m * dy1m;
pm.cost = dy.atan2(dx); debug_assert!(!pm.cost.is_nan());
}
}
#[inline]
pub(crate) fn segment_vp(
p0: Coord,
p1: Coord,
len: Lengths,
in_vp: bool
) -> f64 {
if ! in_vp { return f64::NEG_INFINITY }
segment(p0, p1, len)
}
#[inline]
pub(crate) fn segment(p0: Coord, p1: Coord, len: Lengths) -> f64 {
let dt = (p1.t - p0.t).abs() / len.t; debug_assert!((0. ..=1.).contains(&dt), "dt = {dt} ∉ [0,1]");
let [x0, y0] = p0.xy;
let [x1, y1] = p1.xy;
let dx = ((x1 - x0) / len.x).abs();
let dy = ((y1 - y0) / len.y).abs();
let mut cost = p0.cost.abs() + p1.cost.abs(); if p0.cost * p1.cost < 0. {
if dx <= 0.01 && dy <= 0.01 { cost *= 0.5 }
else if !(dx <= 0.05 && dy <= 0.05) { cost *= 8. }
}
if dt >= 0.8 { cost }
else {
let dt = dt / 0.8;
dt * dt * (6. + (-8. + 3. * dt) * dt) * cost
}
}
}
fn push_segment<D>(
pq: &mut PQ<D>,
p0: &Point<D>, w0: &list::Witness<Point<D>>, p1: Coord,
len: Lengths, in_vp: bool
) {
let cost_segment = cost::segment_vp(p0.coord(), p1, len, in_vp);
let w = pq.push(cost_segment, w0.clone());
*p0.witness.borrow_mut() = Some(w);
}
unsafe fn update_segment<D>(
pq: &mut PQ<D>,
p0: &Point<D>,
p1: Coord,
len: Lengths
) {
match &*p0.witness.borrow() {
Some(w) => {
let priority = cost::segment(p0.coord(), p1, len);
pq.increase_priority(w, priority)
}
None => panic!("Sampling::update_segment: unset witness"),
}
}
fn compute<D>(s: &mut Sampling<D>, in_vp: impl Fn(&Point<D>) -> bool) {
if let Some(len) = s.lengths() {
s.path.first_mut().unwrap().cost = 0.;
s.path.last_mut().unwrap().cost = 0.;
let segments = unsafe { s.path.iter_segments_mut() };
for (p0, w0, pm, p1) in segments {
let p0_is_valid = p0.is_valid();
let p0_in_vp = p0_is_valid && in_vp(p0);
let pm_is_valid = pm.is_valid();
let pm_in_vp;
if let Some(p1) = p1 {
if pm_is_valid {
pm_in_vp = in_vp(pm);
if p0_is_valid && p1.is_valid() {
let c0 = p0.coord();
let c1 = p1.coord();
cost::set_middle(c0, pm, c1, len);
} else {
pm.cost = cost::HANGING_NODE;
}
} else { pm_in_vp = false;
pm.cost = 0.;
}
if p0_is_valid || pm_is_valid {
push_segment(&mut s.pq, p0, &w0, pm.coord(), len,
p0_in_vp || pm_in_vp);
}
} else { if p0_is_valid || pm_is_valid {
let mut vp = p0_in_vp;
if pm.is_valid() { vp = vp || in_vp(pm) };
push_segment(&mut s.pq, p0, &w0, pm.coord(), len, vp);
}
}
}
}
}
fn refine_gen<D>(
s: &mut Sampling<D>, n: usize,
mut f: impl FnMut(f64) -> Point<D>,
in_vp: impl Fn(&Point<D>) -> bool
) {
let len = match s.lengths() {
Some(lengths) => lengths,
None => return };
macro_rules! r { ($x: ident) => { unsafe { s.path.get(&$x) } } }
macro_rules! m { ($x: ident) => { unsafe { s.path.get_mut(&$x) } } }
macro_rules! prev { ($x: ident) => { unsafe { s.path.prev(& $x) } } }
macro_rules! next { ($x: ident) => { unsafe { s.path.next(& $x) } } }
for _ in 0 .. n {
let mut p0: list::Witness<Point<D>> = match s.pq.pop() {
None => break,
Some(p) => p };
*r!(p0).witness.borrow_mut() = None; let p1 = next!(p0).unwrap();
let t = (r!(p0).t + r!(p1).t) * 0.5;
let mut pm = f(t);
if r!(p0).is_valid() {
if r!(p1).is_valid() {
let pm = unsafe { s.path.insert_after(&mut p0, pm) };
let mut pm_in_vp = false;
if r!(pm).is_valid() {
pm_in_vp = in_vp(r!(pm));
let c0 = r!(p0).coord();
let c1 = r!(p1).coord();
cost::set_middle(c0, m!(pm), c1, len);
let cm = r!(pm).coord();
if let Some(p_1) = prev!(p0) {
if r!(p_1).is_valid() {
let c_1 = r!(p_1).coord();
cost::set_middle(c_1, m!(p0), cm, len);
let p_1 = r!(p_1);
let p0 = r!(p0).coord();
unsafe { update_segment(&mut s.pq, p_1, p0, len) }
}
if let Some(p2) = next!(p1) && r!(p2).is_valid() {
let c2 = r!(p2).coord();
cost::set_middle(cm, m!(p1), c2, len);
let p1 = r!(p1);
unsafe { update_segment(&mut s.pq, p1, c2, len) }
}
} else { m!(p0).cost = cost::HANGING_NODE;
m!(pm).cost = 0.;
m!(p1).cost = cost::HANGING_NODE;
if let Some(p_1) = prev!(p0) {
let p_1 = r!(p_1);
let p0 = r!(p0).coord();
unsafe { update_segment(&mut s.pq, p_1, p0, len) }
}
if let Some(p2) = next!(p1) {
let p1 = r!(p1);
let p2 = r!(p2).coord();
unsafe { update_segment(&mut s.pq, p1, p2, len) }
}
}
}
let vp = pm_in_vp || in_vp(r!(p0));
let cm = r!(pm).coord();
push_segment(&mut s.pq, r!(p0), &p0, cm, len, vp);
let vp = pm_in_vp || in_vp(r!(p1));
let c1 = r!(p1).coord();
push_segment(&mut s.pq, r!(pm), &pm, c1, len, vp);
} else { if pm.is_valid() {
pm.cost = cost::HANGING_NODE;
let pm = unsafe { s.path.insert_after(&mut p0, pm) };
if let Some(p_1) = prev!(p0) && r!(p_1).is_valid() {
let c_1 = r!(p_1).coord();
let pm = r!(pm).coord();
cost::set_middle(c_1, m!(p0), pm, len);
let p_1 = r!(p_1);
let p0 = r!(p0).coord();
unsafe{ update_segment(&mut s.pq, p_1, p0, len) }
}
let pm_in_vp = in_vp(r!(pm));
let vp = pm_in_vp || in_vp(r!(p0));
let cm = r!(pm).coord();
let c1 = r!(p1).coord();
push_segment(&mut s.pq, r!(p0), &p0, cm, len, vp);
push_segment(&mut s.pq, r!(pm), &pm, c1, len, pm_in_vp)
} else { pm.cost = 0.;
let pm = {
if r!(p1).witness.borrow().is_none() {
unsafe { *s.path.get_mut(&p1) = pm } ;
p1 } else {
unsafe { s.path.insert_after(&mut p0, pm) }
}
};
let vp = in_vp(r!(p0));
let cm = r!(pm).coord();
push_segment(&mut s.pq, r!(p0), &p0, cm, len, vp)
}
}
} else { debug_assert!(r!(p1).is_valid());
if pm.is_valid() {
pm.cost = cost::HANGING_NODE;
let pm = unsafe { s.path.insert_after(&mut p0, pm) };
if let Some(p2) = next!(p1) && r!(p2).is_valid() {
let pm = r!(pm).coord();
let p2 = r!(p2).coord();
cost::set_middle(pm, m!(p1), p2, len);
let p1 = r!(p1);
unsafe{ update_segment(&mut s.pq, p1, p2, len) }
}
let pm_in_vp = in_vp(r!(pm));
let cm = r!(pm).coord();
let c1 = r!(p1).coord();
push_segment(&mut s.pq, r!(p0), &p0, cm, len, pm_in_vp);
let in_vp = pm_in_vp || in_vp(r!(p1));
push_segment(&mut s.pq, r!(pm), &pm, c1, len, in_vp);
} else { pm.cost = 0.;
let pm = {
if let Some(p_1) = prev!(p0) {
if r!(p_1).is_valid() {
unsafe { s.path.insert_after(&mut p0, pm) }
} else {
unsafe { *s.path.get_mut(&p0) = pm };
p0
}
} else {
unsafe { s.path.insert_after(&mut p0, pm) }
}
};
let vp = in_vp(r!(p1));
let c1 = r!(p1).coord();
push_segment(&mut s.pq, r!(pm), &pm, c1, len, vp)
}
}
}
}
fn push_almost_uniform_sampling<D>(
points: &mut Vec<Point<D>>,
f: &mut impl FnMut(f64) -> Point<D>,
a: f64, b: f64, n: usize
) {
debug_assert!(n >= 4);
let dt = (b - a) / (n - 3) as f64;
let mut random = (n as u32).wrapping_mul(1_000_000);
const NORMALIZE_01: f64 = 1. / u32::MAX as f64;
let mut rand = move || {
random ^= random << 13;
random ^= random >> 17;
random ^= random << 5;
random as f64 * NORMALIZE_01
};
points.push(f(a));
points.push(f(a + 0.0625 * dt));
for i in 1 ..= n - 4 {
let j = i as f64 + rand() * 0.125 - 0.0625;
points.push(f(a + j * dt));
}
points.push(f(b - 0.0625 * dt));
points.push(f(b));
}
impl<D> Sampling<D> {
fn build(
mut points: Vec<Point<D>>,
mut f: impl FnMut(f64) -> Point<D>,
a: f64, b: f64, n: usize,
viewport: Option<BoundingBox>
) -> Self {
if a == b {
let p = f(a);
if p.is_valid() {
return Sampling::singleton(p);
} else {
return Sampling::empty()
}
}
let n0 = (n / 10).max(10);
push_almost_uniform_sampling(&mut points, &mut f, a, b, n0);
let mut s = Sampling::from_vec(points, a < b);
match viewport {
Some(vp) => {
let in_vp = |p: &Point<_>| vp.contains(p);
compute(&mut s, in_vp);
refine_gen(&mut s, n - n0, &mut f, in_vp)
}
None => {
compute(&mut s, |_| true);
refine_gen(&mut s, n - n0, &mut f, |_| true)
}
}
s
}
}
new_sampling_fn!(
,
#[cfg_attr(feature = "num-complex", doc = "
With the feature `num-complex`, complex-valued functions may be used directly.
```
use num_complex::Complex64 as C64;
use curve_sampling::{Sampling, Data};
# fn main() -> Result<(), Box<dyn std::error::Error>> {
fn f(t: f64) -> C64 {
C64::i().powf(t)
}
let s = Sampling::fun(f, 0., 1.).build();
// ...
# Ok(()) }
```
")]
fun -> f64,
Fun,
FnPoint);
impl<F, Y, D> Fun<F, D>
where F: FnMut(f64) -> Y,
Y: Img<D> {
pub fn build(mut self) -> Sampling<D> {
self.eval_init();
Sampling::build(
self.init_pt,
|x| (self.f)(x).into_point(x),
self.a, self.b, self.n, self.viewport)
}
}
new_sampling_fn!(
#[deprecated(note = "Use Sampling::fun instead")]
,
param -> [f64; 2],
#[deprecated(note = "See curve_sampling::Fun instead")]
Param,
ParamPoint);
#[allow(deprecated)]
impl<F, Y, D> Param<F, D>
where F: FnMut(f64) -> Y,
Y: Img<D> {
pub fn build(mut self) -> Sampling<D> {
self.eval_init();
Sampling::build(
self.init_pt,
|t| (self.f)(t).into_point(t),
self.a, self.b, self.n, self.viewport)
}
}
pub struct LaTeX<'a, D> {
sampling: &'a Sampling<D>,
n: usize,
color: Option<RGB8>,
arrow: Option<&'a str>,
arrow_pos: Option<f64>,
}
impl<'a, D> LaTeX<'a, D> {
#[inline]
fn new(s: &'a Sampling<D>) -> Self {
Self { sampling: s, n: 20_000, color: None,
arrow: None, arrow_pos: None }
}
pub fn n(&mut self, n: usize) -> &mut Self {
self.n = n;
self
}
pub fn color(&mut self, color: RGB8) -> &mut Self {
self.color = Some(color);
self
}
pub fn arrow(&mut self, arrow: &'a str) -> &mut Self {
self.arrow = Some(arrow);
self
}
pub fn arrow_pos(&mut self, arrow_pos: f64) -> &mut Self {
if ! arrow_pos.is_finite() {
panic!("curve_sampling::LaTeX::arrow_pos: \
position must be finite");
}
self.arrow_pos = Some(arrow_pos.clamp(0., 1.));
self
}
fn write_with_lines(&self, f: &mut impl Write) -> Result<(), io::Error> {
let mut n = 0;
let mut new_path = true;
for [x,y] in self.sampling.iter() {
if x.is_nan() {
writeln!(f, "\\pgfusepath{{stroke}}")?;
n = 0;
new_path = true;
} else {
n += 1;
if new_path {
writeln!(f, "\\pgfpathmoveto{{\\pgfpointxy\
{{{:.16}}}{{{:.16}}}}}", x, y)?
} else if n >= self.n {
write!(f, "\\pgfpathlineto{{\\pgfpointxy\
{{{:.16}}}{{{:.16}}}}}\n\
\\pgfusepath{{stroke}}\n\
\\pgfpathmoveto{{\\pgfpointxy\
{{{:.16}}}{{{:.16}}}}}\n", x, y, x, y)?;
n = 0;
} else {
writeln!(f, "\\pgfpathlineto{{\\pgfpointxy\
{{{:.16}}}{{{:.16}}}}}", x, y)?
}
new_path = false;
}
}
Ok(())
}
fn write_with_arrows(&self, f: &mut impl Write, arrow: &str,
arrow_pos: f64) -> Result<(), io::Error> {
let mut lens = vec![];
let mut prev_pt: Option<[f64; 2]> = None;
let mut cur_len = 0.;
for p @ [x, y] in self.sampling.iter() {
if x.is_nan() {
lens.push(arrow_pos * cur_len);
prev_pt = None;
cur_len = 0.;
} else {
if let Some([x0, y0]) = prev_pt {
cur_len += (x - x0).hypot(y - y0);
}
prev_pt = Some(p);
}
}
lens.push(arrow_pos * cur_len);
if lens.is_empty() { return Ok(()) }
let mut lens = lens.iter();
let mut rem_len = *lens.next().unwrap(); prev_pt = None;
let mut n = 0;
for p @ [x, y] in self.sampling.iter() {
if x.is_nan() {
writeln!(f, "\\pgfusepath{{stroke}}")?;
rem_len = *lens.next().unwrap();
prev_pt = None;
} else {
n += 1;
if let Some([x0, y0]) = prev_pt {
let dx = x - x0;
let dy = y - y0;
let l = dx.hypot(dy);
if rem_len <= l {
writeln!(f, "\\pgfusepath{{stroke}}")?;
let pct = rem_len / l;
if pct <= 1e-14 {
write!(f, "\\pgfsetarrowsstart{{{}}}\n\
\\pgfpathmoveto{{\\pgfpointxy
{{{:.16}}}{{{:.16}}}}}\n\
\\pgfpathlineto{{\\pgfpointxy\
{{{:.16}}}{{{:.16}}}}}\n\
\\pgfusepath{{stroke}}\n\
\\pgfsetarrowsend{{}}\n\
\\pgfpathmoveto{{\\pgfpointxy\
{{{:.16}}}{{{:.16}}}}}\n",
arrow, x0, y0, x, y, x, y)?;
} else {
let xm = x0 + pct * dx;
let ym = y0 + pct * dy;
write!(f, "\\pgfsetarrowsend{{{}}}\n\
\\pgfpathmoveto{{\\pgfpointxy\
{{{:.16}}}{{{:.16}}}}}\n\
\\pgfpathlineto{{\\pgfpointxy\
{{{:.16}}}{{{:.16}}}}}\n\
\\pgfusepath{{stroke}}\n\
\\pgfsetarrowsend{{}}\n\
\\pgfpathmoveto{{\\pgfpointxy\
{{{:.16}}}{{{:.16}}}}}\n\
\\pgfpathlineto{{\\pgfpointxy\
{{{:.16}}}{{{:.16}}}}}\n",
arrow, x0, y0, xm, ym, xm, ym, x, y)?;
n = 2;
}
rem_len = f64::INFINITY; } else if n >= self.n {
write!(f, "\\pgfpathlineto{{\\pgfpointxy\
{{{:.16}}}{{{:.16}}}}}\n\
\\pgfusepath{{stroke}}\n\
\\pgfpathmoveto{{\\pgfpointxy\
{{{:.16}}}{{{:.16}}}}}\n", x, y, x, y)?;
n = 0;
} else {
writeln!(f, "\\pgfpathlineto{{\\pgfpointxy\
{{{:.16}}}{{{:.16}}}}}", x, y)?
}
rem_len -= l;
} else {
writeln!(f, "\\pgfpathmoveto{{\\pgfpointxy\
{{{:.16}}}{{{:.16}}}}}", x, y)?
}
prev_pt = Some(p);
}
}
Ok(())
}
pub fn write(&self, f: &mut impl Write) -> Result<(), io::Error> {
writeln!(f, "% Written by the Rust curve-sampling crate.")?;
writeln!(f, "\\begin{{pgfscope}}")?;
if let Some(RGB8 {r, g, b}) = self.color {
write!(f, "\\definecolor{{RustCurveSamplingColor}}{{RGB}}\
{{{},{},{}}}\n\
\\pgfsetstrokecolor{{RustCurveSamplingColor}}\n",
r, g, b)?
}
match (self.arrow, self.arrow_pos) {
(None, None) => self.write_with_lines(f)?,
(Some(arrow), None) =>
self.write_with_arrows(f, arrow, 0.5)?,
(None, Some(arrow_pos)) =>
self.write_with_arrows(f, ">",arrow_pos)?,
(Some(arrow), Some(arrow_pos)) =>
self.write_with_arrows(f, arrow, arrow_pos)?,
}
write!(f, "\\pgfusepath{{stroke}}\n\\end{{pgfscope}}\n")
}
}
impl<D> Sampling<D> {
pub fn latex(&self) -> LaTeX<'_, D> { LaTeX::new(self) }
pub fn write(&self, f: &mut impl Write) -> Result<(), io::Error> {
for [x, y] in self.iter() {
if x.is_nan() {
writeln!(f)?
} else {
writeln!(f, "{:e} {:e}", x, y)?
}
}
Ok(())
}
}
impl<D> Display for Sampling<D> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
for [x, y] in self.iter() {
if x.is_nan() {
writeln!(f)?
} else {
writeln!(f, "{:e} {:e}", x, y)?
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::{error::Error,
fs::File,
io::Write, path::Path};
use crate::{BoundingBox, Data, NoData, Point, Sampling};
type R<T> = Result<T, Box<dyn Error>>;
fn xy_of_sampling<D>(s: &Sampling<D>) -> Vec<Option<(f64, f64)>> {
s.path.iter().map(|p| {
if p.is_valid() { Some((p.xy[0], p.xy[1])) } else { None }
})
.collect()
}
#[test]
fn clone_sampling() {
let s = Sampling::from_iter([[0.,0.], [1.,1.]]);
let xy0: Vec<_> = s.iter().collect();
let xy1: Vec<_> = s.clone().iter().collect();
assert_eq!(xy0, xy1)
}
#[test]
fn io() {
let s = Sampling::from_iter([[0.,0.], [1.,1.]]);
assert_eq!(xy_of_sampling(&s), vec![Some((0.,0.)), Some((1.,1.))]);
let s = Sampling::from_iter([[0.,0.], [1.,1.], [f64::NAN, 1.],
[2.,2.]]);
assert_eq!(xy_of_sampling(&s),
vec![Some((0.,0.)), Some((1.,1.)), None, Some((2.,2.))]);
}
#[test]
fn bounding_box_singleton() {
let s = Sampling::singleton(
Point::new_unchecked(0., [1., 2.], NoData::new()));
let bb = BoundingBox {xmin: 1., xmax: 1., ymin: 2., ymax: 2.};
assert_eq!(s.bounding_box(), bb);
}
fn test_clip(bb: BoundingBox,
path: Vec<[f64;2]>,
expected: Vec<Option<(f64,f64)>>) {
let s = Sampling::from_iter(path).clip(bb);
assert_eq!(xy_of_sampling(&s), expected);
}
#[test]
fn clip_base () {
let bb = BoundingBox { xmin: 0., xmax: 3., ymin: 0., ymax: 2.};
test_clip(bb, vec![[0.,1.], [2.,3.]],
vec![Some((0.,1.)), Some((1.,2.))]);
test_clip(bb,
vec![[-1.,0.], [2.,3.], [4.,1.]],
vec![Some((0.,1.)), Some((1.,2.)), None, Some((3., 2.))]);
test_clip(bb,
vec![[0.,1.], [2.,3.], [4.,1.], [2.,1.], [2., -1.],
[0., -1.], [0., 1.] ],
vec![Some((0.,1.)), Some((1.,2.)), None,
Some((3., 2.)), None,
Some((3., 1.)), Some((2., 1.)), Some((2., 0.)), None,
Some((0., 0.)), Some((0., 1.))]);
}
#[test]
fn clip_empty() {
let bb = BoundingBox {xmin: 0., xmax: 1., ymin: 0., ymax: 1.};
let path = xy_of_sampling(&Sampling::<()>::empty().clip(bb));
assert_eq!(path, vec![]);
}
#[test]
fn clip_double_cut() {
let bb = BoundingBox { xmin: 0., xmax: 4., ymin: 0., ymax: 2.};
test_clip(bb,
vec![[1., 2.], [2.,3.], [5.,0.], [-1., 0.] ],
vec![Some((1., 2.)), None,
Some((3., 2.)), Some((4., 1.)), None,
Some((4., 0.)), Some((0., 0.)) ]);
}
#[test]
fn clip_almost_horiz() {
let bb = BoundingBox { xmin: 0., xmax: 2., ymin: -1., ymax: 1.};
test_clip(bb,
vec![[1., 1e-100], [3., -1e-100] ],
vec![Some((1., 1e-100)), Some((2., 0.))]);
}
#[test]
fn clip_touches_1pt_cut() {
let bb = BoundingBox {xmin: 0., xmax: 2., ymax: 0., ymin: -1.};
let cut = [f64::NAN, f64::NAN];
test_clip(bb,
vec![[0.,1.], cut, [1., 0.], cut, cut, [2., 1.]],
vec![Some((1., 0.))])
}
#[test]
fn clip_final_cut() {
let bb = BoundingBox {xmin: 0., xmax: 1., ymin: 0., ymax: 2.};
test_clip(bb,
vec![[0., 0.], [2., 2.]],
vec![Some((0., 0.)), Some((1., 1.))])
}
#[test]
fn uniform1() {
let s = Sampling::uniform(|x| x, 0., 4.).n(3)
.init(&[1.]).init_pt([(3., 0.)]).build();
assert_eq!(xy_of_sampling(&s),
vec![Some((0.,0.)), Some((1.,1.)), Some((2.,2.)),
Some((3., 0.)), Some((4.,4.))]);
}
#[test]
fn uniform2() {
let s = Sampling::uniform(|x| (4. - x).sqrt(), 0., 6.).n(4).build();
assert_eq!(xy_of_sampling(&s),
vec![Some((0.,2.)), Some((2., 2f64.sqrt())),
Some((4., 0.)), None]);
}
#[test]
fn iter_data() {
let s = Sampling::uniform(|x| (x, Data(x as i32)), 0., 4.).n(3)
.init(&[1.]).init_pt([(3., (0., Data(-1)))]).build();
let expected = vec![
([0.,0.], &0), ([1.,1.], &1), ([2.,2.], &2), ([3., 0.], &-1),
([4.,4.], &4)];
let (_, size_hint) = s.iter_data().size_hint();
assert_eq!(size_hint, Some(expected.len()));
for (i, d) in s.iter_data().enumerate() {
assert_eq!(d, expected[i]);
}
}
#[test]
fn into_iter_data() {
let s = Sampling::uniform(|x| (x, Data(x as i32)), 0., 4.).n(3)
.init(&[1.]).init_pt([(3., (0., Data(-1)))]).build();
let expected = vec![
([0.,0.], 0), ([1.,1.], 1), ([2.,2.], 2), ([3., 0.], -1),
([4.,4.], 4)];
let (_, size_hint) = s.iter_data().size_hint();
assert_eq!(size_hint, Some(expected.len()));
for (i, d) in s.into_iter_data().enumerate() {
assert_eq!(d, expected[i]);
}
}
fn write_with_point_costs<D>(
s: &Sampling<D>,
fname: impl AsRef<Path>
) -> R<()> {
let mut fh = File::create(fname)?;
for p in s.path.iter() {
if p.is_valid() {
let [x, y] = p.xy;
writeln!(fh, "{x} {y} {}", p.cost)?;
} else {
writeln!(fh)?;
}
}
Ok(())
}
fn write_segments<D>(mut s: Sampling<D>, fname: impl AsRef<Path>) -> R<()> {
let mut fh = File::create(fname)?;
let mut seg = vec![];
loop {
let priority = s.pq.max_priority();
if let Some(p0) = s.pq.pop() {
let p1 = unsafe { s.path.next(&p0).unwrap() };
let p1 = unsafe { s.path.get(&p1) };
let p0 = unsafe { s.path.get(&p0) };
let tm = (p0.t + p1.t) / 2.;
seg.push((tm, p0.coord(), p1.coord(), priority))
} else {
break;
}
}
seg.sort_by(|(t1,_,_,_), (t2,_,_,_)| t1.partial_cmp(t2).unwrap());
for (tm, p0, p1, priority) in seg {
writeln!(fh, "{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}",
tm, p0.t, p0.xy[0], p0.xy[1],
p1.t, p1.xy[0], p1.xy[1], priority)?;
}
Ok(())
}
#[derive(Clone)]
struct Plot {
xmin: f64, xmax: f64,
ymin: f64, ymax: f64,
n: usize,
init: Vec<f64>,
}
impl Plot {
fn plot<F>(&self, f: F, title: &str) -> R<String>
where F: FnMut(f64) -> f64 {
let vp = BoundingBox { xmin: self.xmin, xmax: self.xmax,
ymin: self.ymin, ymax: self.ymax };
let s = Sampling::fun(f, self.xmin, self.xmax)
.n(self.n).init(self.init.iter()).viewport(vp).build();
static mut NDAT: usize = 0;
let ndat = unsafe { NDAT += 1; NDAT };
println!("Horror {ndat}");
s.pq.print_stats();
let dir = Path::new("target");
let fname = format!("horror{}.dat", ndat);
s.write(&mut File::create(dir.join(&fname))?)?;
let fname_p = format!("horror{}_p.dat", ndat);
write_with_point_costs(&s, dir.join(&fname_p))?;
let fname_s = format!("horror{}_s.dat", ndat);
write_segments(s, dir.join(&fname_s))?;
Ok(format!(
"unset title\n\
unset y2tics\n\
plot [{}:{}] \"{}\" with l lt 5 title \"{}\", \
\"{}\" with p lt 3 pt 6 ps 0.2 title \"n={}\"\n\
set title \"#{ndat} Restricted to viewport [{}:{}]×[{}:{}]\"\n\
set y2tics\n\
set y2range [-1e-6:]\n\
plot [{}:{}] [{}:{}] \"{}\" with l lt 5 title \"{}\", \
\"{}\" with p lt 3 pt 7 ps 0.2 title \"n={}\", \
\"{}\" using 1:3 with lp ps 0.2 lt rgb \"#760b0b\" \
title \"cost points\", \
\"{}\" using 1:8 with lp ps 0.2 lt rgb \"#909090\" \
axes x1y2 title \"cost segments\"\n",
self.xmin, self.xmax, &fname, title, &fname, self.n,
self.xmin, self.xmax, self.ymin, self.ymax,
self.xmin, self.xmax, self.ymin, self.ymax, &fname, title,
&fname, self.n, &fname_p, &fname_s))
}
}
#[test]
#[cfg_attr(miri, ignore)] fn horror() -> R<()> {
let d = Plot {
xmin: -5., xmax: 5., ymin: -5., ymax: 5., n: 100, init: vec![] };
macro_rules! p {
($($id:ident $l:tt $e:expr),*) => {
Plot{ $($id: $e,)* ..d.clone() } };
}
let s = [
p!(n: 10).plot(|_| 2., "x ↦ 2")?,
p!().plot(|x| x, "x ↦ x")?,
p!().plot(|x| 5. * x, "x ↦ 5x")?,
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(
|x| 1. / x, "x ↦ 1/x")?, p!(xmin: -0.4, xmax: 2., ymin: 0., ymax: 1.6, n: 50).plot(
|x| x.sqrt(), "x ↦ √x")?,
p!(xmin: -2., xmax: 1., ymin: 0., ymax: 1.6, n: 50).plot(
|x| (-x).sqrt(), "x ↦ √(-x)")?,
p!(n: 200).plot(|x| x.tan(), "tan")?,
p!().plot(|x| 1. / x.abs(), "x ↦ 1/|x|")?,
p!(xmin: -6., xmax: 6., ymin: -2., ymax: 2.).plot(
|x| (1. + x.cos().sin()).ln(), "1 + sin(cos x)")?,
p!(xmin: 0., xmax: 6.28, ymin: -1.5, ymax: 1.5, n: 400).plot(
|x| x.powi(3).sin() + x.powi(3).cos(), "sin x³ + cos x³")?,
p!(xmin: -5., xmax:200., ymin: -1., ymax: 1., n: 400).plot(
|x| x.sin(), "sin")?,
p!(xmin: -4., xmax: 4., ymin: -1., ymax: 1.).plot(
|x| (300. * x).sin(), "sin(300 x)")?,
p!(xmin: -4., xmax: 4., ymin: -1., ymax: 1., n: 1000).plot(
|x| (300. * x).sin(), "sin(300 x)")?,
p!(xmin: -2., xmax: 2., ymin: 0., ymax: 3.).plot(
|x| 1. + x * x + 0.0125 * (1. - 3. * (x - 1.)).abs().ln(),
"1 + x² + 0.0125 ln|1 - 3(x-1)|")?,
p!(xmin: -2., xmax: 2., ymin: 0., ymax: 3., n: 300,
init:vec![4. / 3.]).plot(
|x| 1. + x * x + 0.0125 * (1. - 3. * (x - 1.)).abs().ln(),
"1 + x² + 0.0125 ln|1 - 3(x-1)| (specifying x:4/3")?,
p!(xmin: -0.5, xmax: 0.5, ymin: -1., ymax: 1.).plot(
|x| x * (1. / x).sin(), "x sin(1/x)")?,
p!(xmin: -0.5, xmax: 0.5, ymin: -1., ymax: 1., n:200).plot(
|x| x * (1. / x).sin(), "x sin(1/x)")?,
p!(xmin: -2., xmax: 2., ymin: -1., ymax: 1.).plot(
|x| (1. / x).sin(), "sin(1/x)")?,
p!(xmin: -2., xmax: 2., ymin: -1., ymax: 1., n: 400).plot(
|x| (1. / x).sin(), "sin(1/x)")?,
p!(xmin: -4., xmax: 4., ymin: -1., ymax: 1.).plot(
|x| x.powi(4).sin(), "sin(x⁴)")?,
p!(xmin: -4., xmax: 4., ymin: -1., ymax: 1., n: 600).plot(
|x| x.powi(4).sin(), "sin(x⁴)")?,
p!(xmin: -6., xmax: 6., ymin: -1., ymax: 1.).plot(
|x| x.exp().sin(), "sin(exp x)")?,
p!(xmin: -6., xmax: 6., ymin: -1., ymax: 1., n: 500).plot(
|x| x.exp().sin(), "sin(exp x)")?,
p!(xmin: -10., xmax: 10., ymin: 0., ymax: 10.).plot(
|x| 1. / x.sin(), "1 / sin x")?,
p!(xmin: -6., xmax: 6., ymin: -1., ymax: 1.).plot(
|x| x.sin() / x, "(sin x)/x")?,
p!(xmin: -2., xmax: 2., ymin: -15., ymax: 15.).plot(
|x| (x.powi(3) - x + 1.).tan() + 1. / (x + 3. * x.exp()),
"tan(x³ - x + 1) + 1/(x + 3 eˣ)")?,
p!(xmin: 0., xmax: 17., ymin: 0., ymax: 2.).plot(
|x| (1. + x.cos()) * (-0.1 * x).exp(),
"(1 + cos x) exp(-x/10)")?,
p!(xmin: -2., xmax: 17., ymin: 0., ymax: 2.).plot(
|x| (1. + x.cos()) * (-0.1 * x).exp(),
"(1 + cos x) exp(-x/10)")?,
p!(xmin: 0., xmax: 17., ymin: 0., ymax: 2.).plot(
|x| (1. + x.cos()) * (-0.01 * x * x).exp(),
"(1 + cos x) exp(-x²/100)")?,
].join("");
let mut fh = File::create("target/horror.gp")?;
write!(fh, "set terminal pdfcairo\n\
set output \"horror.pdf\"\n\
set grid\n\
{}\n", s)?;
Ok(())
}
}