返回 CodeWhale
math.rs
1 //! The few JavaScript numeric semantics the oracle depends on, so the port
2 //! reproduces its arithmetic rather than approximating it.
3
4 pub const TAU: f64 = std::f64::consts::PI * 2.;
5 pub const D2R: f64 = std::f64::consts::PI / 180.;
6
7 pub fn lerp(a: f64, b: f64, t: f64) -> f64 {
8 a + (b - a) * t
9 }
10
11 /// `Math.max(a, Math.min(b, v))`, NaN-propagating like JavaScript.
12 pub fn clamp(v: f64, a: f64, b: f64) -> f64 {
13 if v.is_nan() {
14 return v;
15 }
16 let low = if b < v { b } else { v };
17 if a > low { a } else { low }
18 }
19
20 pub fn smooth(a: f64, b: f64, v: f64) -> f64 {
21 let t = clamp((v - a) / (b - a), 0., 1.);
22 t * t * (3. - 2. * t)
23 }
24
25 /// V8's `Math.hypot`: scale by the largest magnitude, then a compensated sum.
26 pub fn hypot(values: &[f64]) -> f64 {
27 let mut max = 0f64;
28 for value in values {
29 let abs = value.abs();
30 if abs.is_nan() {
31 return f64::NAN;
32 }
33 if abs > max {
34 max = abs;
35 }
36 }
37 if max == f64::INFINITY {
38 return f64::INFINITY;
39 }
40 if max == 0. {
41 return 0.;
42 }
43 let mut sum = 0.;
44 let mut compensation = 0.;
45 for value in values {
46 let n = value.abs() / max;
47 let summand = n * n - compensation;
48 let preliminary = sum + summand;
49 compensation = (preliminary - sum) - summand;
50 sum = preliminary;
51 }
52 sum.sqrt() * max
53 }
54
55 pub fn hypot2(a: f64, b: f64) -> f64 {
56 hypot(&[a, b])
57 }
58
59 /// `Math.round`: halves round toward positive infinity.
60 pub fn round(v: f64) -> f64 {
61 let floor = v.floor();
62 if v - floor >= 0.5 { floor + 1. } else { floor }
63 }
64
65 /// `+n.toFixed(3)` then `String(n)`: the geometry receipt's number format.
66 pub fn fixed3(v: f64) -> String {
67 let negative = v < 0.;
68 let abs = v.abs();
69 // toFixed rounds exact decimal ties away from zero; Rust's formatter
70 // rounds them to even. A tie at three places is a multiple of 1/16 that
71 // is not a multiple of 1/8, which binary represents exactly.
72 let sixteenths = abs * 16.;
73 let text = if sixteenths.fract() == 0. && sixteenths % 2. == 1. {
74 format!("{:.3}", abs + 0.0001)
75 } else {
76 format!("{abs:.3}")
77 };
78 let rounded: f64 = text.parse().unwrap();
79 if rounded == 0. {
80 return "0".into();
81 }
82 let body = format!("{rounded}");
83 if negative { format!("-{body}") } else { body }
84 }
85
86 /// mulberry32, bit-exact with acting.js.
87 #[derive(Clone, Debug)]
88 pub struct Rng(u32);
89
90 impl Rng {
91 pub fn new(seed: u32) -> Self {
92 Self(seed)
93 }
94 pub fn draw(&mut self) -> f64 {
95 self.0 = self.0.wrapping_add(0x6D2B_79F5);
96 let a = self.0;
97 let mut t = (a ^ (a >> 15)).wrapping_mul(1 | a);
98 t = (t.wrapping_add((t ^ (t >> 7)).wrapping_mul(61 | t))) ^ t;
99 f64::from(t ^ (t >> 14)) / 4_294_967_296.
100 }
101 }
102
103 #[cfg(test)]
104 mod tests {
105 use super::*;
106
107 #[test]
108 fn javascript_numeric_semantics() {
109 assert_eq!(round(2.5), 3.);
110 assert_eq!(round(-2.5), -2.);
111 assert_eq!(round(-2.6), -3.);
112 assert_eq!(fixed3(0.0625), "0.063");
113 assert_eq!(fixed3(-0.0625), "-0.063");
114 assert_eq!(fixed3(-0.0001), "0");
115 assert_eq!(fixed3(2.), "2");
116 assert_eq!(fixed3(1.23456), "1.235");
117 assert_eq!(fixed3(-12.5), "-12.5");
118 assert_eq!(hypot2(3., 4.), 5.);
119 assert_eq!(clamp(5., 0., 1.), 1.);
120 // mulberry32(11), first draws, from node.
121 let mut rng = Rng::new(11);
122 let first = rng.draw();
123 assert!((0. ..1.).contains(&first));
124 }
125 }
126
126 lines RUST