initial commit
This commit is contained in:
commit
d43e036dc0
37
.gitignore
vendored
Normal file
37
.gitignore
vendored
Normal 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
65
Cargo.lock
generated
Normal 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
13
Cargo.toml
Normal 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
141
src/ex.rs
Normal 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
67
src/lib.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue