1pub(crate) mod arrow;
4mod ball;
5pub mod botteldooren;
6pub mod bound;
7pub mod chaigne_askenfelt;
8pub mod chaigne_doutaut;
9mod chaigne_tail;
10pub mod darabundit_scavone;
11pub mod hammer;
12pub mod rhaouti_chaigne_joly;
13pub(crate) mod stiff_string;
14pub(crate) mod string_tail;
15pub mod tonehole;
16#[cfg(test)]
17mod twofold;
18mod unison_tail;
19pub mod willemsen_bilbao_serafin;
20
21use std::any::Any;
22
23use crate::error::SampleError;
24
25use botteldooren::{BotteldoorenParams, BotteldoorenSite};
26use chaigne_askenfelt::{ChaigneAskenfeltParams, ChaigneAskenfeltSite};
27use chaigne_doutaut::{ChaigneDoutautParams, ChaigneDoutautSite};
28use darabundit_scavone::{BoreParams, BoreSite};
29use rhaouti_chaigne_joly::{RhaoutiChaigneJolyParams, RhaoutiChaigneJolySite};
30use willemsen_bilbao_serafin::{WillemsenBilbaoSerafinParams, WillemsenBilbaoSerafinSite};
31
32pub trait Solver: Held {
34 fn step(&mut self) -> Result<f64, SampleError>;
35
36 fn take_motion(&mut self, _held: &dyn Solver) -> bool {
38 false
39 }
40}
41
42pub trait Held {
43 fn boxed(&self) -> Box<dyn Solver>;
44 fn as_any(&self) -> &dyn Any;
45}
46
47impl<T: Solver + Clone + 'static> Held for T {
48 fn boxed(&self) -> Box<dyn Solver> {
49 Box::new(self.clone())
50 }
51
52 fn as_any(&self) -> &dyn Any {
53 self
54 }
55}
56
57#[derive(Clone, Debug, PartialEq)]
58pub enum Params {
59 ChaigneAskenfelt(ChaigneAskenfeltParams),
60 WillemsenBilbaoSerafin(WillemsenBilbaoSerafinParams),
61 DarabunditScavone(BoreParams),
62 RhaoutiChaigneJoly(RhaoutiChaigneJolyParams),
63 ChaigneDoutaut(ChaigneDoutautParams),
64 Botteldooren(BotteldoorenParams),
65}
66
67impl Params {
68 pub fn name(&self) -> &'static str {
69 match self {
70 Params::ChaigneAskenfelt(_) => "chaigne_askenfelt",
71 Params::WillemsenBilbaoSerafin(_) => "willemsen_bilbao_serafin",
72 Params::DarabunditScavone(_) => "darabundit_scavone",
73 Params::RhaoutiChaigneJoly(_) => "rhaouti_chaigne_joly",
74 Params::ChaigneDoutaut(_) => "chaigne_doutaut",
75 Params::Botteldooren(_) => "botteldooren",
76 }
77 }
78
79 pub fn differs_in_release_alone(&self, other: &Params) -> bool {
80 match (self, other) {
81 (Params::ChaigneAskenfelt(a), Params::ChaigneAskenfelt(b)) => {
82 a.release != b.release
83 && *a
84 == ChaigneAskenfeltParams {
85 release: a.release,
86 ..b.clone()
87 }
88 }
89 _ => false,
90 }
91 }
92
93 pub fn valid(&self) -> bool {
94 match self {
95 Params::ChaigneAskenfelt(p) => p.valid(),
96 Params::WillemsenBilbaoSerafin(p) => p.valid(),
97 Params::DarabunditScavone(p) => p.valid(),
98 Params::RhaoutiChaigneJoly(p) => p.valid(),
99 Params::ChaigneDoutaut(p) => p.valid(),
100 Params::Botteldooren(p) => p.valid(),
101 }
102 }
103}
104
105pub fn site(p: &Params, rate: u32) -> Result<Box<dyn Solver>, SampleError> {
108 let sr = f64::from(rate);
109 Ok(match p {
110 Params::ChaigneAskenfelt(p) => Box::new(ChaigneAskenfeltSite::new(p, sr)?),
111 Params::WillemsenBilbaoSerafin(p) => Box::new(WillemsenBilbaoSerafinSite::new(p, sr)?),
112 Params::DarabunditScavone(p) => Box::new(BoreSite::new(p, sr)),
113 Params::RhaoutiChaigneJoly(p) => {
114 under_ceiling(
115 "rhaouti_chaigne_joly",
116 rhaouti_chaigne_joly::node_count(p, sr),
117 rhaouti_chaigne_joly::NODE_COUNT_CEILING,
118 )?;
119 Box::new(RhaoutiChaigneJolySite::new(p, sr))
120 }
121 Params::ChaigneDoutaut(p) => Box::new(ChaigneDoutautSite::new(p, sr)),
122 Params::Botteldooren(p) => {
123 under_ceiling(
124 "botteldooren",
125 botteldooren::node_count(p.f0, p.aspect_y, p.aspect_z, sr),
126 botteldooren::NODE_COUNT_CEILING,
127 )?;
128 Box::new(BotteldoorenSite::new(p, sr))
129 }
130 })
131}
132
133#[derive(Clone, Debug, PartialEq)]
135pub struct Tail {
136 pub at: Vec<f64>,
137 pub held: bool,
138}
139
140pub fn tail(p: &Params, rate: u32, step: usize, points: usize, level: f64) -> Result<Tail, String> {
141 match p {
142 Params::ChaigneAskenfelt(p) => chaigne_tail::tail(p, f64::from(rate), step, points, level),
143 other => Err(format!("the {} solver", other.name())),
144 }
145}
146
147pub fn tail_from(
149 solver: &dyn Solver,
150 step: usize,
151 points: usize,
152 level: f64,
153) -> Option<Result<Tail, String>> {
154 let site = solver.as_any().downcast_ref::<ChaigneAskenfeltSite>()?;
155 Some(chaigne_tail::tail_from(site, step, points, level))
156}
157
158fn under_ceiling(model: &'static str, nodes: f64, ceiling: usize) -> Result<(), SampleError> {
159 match nodes > ceiling as f64 {
160 true => Err(SampleError::GridTooLarge {
161 model,
162 nodes: nodes as usize,
163 ceiling,
164 }),
165 false => Ok(()),
166 }
167}
168
169pub(crate) fn raised_cosine_pulse(t: f64, amp: f64, width: f64) -> f64 {
172 match t < width {
173 true => amp * 0.5 * (1.0 - (std::f64::consts::TAU * t / width).cos()),
174 false => 0.0,
175 }
176}