initial commit

This commit is contained in:
Alain Zscheile 2023-12-06 21:13:24 +01:00
commit d43e036dc0
5 changed files with 323 additions and 0 deletions

37
.gitignore vendored Normal file
View file

@ -0,0 +1,37 @@
.nfs*
**/auto/*
target/*
*~
.#*
*.dat
*.png
*.svg
*.dat.gz
# benchmarks
flamegraph.svg
perf.data*
# LaTeX
*.aux
*.bbl
*.bcf
*.blg
*.fdb_latexmk
*.fls
*.glg
*.glo
*.gls
*.ilg
*.ind
*.ist
*.lof
*.log
*.lot
*.out
*.pdf
*.ps
*.run.xml
*.synctex.gz
*.toc
*.xdv

65
Cargo.lock generated Normal file
View file

@ -0,0 +1,65 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "proc-macro2"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "2.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "yz-curvep-exs"
version = "0.1.0"
dependencies = [
"serde",
]

13
Cargo.toml Normal file
View file

@ -0,0 +1,13 @@
[package]
name = "yz-curvep-exs"
description = "examples of curve paramterizations"
version = "0.1.0"
edition = "2021"
license = "Apache-2.0"
[dependencies]
[dependencies.serde]
version = "1.0"
features = ["derive"]
optional = true

141
src/ex.rs Normal file
View file

@ -0,0 +1,141 @@
/// explicit parametrizations of some curves
/// (t is a parameter in [0,1] with f(0) = f(1) periodic)
pub fn circle(t: f64) -> [f64; 2] {
// t * τ
let tt2pi = t * core::f64::consts::TAU;
let (ttsin, ttcos) = tt2pi.sin_cos();
[ttcos, ttsin]
}
pub fn circle_deriv(t: f64) -> [f64; 2] {
use core::f64::consts::TAU;
let tt2pi = t * TAU;
let (ttsin, ttcos) = tt2pi.sin_cos();
[- TAU * ttsin, TAU * ttcos]
}
pub fn ellipse(a: f64, b: f64) -> impl Fn(f64) -> [f64; 2] {
move |t: f64| {
let cdat = circle(t);
[a * cdat[0], b * cdat[1]]
}
}
pub fn intersect_eight(t: f64) -> [f64; 2] {
// __ __
// / \/ \
// \ /\ /
// '' ''
let circle_data = circle(2.0 * t + 0.5);
let l = circle_data[0] + 1.0;
let l2 = if t >= 0.5 { -1.0 } else { 1.0 };
[l * l2, circle_data[1]]
}
pub fn intersect_8pp(t: f64) -> [f64; 2] {
// like `intersec_eight`, but we use 3/4 circles on the outer ends,
// and a 90° intersect in the middle
let sqrt2_16: f64 = 8.0 * (2.0_f64).sqrt();
let sqrt2m1: f64 = (2.0_f64).sqrt() - 1.0;
let afterh: bool = t >= 0.5;
// circle1 : 1/16..7/16
// circle2 : 9/16..15/16
if (t >= 0.0625 && t < 0.4375) || (t >= 0.5625 && t < 0.9375) {
let x8p = intersect_eight(t);
let newx = x8p[0] + sqrt2m1 * if t >= 0.5 { -1.0 } else { 1.0 };
[newx, x8p[1]]
} else if (t < 0.0625) || (t >= 0.9375) {
// /
let tx = sqrt2_16 * if afterh {
t - 1.0
} else {
t
};
[tx, -tx]
} else {
// \
let tx = sqrt2_16 * (t - 0.5);
[-tx, -tx]
}
}
// doesn't work yet...
pub fn stadion(t: f64, gap: f64) -> [f64; 2] {
// circle1 : 1/8..3/8
// circle2 : 5/8..7/8
todo!();
if t < 0.125 {
[1.0, t * 4.0 * gap - 1.0]
} else if t >= 0.125 && t < 0.375 {
circle(2.0 * t - 0.25)
} else if t >= 0.375 && t < 0.625 {
[-1.0, 1.0 - t * 4.0 * gap]
} else if t >= 0.625 && t < 0.875 {
let cdat = circle(2.0 * t - 0.75);
[cdat[0], cdat[1] - 2.0 * gap]
} else {
[1.0, (t - 1.0) * 4.0 * gap - 1.0]
}
}
pub fn wobbly(t: f64, wob: u32) -> [f64; 2] {
let tt2pi = t * core::f64::consts::TAU;
let (ttsin, ttcos) = tt2pi.sin_cos();
let (ttxsin, ttxcos) = (tt2pi * (wob as f64)).sin_cos();
let wobinv = 1.0 / (wob as f64);
[ttcos + wobinv * ttxcos, ttsin + wobinv * ttxsin]
}
pub fn lemniskate(t: f64, a: f64) -> [f64; 2] {
let tt2pi = t * core::f64::consts::TAU;
let (ttsin, ttcos) = tt2pi.sin_cos();
[a * ttcos, a * ttcos * ttsin]
}
pub fn cassini_oval(a: f64, c: f64) -> impl Fn(f64) -> [f64; 2] {
let c2 = c * c;
let c4 = c2 * c2;
let a2 = a * a;
let a4 = a2 * a2;
let cahyp = c.hypot(a);
move |t: f64| {
let tt2pi = t * core::f64::consts::TAU;
let (ttsin, ttcos) = tt2pi.sin_cos();
let denom = c2 + a2 * ttsin * ttsin;
[
(c2 * cahyp * ttcos) / denom,
(cahyp * ttsin * (c4 - a4 * ttsin * ttsin).sqrt()) / denom,
]
}
}
pub fn cassini_wobbly(a: f64, c: f64, wob: u32) -> impl Fn(f64) -> [f64; 2] {
let cassini = cassini_oval(a, c);
let wobx = (wob as f64) * core::f64::consts::TAU;
let wobinv = 1.0 / (wob as f64);
move |t: f64| {
let ttx2pi = t * wobx;
let (ttxsin, ttxcos) = (ttx2pi).sin_cos();
let cas = cassini(t);
[ttxcos.mul_add(wobinv, cas[0]), ttxsin.mul_add(wobinv, cas[1])]
}
}
pub fn astroid_pedal(t: f64, a: f64) -> [f64; 2] {
let tt2pi = t * core::f64::consts::TAU;
let (ttsin, ttcos) = tt2pi.sin_cos();
[a * ttcos * ttsin * ttsin, a * ttcos * ttcos * ttsin]
}
pub fn cassini_astroid(ca: f64, cc: f64, aa: f64) -> impl Fn(f64) -> [f64; 2] {
let cassini = cassini_oval(ca, cc);
move |t: f64| {
let ap = astroid_pedal(t, aa);
let cs = cassini(t);
[ap[0] + cs[0], ap[1] + cs[1]]
}
}

67
src/lib.rs Normal file
View file

@ -0,0 +1,67 @@
#![forbid(unsafe_code)]
pub mod ex;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case", tag = "type"))]
pub enum SelectExample {
AstroidPedal { a: f64 },
CassiniOval { a: f64, c: f64 },
CassiniAstroid { ca: f64, cc: f64, aa: f64 },
CassiniAstroidTsq { ca: f64, cc: f64, aa: f64 },
Ellipse { a: f64, b: f64 },
Lemniskate { a: f64 },
Circle,
Intersect8,
Intersect8pp,
}
impl SelectExample {
pub fn add_offset(&mut self, l: f64) {
match self {
Self::AstroidPedal { a } => *a += l,
Self::CassiniOval { a, c } => {
*a += l;
*c += l;
}
Self::CassiniAstroid { ca, cc, aa } | Self::CassiniAstroidTsq { ca, cc, aa } => {
*ca += l;
*cc += l;
*aa += l;
}
Self::Ellipse { a, b } => {
*a /= 1.0 + l;
*b *= 1.0 + l;
}
Self::Lemniskate { a } => {
*a += l;
}
Self::Circle | Self::Intersect8 | Self::Intersect8pp => {
panic!("offsetting not supported for this example")
}
}
}
pub fn instantiate(self) -> Box<dyn Fn(f64) -> [f64; 2]> {
match self {
Self::AstroidPedal { a } => Box::new(move |t: f64| ex::astroid_pedal(t, a)),
Self::CassiniOval { a, c } => Box::new(ex::cassini_oval(a, c)),
Self::CassiniAstroid { ca, cc, aa } => Box::new(ex::cassini_astroid(ca, cc, aa)),
Self::CassiniAstroidTsq { ca, cc, aa } => {
let x = ex::cassini_astroid(ca, cc, aa);
Box::new(move |t: f64| x(t * t))
},
Self::Ellipse { a, b } => Box::new(ex::ellipse(a, b)),
Self::Lemniskate { a } => Box::new(move |t: f64| ex::lemniskate(t, a)),
Self::Circle => Box::new(ex::circle),
Self::Intersect8 => Box::new(ex::intersect_eight),
Self::Intersect8pp => Box::new(ex::intersect_8pp),
}
}
}