Math, time and randomness¶
Points and vectors, random numbers and noise, easing and tweens, and the two clocks. Common
std.math functions (sqrt, sin, cos, atan2, clamp, floor, …) come with
from create import *, as in Processing.
Points and vectors¶
Create has two types for a pair of numbers, chosen by role:
- A location is a
Point2D:canvas.circle(position, r),context.mouse.position. - A displacement is a
Vector2D:translate(delta), a velocity. - An extent is a scalar:
w,h,r.
# A fragment.
var position = Point2D(0, 0)
var velocity = Vector2D.from_angle(PI / 4, 100) # 100 units a second, up and to the right
position = position + velocity * context.time.delta # a point moved by a vector
var to_mouse = context.mouse.position - position # point minus point is a vector
var speed = to_mouse.limit(300) # at most 300 long
- Crossing between them is an explicit constructor,
Vector2D(p)orPoint2D(v), never an implicit one. Point2Ddeliberately lacksmagnitude,normalize,dot, scalar*, unary-andPoint2D + Point2D: those mean nothing for a location.- Only
Point2Dtakes a bare tuple implicitly, so(0, 0)fills aPoint2Dparameter. A vector literal names its type (p + Vector2D(1, 2)), andp - (1, 2)is the displacement from(1, 2), not a move. - Named vectors are
comptimeconstants:Vector2D.ZERO,ONE,UP,DOWN,LEFT,RIGHT(y is up, soDOWNis(0, -1)). - Angles are radians counter-clockwise from +x:
Vector2D.from_angle(a, length),v.angle(),v.rotate(a), withv.with_length(l)andv.limit(max)for speeds. Each returns a new vector, likenormalize.
Random numbers and noise¶
Random and Noise are both seeded, and both give values in [0, 1], but they answer different
questions:
# A fragment: both are fields, built in `create`.
var random = Random(42) # Random() seeds from the clock
var noise = Noise(42, feature_size=200)
# In `update`:
var x = self.random.float(-100, 100) # a fresh, independent sample each call
var height = self.noise.at(Point2D(x, 0)) # the same input always gives the same value
Randomis stateful (mut,Movable): each call is an independent sample.float(),float(low, high),int(low, high)andbool().Noiseis immutable (Copyable):at(...)is a pure function of its input, and nearby inputs give nearby values, for terrain, clouds or wobble.feature_sizedivides space only;at(position, time)leavestimefor the caller to scale.- Averaged octaves cluster around 0.5; stretch with
smoothstepfor contrast.
Easing and tweens¶
An Easing is a stateless curve over a 0-to-1 fraction: ease(Easing.OUT_CUBIC, t). A
Tween walks that fraction over a duration:
# A fragment: `slide` is a field, built in `create`.
var slide = Tween(-300, 0, 0.5, Easing.OUT_BACK) # from -300 to 0 over half a second
# In `update`:
if context.keyboard.pressed(Key.SPACE):
self.slide.play()
self.slide.update(context.time.delta) # every frame, or it never moves
canvas.rectangle((self.slide.value(), 0), 100, 100)
Each entity owns its own Tween. Frame-by-frame animation, a sprite sheet, is
Animation/Animator (in image), not a tween.
Time¶
There are two clocks:
context.timeis the run's clock: ticked by the loop, zero at the first frame, synthetic in headless runs.deltais the seconds since the last frame (move things by it), andelapsedthe seconds since the run started;delta_millisandelapsed_millisare the same in whole milliseconds.DateTime.now()is the computer's local wall clock, read once into consistent fields (year…millisecond). Take one reading per frame rather than calling it per field.