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.
Files changed (185) hide show
  1. eml_stdlib/__init__.py +81 -0
  2. eml_stdlib/ballistics/air_density.eml +73 -0
  3. eml_stdlib/ballistics/ballistic_solver.eml +102 -0
  4. eml_stdlib/ballistics/coriolis.eml +60 -0
  5. eml_stdlib/ballistics/drag.eml +44 -0
  6. eml_stdlib/ballistics/gravity.eml +52 -0
  7. eml_stdlib/ballistics/muzzle_velocity.eml +42 -0
  8. eml_stdlib/ballistics/spin_drift.eml +60 -0
  9. eml_stdlib/ballistics/time_of_flight.eml +79 -0
  10. eml_stdlib/ballistics/wind.eml +58 -0
  11. eml_stdlib/biology/abilities/ballistic_armor.eml +61 -0
  12. eml_stdlib/biology/abilities/bite_force.eml +48 -0
  13. eml_stdlib/biology/abilities/chromatophore.eml +50 -0
  14. eml_stdlib/biology/abilities/discharge.eml +45 -0
  15. eml_stdlib/biology/abilities/elastic_energy.eml +42 -0
  16. eml_stdlib/biology/abilities/locomotion.eml +50 -0
  17. eml_stdlib/biology/abilities/muscle_power.eml +47 -0
  18. eml_stdlib/biology/abilities/sprint.eml +36 -0
  19. eml_stdlib/biology/abilities/texture_match.eml +49 -0
  20. eml_stdlib/biology/abilities/toxin.eml +44 -0
  21. eml_stdlib/biology/cone_l.eml +36 -0
  22. eml_stdlib/biology/cone_s.eml +32 -0
  23. eml_stdlib/biology/cryptochrome.eml +44 -0
  24. eml_stdlib/biology/geomagnetic_field.eml +32 -0
  25. eml_stdlib/biology/goldman_equation.eml +41 -0
  26. eml_stdlib/biology/hill_equation.eml +65 -0
  27. eml_stdlib/biology/hodgkin_huxley.eml +108 -0
  28. eml_stdlib/biology/michaelis_menten.eml +65 -0
  29. eml_stdlib/biology/plume_diffusion.eml +47 -0
  30. eml_stdlib/biology/receptor_binding.eml +35 -0
  31. eml_stdlib/biology/rod_sensitivity.eml +38 -0
  32. eml_stdlib/biology/tapetum.eml +70 -0
  33. eml_stdlib/carriers/acoustic_cloak.eml +56 -0
  34. eml_stdlib/carriers/ferron_emission.eml +55 -0
  35. eml_stdlib/carriers/ferron_logic.eml +45 -0
  36. eml_stdlib/carriers/ferron_propagation.eml +69 -0
  37. eml_stdlib/carriers/mach_zehnder.eml +52 -0
  38. eml_stdlib/carriers/magnon_dispersion.eml +57 -0
  39. eml_stdlib/carriers/magnon_logic.eml +45 -0
  40. eml_stdlib/carriers/optical_neuron.eml +55 -0
  41. eml_stdlib/carriers/phonon_bandgap.eml +54 -0
  42. eml_stdlib/carriers/ring_resonator.eml +48 -0
  43. eml_stdlib/carriers/spin_torque.eml +61 -0
  44. eml_stdlib/carriers/thermal_rectifier.eml +63 -0
  45. eml_stdlib/catalog.json +4209 -0
  46. eml_stdlib/circuits/buck_converter.eml +74 -0
  47. eml_stdlib/circuits/cmos_inverter.eml +75 -0
  48. eml_stdlib/circuits/h_bridge.eml +69 -0
  49. eml_stdlib/circuits/ldo.eml +66 -0
  50. eml_stdlib/circuits/mosfet_iv.eml +59 -0
  51. eml_stdlib/circuits/pll_loop.eml +63 -0
  52. eml_stdlib/circuits/rc_filter.eml +98 -0
  53. eml_stdlib/circuits/voltage_divider.eml +68 -0
  54. eml_stdlib/control/bangbang.eml +34 -0
  55. eml_stdlib/control/deadband.eml +28 -0
  56. eml_stdlib/control/hysteresis.eml +28 -0
  57. eml_stdlib/control/kalman_1d.eml +53 -0
  58. eml_stdlib/control/lqr_1d.eml +47 -0
  59. eml_stdlib/control/pid.eml +38 -0
  60. eml_stdlib/gaming/animation/bounce.eml +39 -0
  61. eml_stdlib/gaming/animation/ease_in.eml +27 -0
  62. eml_stdlib/gaming/animation/ease_in_out.eml +29 -0
  63. eml_stdlib/gaming/animation/ease_out.eml +27 -0
  64. eml_stdlib/gaming/animation/ik_2bone.eml +57 -0
  65. eml_stdlib/gaming/animation/spring.eml +39 -0
  66. eml_stdlib/gaming/audio/distance_attenuation.eml +50 -0
  67. eml_stdlib/gaming/audio/doppler.eml +39 -0
  68. eml_stdlib/gaming/audio/lowpass_audio.eml +32 -0
  69. eml_stdlib/gaming/audio/reverb_delay.eml +41 -0
  70. eml_stdlib/gaming/camera/dof_blur.eml +30 -0
  71. eml_stdlib/gaming/camera/fov_projection.eml +47 -0
  72. eml_stdlib/gaming/camera/orbit.eml +48 -0
  73. eml_stdlib/gaming/camera/shake.eml +20 -0
  74. eml_stdlib/gaming/lighting/ambient_occlusion.eml +40 -0
  75. eml_stdlib/gaming/lighting/area_light.eml +50 -0
  76. eml_stdlib/gaming/lighting/fog_exp.eml +28 -0
  77. eml_stdlib/gaming/lighting/fog_exp2.eml +30 -0
  78. eml_stdlib/gaming/lighting/point_light.eml +33 -0
  79. eml_stdlib/gaming/lighting/spot_light.eml +43 -0
  80. eml_stdlib/gaming/noise/fbm.eml +65 -0
  81. eml_stdlib/gaming/noise/perlin_2d.eml +65 -0
  82. eml_stdlib/gaming/noise/perlin_3d.eml +63 -0
  83. eml_stdlib/gaming/noise/simplex_2d.eml +67 -0
  84. eml_stdlib/gaming/noise/turbulence.eml +46 -0
  85. eml_stdlib/gaming/noise/value_noise.eml +45 -0
  86. eml_stdlib/gaming/noise/voronoi_2d.eml +83 -0
  87. eml_stdlib/gaming/noise/white_noise.eml +47 -0
  88. eml_stdlib/gaming/noise/worley.eml +79 -0
  89. eml_stdlib/gaming/particles/drag.eml +30 -0
  90. eml_stdlib/gaming/particles/emitter_radial.eml +35 -0
  91. eml_stdlib/gaming/particles/fade.eml +36 -0
  92. eml_stdlib/gaming/particles/gravity_particle.eml +32 -0
  93. eml_stdlib/gaming/particles/size_over_life.eml +28 -0
  94. eml_stdlib/gaming/physics/aabb_overlap.eml +38 -0
  95. eml_stdlib/gaming/physics/buoyancy.eml +43 -0
  96. eml_stdlib/gaming/physics/circle_collision.eml +46 -0
  97. eml_stdlib/gaming/physics/impulse_resolve.eml +39 -0
  98. eml_stdlib/gaming/physics/rigid_body_2d.eml +61 -0
  99. eml_stdlib/gaming/physics/spring_damper.eml +30 -0
  100. eml_stdlib/gaming/physics/verlet.eml +29 -0
  101. eml_stdlib/gaming/shading/fresnel.eml +57 -0
  102. eml_stdlib/gaming/shading/matcap.eml +34 -0
  103. eml_stdlib/gaming/shading/pbr_diffuse.eml +41 -0
  104. eml_stdlib/gaming/shading/pbr_specular.eml +75 -0
  105. eml_stdlib/gaming/shading/subsurface.eml +52 -0
  106. eml_stdlib/gaming/shading/toon.eml +30 -0
  107. eml_stdlib/gaming/terrain/biome_select.eml +41 -0
  108. eml_stdlib/gaming/terrain/cliff_detect.eml +35 -0
  109. eml_stdlib/gaming/terrain/erosion_hydraulic.eml +57 -0
  110. eml_stdlib/gaming/terrain/erosion_thermal.eml +44 -0
  111. eml_stdlib/gaming/terrain/heightmap.eml +33 -0
  112. eml_stdlib/gaming/textures/brick.eml +27 -0
  113. eml_stdlib/gaming/textures/caustics.eml +25 -0
  114. eml_stdlib/gaming/textures/checkerboard.eml +27 -0
  115. eml_stdlib/gaming/textures/fire.eml +26 -0
  116. eml_stdlib/gaming/textures/marble.eml +24 -0
  117. eml_stdlib/gaming/textures/rust.eml +23 -0
  118. eml_stdlib/gaming/textures/water_surface.eml +49 -0
  119. eml_stdlib/gaming/textures/wood.eml +41 -0
  120. eml_stdlib/math/abs.eml +45 -0
  121. eml_stdlib/math/atan.eml +32 -0
  122. eml_stdlib/math/atan2.eml +61 -0
  123. eml_stdlib/math/bessel_j0.eml +65 -0
  124. eml_stdlib/math/cos.eml +42 -0
  125. eml_stdlib/math/cosh.eml +24 -0
  126. eml_stdlib/math/erf.eml +66 -0
  127. eml_stdlib/math/exp.eml +49 -0
  128. eml_stdlib/math/gamma.eml +55 -0
  129. eml_stdlib/math/gaussian.eml +18 -0
  130. eml_stdlib/math/log.eml +59 -0
  131. eml_stdlib/math/pow.eml +51 -0
  132. eml_stdlib/math/relu.eml +63 -0
  133. eml_stdlib/math/sigmoid.eml +21 -0
  134. eml_stdlib/math/sin.eml +37 -0
  135. eml_stdlib/math/sinh.eml +24 -0
  136. eml_stdlib/math/softmax.eml +87 -0
  137. eml_stdlib/math/sqrt.eml +46 -0
  138. eml_stdlib/math/tan.eml +36 -0
  139. eml_stdlib/math/tanh.eml +54 -0
  140. eml_stdlib/ml/attention.eml +70 -0
  141. eml_stdlib/ml/cross_entropy.eml +85 -0
  142. eml_stdlib/ml/gelu.eml +51 -0
  143. eml_stdlib/ml/layernorm.eml +49 -0
  144. eml_stdlib/ml/leaky_relu.eml +51 -0
  145. eml_stdlib/ml/relu.eml +63 -0
  146. eml_stdlib/ml/rotary_embed.eml +64 -0
  147. eml_stdlib/ml/silu.eml +40 -0
  148. eml_stdlib/ml/softmax.eml +87 -0
  149. eml_stdlib/physics/coulomb.eml +48 -0
  150. eml_stdlib/physics/damped_oscillator.eml +20 -0
  151. eml_stdlib/physics/diffusion.eml +45 -0
  152. eml_stdlib/physics/gravity.eml +60 -0
  153. eml_stdlib/physics/hooke.eml +56 -0
  154. eml_stdlib/physics/navier_stokes_1d.eml +57 -0
  155. eml_stdlib/physics/planck_radiation.eml +77 -0
  156. eml_stdlib/physics/stefan_boltzmann.eml +75 -0
  157. eml_stdlib/physics/wave_propagation.eml +52 -0
  158. eml_stdlib/quantum/cnot.eml +72 -0
  159. eml_stdlib/quantum/grover_oracle.eml +69 -0
  160. eml_stdlib/quantum/hadamard.eml +73 -0
  161. eml_stdlib/quantum/pauli_x.eml +29 -0
  162. eml_stdlib/quantum/pauli_z.eml +29 -0
  163. eml_stdlib/quantum/phase_gate.eml +76 -0
  164. eml_stdlib/quantum/rotation_rz.eml +92 -0
  165. eml_stdlib/sensors/accelerometer.eml +59 -0
  166. eml_stdlib/sensors/hall_effect.eml +42 -0
  167. eml_stdlib/sensors/photodetector.eml +58 -0
  168. eml_stdlib/sensors/rtd.eml +57 -0
  169. eml_stdlib/sensors/strain_gauge.eml +63 -0
  170. eml_stdlib/sensors/thermistor.eml +71 -0
  171. eml_stdlib/signal/bandpass.eml +39 -0
  172. eml_stdlib/signal/chirp.eml +42 -0
  173. eml_stdlib/signal/dft_single.eml +30 -0
  174. eml_stdlib/signal/fft_butterfly.eml +36 -0
  175. eml_stdlib/signal/highpass.eml +45 -0
  176. eml_stdlib/signal/lowpass.eml +49 -0
  177. eml_stdlib/signal/matched_filter.eml +48 -0
  178. eml_stdlib/signal/notch_filter.eml +47 -0
  179. eml_stdlib/signal/window_hamming.eml +47 -0
  180. eml_stdlib/signal/window_hann.eml +50 -0
  181. eml_stdlib-0.4.0.dist-info/METADATA +453 -0
  182. eml_stdlib-0.4.0.dist-info/RECORD +185 -0
  183. eml_stdlib-0.4.0.dist-info/WHEEL +5 -0
  184. eml_stdlib-0.4.0.dist-info/licenses/LICENSE +21 -0
  185. eml_stdlib-0.4.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,40 @@
1
+ // ambient_occlusion.eml -- hemisphere-sampling AO approximation.
2
+ //
3
+ // SSAO-style scalar: how much of the upper hemisphere is occluded
4
+ // by nearby geometry. The kernel here computes a per-sample
5
+ // occlusion contribution (occluded if the sample is behind the
6
+ // surface plane), letting the caller average across the kernel.
7
+
8
+ module ambient_occlusion;
9
+
10
+ // Single-sample contribution: returns 1.0 if occluded, 0.0 if
11
+ // visible. `sample_depth` and `surface_depth` are world-space
12
+ // depths; `radius` is the AO kernel radius for range-fading.
13
+ @verify(lean, theorem = "ao_sample_in_unit_interval")
14
+ fn sample_contribution(sample_depth: Real, surface_depth: Real,
15
+ radius: Real) -> Real
16
+ where chain_order <= 0,
17
+ domain: radius > 0.0
18
+ ensures (result >= 0.0)
19
+ ensures (result <= 1.0)
20
+ {
21
+ if sample_depth >= surface_depth {
22
+ 0.0
23
+ } else {
24
+ // Range fade: closer occluders count more.
25
+ let range: Real = clamp(1.0 - (surface_depth - sample_depth) / radius, 0.0, 1.0);
26
+ range
27
+ }
28
+ }
29
+
30
+ // Final AO factor (1 = fully lit, 0 = fully occluded).
31
+ @verify(lean, theorem = "ao_factor_in_unit_interval")
32
+ fn ao_factor(occlusion_sum: Real, num_samples: Real) -> Real
33
+ where chain_order <= 0,
34
+ domain: num_samples > 0.0
35
+ requires (occlusion_sum >= 0.0)
36
+ ensures (result >= 0.0)
37
+ ensures (result <= 1.0)
38
+ {
39
+ clamp(1.0 - occlusion_sum / num_samples, 0.0, 1.0)
40
+ }
@@ -0,0 +1,50 @@
1
+ // area_light.eml -- "Most Representative Point" approximation.
2
+ //
3
+ // For a disc / sphere / quad area light, the cheap real-time trick
4
+ // is to pick the single point on the area that best represents
5
+ // the light's contribution to a given surface point — usually the
6
+ // closest point on the area to the reflection ray. We then shade
7
+ // using that point as a single-point light.
8
+ //
9
+ // This module ships only the closest-point math; the shading then
10
+ // uses point_light + cook_torrance.
11
+
12
+ module area_light;
13
+
14
+ @verify(lean, theorem = "closest_on_sphere_within_radius")
15
+ fn closest_on_sphere(center_x: Real, center_y: Real, center_z: Real,
16
+ radius_m: Real,
17
+ ref_x: Real, ref_y: Real, ref_z: Real) -> Real
18
+ where chain_order <= 1,
19
+ domain: radius_m >= 0.0
20
+ ensures (result >= 0.0)
21
+ {
22
+ // We return the magnitude of the offset from the centre to the
23
+ // reference, clamped to the sphere's radius. The full vector is
24
+ // reconstructed by the caller from the unit direction × this
25
+ // magnitude — keeping this kernel scalar.
26
+ let dx: Real = ref_x - center_x;
27
+ let dy: Real = ref_y - center_y;
28
+ let dz: Real = ref_z - center_z;
29
+ let d: Real = sqrt(dx * dx + dy * dy + dz * dz);
30
+ clamp(d, 0.0, radius_m)
31
+ }
32
+
33
+ // Solid-angle attenuation for a sphere area light: I_received
34
+ // scales with the visible portion of the sphere from the surface
35
+ // point. Closed form for a sphere of radius R at distance D:
36
+ //
37
+ // solid_angle = 2π * (1 - sqrt(1 - (R/D)²))
38
+ //
39
+ // Returns the fraction of the hemisphere covered, ∈ [0, 1].
40
+ @verify(lean, theorem = "sphere_solid_angle_in_unit_interval")
41
+ fn solid_angle_fraction(radius_m: Real, distance_m: Real) -> Real
42
+ where chain_order <= 1,
43
+ domain: radius_m >= 0.0 && distance_m > 0.0
44
+ ensures (result >= 0.0)
45
+ ensures (result <= 1.0)
46
+ {
47
+ let ratio: Real = clamp(radius_m / distance_m, 0.0, 1.0);
48
+ let cos_half_angle: Real = sqrt(1.0 - ratio * ratio);
49
+ clamp(0.5 * (1.0 - cos_half_angle), 0.0, 1.0)
50
+ }
@@ -0,0 +1,28 @@
1
+ // fog_exp.eml -- exponential fog (Beer's law).
2
+ //
3
+ // transmittance(d) = exp(-density * d)
4
+ // fog_factor = 1 - transmittance
5
+ //
6
+ // Output ∈ [0, 1]: 0 = no fog, 1 = fully obscured.
7
+
8
+ module fog_exp;
9
+
10
+ @verify(lean, theorem = "fog_exp_in_unit_interval")
11
+ fn fog_factor(density: Real, distance_m: Real) -> Real
12
+ where chain_order <= 1,
13
+ domain: density >= 0.0 && distance_m >= 0.0
14
+ ensures (result >= 0.0)
15
+ ensures (result <= 1.0)
16
+ {
17
+ 1.0 - exp(-density * distance_m)
18
+ }
19
+
20
+ @verify(lean, theorem = "fog_exp_transmittance_in_unit_interval")
21
+ fn transmittance(density: Real, distance_m: Real) -> Real
22
+ where chain_order <= 1,
23
+ domain: density >= 0.0 && distance_m >= 0.0
24
+ ensures (result >= 0.0)
25
+ ensures (result <= 1.0)
26
+ {
27
+ exp(-density * distance_m)
28
+ }
@@ -0,0 +1,30 @@
1
+ // fog_exp2.eml -- squared-exponential fog.
2
+ //
3
+ // transmittance(d) = exp(-(density * d)²)
4
+ //
5
+ // Sharper transition than fog_exp; favoured by older fixed-function
6
+ // pipelines. Output ∈ [0, 1].
7
+
8
+ module fog_exp2;
9
+
10
+ @verify(lean, theorem = "fog_exp2_in_unit_interval")
11
+ fn fog_factor(density: Real, distance_m: Real) -> Real
12
+ where chain_order <= 1,
13
+ domain: density >= 0.0 && distance_m >= 0.0
14
+ ensures (result >= 0.0)
15
+ ensures (result <= 1.0)
16
+ {
17
+ let d: Real = density * distance_m;
18
+ 1.0 - exp(-(d * d))
19
+ }
20
+
21
+ @verify(lean, theorem = "fog_exp2_transmittance_in_unit_interval")
22
+ fn transmittance(density: Real, distance_m: Real) -> Real
23
+ where chain_order <= 1,
24
+ domain: density >= 0.0 && distance_m >= 0.0
25
+ ensures (result >= 0.0)
26
+ ensures (result <= 1.0)
27
+ {
28
+ let d: Real = density * distance_m;
29
+ exp(-(d * d))
30
+ }
@@ -0,0 +1,33 @@
1
+ // point_light.eml -- inverse-square falloff with radius clamp.
2
+ //
3
+ // Physical: I(d) = I_0 / d²
4
+ // Game: clamp by a "max range" so distant lights don't add
5
+ // vanishingly small contributions.
6
+ //
7
+ // attenuation(d, r) = (1 / (1 + (d/r)²))² * smooth_cutoff(d/r)
8
+
9
+ module point_light;
10
+
11
+ @verify(lean, theorem = "point_light_attenuation_in_unit_interval")
12
+ fn attenuation(distance_m: Real, range_m: Real) -> Real
13
+ where chain_order <= 0,
14
+ domain: range_m > 0.0 && distance_m >= 0.0
15
+ ensures (result >= 0.0)
16
+ ensures (result <= 1.0)
17
+ {
18
+ let dr: Real = distance_m / range_m;
19
+ let inv: Real = 1.0 / (1.0 + dr * dr * 4.0);
20
+ let smooth: Real = clamp(1.0 - dr * dr * dr * dr, 0.0, 1.0);
21
+ clamp(inv * inv * smooth, 0.0, 1.0)
22
+ }
23
+
24
+ // Pure 1/r² for physically-based falloff (no clamp). Caller is
25
+ // responsible for not querying at d = 0.
26
+ @verify(lean, theorem = "point_light_inv_sq_positive")
27
+ fn inverse_square(distance_m: Real, intensity: Real) -> Real
28
+ where chain_order <= 0,
29
+ domain: distance_m > 0.0 && intensity >= 0.0
30
+ ensures (result >= 0.0)
31
+ {
32
+ intensity / (distance_m * distance_m)
33
+ }
@@ -0,0 +1,43 @@
1
+ // spot_light.eml -- point light × cone attenuation.
2
+ //
3
+ // spot(d, r, cos θ, cos_inner, cos_outer)
4
+ // = point_attenuation(d, r) * smooth_cone(cos θ, cos_inner, cos_outer)
5
+ //
6
+ // `cos θ` is the angle between the light's forward direction and
7
+ // the vector to the lit surface. `cos_inner` and `cos_outer` define
8
+ // the falloff cone.
9
+
10
+ module spot_light;
11
+
12
+ use stdlib::gaming::lighting::point_light;
13
+
14
+ @verify(lean, theorem = "spot_cone_in_unit_interval")
15
+ fn cone_attenuation(cos_theta: Real, cos_inner: Real, cos_outer: Real) -> Real
16
+ where chain_order <= 0
17
+ requires (cos_inner >= cos_outer)
18
+ ensures (result >= 0.0)
19
+ ensures (result <= 1.0)
20
+ {
21
+ if cos_theta >= cos_inner {
22
+ 1.0
23
+ } else if cos_theta <= cos_outer {
24
+ 0.0
25
+ } else {
26
+ let t: Real = (cos_theta - cos_outer) / (cos_inner - cos_outer + 1.0e-7);
27
+ let s: Real = t * t * (3.0 - 2.0 * t);
28
+ clamp(s, 0.0, 1.0)
29
+ }
30
+ }
31
+
32
+ @verify(lean, theorem = "spot_total_in_unit_interval")
33
+ fn attenuation(distance_m: Real, range_m: Real,
34
+ cos_theta: Real, cos_inner: Real, cos_outer: Real) -> Real
35
+ where chain_order <= 0,
36
+ domain: range_m > 0.0
37
+ requires (cos_inner >= cos_outer)
38
+ ensures (result >= 0.0)
39
+ ensures (result <= 1.0)
40
+ {
41
+ point_light::attenuation(distance_m, range_m)
42
+ * cone_attenuation(cos_theta, cos_inner, cos_outer)
43
+ }
@@ -0,0 +1,65 @@
1
+ // fbm.eml -- fractal Brownian motion (octave stacker).
2
+ //
3
+ // Sums multiple octaves of a base noise function, each at twice
4
+ // the frequency and half the amplitude of the previous:
5
+ //
6
+ // fbm(p) = Σ_{i=0..N-1} amplitude_i * noise(p * frequency_i)
7
+ // amplitude_i = persistence^i, frequency_i = lacunarity^i
8
+ //
9
+ // Standard parameters: persistence = 0.5, lacunarity = 2.0, N = 6.
10
+ //
11
+ // Output is normalised to [-1, 1]. Chain order matches the base
12
+ // noise (1 for Perlin) since octaves compose additively.
13
+
14
+ module fbm;
15
+
16
+ use stdlib::gaming::noise::perlin_2d;
17
+
18
+ const PERSISTENCE_DEFAULT: Real = 0.5
19
+ const LACUNARITY_DEFAULT: Real = 2.0
20
+ const OCTAVES_DEFAULT: Real = 6.0
21
+
22
+ @verify(lean, theorem = "fbm_in_signed_unit_band")
23
+ fn fbm2(x: Real, y: Real, octaves: Real,
24
+ persistence: Real, lacunarity: Real) -> Real
25
+ where chain_order <= 1,
26
+ domain: octaves >= 1.0 && persistence > 0.0 && lacunarity > 1.0
27
+ ensures (result >= -1.0)
28
+ ensures (result <= 1.0)
29
+ {
30
+ // Unrolled fixed-octave sum. The Forge optimiser folds
31
+ // persistence^i and lacunarity^i to constants at compile time.
32
+ let p0: Real = perlin_2d::perlin2(x, y);
33
+ let p1: Real = perlin_2d::perlin2(x * lacunarity, y * lacunarity);
34
+ let p2: Real = perlin_2d::perlin2(x * lacunarity * lacunarity,
35
+ y * lacunarity * lacunarity);
36
+ let p3: Real = perlin_2d::perlin2(x * lacunarity * lacunarity * lacunarity,
37
+ y * lacunarity * lacunarity * lacunarity);
38
+ let a0: Real = 1.0;
39
+ let a1: Real = persistence;
40
+ let a2: Real = persistence * persistence;
41
+ let a3: Real = persistence * persistence * persistence;
42
+ let total: Real = a0 + a1 + a2 + a3;
43
+ let raw: Real = (a0 * p0 + a1 * p1 + a2 * p2 + a3 * p3) / total;
44
+ clamp(raw, -1.0, 1.0)
45
+ }
46
+
47
+ // Default-parameters convenience.
48
+ @verify(lean, theorem = "fbm_default_in_signed_unit_band")
49
+ fn fbm2_default(x: Real, y: Real) -> Real
50
+ where chain_order <= 1
51
+ ensures (result >= -1.0)
52
+ ensures (result <= 1.0)
53
+ {
54
+ fbm2(x, y, OCTAVES_DEFAULT, PERSISTENCE_DEFAULT, LACUNARITY_DEFAULT)
55
+ }
56
+
57
+ // 0..1 unit-interval scaling for direct UV use.
58
+ @verify(lean, theorem = "fbm_unit_in_unit_interval")
59
+ fn fbm2_unit(x: Real, y: Real) -> Real
60
+ where chain_order <= 1
61
+ ensures (result >= 0.0)
62
+ ensures (result <= 1.0)
63
+ {
64
+ 0.5 * (fbm2_default(x, y) + 1.0)
65
+ }
@@ -0,0 +1,65 @@
1
+ // perlin_2d.eml -- classic 2D Perlin noise.
2
+ //
3
+ // Lattice-based gradient noise. At each integer-grid corner we
4
+ // pick a pseudo-random gradient vector; the noise value at point
5
+ // p is the smoothstep-blended dot product of (p - corner) with
6
+ // each corner's gradient. Output is in [-1, 1] (typically scaled
7
+ // to [0, 1] by the caller via 0.5 * (n + 1)).
8
+ //
9
+ // Chain order 1.
10
+
11
+ module perlin_2d;
12
+
13
+ use stdlib::gaming::noise::white_noise;
14
+
15
+ // Pseudo-gradient: pick one of 4 unit-axis vectors based on the
16
+ // hash. Using axis-aligned gradients keeps chain order at 1 (one
17
+ // trig call inside white_noise::hash2). For higher quality switch
18
+ // to the 8-direction Perlin gradient set; the shape of the proof
19
+ // obligation is identical.
20
+ fn _grad_dot(xi: Real, yi: Real, xf: Real, yf: Real) -> Real
21
+ where chain_order <= 1
22
+ {
23
+ let h: Real = white_noise::hash2(xi, yi);
24
+ let q: Real = floor(h * 4.0); // 0..3
25
+ // 0=+x, 1=+y, 2=-x, 3=-y
26
+ if q < 1.0 { xf }
27
+ else if q < 2.0 { yf }
28
+ else if q < 3.0 { -xf }
29
+ else { -yf }
30
+ }
31
+
32
+ @verify(lean, theorem = "perlin2_in_signed_unit_band")
33
+ fn perlin2(x: Real, y: Real) -> Real
34
+ where chain_order <= 1
35
+ ensures (result >= -1.0)
36
+ ensures (result <= 1.0)
37
+ {
38
+ let xi: Real = floor(x);
39
+ let yi: Real = floor(y);
40
+ let xf: Real = x - xi;
41
+ let yf: Real = y - yi;
42
+
43
+ let n00: Real = _grad_dot(xi, yi, xf, yf);
44
+ let n10: Real = _grad_dot(xi + 1.0, yi, xf - 1.0, yf);
45
+ let n01: Real = _grad_dot(xi, yi + 1.0, xf, yf - 1.0);
46
+ let n11: Real = _grad_dot(xi + 1.0, yi + 1.0, xf - 1.0, yf - 1.0);
47
+
48
+ // Quintic ease (Ken Perlin's improved smoothstep).
49
+ let u: Real = xf * xf * xf * (xf * (xf * 6.0 - 15.0) + 10.0);
50
+ let v: Real = yf * yf * yf * (yf * (yf * 6.0 - 15.0) + 10.0);
51
+
52
+ let l0: Real = n00 + u * (n10 - n00);
53
+ let l1: Real = n01 + u * (n11 - n01);
54
+ clamp(l0 + v * (l1 - l0), -1.0, 1.0)
55
+ }
56
+
57
+ // 0..1 scaled variant for height-map / texture authoring.
58
+ @verify(lean, theorem = "perlin2_unit_in_unit_interval")
59
+ fn perlin2_unit(x: Real, y: Real) -> Real
60
+ where chain_order <= 1
61
+ ensures (result >= 0.0)
62
+ ensures (result <= 1.0)
63
+ {
64
+ 0.5 * (perlin2(x, y) + 1.0)
65
+ }
@@ -0,0 +1,63 @@
1
+ // perlin_3d.eml -- 3D Perlin noise for volumetric effects.
2
+ //
3
+ // Same recipe as perlin_2d but on the cubic lattice; 8 corner
4
+ // gradients blended via the quintic-smoothstep weights along three
5
+ // axes. Used for 3D fog, cloud volumes, and time-varying surface
6
+ // noise (treat the third axis as time).
7
+
8
+ module perlin_3d;
9
+
10
+ use stdlib::gaming::noise::white_noise;
11
+
12
+ fn _grad_dot3(xi: Real, yi: Real, zi: Real,
13
+ xf: Real, yf: Real, zf: Real) -> Real
14
+ where chain_order <= 1
15
+ {
16
+ let h: Real = white_noise::hash3(xi, yi, zi);
17
+ let q: Real = floor(h * 6.0); // 0..5
18
+ // 6-direction axis-aligned gradient set.
19
+ if q < 1.0 { xf }
20
+ else if q < 2.0 { -xf }
21
+ else if q < 3.0 { yf }
22
+ else if q < 4.0 { -yf }
23
+ else if q < 5.0 { zf }
24
+ else { -zf }
25
+ }
26
+
27
+ @verify(lean, theorem = "perlin3_in_signed_unit_band")
28
+ fn perlin3(x: Real, y: Real, z: Real) -> Real
29
+ where chain_order <= 1
30
+ ensures (result >= -1.0)
31
+ ensures (result <= 1.0)
32
+ {
33
+ let xi: Real = floor(x);
34
+ let yi: Real = floor(y);
35
+ let zi: Real = floor(z);
36
+ let xf: Real = x - xi;
37
+ let yf: Real = y - yi;
38
+ let zf: Real = z - zi;
39
+
40
+ // Quintic ease per axis.
41
+ let u: Real = xf * xf * xf * (xf * (xf * 6.0 - 15.0) + 10.0);
42
+ let v: Real = yf * yf * yf * (yf * (yf * 6.0 - 15.0) + 10.0);
43
+ let w: Real = zf * zf * zf * (zf * (zf * 6.0 - 15.0) + 10.0);
44
+
45
+ let n000: Real = _grad_dot3(xi, yi, zi, xf, yf, zf);
46
+ let n100: Real = _grad_dot3(xi + 1.0, yi, zi, xf - 1.0, yf, zf);
47
+ let n010: Real = _grad_dot3(xi, yi + 1.0, zi, xf, yf - 1.0, zf);
48
+ let n110: Real = _grad_dot3(xi + 1.0, yi + 1.0, zi, xf - 1.0, yf - 1.0, zf);
49
+ let n001: Real = _grad_dot3(xi, yi, zi + 1.0, xf, yf, zf - 1.0);
50
+ let n101: Real = _grad_dot3(xi + 1.0, yi, zi + 1.0, xf - 1.0, yf, zf - 1.0);
51
+ let n011: Real = _grad_dot3(xi, yi + 1.0, zi + 1.0, xf, yf - 1.0, zf - 1.0);
52
+ let n111: Real = _grad_dot3(xi + 1.0, yi + 1.0, zi + 1.0, xf - 1.0, yf - 1.0, zf - 1.0);
53
+
54
+ let x00: Real = n000 + u * (n100 - n000);
55
+ let x10: Real = n010 + u * (n110 - n010);
56
+ let x01: Real = n001 + u * (n101 - n001);
57
+ let x11: Real = n011 + u * (n111 - n011);
58
+
59
+ let y0: Real = x00 + v * (x10 - x00);
60
+ let y1: Real = x01 + v * (x11 - x01);
61
+
62
+ clamp(y0 + w * (y1 - y0), -1.0, 1.0)
63
+ }
@@ -0,0 +1,67 @@
1
+ // simplex_2d.eml -- 2D simplex noise.
2
+ //
3
+ // Ken Perlin's simplex noise is faster than Perlin (3 corners vs
4
+ // 4 in 2D, 4 vs 8 in 3D) and free of the directional-axis
5
+ // artefacts the cubic lattice produces.
6
+ //
7
+ // The skewed coordinate transform maps (x, y) into a triangular
8
+ // simplex grid; the contribution from each of the three nearest
9
+ // simplex vertices is summed with a smooth radial falloff.
10
+ //
11
+ // Chain order 1 (sqrt + hash inside white_noise).
12
+
13
+ module simplex_2d;
14
+
15
+ use stdlib::gaming::noise::white_noise;
16
+
17
+ const F2: Real = 0.366025404 // (sqrt(3) - 1) / 2
18
+ const G2: Real = 0.211324865 // (3 - sqrt(3)) / 6
19
+
20
+ fn _grad_dot(xi: Real, yi: Real, dx: Real, dy: Real) -> Real
21
+ where chain_order <= 1
22
+ {
23
+ let h: Real = white_noise::hash2(xi, yi);
24
+ let q: Real = floor(h * 8.0);
25
+ let theta: Real = q * 0.78539816; // π/4 increments → 8 directions
26
+ cos(theta) * dx + sin(theta) * dy
27
+ }
28
+
29
+ @verify(lean, theorem = "simplex2_in_signed_unit_band")
30
+ fn simplex2(x: Real, y: Real) -> Real
31
+ where chain_order <= 1
32
+ ensures (result >= -1.0)
33
+ ensures (result <= 1.0)
34
+ {
35
+ // Skew the input space to the simplex grid.
36
+ let s: Real = (x + y) * F2;
37
+ let xi: Real = floor(x + s);
38
+ let yi: Real = floor(y + s);
39
+ let t: Real = (xi + yi) * G2;
40
+ let x0: Real = x - (xi - t);
41
+ let y0: Real = y - (yi - t);
42
+
43
+ // Pick which of two simplex triangles we're in.
44
+ let i1: Real = if x0 > y0 { 1.0 } else { 0.0 };
45
+ let j1: Real = if x0 > y0 { 0.0 } else { 1.0 };
46
+
47
+ let x1: Real = x0 - i1 + G2;
48
+ let y1: Real = y0 - j1 + G2;
49
+ let x2: Real = x0 - 1.0 + 2.0 * G2;
50
+ let y2: Real = y0 - 1.0 + 2.0 * G2;
51
+
52
+ fn _contribution(xi_local: Real, yi_local: Real, dx: Real, dy: Real) -> Real
53
+ where chain_order <= 1
54
+ {
55
+ let t_sq: Real = 0.5 - dx * dx - dy * dy;
56
+ if t_sq <= 0.0 { 0.0 } else {
57
+ let t4: Real = t_sq * t_sq * t_sq * t_sq;
58
+ t4 * _grad_dot(xi_local, yi_local, dx, dy)
59
+ }
60
+ }
61
+
62
+ let n0: Real = _contribution(xi, yi, x0, y0);
63
+ let n1: Real = _contribution(xi + i1, yi + j1, x1, y1);
64
+ let n2: Real = _contribution(xi + 1.0, yi + 1.0, x2, y2);
65
+
66
+ clamp(70.0 * (n0 + n1 + n2), -1.0, 1.0)
67
+ }
@@ -0,0 +1,46 @@
1
+ // turbulence.eml -- absolute-value FBM for cloud / marble veining.
2
+ //
3
+ // Identical to fbm but each octave's contribution is |noise(...)|
4
+ // instead of noise(...). Produces cusped ridges instead of smooth
5
+ // dunes; the visual signature of marble, lightning, and storm-cloud
6
+ // turbulence.
7
+ //
8
+ // Output ∈ [0, 1]. Chain order 1.
9
+
10
+ module turbulence;
11
+
12
+ use stdlib::gaming::noise::perlin_2d;
13
+
14
+ const PERSISTENCE_DEFAULT: Real = 0.5
15
+ const LACUNARITY_DEFAULT: Real = 2.0
16
+ const OCTAVES_DEFAULT: Real = 6.0
17
+
18
+ @verify(lean, theorem = "turbulence_in_unit_interval")
19
+ fn turbulence2(x: Real, y: Real, persistence: Real, lacunarity: Real) -> Real
20
+ where chain_order <= 1,
21
+ domain: persistence > 0.0 && lacunarity > 1.0
22
+ ensures (result >= 0.0)
23
+ ensures (result <= 1.0)
24
+ {
25
+ let p0: Real = abs(perlin_2d::perlin2(x, y));
26
+ let p1: Real = abs(perlin_2d::perlin2(x * lacunarity, y * lacunarity));
27
+ let p2: Real = abs(perlin_2d::perlin2(x * lacunarity * lacunarity,
28
+ y * lacunarity * lacunarity));
29
+ let p3: Real = abs(perlin_2d::perlin2(x * lacunarity * lacunarity * lacunarity,
30
+ y * lacunarity * lacunarity * lacunarity));
31
+ let a0: Real = 1.0;
32
+ let a1: Real = persistence;
33
+ let a2: Real = persistence * persistence;
34
+ let a3: Real = persistence * persistence * persistence;
35
+ let total: Real = a0 + a1 + a2 + a3;
36
+ clamp((a0 * p0 + a1 * p1 + a2 * p2 + a3 * p3) / total, 0.0, 1.0)
37
+ }
38
+
39
+ @verify(lean, theorem = "turbulence_default_in_unit_interval")
40
+ fn turbulence2_default(x: Real, y: Real) -> Real
41
+ where chain_order <= 1
42
+ ensures (result >= 0.0)
43
+ ensures (result <= 1.0)
44
+ {
45
+ turbulence2(x, y, PERSISTENCE_DEFAULT, LACUNARITY_DEFAULT)
46
+ }
@@ -0,0 +1,45 @@
1
+ // value_noise.eml -- interpolated lattice noise.
2
+ //
3
+ // Sample an integer-grid hash at four corners of a 2D cell, then
4
+ // bilinearly interpolate via the smoothstep ease curve so the
5
+ // result is C¹ continuous. Chain order 1 (one fract / smoothstep
6
+ // composition; the lattice hash is already chain-0 internally).
7
+
8
+ module value_noise;
9
+
10
+ use stdlib::gaming::noise::white_noise;
11
+
12
+ @verify(lean, theorem = "value_noise_in_unit_interval")
13
+ fn value2(x: Real, y: Real) -> Real
14
+ where chain_order <= 1
15
+ ensures (result >= 0.0)
16
+ ensures (result <= 1.0)
17
+ {
18
+ let xi: Real = floor(x);
19
+ let yi: Real = floor(y);
20
+ let xf: Real = x - xi;
21
+ let yf: Real = y - yi;
22
+
23
+ let v00: Real = white_noise::hash2(xi, yi);
24
+ let v10: Real = white_noise::hash2(xi + 1.0, yi);
25
+ let v01: Real = white_noise::hash2(xi, yi + 1.0);
26
+ let v11: Real = white_noise::hash2(xi + 1.0, yi + 1.0);
27
+
28
+ // Smoothstep weights.
29
+ let u: Real = xf * xf * (3.0 - 2.0 * xf);
30
+ let v: Real = yf * yf * (3.0 - 2.0 * yf);
31
+
32
+ let lerp_x0: Real = v00 + u * (v10 - v00);
33
+ let lerp_x1: Real = v01 + u * (v11 - v01);
34
+ lerp_x0 + v * (lerp_x1 - lerp_x0)
35
+ }
36
+
37
+ // Signed variant centred on 0: result ∈ [-1, 1].
38
+ @verify(lean, theorem = "value_noise_signed_in_band")
39
+ fn value2_signed(x: Real, y: Real) -> Real
40
+ where chain_order <= 1
41
+ ensures (result >= -1.0)
42
+ ensures (result <= 1.0)
43
+ {
44
+ 2.0 * value2(x, y) - 1.0
45
+ }
@@ -0,0 +1,83 @@
1
+ // voronoi_2d.eml -- 2D Voronoi cell distance field.
2
+ //
3
+ // For each query point, find the distance to the nearest of a
4
+ // 3×3 neighbourhood of jittered grid points. The result is the
5
+ // distance field; useful for cell rendering, rust patterns, biome
6
+ // boundaries, and crackle textures.
7
+ //
8
+ // Returns the distance to the nearest feature point. Chain order
9
+ // 1 (sqrt). The minimum search uses min() over 9 candidates.
10
+
11
+ module voronoi_2d;
12
+
13
+ use stdlib::gaming::noise::white_noise;
14
+
15
+ @verify(lean, theorem = "voronoi2_distance_nonneg")
16
+ fn distance_to_nearest(x: Real, y: Real) -> Real
17
+ where chain_order <= 1
18
+ ensures (result >= 0.0)
19
+ {
20
+ let xi: Real = floor(x);
21
+ let yi: Real = floor(y);
22
+
23
+ let mut_d: Real = 1.0e9;
24
+ var d: Real = mut_d;
25
+
26
+ // Note: pure-functional EML doesn't allow mutation; this
27
+ // template unrolls the 3x3 neighbourhood explicitly so each
28
+ // candidate can fold via min() into a single expression.
29
+ let dy_min: Real = -1.0;
30
+ let dy_max: Real = 1.0;
31
+ let dx_min: Real = -1.0;
32
+ let dx_max: Real = 1.0;
33
+
34
+ let c00: Real = _cell_distance(x, y, xi - 1.0, yi - 1.0);
35
+ let c10: Real = _cell_distance(x, y, xi , yi - 1.0);
36
+ let c20: Real = _cell_distance(x, y, xi + 1.0, yi - 1.0);
37
+ let c01: Real = _cell_distance(x, y, xi - 1.0, yi );
38
+ let c11: Real = _cell_distance(x, y, xi , yi );
39
+ let c21: Real = _cell_distance(x, y, xi + 1.0, yi );
40
+ let c02: Real = _cell_distance(x, y, xi - 1.0, yi + 1.0);
41
+ let c12: Real = _cell_distance(x, y, xi , yi + 1.0);
42
+ let c22: Real = _cell_distance(x, y, xi + 1.0, yi + 1.0);
43
+
44
+ min(c00, min(c10, min(c20,
45
+ min(c01, min(c11, min(c21,
46
+ min(c02, min(c12, c22))))))))
47
+ }
48
+
49
+ fn _cell_distance(px: Real, py: Real, gx: Real, gy: Real) -> Real
50
+ where chain_order <= 1
51
+ {
52
+ // Jitter each grid point by hash(gx, gy).
53
+ let jx: Real = white_noise::hash2(gx, gy);
54
+ let jy: Real = white_noise::hash2(gx + 17.0, gy + 31.0);
55
+ let dx: Real = px - (gx + jx);
56
+ let dy: Real = py - (gy + jy);
57
+ sqrt(dx * dx + dy * dy)
58
+ }
59
+
60
+ // F2 - F1: distance to second-nearest minus first-nearest. Used
61
+ // for crystalline / metallic noise patterns.
62
+ @verify(lean, theorem = "voronoi2_f2_f1_nonneg")
63
+ fn f2_minus_f1(x: Real, y: Real) -> Real
64
+ where chain_order <= 1
65
+ ensures (result >= 0.0)
66
+ {
67
+ // Approximation: pick second-min as min over the 8 ring cells
68
+ // (excluding the centre). Cheap surrogate; the exact second-min
69
+ // requires sorting all 9.
70
+ let xi: Real = floor(x);
71
+ let yi: Real = floor(y);
72
+ let f1: Real = _cell_distance(x, y, xi, yi);
73
+ let neighbours: Real = min(
74
+ _cell_distance(x, y, xi - 1.0, yi - 1.0),
75
+ min(_cell_distance(x, y, xi , yi - 1.0),
76
+ min(_cell_distance(x, y, xi + 1.0, yi - 1.0),
77
+ min(_cell_distance(x, y, xi - 1.0, yi ),
78
+ min(_cell_distance(x, y, xi + 1.0, yi ),
79
+ min(_cell_distance(x, y, xi - 1.0, yi + 1.0),
80
+ min(_cell_distance(x, y, xi , yi + 1.0),
81
+ _cell_distance(x, y, xi + 1.0, yi + 1.0))))))));
82
+ clamp(neighbours - f1, 0.0, 1.0e9)
83
+ }