feat(stdlib): Math.exp/log/pow + Ease.elastic transcendentals (issue #25)
Some checks are pending
docs / build-and-deploy (push) Waiting to run
Some checks are pending
docs / build-and-deploy (push) Waiting to run
Finish the unblocked "Math / Ease" half of #25: the fixed-point transcendentals deferred from #2. Vec.* stays blocked on the vec2 type in #1. - Math.exp, Math.log (natural), Math.pow — deterministic Q16.16 via two new prelude fns in emit_math_prelude: @fn_fx_exp2 (range-reduced 5th-order Taylor 2^f, then a clamped shift by the integer part) and @fn_fx_log2 (llvm.ctlz for the exponent + an atanh series on (m-1)/(m+1) for the mantissa). exp=2^(x·log2 e), log=log2(x)·ln2, pow=2^(b·log2 a). - Ease.elastic — ease-out elastic 2^(-10t)·sin((10t-0.75)·2pi/3)+1. - Pure integer IR, so bit-identical on every platform. Results must fit the Q16.16 range (|x| < 32768); larger magnitudes saturate (documented). Test selfhost/tests/transcend.ludic (registered in the self-host suite) + docs for all four. Reseeded; the C-free bootstrap fixpoint holds. All suites green (24 self-host, 45 regression, 29 tool). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
2002e977d9
commit
6e6467b515
9 changed files with 9278 additions and 8791 deletions
22
docs/language/ease/ease-elastic.md
Normal file
22
docs/language/ease/ease-elastic.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: ease-elastic
|
||||
name: Ease.elastic
|
||||
category: ease
|
||||
kind: namespace-method
|
||||
tokens: Ease.elastic
|
||||
sig: Ease.elastic(t) -> fixed
|
||||
tip: Ease out with a springy wobble.
|
||||
order: 5
|
||||
ns: Ease
|
||||
member: elastic
|
||||
---
|
||||
|
||||
Returns an ease-out that shoots past <code>1.0</code> and wobbles around it a few times with shrinking amplitude before settling — a spring released under tension. The overshoot comes from a decaying <code>2<sup>-10t</sup></code> envelope on a sine, all in Q16.16, so it is deterministic and bit-identical on every platform. Great for UI that snaps in with personality: a panel that springs open, a picked-up item that jiggles, a selection that bounces to place.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let scale = Math.lerp(0.0, 1.0, Ease.elastic(progress))
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-exp.md
Normal file
22
docs/language/math/math-exp.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-exp
|
||||
name: Math.exp
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.exp
|
||||
sig: Math.exp(x) -> fixed
|
||||
tip: e raised to the power x.
|
||||
order: 29
|
||||
ns: Math
|
||||
member: exp
|
||||
---
|
||||
|
||||
Returns <code>e<sup>x</sup></code> (the natural exponential), computed in Q16.16 as <code>2<sup>x·log₂e</sup></code> through a range-reduced integer polynomial — so it is bit-identical on every platform, the same guarantee the rest of the runtime gives. Pair it with <code>Math.log</code> for exponential decay and growth: cooldowns that ease off, difficulty ramps, or a value that relaxes toward a target. The result must land inside the fixed-point range (magnitudes below <code>32768</code>), so keep <code>x</code> under about <code>10</code>; larger exponents saturate.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let remaining = Math.exp(0.0 - decay * elapsed) # smooth decay 1 -> 0
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-log.md
Normal file
22
docs/language/math/math-log.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-log
|
||||
name: Math.log
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.log
|
||||
sig: Math.log(x) -> fixed
|
||||
tip: The natural logarithm of x.
|
||||
order: 30
|
||||
ns: Math
|
||||
member: log
|
||||
---
|
||||
|
||||
Returns the natural logarithm <code>ln(x)</code> for <code>x > 0</code>, computed in Q16.16 from a deterministic integer <code>log₂</code> (bit-scan for the exponent, a short series for the mantissa) scaled by <code>ln 2</code> — bit-identical on every platform. It is the inverse of <code>Math.exp</code>: reach for it to turn multiplicative growth into a straight line — score-to-level curves, logarithmic difficulty, decibel-style volume. Non-positive inputs are out of domain and saturate to a large negative value rather than returning a meaningful number.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let level = Math.floor(Math.log(score)) # diminishing returns on score
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/math/math-pow.md
Normal file
22
docs/language/math/math-pow.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: math-pow
|
||||
name: Math.pow
|
||||
category: math
|
||||
kind: namespace-method
|
||||
tokens: Math.pow
|
||||
sig: Math.pow(base, exp) -> fixed
|
||||
tip: base raised to a fixed exponent.
|
||||
order: 31
|
||||
ns: Math
|
||||
member: pow
|
||||
---
|
||||
|
||||
Returns <code>base<sup>exp</sup></code> for <code>base > 0</code> and any fixed <code>exp</code>, computed in Q16.16 as <code>2<sup>exp·log₂base</sup></code> — so non-integer exponents work (<code>Math.pow(x, 0.5)</code> is a square root, though <code>Math.sqrt</code> is cheaper and exact) and it stays bit-identical on every platform. Use it for gameplay curves that need a tunable exponent: damage falloff, easing shaped by a designer-set power, or compound-growth economies. Keep the result inside the fixed range (magnitudes below <code>32768</code>); combinations that exceed it saturate.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let falloff = Math.pow(0.9, distance) # each unit keeps 90% of the effect
|
||||
}
|
||||
}
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue