eml-stdlib 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- eml_stdlib/__init__.py +81 -0
- eml_stdlib/ballistics/air_density.eml +73 -0
- eml_stdlib/ballistics/ballistic_solver.eml +102 -0
- eml_stdlib/ballistics/coriolis.eml +60 -0
- eml_stdlib/ballistics/drag.eml +44 -0
- eml_stdlib/ballistics/gravity.eml +52 -0
- eml_stdlib/ballistics/muzzle_velocity.eml +42 -0
- eml_stdlib/ballistics/spin_drift.eml +60 -0
- eml_stdlib/ballistics/time_of_flight.eml +79 -0
- eml_stdlib/ballistics/wind.eml +58 -0
- eml_stdlib/biology/abilities/ballistic_armor.eml +61 -0
- eml_stdlib/biology/abilities/bite_force.eml +48 -0
- eml_stdlib/biology/abilities/chromatophore.eml +50 -0
- eml_stdlib/biology/abilities/discharge.eml +45 -0
- eml_stdlib/biology/abilities/elastic_energy.eml +42 -0
- eml_stdlib/biology/abilities/locomotion.eml +50 -0
- eml_stdlib/biology/abilities/muscle_power.eml +47 -0
- eml_stdlib/biology/abilities/sprint.eml +36 -0
- eml_stdlib/biology/abilities/texture_match.eml +49 -0
- eml_stdlib/biology/abilities/toxin.eml +44 -0
- eml_stdlib/biology/cone_l.eml +36 -0
- eml_stdlib/biology/cone_s.eml +32 -0
- eml_stdlib/biology/cryptochrome.eml +44 -0
- eml_stdlib/biology/geomagnetic_field.eml +32 -0
- eml_stdlib/biology/goldman_equation.eml +41 -0
- eml_stdlib/biology/hill_equation.eml +65 -0
- eml_stdlib/biology/hodgkin_huxley.eml +108 -0
- eml_stdlib/biology/michaelis_menten.eml +65 -0
- eml_stdlib/biology/plume_diffusion.eml +47 -0
- eml_stdlib/biology/receptor_binding.eml +35 -0
- eml_stdlib/biology/rod_sensitivity.eml +38 -0
- eml_stdlib/biology/tapetum.eml +70 -0
- eml_stdlib/carriers/acoustic_cloak.eml +56 -0
- eml_stdlib/carriers/ferron_emission.eml +55 -0
- eml_stdlib/carriers/ferron_logic.eml +45 -0
- eml_stdlib/carriers/ferron_propagation.eml +69 -0
- eml_stdlib/carriers/mach_zehnder.eml +52 -0
- eml_stdlib/carriers/magnon_dispersion.eml +57 -0
- eml_stdlib/carriers/magnon_logic.eml +45 -0
- eml_stdlib/carriers/optical_neuron.eml +55 -0
- eml_stdlib/carriers/phonon_bandgap.eml +54 -0
- eml_stdlib/carriers/ring_resonator.eml +48 -0
- eml_stdlib/carriers/spin_torque.eml +61 -0
- eml_stdlib/carriers/thermal_rectifier.eml +63 -0
- eml_stdlib/catalog.json +4209 -0
- eml_stdlib/circuits/buck_converter.eml +74 -0
- eml_stdlib/circuits/cmos_inverter.eml +75 -0
- eml_stdlib/circuits/h_bridge.eml +69 -0
- eml_stdlib/circuits/ldo.eml +66 -0
- eml_stdlib/circuits/mosfet_iv.eml +59 -0
- eml_stdlib/circuits/pll_loop.eml +63 -0
- eml_stdlib/circuits/rc_filter.eml +98 -0
- eml_stdlib/circuits/voltage_divider.eml +68 -0
- eml_stdlib/control/bangbang.eml +34 -0
- eml_stdlib/control/deadband.eml +28 -0
- eml_stdlib/control/hysteresis.eml +28 -0
- eml_stdlib/control/kalman_1d.eml +53 -0
- eml_stdlib/control/lqr_1d.eml +47 -0
- eml_stdlib/control/pid.eml +38 -0
- eml_stdlib/gaming/animation/bounce.eml +39 -0
- eml_stdlib/gaming/animation/ease_in.eml +27 -0
- eml_stdlib/gaming/animation/ease_in_out.eml +29 -0
- eml_stdlib/gaming/animation/ease_out.eml +27 -0
- eml_stdlib/gaming/animation/ik_2bone.eml +57 -0
- eml_stdlib/gaming/animation/spring.eml +39 -0
- eml_stdlib/gaming/audio/distance_attenuation.eml +50 -0
- eml_stdlib/gaming/audio/doppler.eml +39 -0
- eml_stdlib/gaming/audio/lowpass_audio.eml +32 -0
- eml_stdlib/gaming/audio/reverb_delay.eml +41 -0
- eml_stdlib/gaming/camera/dof_blur.eml +30 -0
- eml_stdlib/gaming/camera/fov_projection.eml +47 -0
- eml_stdlib/gaming/camera/orbit.eml +48 -0
- eml_stdlib/gaming/camera/shake.eml +20 -0
- eml_stdlib/gaming/lighting/ambient_occlusion.eml +40 -0
- eml_stdlib/gaming/lighting/area_light.eml +50 -0
- eml_stdlib/gaming/lighting/fog_exp.eml +28 -0
- eml_stdlib/gaming/lighting/fog_exp2.eml +30 -0
- eml_stdlib/gaming/lighting/point_light.eml +33 -0
- eml_stdlib/gaming/lighting/spot_light.eml +43 -0
- eml_stdlib/gaming/noise/fbm.eml +65 -0
- eml_stdlib/gaming/noise/perlin_2d.eml +65 -0
- eml_stdlib/gaming/noise/perlin_3d.eml +63 -0
- eml_stdlib/gaming/noise/simplex_2d.eml +67 -0
- eml_stdlib/gaming/noise/turbulence.eml +46 -0
- eml_stdlib/gaming/noise/value_noise.eml +45 -0
- eml_stdlib/gaming/noise/voronoi_2d.eml +83 -0
- eml_stdlib/gaming/noise/white_noise.eml +47 -0
- eml_stdlib/gaming/noise/worley.eml +79 -0
- eml_stdlib/gaming/particles/drag.eml +30 -0
- eml_stdlib/gaming/particles/emitter_radial.eml +35 -0
- eml_stdlib/gaming/particles/fade.eml +36 -0
- eml_stdlib/gaming/particles/gravity_particle.eml +32 -0
- eml_stdlib/gaming/particles/size_over_life.eml +28 -0
- eml_stdlib/gaming/physics/aabb_overlap.eml +38 -0
- eml_stdlib/gaming/physics/buoyancy.eml +43 -0
- eml_stdlib/gaming/physics/circle_collision.eml +46 -0
- eml_stdlib/gaming/physics/impulse_resolve.eml +39 -0
- eml_stdlib/gaming/physics/rigid_body_2d.eml +61 -0
- eml_stdlib/gaming/physics/spring_damper.eml +30 -0
- eml_stdlib/gaming/physics/verlet.eml +29 -0
- eml_stdlib/gaming/shading/fresnel.eml +57 -0
- eml_stdlib/gaming/shading/matcap.eml +34 -0
- eml_stdlib/gaming/shading/pbr_diffuse.eml +41 -0
- eml_stdlib/gaming/shading/pbr_specular.eml +75 -0
- eml_stdlib/gaming/shading/subsurface.eml +52 -0
- eml_stdlib/gaming/shading/toon.eml +30 -0
- eml_stdlib/gaming/terrain/biome_select.eml +41 -0
- eml_stdlib/gaming/terrain/cliff_detect.eml +35 -0
- eml_stdlib/gaming/terrain/erosion_hydraulic.eml +57 -0
- eml_stdlib/gaming/terrain/erosion_thermal.eml +44 -0
- eml_stdlib/gaming/terrain/heightmap.eml +33 -0
- eml_stdlib/gaming/textures/brick.eml +27 -0
- eml_stdlib/gaming/textures/caustics.eml +25 -0
- eml_stdlib/gaming/textures/checkerboard.eml +27 -0
- eml_stdlib/gaming/textures/fire.eml +26 -0
- eml_stdlib/gaming/textures/marble.eml +24 -0
- eml_stdlib/gaming/textures/rust.eml +23 -0
- eml_stdlib/gaming/textures/water_surface.eml +49 -0
- eml_stdlib/gaming/textures/wood.eml +41 -0
- eml_stdlib/math/abs.eml +45 -0
- eml_stdlib/math/atan.eml +32 -0
- eml_stdlib/math/atan2.eml +61 -0
- eml_stdlib/math/bessel_j0.eml +65 -0
- eml_stdlib/math/cos.eml +42 -0
- eml_stdlib/math/cosh.eml +24 -0
- eml_stdlib/math/erf.eml +66 -0
- eml_stdlib/math/exp.eml +49 -0
- eml_stdlib/math/gamma.eml +55 -0
- eml_stdlib/math/gaussian.eml +18 -0
- eml_stdlib/math/log.eml +59 -0
- eml_stdlib/math/pow.eml +51 -0
- eml_stdlib/math/relu.eml +63 -0
- eml_stdlib/math/sigmoid.eml +21 -0
- eml_stdlib/math/sin.eml +37 -0
- eml_stdlib/math/sinh.eml +24 -0
- eml_stdlib/math/softmax.eml +87 -0
- eml_stdlib/math/sqrt.eml +46 -0
- eml_stdlib/math/tan.eml +36 -0
- eml_stdlib/math/tanh.eml +54 -0
- eml_stdlib/ml/attention.eml +70 -0
- eml_stdlib/ml/cross_entropy.eml +85 -0
- eml_stdlib/ml/gelu.eml +51 -0
- eml_stdlib/ml/layernorm.eml +49 -0
- eml_stdlib/ml/leaky_relu.eml +51 -0
- eml_stdlib/ml/relu.eml +63 -0
- eml_stdlib/ml/rotary_embed.eml +64 -0
- eml_stdlib/ml/silu.eml +40 -0
- eml_stdlib/ml/softmax.eml +87 -0
- eml_stdlib/physics/coulomb.eml +48 -0
- eml_stdlib/physics/damped_oscillator.eml +20 -0
- eml_stdlib/physics/diffusion.eml +45 -0
- eml_stdlib/physics/gravity.eml +60 -0
- eml_stdlib/physics/hooke.eml +56 -0
- eml_stdlib/physics/navier_stokes_1d.eml +57 -0
- eml_stdlib/physics/planck_radiation.eml +77 -0
- eml_stdlib/physics/stefan_boltzmann.eml +75 -0
- eml_stdlib/physics/wave_propagation.eml +52 -0
- eml_stdlib/quantum/cnot.eml +72 -0
- eml_stdlib/quantum/grover_oracle.eml +69 -0
- eml_stdlib/quantum/hadamard.eml +73 -0
- eml_stdlib/quantum/pauli_x.eml +29 -0
- eml_stdlib/quantum/pauli_z.eml +29 -0
- eml_stdlib/quantum/phase_gate.eml +76 -0
- eml_stdlib/quantum/rotation_rz.eml +92 -0
- eml_stdlib/sensors/accelerometer.eml +59 -0
- eml_stdlib/sensors/hall_effect.eml +42 -0
- eml_stdlib/sensors/photodetector.eml +58 -0
- eml_stdlib/sensors/rtd.eml +57 -0
- eml_stdlib/sensors/strain_gauge.eml +63 -0
- eml_stdlib/sensors/thermistor.eml +71 -0
- eml_stdlib/signal/bandpass.eml +39 -0
- eml_stdlib/signal/chirp.eml +42 -0
- eml_stdlib/signal/dft_single.eml +30 -0
- eml_stdlib/signal/fft_butterfly.eml +36 -0
- eml_stdlib/signal/highpass.eml +45 -0
- eml_stdlib/signal/lowpass.eml +49 -0
- eml_stdlib/signal/matched_filter.eml +48 -0
- eml_stdlib/signal/notch_filter.eml +47 -0
- eml_stdlib/signal/window_hamming.eml +47 -0
- eml_stdlib/signal/window_hann.eml +50 -0
- eml_stdlib-0.4.0.dist-info/METADATA +453 -0
- eml_stdlib-0.4.0.dist-info/RECORD +185 -0
- eml_stdlib-0.4.0.dist-info/WHEEL +5 -0
- eml_stdlib-0.4.0.dist-info/licenses/LICENSE +21 -0
- eml_stdlib-0.4.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// kalman_1d.eml -- one-step 1-D Kalman filter.
|
|
2
|
+
//
|
|
3
|
+
// State: estimate (scalar), variance P (scalar)
|
|
4
|
+
// Inputs: measurement z, measurement variance R, process variance Q
|
|
5
|
+
//
|
|
6
|
+
// The two functions update / predict the state. Caller chains them
|
|
7
|
+
// each frame. All chain order 0 -- pure rational arithmetic.
|
|
8
|
+
//
|
|
9
|
+
// The 1-D Kalman is the workhorse for IMU heading, RC battery state
|
|
10
|
+
// of charge, and any single-channel "noisy sensor + smooth dynamics"
|
|
11
|
+
// fusion.
|
|
12
|
+
|
|
13
|
+
module kalman_1d;
|
|
14
|
+
|
|
15
|
+
// Predict step: drift the variance forward by Q.
|
|
16
|
+
@verify(lean, theorem = "kalman1d_predict_variance_grows")
|
|
17
|
+
fn kalman1d_predict(estimate: Real, p: Real, q: Real) -> (Real, Real)
|
|
18
|
+
where chain_order <= 0
|
|
19
|
+
requires (p >= 0.0)
|
|
20
|
+
requires (q >= 0.0)
|
|
21
|
+
{
|
|
22
|
+
(estimate, p + q)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Update step: combine with measurement z.
|
|
26
|
+
@verify(lean, theorem = "kalman1d_update_variance_shrinks")
|
|
27
|
+
fn kalman1d_update(estimate: Real, p: Real, z: Real, r: Real) -> (Real, Real)
|
|
28
|
+
where chain_order <= 0
|
|
29
|
+
requires (p >= 0.0)
|
|
30
|
+
requires (r > 0.0)
|
|
31
|
+
{
|
|
32
|
+
let k = p / (p + r);
|
|
33
|
+
let new_est = estimate + k * (z - estimate);
|
|
34
|
+
let new_p = (1.0 - k) * p;
|
|
35
|
+
(new_est, new_p)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Combined predict-then-update in one call -- the form most
|
|
39
|
+
// users want when running on a fixed sample rate.
|
|
40
|
+
@verify(lean, theorem = "kalman1d_step_variance_bounded")
|
|
41
|
+
fn kalman1d_step(estimate: Real, p: Real, z: Real, q: Real, r: Real)
|
|
42
|
+
-> (Real, Real)
|
|
43
|
+
where chain_order <= 0
|
|
44
|
+
requires (p >= 0.0)
|
|
45
|
+
requires (q >= 0.0)
|
|
46
|
+
requires (r > 0.0)
|
|
47
|
+
{
|
|
48
|
+
let p_pred = p + q;
|
|
49
|
+
let k = p_pred / (p_pred + r);
|
|
50
|
+
let new_est = estimate + k * (z - estimate);
|
|
51
|
+
let new_p = (1.0 - k) * p_pred;
|
|
52
|
+
(new_est, new_p)
|
|
53
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// lqr_1d.eml -- 1-D infinite-horizon LQR.
|
|
2
|
+
//
|
|
3
|
+
// For a scalar plant
|
|
4
|
+
// x' = a*x + b*u
|
|
5
|
+
// with cost J = sum q*x^2 + r*u^2
|
|
6
|
+
// the optimal control is u = -k*x with
|
|
7
|
+
// k = (b*p) / r
|
|
8
|
+
// p = solution of the algebraic Riccati equation
|
|
9
|
+
// p = q + a^2*p - (a*b*p)^2 / (r + b^2*p)
|
|
10
|
+
//
|
|
11
|
+
// For the closed-form 1-D scalar Riccati this simplifies to the
|
|
12
|
+
// quadratic
|
|
13
|
+
// b^2 * p^2 - (b^2 * q / r + a^2 - 1) * p - q = 0
|
|
14
|
+
// which has one positive root.
|
|
15
|
+
|
|
16
|
+
module lqr_1d;
|
|
17
|
+
|
|
18
|
+
// Closed-form solution of the scalar discrete-time ARE,
|
|
19
|
+
// returning the Riccati P > 0.
|
|
20
|
+
@verify(lean, theorem = "lqr_1d_riccati_positive")
|
|
21
|
+
fn riccati(a: Real, b: Real, q: Real, r: Real) -> Real
|
|
22
|
+
where chain_order <= 1,
|
|
23
|
+
domain: r > 0.0 && q >= 0.0 && b != 0.0
|
|
24
|
+
ensures (result >= 0.0)
|
|
25
|
+
{
|
|
26
|
+
let alpha = b * b / r;
|
|
27
|
+
let beta = a * a - 1.0;
|
|
28
|
+
let disc = (beta + alpha * q) * (beta + alpha * q) + 4.0 * alpha * q;
|
|
29
|
+
(beta + alpha * q + sqrt(disc)) / (2.0 * alpha)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Optimal feedback gain k from the Riccati P.
|
|
33
|
+
@verify(lean, theorem = "lqr_1d_gain_positive_for_positive_b")
|
|
34
|
+
fn lqr_gain(b: Real, p: Real, r: Real) -> Real
|
|
35
|
+
where chain_order <= 0,
|
|
36
|
+
domain: r > 0.0 && p >= 0.0
|
|
37
|
+
{
|
|
38
|
+
b * p / r
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Closed-loop control law u = -k * x.
|
|
42
|
+
@verify(lean, theorem = "lqr_1d_control_proportional_to_state")
|
|
43
|
+
fn lqr_control(x: Real, k: Real) -> Real
|
|
44
|
+
where chain_order <= 0
|
|
45
|
+
{
|
|
46
|
+
-k * x
|
|
47
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// pid_controller.eml -- the textbook PID controller.
|
|
2
|
+
//
|
|
3
|
+
// output = Kp * error + Ki * integral + Kd * derivative
|
|
4
|
+
//
|
|
5
|
+
// Universal control loop: heating, motors, robots, drones,
|
|
6
|
+
// rockets. Chain order 0 (a linear combination of the inputs).
|
|
7
|
+
// The interesting part is the @verify contract: the output is
|
|
8
|
+
// always clamped to the actuator's physical range.
|
|
9
|
+
//
|
|
10
|
+
// Phase F migration: the three single-variable input bounds that
|
|
11
|
+
// were previously expressed as `requires (abs(x) <= 100.0)` clauses
|
|
12
|
+
// are now refinement types directly on the parameters. Semantics
|
|
13
|
+
// are equivalent -- every backend lowers refinements to the same
|
|
14
|
+
// guard form (`assert`, `require`, hypothesis, ...) the old
|
|
15
|
+
// `requires` produced -- but the contract now lives at the type,
|
|
16
|
+
// where dimensional + value bounds belong, instead of trailing
|
|
17
|
+
// the signature.
|
|
18
|
+
|
|
19
|
+
module pid_controller;
|
|
20
|
+
|
|
21
|
+
const Kp: Real = 1.5
|
|
22
|
+
const Ki: Real = 0.4
|
|
23
|
+
const Kd: Real = 0.05
|
|
24
|
+
|
|
25
|
+
const OUT_MIN: Real = -1.0
|
|
26
|
+
const OUT_MAX: Real = 1.0
|
|
27
|
+
|
|
28
|
+
@verify(lean, theorem = "pid_output_clamped")
|
|
29
|
+
fn pid(error: Real{e | abs(e) <= 100.0},
|
|
30
|
+
integral: Real{i | abs(i) <= 100.0},
|
|
31
|
+
derivative: Real{d | abs(d) <= 100.0}) -> Real
|
|
32
|
+
where chain_order <= 0
|
|
33
|
+
ensures (result >= OUT_MIN)
|
|
34
|
+
ensures (result <= OUT_MAX)
|
|
35
|
+
{
|
|
36
|
+
let raw = Kp * error + Ki * integral + Kd * derivative;
|
|
37
|
+
clamp(raw, OUT_MIN, OUT_MAX)
|
|
38
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// bounce.eml -- piecewise parabolic bounce-out curve.
|
|
2
|
+
//
|
|
3
|
+
// The standard easing-functions bounce-out: four parabolic
|
|
4
|
+
// segments of decreasing amplitude. Output ∈ [0, 1].
|
|
5
|
+
|
|
6
|
+
module bounce;
|
|
7
|
+
|
|
8
|
+
@verify(lean, theorem = "bounce_out_in_unit_interval")
|
|
9
|
+
fn bounce_out(t: Real) -> Real
|
|
10
|
+
where chain_order <= 0
|
|
11
|
+
requires (t >= 0.0)
|
|
12
|
+
requires (t <= 1.0)
|
|
13
|
+
ensures (result >= 0.0)
|
|
14
|
+
ensures (result <= 1.0)
|
|
15
|
+
{
|
|
16
|
+
if t < 0.36364 { // 1 / 2.75
|
|
17
|
+
7.5625 * t * t
|
|
18
|
+
} else if t < 0.72727 { // 2 / 2.75
|
|
19
|
+
let u: Real = t - 0.54545; // 1.5 / 2.75
|
|
20
|
+
7.5625 * u * u + 0.75
|
|
21
|
+
} else if t < 0.90909 { // 2.5 / 2.75
|
|
22
|
+
let u: Real = t - 0.81818; // 2.25 / 2.75
|
|
23
|
+
7.5625 * u * u + 0.9375
|
|
24
|
+
} else {
|
|
25
|
+
let u: Real = t - 0.95455; // 2.625 / 2.75
|
|
26
|
+
7.5625 * u * u + 0.984375
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@verify(lean, theorem = "bounce_in_in_unit_interval")
|
|
31
|
+
fn bounce_in(t: Real) -> Real
|
|
32
|
+
where chain_order <= 0
|
|
33
|
+
requires (t >= 0.0)
|
|
34
|
+
requires (t <= 1.0)
|
|
35
|
+
ensures (result >= 0.0)
|
|
36
|
+
ensures (result <= 1.0)
|
|
37
|
+
{
|
|
38
|
+
1.0 - bounce_out(1.0 - t)
|
|
39
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// ease_in.eml -- quadratic ease-in: t².
|
|
2
|
+
//
|
|
3
|
+
// t ∈ [0, 1] → result ∈ [0, 1], starts slow, accelerates.
|
|
4
|
+
|
|
5
|
+
module ease_in;
|
|
6
|
+
|
|
7
|
+
@verify(lean, theorem = "ease_in_in_unit_interval")
|
|
8
|
+
fn ease_in_quad(t: Real) -> Real
|
|
9
|
+
where chain_order <= 0
|
|
10
|
+
requires (t >= 0.0)
|
|
11
|
+
requires (t <= 1.0)
|
|
12
|
+
ensures (result >= 0.0)
|
|
13
|
+
ensures (result <= 1.0)
|
|
14
|
+
{
|
|
15
|
+
t * t
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@verify(lean, theorem = "ease_in_cubic_in_unit_interval")
|
|
19
|
+
fn ease_in_cubic(t: Real) -> Real
|
|
20
|
+
where chain_order <= 0
|
|
21
|
+
requires (t >= 0.0)
|
|
22
|
+
requires (t <= 1.0)
|
|
23
|
+
ensures (result >= 0.0)
|
|
24
|
+
ensures (result <= 1.0)
|
|
25
|
+
{
|
|
26
|
+
t * t * t
|
|
27
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// ease_in_out.eml -- smoothstep: 3t² - 2t³.
|
|
2
|
+
|
|
3
|
+
module ease_in_out;
|
|
4
|
+
|
|
5
|
+
@verify(lean, theorem = "smoothstep_in_unit_interval")
|
|
6
|
+
fn smoothstep(t: Real) -> Real
|
|
7
|
+
where chain_order <= 0
|
|
8
|
+
requires (t >= 0.0)
|
|
9
|
+
requires (t <= 1.0)
|
|
10
|
+
ensures (result >= 0.0)
|
|
11
|
+
ensures (result <= 1.0)
|
|
12
|
+
{
|
|
13
|
+
3.0 * t * t - 2.0 * t * t * t
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Quintic smootherstep — 6t⁵ - 15t⁴ + 10t³, C² continuous.
|
|
17
|
+
@verify(lean, theorem = "smootherstep_in_unit_interval")
|
|
18
|
+
fn smootherstep(t: Real) -> Real
|
|
19
|
+
where chain_order <= 0
|
|
20
|
+
requires (t >= 0.0)
|
|
21
|
+
requires (t <= 1.0)
|
|
22
|
+
ensures (result >= 0.0)
|
|
23
|
+
ensures (result <= 1.0)
|
|
24
|
+
{
|
|
25
|
+
let t3: Real = t * t * t;
|
|
26
|
+
let t4: Real = t3 * t;
|
|
27
|
+
let t5: Real = t4 * t;
|
|
28
|
+
6.0 * t5 - 15.0 * t4 + 10.0 * t3
|
|
29
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// ease_out.eml -- quadratic ease-out: 1 - (1 - t)².
|
|
2
|
+
|
|
3
|
+
module ease_out;
|
|
4
|
+
|
|
5
|
+
@verify(lean, theorem = "ease_out_in_unit_interval")
|
|
6
|
+
fn ease_out_quad(t: Real) -> Real
|
|
7
|
+
where chain_order <= 0
|
|
8
|
+
requires (t >= 0.0)
|
|
9
|
+
requires (t <= 1.0)
|
|
10
|
+
ensures (result >= 0.0)
|
|
11
|
+
ensures (result <= 1.0)
|
|
12
|
+
{
|
|
13
|
+
let one_minus_t: Real = 1.0 - t;
|
|
14
|
+
1.0 - one_minus_t * one_minus_t
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@verify(lean, theorem = "ease_out_cubic_in_unit_interval")
|
|
18
|
+
fn ease_out_cubic(t: Real) -> Real
|
|
19
|
+
where chain_order <= 0
|
|
20
|
+
requires (t >= 0.0)
|
|
21
|
+
requires (t <= 1.0)
|
|
22
|
+
ensures (result >= 0.0)
|
|
23
|
+
ensures (result <= 1.0)
|
|
24
|
+
{
|
|
25
|
+
let one_minus_t: Real = 1.0 - t;
|
|
26
|
+
1.0 - one_minus_t * one_minus_t * one_minus_t
|
|
27
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// ik_2bone.eml -- two-bone inverse kinematics via law of cosines.
|
|
2
|
+
//
|
|
3
|
+
// Given an upper bone of length L1 and a lower bone of length L2,
|
|
4
|
+
// reach a target at distance D from the shoulder. Compute the
|
|
5
|
+
// shoulder + elbow joint angles:
|
|
6
|
+
//
|
|
7
|
+
// cos(elbow) = (L1² + L2² - D²) / (2 * L1 * L2)
|
|
8
|
+
// cos(α) = (L1² + D² - L2²) / (2 * L1 * D) // shoulder, target-relative
|
|
9
|
+
//
|
|
10
|
+
// Returns the elbow angle (rad) and the shoulder offset angle (rad)
|
|
11
|
+
// as separate functions so the caller can compose with the
|
|
12
|
+
// shoulder-to-target azimuth.
|
|
13
|
+
|
|
14
|
+
module ik_2bone;
|
|
15
|
+
|
|
16
|
+
@verify(lean, theorem = "elbow_angle_in_pi_band")
|
|
17
|
+
fn elbow_angle(l1: Real, l2: Real, distance: Real) -> Real
|
|
18
|
+
where chain_order <= 1,
|
|
19
|
+
domain: l1 > 0.0 && l2 > 0.0 && distance > 0.0
|
|
20
|
+
ensures (result >= 0.0)
|
|
21
|
+
ensures (result <= 3.141592653589793)
|
|
22
|
+
{
|
|
23
|
+
let cos_e: Real = clamp(
|
|
24
|
+
(l1 * l1 + l2 * l2 - distance * distance) / (2.0 * l1 * l2),
|
|
25
|
+
-1.0, 1.0
|
|
26
|
+
);
|
|
27
|
+
acos(cos_e)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@verify(lean, theorem = "shoulder_angle_in_half_pi_band")
|
|
31
|
+
fn shoulder_offset(l1: Real, l2: Real, distance: Real) -> Real
|
|
32
|
+
where chain_order <= 1,
|
|
33
|
+
domain: l1 > 0.0 && l2 > 0.0 && distance > 0.0
|
|
34
|
+
ensures (result >= 0.0)
|
|
35
|
+
ensures (result <= 1.5707963267948966)
|
|
36
|
+
{
|
|
37
|
+
let cos_a: Real = clamp(
|
|
38
|
+
(l1 * l1 + distance * distance - l2 * l2) / (2.0 * l1 * distance),
|
|
39
|
+
-1.0, 1.0
|
|
40
|
+
);
|
|
41
|
+
acos(cos_a)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Reachability: returns 1.0 if the target is reachable, 0.0 if not.
|
|
45
|
+
@verify(lean, theorem = "ik_reachable_in_zero_one")
|
|
46
|
+
fn reachable(l1: Real, l2: Real, distance: Real) -> Real
|
|
47
|
+
where chain_order <= 0,
|
|
48
|
+
domain: l1 > 0.0 && l2 > 0.0 && distance >= 0.0
|
|
49
|
+
ensures (result >= 0.0)
|
|
50
|
+
ensures (result <= 1.0)
|
|
51
|
+
{
|
|
52
|
+
let max_reach: Real = l1 + l2;
|
|
53
|
+
let min_reach: Real = abs(l1 - l2);
|
|
54
|
+
if distance > max_reach { 0.0 }
|
|
55
|
+
else if distance < min_reach { 0.0 }
|
|
56
|
+
else { 1.0 }
|
|
57
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// spring.eml -- damped harmonic oscillator response.
|
|
2
|
+
//
|
|
3
|
+
// x(t) = A * exp(-ζ ω t) * cos(ω_d t + φ)
|
|
4
|
+
//
|
|
5
|
+
// where ω is natural frequency, ζ damping ratio, ω_d = ω√(1-ζ²)
|
|
6
|
+
// the damped frequency. Underdamped (ζ < 1) is the case actually
|
|
7
|
+
// useful for animation; critically damped (ζ = 1) is a smooth
|
|
8
|
+
// follow.
|
|
9
|
+
|
|
10
|
+
module spring;
|
|
11
|
+
|
|
12
|
+
@verify(lean, theorem = "spring_underdamped_signed")
|
|
13
|
+
fn underdamped(amplitude: Real, omega: Real, damping_ratio: Real,
|
|
14
|
+
phase: Real, t_s: Real) -> Real
|
|
15
|
+
where chain_order <= 1,
|
|
16
|
+
domain: omega > 0.0 && damping_ratio >= 0.0 && damping_ratio < 1.0 && t_s >= 0.0
|
|
17
|
+
{
|
|
18
|
+
let omega_d: Real = omega * sqrt(1.0 - damping_ratio * damping_ratio);
|
|
19
|
+
amplitude * exp(-damping_ratio * omega * t_s) * cos(omega_d * t_s + phase)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Critically damped: x(t) = A * (1 + ω t) * exp(-ω t)
|
|
23
|
+
@verify(lean, theorem = "spring_critical_signed")
|
|
24
|
+
fn critically_damped(amplitude: Real, omega: Real, t_s: Real) -> Real
|
|
25
|
+
where chain_order <= 1,
|
|
26
|
+
domain: omega > 0.0 && t_s >= 0.0
|
|
27
|
+
{
|
|
28
|
+
amplitude * (1.0 + omega * t_s) * exp(-omega * t_s)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Time constant τ = 1/(ζω) for underdamped exponential envelope.
|
|
32
|
+
@verify(lean, theorem = "spring_tau_positive")
|
|
33
|
+
fn time_constant(omega: Real, damping_ratio: Real) -> Real
|
|
34
|
+
where chain_order <= 0,
|
|
35
|
+
domain: omega > 0.0 && damping_ratio > 0.0
|
|
36
|
+
ensures (result > 0.0)
|
|
37
|
+
{
|
|
38
|
+
1.0 / (damping_ratio * omega)
|
|
39
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// distance_attenuation.eml -- 3D audio falloff models.
|
|
2
|
+
//
|
|
3
|
+
// Three common laws:
|
|
4
|
+
// inverse: a = ref_dist / max(d, ref_dist)
|
|
5
|
+
// inv_square: a = ref_dist² / max(d, ref_dist)² (physical)
|
|
6
|
+
// linear: a = max(0, 1 - (d - ref_dist) / (max_dist - ref_dist))
|
|
7
|
+
//
|
|
8
|
+
// Output ∈ [0, 1]: 1 = full volume at ref_dist, 0 = silent at max_dist.
|
|
9
|
+
|
|
10
|
+
module distance_attenuation;
|
|
11
|
+
|
|
12
|
+
@verify(lean, theorem = "audio_attenuation_inverse_in_unit_interval")
|
|
13
|
+
fn inverse(distance_m: Real, ref_distance_m: Real) -> Real
|
|
14
|
+
where chain_order <= 0,
|
|
15
|
+
domain: distance_m >= 0.0 && ref_distance_m > 0.0
|
|
16
|
+
ensures (result >= 0.0)
|
|
17
|
+
ensures (result <= 1.0)
|
|
18
|
+
{
|
|
19
|
+
let d: Real = max(distance_m, ref_distance_m);
|
|
20
|
+
ref_distance_m / d
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@verify(lean, theorem = "audio_attenuation_inverse_square_in_unit_interval")
|
|
24
|
+
fn inverse_square(distance_m: Real, ref_distance_m: Real) -> Real
|
|
25
|
+
where chain_order <= 0,
|
|
26
|
+
domain: distance_m >= 0.0 && ref_distance_m > 0.0
|
|
27
|
+
ensures (result >= 0.0)
|
|
28
|
+
ensures (result <= 1.0)
|
|
29
|
+
{
|
|
30
|
+
let d: Real = max(distance_m, ref_distance_m);
|
|
31
|
+
let ratio: Real = ref_distance_m / d;
|
|
32
|
+
ratio * ratio
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@verify(lean, theorem = "audio_attenuation_linear_in_unit_interval")
|
|
36
|
+
fn linear(distance_m: Real, ref_distance_m: Real, max_distance_m: Real) -> Real
|
|
37
|
+
where chain_order <= 0,
|
|
38
|
+
domain: distance_m >= 0.0 && max_distance_m > ref_distance_m
|
|
39
|
+
ensures (result >= 0.0)
|
|
40
|
+
ensures (result <= 1.0)
|
|
41
|
+
{
|
|
42
|
+
if distance_m <= ref_distance_m {
|
|
43
|
+
1.0
|
|
44
|
+
} else if distance_m >= max_distance_m {
|
|
45
|
+
0.0
|
|
46
|
+
} else {
|
|
47
|
+
clamp(1.0 - (distance_m - ref_distance_m) / (max_distance_m - ref_distance_m),
|
|
48
|
+
0.0, 1.0)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// doppler.eml -- Doppler frequency shift.
|
|
2
|
+
//
|
|
3
|
+
// f_observed = f_source * (v_sound + v_listener) / (v_sound + v_source)
|
|
4
|
+
//
|
|
5
|
+
// Sign convention: velocities are positive when the entity moves
|
|
6
|
+
// AWAY FROM the listener / source (for the source) or TOWARD the
|
|
7
|
+
// source (for the listener). Game engines typically supply velocity
|
|
8
|
+
// vectors and project onto the source→listener direction.
|
|
9
|
+
|
|
10
|
+
module doppler;
|
|
11
|
+
|
|
12
|
+
const V_SOUND_AIR: Real = 343.0 // m/s
|
|
13
|
+
|
|
14
|
+
@verify(lean, theorem = "doppler_observed_freq_positive")
|
|
15
|
+
fn observed_frequency(f_source: Real, v_sound: Real,
|
|
16
|
+
v_listener_toward_source: Real,
|
|
17
|
+
v_source_away_from_listener: Real) -> Real
|
|
18
|
+
where chain_order <= 0,
|
|
19
|
+
domain: v_sound > 0.0
|
|
20
|
+
requires (f_source >= 0.0)
|
|
21
|
+
requires (v_sound + v_source_away_from_listener > 0.0)
|
|
22
|
+
ensures (result >= 0.0)
|
|
23
|
+
{
|
|
24
|
+
f_source * (v_sound + v_listener_toward_source)
|
|
25
|
+
/ (v_sound + v_source_away_from_listener)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@verify(lean, theorem = "doppler_air_default_freq_positive")
|
|
29
|
+
fn observed_frequency_air(f_source: Real,
|
|
30
|
+
v_listener_toward_source: Real,
|
|
31
|
+
v_source_away_from_listener: Real) -> Real
|
|
32
|
+
where chain_order <= 0
|
|
33
|
+
requires (f_source >= 0.0)
|
|
34
|
+
requires (V_SOUND_AIR + v_source_away_from_listener > 0.0)
|
|
35
|
+
ensures (result >= 0.0)
|
|
36
|
+
{
|
|
37
|
+
f_source * (V_SOUND_AIR + v_listener_toward_source)
|
|
38
|
+
/ (V_SOUND_AIR + v_source_away_from_listener)
|
|
39
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// lowpass_audio.eml -- first-order IIR low-pass for audio.
|
|
2
|
+
//
|
|
3
|
+
// y[n] = α * x[n] + (1 - α) * y[n-1]
|
|
4
|
+
//
|
|
5
|
+
// Where α = dt / (RC + dt) maps cutoff frequency to coefficient.
|
|
6
|
+
// Used for muffling audio behind walls, motion blur on sound,
|
|
7
|
+
// or simulating a chest-cavity filter on bullet impacts.
|
|
8
|
+
|
|
9
|
+
module lowpass_audio;
|
|
10
|
+
|
|
11
|
+
const TWO_PI: Real = 6.283185307179586
|
|
12
|
+
|
|
13
|
+
@verify(lean, theorem = "lowpass_alpha_in_unit_interval")
|
|
14
|
+
fn alpha_from_cutoff(cutoff_hz: Real, sample_rate_hz: Real) -> Real
|
|
15
|
+
where chain_order <= 0,
|
|
16
|
+
domain: cutoff_hz > 0.0 && sample_rate_hz > 0.0
|
|
17
|
+
ensures (result >= 0.0)
|
|
18
|
+
ensures (result <= 1.0)
|
|
19
|
+
{
|
|
20
|
+
let dt: Real = 1.0 / sample_rate_hz;
|
|
21
|
+
let rc: Real = 1.0 / (TWO_PI * cutoff_hz);
|
|
22
|
+
clamp(dt / (rc + dt), 0.0, 1.0)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@verify(lean, theorem = "lowpass_step_signed_bounded")
|
|
26
|
+
fn step(x_n: Real, y_prev: Real, alpha: Real) -> Real
|
|
27
|
+
where chain_order <= 0
|
|
28
|
+
requires (alpha >= 0.0)
|
|
29
|
+
requires (alpha <= 1.0)
|
|
30
|
+
{
|
|
31
|
+
alpha * x_n + (1.0 - alpha) * y_prev
|
|
32
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// reverb_delay.eml -- single-tap delay line with feedback.
|
|
2
|
+
//
|
|
3
|
+
// y[n] = x[n] + feedback * y[n - delay_samples]
|
|
4
|
+
//
|
|
5
|
+
// Feedback ∈ [0, 1) so the recursive sum converges. Used as the
|
|
6
|
+
// elementary building block of comb-filter reverbs (chain N copies
|
|
7
|
+
// in parallel with prime-number delays for natural decay).
|
|
8
|
+
|
|
9
|
+
module reverb_delay;
|
|
10
|
+
|
|
11
|
+
@verify(lean, theorem = "reverb_feedback_in_open_unit_interval")
|
|
12
|
+
fn feedback_safe(coeff: Real) -> Real
|
|
13
|
+
where chain_order <= 0
|
|
14
|
+
requires (coeff >= 0.0)
|
|
15
|
+
requires (coeff < 1.0)
|
|
16
|
+
ensures (result >= 0.0)
|
|
17
|
+
ensures (result < 1.0)
|
|
18
|
+
{
|
|
19
|
+
clamp(coeff, 0.0, 0.999)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// One step of a feedback delay line.
|
|
23
|
+
@verify(lean, theorem = "delay_step_signed_bounded")
|
|
24
|
+
fn delay_step(x_n: Real, y_delayed: Real, feedback: Real) -> Real
|
|
25
|
+
where chain_order <= 0
|
|
26
|
+
requires (feedback >= 0.0)
|
|
27
|
+
requires (feedback < 1.0)
|
|
28
|
+
{
|
|
29
|
+
x_n + feedback * y_delayed
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Decay time for a feedback coefficient: T60 = 3 * delay_seconds / -log10(feedback)
|
|
33
|
+
@verify(lean, theorem = "reverb_t60_positive")
|
|
34
|
+
fn t60_seconds(delay_seconds: Real, feedback: Real) -> Real
|
|
35
|
+
where chain_order <= 1,
|
|
36
|
+
domain: delay_seconds > 0.0 && feedback > 0.0 && feedback < 1.0
|
|
37
|
+
ensures (result > 0.0)
|
|
38
|
+
{
|
|
39
|
+
let log_term: Real = -ln(feedback) / 2.302585093; // -log10(feedback)
|
|
40
|
+
3.0 * delay_seconds / log_term
|
|
41
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// dof_blur.eml -- depth-of-field circle of confusion.
|
|
2
|
+
//
|
|
3
|
+
// coc = aperture * |z_depth - z_focus| / z_depth
|
|
4
|
+
//
|
|
5
|
+
// `coc` is the circle of confusion radius in screen-space pixels;
|
|
6
|
+
// the post-process samples a disc of that radius around each
|
|
7
|
+
// fragment for the blur.
|
|
8
|
+
|
|
9
|
+
module dof_blur;
|
|
10
|
+
|
|
11
|
+
@verify(lean, theorem = "coc_nonneg")
|
|
12
|
+
fn circle_of_confusion(aperture: Real, z_depth: Real, z_focus: Real) -> Real
|
|
13
|
+
where chain_order <= 0,
|
|
14
|
+
domain: aperture >= 0.0 && z_depth > 0.0
|
|
15
|
+
ensures (result >= 0.0)
|
|
16
|
+
{
|
|
17
|
+
aperture * abs(z_depth - z_focus) / z_depth
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Clamp the COC to a maximum radius (texture sample budget).
|
|
21
|
+
@verify(lean, theorem = "coc_clamped_in_band")
|
|
22
|
+
fn circle_of_confusion_clamped(aperture: Real, z_depth: Real,
|
|
23
|
+
z_focus: Real, max_radius_px: Real) -> Real
|
|
24
|
+
where chain_order <= 0,
|
|
25
|
+
domain: aperture >= 0.0 && z_depth > 0.0 && max_radius_px >= 0.0
|
|
26
|
+
ensures (result >= 0.0)
|
|
27
|
+
ensures (result <= max_radius_px)
|
|
28
|
+
{
|
|
29
|
+
clamp(aperture * abs(z_depth - z_focus) / z_depth, 0.0, max_radius_px)
|
|
30
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// fov_projection.eml -- perspective projection matrix coefficients.
|
|
2
|
+
//
|
|
3
|
+
// Returns the four non-zero entries of a standard right-handed
|
|
4
|
+
// perspective matrix (column-major):
|
|
5
|
+
//
|
|
6
|
+
// m00 = 1 / (aspect * tan(fov/2))
|
|
7
|
+
// m11 = 1 / tan(fov/2)
|
|
8
|
+
// m22 = -(far + near) / (far - near)
|
|
9
|
+
// m23 = -(2 * far * near) / (far - near)
|
|
10
|
+
|
|
11
|
+
module fov_projection;
|
|
12
|
+
|
|
13
|
+
@verify(lean, theorem = "fov_m00_positive")
|
|
14
|
+
fn m00(fov_y_rad: Real, aspect: Real) -> Real
|
|
15
|
+
where chain_order <= 2,
|
|
16
|
+
domain: fov_y_rad > 0.0 && fov_y_rad < 3.141 && aspect > 0.0
|
|
17
|
+
ensures (result > 0.0)
|
|
18
|
+
{
|
|
19
|
+
1.0 / (aspect * tan(0.5 * fov_y_rad))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@verify(lean, theorem = "fov_m11_positive")
|
|
23
|
+
fn m11(fov_y_rad: Real) -> Real
|
|
24
|
+
where chain_order <= 2,
|
|
25
|
+
domain: fov_y_rad > 0.0 && fov_y_rad < 3.141
|
|
26
|
+
ensures (result > 0.0)
|
|
27
|
+
{
|
|
28
|
+
1.0 / tan(0.5 * fov_y_rad)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@verify(lean, theorem = "fov_m22_signed")
|
|
32
|
+
fn m22(near: Real, far: Real) -> Real
|
|
33
|
+
where chain_order <= 0,
|
|
34
|
+
domain: near > 0.0 && far > near
|
|
35
|
+
ensures (result < 0.0)
|
|
36
|
+
{
|
|
37
|
+
-(far + near) / (far - near)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@verify(lean, theorem = "fov_m23_signed")
|
|
41
|
+
fn m23(near: Real, far: Real) -> Real
|
|
42
|
+
where chain_order <= 0,
|
|
43
|
+
domain: near > 0.0 && far > near
|
|
44
|
+
ensures (result < 0.0)
|
|
45
|
+
{
|
|
46
|
+
-(2.0 * far * near) / (far - near)
|
|
47
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// orbit.eml -- spherical-coordinates orbit camera.
|
|
2
|
+
//
|
|
3
|
+
// x = r * cos(φ) * sin(θ)
|
|
4
|
+
// y = r * sin(φ)
|
|
5
|
+
// z = r * cos(φ) * cos(θ)
|
|
6
|
+
//
|
|
7
|
+
// θ = azimuth (around y-axis), φ = elevation (above XZ plane), r = radius.
|
|
8
|
+
|
|
9
|
+
module orbit;
|
|
10
|
+
|
|
11
|
+
@verify(lean, theorem = "orbit_x_in_band")
|
|
12
|
+
fn position_x(r: Real, theta_rad: Real, phi_rad: Real) -> Real
|
|
13
|
+
where chain_order <= 1,
|
|
14
|
+
domain: r >= 0.0
|
|
15
|
+
ensures (abs(result) <= r)
|
|
16
|
+
{
|
|
17
|
+
r * cos(phi_rad) * sin(theta_rad)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@verify(lean, theorem = "orbit_y_in_band")
|
|
21
|
+
fn position_y(r: Real, phi_rad: Real) -> Real
|
|
22
|
+
where chain_order <= 1,
|
|
23
|
+
domain: r >= 0.0
|
|
24
|
+
ensures (abs(result) <= r)
|
|
25
|
+
{
|
|
26
|
+
r * sin(phi_rad)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@verify(lean, theorem = "orbit_z_in_band")
|
|
30
|
+
fn position_z(r: Real, theta_rad: Real, phi_rad: Real) -> Real
|
|
31
|
+
where chain_order <= 1,
|
|
32
|
+
domain: r >= 0.0
|
|
33
|
+
ensures (abs(result) <= r)
|
|
34
|
+
{
|
|
35
|
+
r * cos(phi_rad) * cos(theta_rad)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Pythagorean witness: x² + y² + z² = r²
|
|
39
|
+
@verify(lean, theorem = "orbit_norm_witness")
|
|
40
|
+
fn norm_witness(theta_rad: Real, phi_rad: Real) -> Real
|
|
41
|
+
where chain_order <= 1
|
|
42
|
+
ensures (result == 1.0)
|
|
43
|
+
{
|
|
44
|
+
let cx: Real = cos(phi_rad) * sin(theta_rad);
|
|
45
|
+
let sy: Real = sin(phi_rad);
|
|
46
|
+
let cz: Real = cos(phi_rad) * cos(theta_rad);
|
|
47
|
+
cx * cx + sy * sy + cz * cz
|
|
48
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// shake.eml -- damped-noise camera shake.
|
|
2
|
+
//
|
|
3
|
+
// shake(t) = magnitude * exp(-decay * t) * perlin(t * frequency, 0)
|
|
4
|
+
//
|
|
5
|
+
// Returns a 1D scalar offset; caller threads this into x and y
|
|
6
|
+
// with two different time offsets to get a 2D displacement.
|
|
7
|
+
|
|
8
|
+
module shake;
|
|
9
|
+
|
|
10
|
+
use stdlib::gaming::noise::perlin_2d;
|
|
11
|
+
|
|
12
|
+
@verify(lean, theorem = "shake_magnitude_in_band")
|
|
13
|
+
fn shake1d(magnitude: Real, decay: Real, frequency: Real, t_s: Real) -> Real
|
|
14
|
+
where chain_order <= 1,
|
|
15
|
+
domain: decay >= 0.0 && frequency >= 0.0 && t_s >= 0.0
|
|
16
|
+
{
|
|
17
|
+
let envelope: Real = magnitude * exp(-decay * t_s);
|
|
18
|
+
let noise: Real = perlin_2d::perlin2(t_s * frequency, 0.0);
|
|
19
|
+
envelope * noise
|
|
20
|
+
}
|