@flatkit/compiler 0.38.0 → 0.39.0
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.
|
@@ -27,7 +27,7 @@ Inside `object "Name" { … }`:
|
|
|
27
27
|
| `when held` | a long press |
|
|
28
28
|
| `when dropped on <Zone> [at pointer]` | released over a drop zone (see [drag & drop](#drag--drop)) |
|
|
29
29
|
|
|
30
|
-
Scene-wide: `when loaded { … }` (once), `every frame { … }` (each
|
|
30
|
+
Scene-wide: `when loaded { … }` (once), `every frame { … }` (each simulation step, 60 Hz — see [how a frame runs](#how-a-frame-runs)), `at frame <n> { … }`,
|
|
31
31
|
`label <frame> "name"`. These live at the TOP LEVEL of the program, outside any `object` block — inside
|
|
32
32
|
one they do nothing, and `--check` says so.
|
|
33
33
|
|
|
@@ -501,6 +501,45 @@ match Word1, Word2 onto Good, Bad {
|
|
|
501
501
|
It generates, per item, `<Item>_placed` / `<Item>_ok` / `<Item>_zone` state and the drag+drop handlers;
|
|
502
502
|
you keep the visual (`var <Item>_x`/`_y` + your channel expressions).
|
|
503
503
|
|
|
504
|
+
## How a frame runs
|
|
505
|
+
|
|
506
|
+
Three things happen, always in this order, and knowing it removes most "it reads the old value" surprises.
|
|
507
|
+
|
|
508
|
+
**1. Events, as they arrive.** A press, a move, a release or a key runs its handlers **at once**, between
|
|
509
|
+
two displays — not at the next step. At a release the order is: the gesture's outputs are written (drag
|
|
510
|
+
position, `link` target), then `when released`, then `when dropped on …` (in declaration order), then
|
|
511
|
+
`when clicked` if the press stayed a tap.
|
|
512
|
+
|
|
513
|
+
**2. Steps of the simulation.** `every frame { … }` runs at a **fixed 60 Hz**: one run is one step of
|
|
514
|
+
exactly 1/60 s, **whatever the `timeline` fps and whatever the display**. `timeline 30 …` only sets the
|
|
515
|
+
speed of the playhead: after 60 steps `clock` has advanced by 1 and `frame` by 30. The step has a name,
|
|
516
|
+
**`DT`** (= 1/60, in seconds), so an integration is written `v = v + a * DT` — never measure it from
|
|
517
|
+
`clock`. In a browser `clock`, `time` and `frame` follow REAL time, once per display, so two steps run in
|
|
518
|
+
the same display read the same `clock`; `clock - previous` is then the display's duration on the first and
|
|
519
|
+
`0` on the second. (`flatc --play` advances them by 1/60 per step, which is why a replay is exact.)
|
|
520
|
+
|
|
521
|
+
How many steps run before each display depends on the display: none or one at 120 Hz, one at 60 Hz, two at
|
|
522
|
+
30. When the display stalls (a tab in the background, a slow device) the player does **not** catch up: it
|
|
523
|
+
counts at most 0.25 s per display and runs at most 30 steps, dropping the rest. The simulation then runs
|
|
524
|
+
slower than the wall clock; it never jumps. Within a step, the scene's `every frame` runs first, then
|
|
525
|
+
those of the active symbols; `at frame <n>` scripts come after, in the same step.
|
|
526
|
+
|
|
527
|
+
**3. The picture.** Channel bindings (`x = px`, `opacity = lit`) are not statements that run: they are
|
|
528
|
+
read whenever something looks at the object — when it is drawn, when it is hit-tested, when a handler
|
|
529
|
+
reads `Target.x` — and always give the value of NOW. What is drawn between two steps is interpolated
|
|
530
|
+
between them, for smoothness; variables are never changed by that.
|
|
531
|
+
|
|
532
|
+
What follows from it:
|
|
533
|
+
|
|
534
|
+
- **A handler reads a derived value as the last step left it.** If `every frame { double = count * 2 }`
|
|
535
|
+
and a handler does `count = count + 1` then reads `double`, it reads the value from BEFORE its own
|
|
536
|
+
write. Two events between two steps: the second sees what the first *wrote*, not what `every frame`
|
|
537
|
+
derives from it. Derive in the handler what the handler needs, or make it a function (`fn`).
|
|
538
|
+
- **`flatc --play` gives every pointer event one step** (see [tooling](tooling.md#headless-play----play)),
|
|
539
|
+
as a real pointer does; `"settle": 0` replays two events with no step in between, which is the case above.
|
|
540
|
+
- **The step is guaranteed**; the number of steps per display is not. A rule that counts steps counts
|
|
541
|
+
sixtieths of a second of simulated time.
|
|
542
|
+
|
|
504
543
|
## See also
|
|
505
544
|
|
|
506
545
|
- The expression language and stdlib → **[Expressions & stdlib](expressions-and-stdlib.md)**
|
package/docs/dsl-gotchas.md
CHANGED
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
- A `.flatink` file splits in two: the **`scene { … }`** block (the VISUAL composition:
|
|
11
11
|
`path`/`circle`/`group`/`image`/`text`) and the **behavior** that follows (`object "Name"
|
|
12
12
|
{ … }`, `every frame`, timeline bindings). The two do NOT share the same grammar.
|
|
13
|
+
- **`every frame` is a 60 Hz simulation step, not a timeline frame.** One run is exactly 1/60 s —
|
|
14
|
+
the constant **`DT`** — whatever `timeline <fps>` says and whatever the display does; integrate with
|
|
15
|
+
`v = v + a * DT`, never with a `dt` measured from `clock`. Handlers run when their event arrives,
|
|
16
|
+
BEFORE the next step: a handler that reads a value derived in `every frame` reads the one of the last
|
|
17
|
+
step. The full order is in [how a frame runs](behavior-and-interactions.md#how-a-frame-runs).
|
|
13
18
|
|
|
14
19
|
## `object "X"` addresses an ANIMATABLE item — a group, instance, text or image
|
|
15
20
|
|
|
@@ -30,7 +30,8 @@ min max hypot clamp(x, lo, hi) lerp(a, b, t) mod(a, b) between(x, lo, hi)
|
|
|
30
30
|
rad(deg) deg(rad) turns(n)
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
Constants: `PI`, `TAU` (2π), `E
|
|
33
|
+
Constants: `PI`, `TAU` (2π), `E`, and **`DT`** — the duration of one `every frame` step, in seconds: 1/60,
|
|
34
|
+
whatever the timeline's fps (`v = v + a * DT`). See [how a frame runs](behavior-and-interactions.md#how-a-frame-runs).
|
|
34
35
|
|
|
35
36
|
> **`lerp` is the exponential smoother.** `lerp(v, target, k)` = `v + (target - v) * k` — so
|
|
36
37
|
> `niv = lerp(niv, target, 0.1)` in `every frame` eases `niv` toward `target` (no new helper needed; the
|
|
@@ -70,6 +71,38 @@ its index are expressions, only the element picked is evaluated, and it indexes
|
|
|
70
71
|
index is rounded; outside the table it is `NaN`, so the binding keeps its fallback). It is not a value on
|
|
71
72
|
its own: `[1, 2, 3]` without an index is an error — to keep a table, declare `var t = [1, 2, 3]`.
|
|
72
73
|
|
|
74
|
+
## Determinism
|
|
75
|
+
|
|
76
|
+
For someone replaying a simulation elsewhere (a Python replica, a test that compares to the bit), what can
|
|
77
|
+
be counted on. Numbers are IEEE 754 doubles, and an expression is evaluated in the order it is written,
|
|
78
|
+
one rounded operation at a time (no fused multiply-add).
|
|
79
|
+
|
|
80
|
+
- **Exact** — the same bits on every engine: the operators `+ - * / %` and the comparisons, and `abs`
|
|
81
|
+
`floor` `ceil` `round` `sign` `min` `max` `sqrt` `clamp` `lerp` `mod` `between` `rad` `deg` `turns`.
|
|
82
|
+
(`sqrt` is the IEEE square root, correctly rounded on every engine in use, although ECMAScript does not
|
|
83
|
+
formally demand it. `rad`, `deg` and `turns` multiply by the double `PI`: exact, provided the replica
|
|
84
|
+
does the same operations in the same order — `rad(d)` is `d * PI / 180`.)
|
|
85
|
+
- **Engine-dependent** — ECMAScript leaves the last bits to the implementation: `sin` `cos` `tan` `asin`
|
|
86
|
+
`acos` `atan` `atan2` `pow` `exp` `log` `hypot`. Two browsers usually agree, and nothing guarantees
|
|
87
|
+
it; a Python or C library need not agree with either. Integrated over thousands of steps, one bit
|
|
88
|
+
becomes a visible gap. A replica that must match to the bit uses its own polynomial for these, on both
|
|
89
|
+
sides.
|
|
90
|
+
|
|
91
|
+
Three spellings that differ from other languages:
|
|
92
|
+
|
|
93
|
+
- **`%`** is the remainder of the truncated division: it takes the sign of the LEFT operand (`-1 % 3` is
|
|
94
|
+
`-1`), and works on decimals (`3.25 % 12` is `3.25`). It is C's `fmod`, Python's `math.fmod` — not
|
|
95
|
+
Python's `%`. **`mod(a, b)`** is the positive one (`mod(-1, 3)` is `2`), Python's `%` for `b > 0`.
|
|
96
|
+
- **`round`** sends a half UP, toward +∞: `round(2.5)` is `3`, `round(-2.5)` is `-2`. Python's `round`
|
|
97
|
+
sends it to the even neighbour. An array index is rounded the same way.
|
|
98
|
+
- **`random()`** draws from a seeded generator made of integer operations only: the same seed gives the
|
|
99
|
+
same sequence on every engine. A replay is always seeded (`flatc --play`, seed `1`, or `--seed N`); a
|
|
100
|
+
player in a page draws from the browser unless its host passes `seed`.
|
|
101
|
+
|
|
102
|
+
Time: one step of `every frame` is exactly **`DT`** = 1/60 s — integrate with it. `clock`, `time` and
|
|
103
|
+
`frame` follow real time in a browser and are exact only in a replay (see
|
|
104
|
+
[how a frame runs](behavior-and-interactions.md#how-a-frame-runs)).
|
|
105
|
+
|
|
73
106
|
## Functions (`fn`)
|
|
74
107
|
|
|
75
108
|
Define reusable helpers — a **value** function (an expression) or a **procedure** (actions):
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flatkit/compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.0",
|
|
4
4
|
"description": "The FlatInk language (parser + AST) and compiler (.flatink → .flatpack). Ships the flatc CLI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Zwyk Studio",
|
|
@@ -57,9 +57,9 @@
|
|
|
57
57
|
"docs"
|
|
58
58
|
],
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@flatkit/
|
|
61
|
-
"@flatkit/
|
|
62
|
-
"@flatkit/types": "0.
|
|
60
|
+
"@flatkit/player": "0.39.0",
|
|
61
|
+
"@flatkit/engine": "0.39.0",
|
|
62
|
+
"@flatkit/types": "0.39.0"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
65
65
|
"skia-canvas": "^3.0.8 || ^4.0.0-rc7"
|
package/prompts/flatink-core.md
CHANGED
|
@@ -288,7 +288,9 @@ align top of "Bin" [offset dx,dy] // pin origin onto ano
|
|
|
288
288
|
Pure & numeric (no booleans: comparisons/logic yield `1`/`0`). Operators: `?: || && == != < > <= >=
|
|
289
289
|
+ - * / % - ! . [] fn()`. A lookup table can be written in place and indexed at once: `[10, 20, 30][i]`.
|
|
290
290
|
Built-ins: `sin cos tan asin acos atan atan2 abs sqrt pow exp log floor ceil round sign min max hypot
|
|
291
|
-
clamp(x,lo,hi) lerp(a,b,t) mod(a,b) between(x,lo,hi) rad(deg) deg(rad) turns(n)`. Constants `PI TAU E
|
|
291
|
+
clamp(x,lo,hi) lerp(a,b,t) mod(a,b) between(x,lo,hi) rad(deg) deg(rad) turns(n)`. Constants `PI TAU E`, and `DT` = 1/60 s: `every frame` is a
|
|
292
|
+
fixed 60 Hz step whatever the timeline's fps, so integrate with `v = v + a * DT`. Handlers run when their event
|
|
293
|
+
arrives, before the next step.
|
|
292
294
|
Reserved: `time` (seconds, **wraps** every `durationFrames`), `clock` (seconds, **monotone**), `frame`,
|
|
293
295
|
`value`, `mouse.x/y`, `keys.<Key>`, `self.*`, `<Name>.*`.
|
|
294
296
|
Packages: `use "collision" | "easing" | "gesture" | "feedback"`; functions are available bare and
|
package/prompts/flatink-lite.md
CHANGED
|
@@ -104,7 +104,7 @@ State/funcs: `var a = 0` · `var arr = fill(8,0)` (also as an ASSIGNMENT: `arr =
|
|
|
104
104
|
## Expressions
|
|
105
105
|
Pure numeric, no booleans (compare/logic → 1/0). Ops: `?: || && == != < > <= >= + - * / % - ! . [] fn()` · inline table `[10, 20, 30][i]`.
|
|
106
106
|
Funcs: `sin cos tan atan2 abs sqrt pow floor ceil round sign min max hypot clamp(x,lo,hi) lerp(a,b,t)
|
|
107
|
-
mod(a,b) between(x,lo,hi) rad(deg) deg(rad) turns(n)`. Const `PI TAU E
|
|
107
|
+
mod(a,b) between(x,lo,hi) rad(deg) deg(rad) turns(n)`. Const `PI TAU E` · `DT` = 1/60 s (the `every frame` step, fixed 60 Hz: `v = v + a * DT`).
|
|
108
108
|
Reserved: `time`(s, **wraps**) `clock`(s, **monotone**) `frame` `value` `mouse.x/y` `keys.<Key>` `self.*` `<Name>.*`.
|
|
109
109
|
Channels: `x y scaleX scaleY rotation opacity` (absolute) + `dx dy` (additive: `pos = at + (dx, dy)`).
|
|
110
110
|
Stateful easing: `spring <ch> "<target>" stiffness <0..1> damping <0..1>` · `smooth <ch> "<target>" k <0..1>`
|
package/prompts/role-coder.md
CHANGED
|
@@ -68,7 +68,7 @@ object "Dial" { spring rotation = aim { stiffness 0.08 damping 0.86 } } // smo
|
|
|
68
68
|
Pure numeric expressions (no booleans — logic/compares yield `1`/`0`). Operators `?: || && == != < >
|
|
69
69
|
<= >= + - * / % - ! . [] fn()`. Built-ins: `sin cos tan atan2 abs sqrt pow exp log floor ceil round
|
|
70
70
|
sign min max hypot clamp(x,lo,hi) lerp(a,b,t) mod(a,b) between(x,lo,hi) rad deg turns`. Constants
|
|
71
|
-
`PI TAU E
|
|
71
|
+
`PI TAU E`, `DT` (= 1/60 s: `every frame` is a fixed 60 Hz step, integrate with `v = v + a * DT`). Reserved: `time` (s), `frame`, `value`, `mouse.x/y`, `keys.<Key>`, `self.*`, `<Name>.*`.
|
|
72
72
|
|
|
73
73
|
Functions: `fn dist(ax,ay,bx,by) = hypot(ax-bx, ay-by)` (value) · `fn reset() { score = 0 }` (procedure).
|
|
74
74
|
|