@m0saic/knowledge 0.2.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.
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +7 -0
- package/docs/README.md +60 -0
- package/docs/file-formats/m0-iteration-protocol.md +120 -0
- package/docs/file-formats/m0p-and-custom-field.md +195 -0
- package/docs/handbook/README.md +27 -0
- package/docs/handbook/composition-arithmetic.md +278 -0
- package/docs/handbook/dsl-complexity.md +75 -0
- package/docs/handbook/dsl-rules.md +367 -0
- package/docs/handbook/feasibility-precision-quantization.md +591 -0
- package/docs/handbook/m0-construction-methods.md +201 -0
- package/docs/handbook/precision-tiers.md +84 -0
- package/docs/m0saic-thesis.md +95 -0
- package/docs/runtime/README.md +17 -0
- package/docs/runtime/cli-usage.md +372 -0
- package/docs/runtime/ffmpeg-expression-limits.md +117 -0
- package/docs/runtime/reduce-to-one.md +96 -0
- package/docs/skills/README.md +40 -0
- package/docs/skills/axis-and-geometry.md +103 -0
- package/docs/skills/dsl-stdlib-method-catalog.md +7 -0
- package/docs/skills/identity.md +123 -0
- package/docs/skills/labels-and-masks.md +170 -0
- package/docs/skills/m0saic-string-generation.md +251 -0
- package/docs/skills/more-atoms-not-bigger-atoms.md +77 -0
- package/docs/skills/overlay-semantics.md +194 -0
- package/docs/skills/parse-apis.md +79 -0
- package/docs/skills/passthrough-semantics.md +136 -0
- package/docs/skills/structural-construction.md +86 -0
- package/docs/skills/text-in-templates.md +126 -0
- package/docs/skills/zero-overlay-analysis.md +87 -0
- package/docs/templates/README.md +65 -0
- package/docs/templates/capability-templates.md +72 -0
- package/docs/templates/construction-strategy.md +329 -0
- package/docs/templates/data-pipeline.md +324 -0
- package/docs/templates/emission-patterns.md +130 -0
- package/docs/templates/geometry-recipes.md +248 -0
- package/docs/templates/layout-contract.md +168 -0
- package/docs/templates/output-resolution-tree.md +202 -0
- package/docs/templates/patterns/case-study-lessons.md +69 -0
- package/docs/templates/patterns/perf-authoring-rules.md +100 -0
- package/docs/templates/patterns/primitive-extraction-pattern.md +103 -0
- package/docs/templates/philosophy-and-contract.md +310 -0
- package/docs/templates/recursion-nested-rendering.md +138 -0
- package/docs/templates/reference/grid.md +104 -0
- package/docs/templates/reference/json-prop-type.md +169 -0
- package/docs/templates/reference/mosaic-color.md +81 -0
- package/docs/templates/reference/mosaic-placement-props.md +103 -0
- package/docs/templates/reference/prop-bindings.md +203 -0
- package/docs/templates/reference/template-flags.md +205 -0
- package/docs/templates/render-lifecycle.md +117 -0
- package/docs/templates/rendering-model-contract.md +392 -0
- package/docs/templates/standalone-pack-authoring.md +233 -0
- package/docs/templates/theming.md +81 -0
- package/docs/templates/ui-controls.md +150 -0
- package/package.json +37 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# Composition arithmetic — the number theory of nesting
|
|
2
|
+
|
|
3
|
+
> [`feasibility-precision-quantization.md`](feasibility-precision-quantization.md)
|
|
4
|
+
> is the math of ONE split. This is the math of splits STACKED — why some trees
|
|
5
|
+
> compose exactly all the way down while others hit a wall three levels in, why
|
|
6
|
+
> drift composes better than quantization, and where exactness is provably
|
|
7
|
+
> impossible so you stop chasing it. Everything here is decidable before rendering:
|
|
8
|
+
> it is all divisibility. Companion to [`precision-tiers.md`](precision-tiers.md)
|
|
9
|
+
> and https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/GCD.md (the single-asset encoder story).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 0. TL;DR
|
|
14
|
+
|
|
15
|
+
- A canvas axis is a **prime-factor budget**; every exact split spends factors, and
|
|
16
|
+
Ω(T) (prime factors with multiplicity) is the nesting-depth budget.
|
|
17
|
+
- Keep every split count **a divisor of its axis and 5-smooth** and the whole tree
|
|
18
|
+
stays 5-smooth forever — composition never hits a prime wall.
|
|
19
|
+
- **Quantization is hereditary poison**: a non-dividing split hands children
|
|
20
|
+
*coprime consecutive* cell sizes (≤1px visually, arithmetically junk). Drift is
|
|
21
|
+
the reverse: lossy to the eye, clean to the arithmetic.
|
|
22
|
+
- An m0 is **base × fiber**: split lines on a shared coarse lattice (costed,
|
|
23
|
+
visible, composable) + leaf-private placement (inset / cell-local masks / xExpr —
|
|
24
|
+
free, invisible, *unable* to break composition).
|
|
25
|
+
- **Nesting refines to the lcm; overlay adds.** Incompatible denominators go on
|
|
26
|
+
separate layers, never into one refined split.
|
|
27
|
+
- On a prime axis, **every exact interior line costs full precision** — theorem,
|
|
28
|
+
not tuning. Self-frame or drift; never emit the 100% string silently.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 1. The canvas is a prime budget
|
|
33
|
+
|
|
34
|
+
Write an axis as `T = 2^a · 3^b · 5^c · (rest)`. An **exact** split needs `N | T`
|
|
35
|
+
and produces cells of `T/N` — it *spends* factors from the multiset. Two numbers
|
|
36
|
+
describe the whole budget:
|
|
37
|
+
|
|
38
|
+
- **Ω(T)** — prime factors with multiplicity — the **depth budget**: how many
|
|
39
|
+
nontrivial exact splits can stack before cells reach 1px.
|
|
40
|
+
- **σ₀(T)** — divisor count — the **menu size**: how many exact split counts are
|
|
41
|
+
available at this node.
|
|
42
|
+
|
|
43
|
+
| dim | factorization | σ₀ (menu) | Ω (depth) |
|
|
44
|
+
|---:|---|---:|---:|
|
|
45
|
+
| 720 | 2⁴·3²·5 | 30 | 7 |
|
|
46
|
+
| 1080 | 2³·3³·5 | 32 | 7 |
|
|
47
|
+
| 1280 | 2⁸·5 | 18 | 9 |
|
|
48
|
+
| 1350 | 2·3³·5² | 24 | 6 |
|
|
49
|
+
| 1920 | 2⁷·3·5 | 32 | 9 |
|
|
50
|
+
| 2160 | 2⁴·3³·5 | 40 | 8 |
|
|
51
|
+
| 3840 | 2⁸·3·5 | 36 | 10 |
|
|
52
|
+
| 4320 | 2⁵·3³·5 | 48 | 9 |
|
|
53
|
+
| 7680 | 2⁹·3·5 | 40 | 11 |
|
|
54
|
+
| *997 (prime)* | *—* | *2* | *1* |
|
|
55
|
+
|
|
56
|
+
**Spend order matters.** Split 1080 into 8 and the cells are `135 = 3³·5` — you are
|
|
57
|
+
out of 2s *forever*; nothing below can ever halve exactly. Split into 9 and the
|
|
58
|
+
cells are `120 = 2³·3·5` — still rich on every prime. Spend the scarce prime late;
|
|
59
|
+
balance the spend across levels.
|
|
60
|
+
|
|
61
|
+
**The hereditary-smoothness invariant.** Divisors of a 5-smooth number are 5-smooth.
|
|
62
|
+
So if the root axes are 5-smooth and *every split count divides its axis*, every
|
|
63
|
+
cell in the entire tree is 5-smooth — the recursion never lands on a prime wall. A
|
|
64
|
+
generator can enforce this mechanically: track the remaining factor multiset per
|
|
65
|
+
node; a child template's exactness needs are a sub-multiset test.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 2. The practical canvas lattice
|
|
70
|
+
|
|
71
|
+
m0 is general-purpose, but this renderer's delivery targets are ~all **5-smooth**
|
|
72
|
+
(video standards descend from 2^a·3^b·5^c tilings). That makes the practical set a
|
|
73
|
+
family of shared lattices — measured, not vibes:
|
|
74
|
+
|
|
75
|
+
| canvas family | per-axis gcd | meaning |
|
|
76
|
+
|---|---:|---|
|
|
77
|
+
| modern core: 1080 / 1920 / 2160 / 3840 / 4320 / 7680 (16:9, 9:16, 1:1) | **120** | any split total dividing 120 is pixel-exact on every axis in the family |
|
|
78
|
+
| + 720p (1280, 720) | 40 | |
|
|
79
|
+
| + 4:5 social (1350) | 10 | the truly universal pitch |
|
|
80
|
+
|
|
81
|
+
- **The universal basis menu for the modern core is `divisors(120)`** — sixteen
|
|
82
|
+
values: 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 24, 30, 40, 60, 120. The absences
|
|
83
|
+
teach as much as the entries: **7 divides no standard dimension** (a 7-split is
|
|
84
|
+
never exact anywhere — fine as a small-basis ratio fill, never for registration),
|
|
85
|
+
and **16 divides the widths but not 1080** (a 16-col split is exact where a
|
|
86
|
+
16-row split quantizes).
|
|
87
|
+
- **Exactness lifts by integer scaling.** 4K = 2× 1080p exactly, 8K = 4×. Prove a
|
|
88
|
+
layout exact at an aspect family's base size and every larger member is free.
|
|
89
|
+
- **Hostile strays exist** — 1.91:1 social lands on `566 = 2·283` (283 prime).
|
|
90
|
+
Don't design for them; route them through §6.
|
|
91
|
+
- A basis cap of **120 is the family gcd**, not folklore — a ≤120-basis template is
|
|
92
|
+
exact across the whole modern core. Persisted/portable m0 should draw totals from
|
|
93
|
+
`divisors(family gcd)`; a runtime template (`(props, canvas) → m0`) can use
|
|
94
|
+
`divisors(actual axis)` — enumerating them is `O(σ₀)` ≈ 30–40 candidates, free.
|
|
95
|
+
- **This is enforced, not advised (2026-09-16):** the `latticeSmooth` template
|
|
96
|
+
convention (throw) holds every split count above 12 to 5-smooth, at the hinted
|
|
97
|
+
canvas and the sweep canvases; `m0saic doctor` applies it to external packs
|
|
98
|
+
(declarations: [`../templates/reference/template-flags.md`](../templates/reference/template-flags.md)).
|
|
99
|
+
The number theory lives in `@m0saic/template-utils` `lattice/`: `gcd`, `lcm`,
|
|
100
|
+
`lcmAll`, `roughPart`, `isSmooth`, `factorize`/`formatFactors`, `divisors`,
|
|
101
|
+
`smoothDivisors`, `ceilToSmooth`/`floorToSmooth`, `nearestSmooth`,
|
|
102
|
+
`FAMILY_GCD = 120`, `FAMILY_BASIS_MENU = divisors(120)`, `latticeWeights`,
|
|
103
|
+
`quantizedSections`, and the scanner/report pair `splitCounts(m0)` /
|
|
104
|
+
`latticeReport(m0s)` / `latticeViolations(m0s, { canvas, allow, physicalCanvas })`.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 3. Quantization is hereditary poison
|
|
109
|
+
|
|
110
|
+
The inverted intuition, and the single most load-bearing fact in this chapter.
|
|
111
|
+
|
|
112
|
+
A non-dividing N-split looks nearly harmless — cells of `⌊T/N⌋` and `⌈T/N⌉`, spread
|
|
113
|
+
≤1px (§3 of the feasibility doc). But consecutive integers are **coprime**: the
|
|
114
|
+
children inherit arithmetically junk axes no matter how rich T was.
|
|
115
|
+
`1080/7 → 154 = 2·7·11 and 155 = 5·31.` The eye can't see the pixel; the child that
|
|
116
|
+
inherits a 155px axis can't split it.
|
|
117
|
+
|
|
118
|
+
Drift does the opposite. A gcd-snapped split puts every line on the `d`-lattice, so
|
|
119
|
+
every band is a **multiple of d** — children inherit d's factors, wealth instead of
|
|
120
|
+
poison.
|
|
121
|
+
|
|
122
|
+
> **Drift is lossy to the eye but clean to the arithmetic; quantization is clean to
|
|
123
|
+
> the eye but poisons descendants.** For anything meant to be composed, the
|
|
124
|
+
> arithmetic cost dominates.
|
|
125
|
+
|
|
126
|
+
Why the RATIO doctrine survives this: a **small-basis ratio child needs no
|
|
127
|
+
inheritance at all** — an N-slot split has cell spread ≤1px on ANY axis, hostile or
|
|
128
|
+
not. That robustness (not exactness) is what the audit's slope ≈ 0 actually
|
|
129
|
+
measures. So the division of labor is: **exactness for what must REGISTER** (across
|
|
130
|
+
siblings, to pixels), **small-basis robustness for what merely FILLS**. (The one
|
|
131
|
+
ratio danger zone is interleaved thin-line weights, where boundary error compounds —
|
|
132
|
+
feasibility doc §3a.)
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 4. Base × fiber — the composability algebra
|
|
137
|
+
|
|
138
|
+
Every m0 placement decomposes into two layers of meaning:
|
|
139
|
+
|
|
140
|
+
- **The base** — split lines on a shared coarse lattice. Costed in slots/chars,
|
|
141
|
+
visible to editors and validators, the thing children subdivide. Composition
|
|
142
|
+
happens HERE and only here.
|
|
143
|
+
- **The fiber** — per-leaf freedom *inside* a cell: `placement.inset`, mask paths in
|
|
144
|
+
cell-local coordinates, cell-local text, `xExpr`/`yExpr`. The fiber is
|
|
145
|
+
structurally free (zero chars, zero precision) and **leaf-private: fibers cannot
|
|
146
|
+
interact across leaves, so nothing in the fiber can break composition** — that is
|
|
147
|
+
a guarantee, not a heuristic.
|
|
148
|
+
|
|
149
|
+
The 2026-07 rebuild patterns are all one move (table):
|
|
150
|
+
|
|
151
|
+
| pattern | base move | fiber move |
|
|
152
|
+
|---|---|---|
|
|
153
|
+
| uniform grid (heatmap v2) | gutterless `grid()` | gap as `latticeCellInset` (`gridCellInset` is deprecated — approximate, see feasibility §3c) |
|
|
154
|
+
| independent chrome (stat-card) | gcd-snapped `placeOptimizedRects` | (accepted drift; or inset-recovery) |
|
|
155
|
+
| curves / charts (kpi-card, line-chart, donut v2) | one ratio cell | mask paths in cell-local coords |
|
|
156
|
+
| many-mask soups (donut v4) | self-framed coarse `placeRects` | origin-relative sector paths |
|
|
157
|
+
| rect soups (`placeInsetRects` / `placeInsetPieces`) | divisor-pitch quantized cells | half-pixel-centered inset recovery |
|
|
158
|
+
|
|
159
|
+
**Engine fine print — the fiber floors.** `applyInsetToRect` (core) computes
|
|
160
|
+
`Math.floor(f · cellSize)`, and `floor((n/W)·W)` genuinely loses a pixel on real
|
|
161
|
+
pairs (verified: n=15/W=22, 13/23, 15/26 …). Exact-recovery insets must be
|
|
162
|
+
**half-pixel-centered**: emit `(n + 0.5)/W` (verified zero failures for all
|
|
163
|
+
`n < W ≤ 7680`). See (internal design history).
|
|
164
|
+
|
|
165
|
+
**The fiber's price is invisibility.** What lives in the fiber doesn't exist in the
|
|
166
|
+
string — previews must read `placement` to show it; editors can't grab it. Spend
|
|
167
|
+
fiber on what nothing else needs to see structurally.
|
|
168
|
+
|
|
169
|
+
Doctrine in one line: **registration geometry on the base; private detail in the
|
|
170
|
+
fiber.**
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 5. Overlay is a sum; nesting is an lcm
|
|
175
|
+
|
|
176
|
+
Forcing two line families into ONE split costs the **lcm** of their denominators;
|
|
177
|
+
putting them on separate overlay layers costs the **sum**. Lines at thirds and at
|
|
178
|
+
sevenths: one split needs a 21-slot basis; two layers need 3 + 7 = 10 slots and no
|
|
179
|
+
lcm ever happens. **lcm blowup is THE char-count killer; overlays are
|
|
180
|
+
lcm-avoidance** — priced in graph depth (the ~25-mask cliff and overlay-depth
|
|
181
|
+
warnings are that budget). `primitives/grid/v2` (fixed-px strips at proportional
|
|
182
|
+
overlay offsets) is exactly this trade, chosen correctly.
|
|
183
|
+
|
|
184
|
+
The "find the coarsest grid describing the most things" game, stated formally:
|
|
185
|
+
partition the required lines per axis into few groups, each on a coarse lattice,
|
|
186
|
+
minimizing `Σ_layers T/gcd(bands)` under the layer budget. Two consequences:
|
|
187
|
+
|
|
188
|
+
- **Per-axis cost is separable** (row slots + Σ per-band col slots, each term
|
|
189
|
+
depending only on its own axis's gcd) — so cross-axis joint optimization gains
|
|
190
|
+
≈ nothing. This resolves https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/GCD.md §14.1.
|
|
191
|
+
- The remaining unexplored optimization is **layer partitioning by divisor-class**
|
|
192
|
+
(put the lines divisible by 8 on one layer, the stubborn ones on another) —
|
|
193
|
+
`placeRects` currently packs layers by overlap only. This is the sharper form of
|
|
194
|
+
GCD.md §14.2.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## 6. Where exactness is impossible (stop chasing it)
|
|
199
|
+
|
|
200
|
+
- **The prime-axis theorem.** An interior split line at `p` on axis `T` makes
|
|
201
|
+
sections `(p, T−p)`; slots = `T/gcd(p, T)`. For prime `T`, `gcd(p, T) = 1` for
|
|
202
|
+
every interior `p` — **every exact interior line costs precision T, by any
|
|
203
|
+
method.** Inset can't help: insets ride cells, and the cell boundary IS the line.
|
|
204
|
+
The remaining moves: **(a) self-frame** — build the interior geometry in its own
|
|
205
|
+
P-divisible frame and letterbox it with a small-basis ratio split; zero *relative*
|
|
206
|
+
drift, ≤1px uniform translation of the whole frame (the donut-v4 move — handbook
|
|
207
|
+
§3c, fix six); **(b) spend per-line drift** (§7).
|
|
208
|
+
- **Inset feasibility.** Inset only shrinks, so a quantized cell must contain its
|
|
209
|
+
target; between two rects, the shared boundary needs a lattice point **inside the
|
|
210
|
+
gap**: possible iff the gap contains a multiple of P, guaranteed iff
|
|
211
|
+
`gap ≥ P − 1`. *The gap is the inset budget* — literally, in pixels.
|
|
212
|
+
- **Flush shared edges at coprime positions.** A structural line two siblings share
|
|
213
|
+
can't move into the fiber (both cells end AT it). Restructure, or drift it.
|
|
214
|
+
- **The pure-inset endpoint.** Pitch `P = T` degenerates to one slot with the inset
|
|
215
|
+
doing all placement: precision 1 on ANY canvas *including primes* — but one
|
|
216
|
+
overlay layer per rect and zero structural legibility. It is the far end of a
|
|
217
|
+
dial (`P ∈ divisors(T)`, with `placeRects` at `P = 1`), not a free lunch: the
|
|
218
|
+
basis knob trades slot count vs layer count vs how much of the design the string
|
|
219
|
+
still shows.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## 7. Drift, principled — bounded-denominator approximation
|
|
224
|
+
|
|
225
|
+
"Best position for a line at fraction φ with basis ≤ B" is the classical
|
|
226
|
+
best-rational-approximation problem, and **continued-fraction convergents**
|
|
227
|
+
(equivalently Stern–Brocot / Farey-mediant descent) are provably optimal. The
|
|
228
|
+
golden builders' Fibonacci weights are literally the convergents of φ — 2/3, 3/5,
|
|
229
|
+
5/8, 8/13, 13/21 — so a golden split at basis 21 is `[13, 8]` with error ~0.09%
|
|
230
|
+
(≈1px at 1080): optimal, not aesthetic.
|
|
231
|
+
|
|
232
|
+
Two search spaces, two tools:
|
|
233
|
+
|
|
234
|
+
- **gcd-snap** (GCD.md §6) — optimizes ALL lines jointly onto ONE lattice of a
|
|
235
|
+
KNOWN canvas. Use at the head / in encoders.
|
|
236
|
+
- **Convergents** — optimize ONE line's denominator with NO canvas in hand. Use
|
|
237
|
+
inside ratio primitives that must travel.
|
|
238
|
+
|
|
239
|
+
And remember §3: snapped geometry composes *better* than quantized geometry — the
|
|
240
|
+
drift budget is an investment in your descendants' axes.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## The agent playbook (in order)
|
|
245
|
+
|
|
246
|
+
1. **Composing tree?** Every split count divides its axis and is 5-smooth; watch
|
|
247
|
+
the factor budget (don't burn all the 2s early — 1080/8 = 135 ends halving
|
|
248
|
+
forever; 1080/9 = 120 stays rich) — the gate measures exactly this
|
|
249
|
+
(`latticeSmooth`); declare the honest exceptions on `template.lattice`.
|
|
250
|
+
2. **Persisted / portable m0** → totals from divisors of the family gcd
|
|
251
|
+
(`divisors(120)` for the modern core; 40 with 720p; 10 with 4:5). **Runtime
|
|
252
|
+
template** → divisors of the actual `ctx` axis.
|
|
253
|
+
3. **Registration on the base lattice; everything leaf-private in the fiber**
|
|
254
|
+
(insets half-pixel-centered; masks cell-local). If nothing else needs to see it
|
|
255
|
+
structurally, it belongs in the fiber.
|
|
256
|
+
4. **Incompatible denominators → separate overlay layers** (sum, not lcm), budgeted
|
|
257
|
+
by the mask cliff. Never refine one split to the lcm.
|
|
258
|
+
5. **Hostile axis or stubborn coprime line** → self-frame (relative exactness +
|
|
259
|
+
≤1px translation) or convergent-optimal drift. Never silently emit a
|
|
260
|
+
canvas-scale basis — that's the §3c laundering anti-pattern; the audit slope
|
|
261
|
+
catches it.
|
|
262
|
+
6. The toolkit is **divisibility, lcm, and rational approximation** — *not* Bézout:
|
|
263
|
+
splits subdivide; they never form integer combinations.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## See also
|
|
268
|
+
|
|
269
|
+
- [`feasibility-precision-quantization.md`](feasibility-precision-quantization.md) —
|
|
270
|
+
the per-split math this chapter stacks.
|
|
271
|
+
- [`precision-tiers.md`](precision-tiers.md) — the head/primitive (ABSOLUTE/RATIO)
|
|
272
|
+
decision this chapter's arithmetic underwrites.
|
|
273
|
+
- [`../templates/construction-strategy.md`](../templates/construction-strategy.md) —
|
|
274
|
+
how the patterns of §4 show up when authoring.
|
|
275
|
+
- https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/GCD.md — the single-asset encoder story (drift as gcd-discovery,
|
|
276
|
+
canvas destiny); §14.1–.2 are resolved in §5 here.
|
|
277
|
+
- (internal design history) — the rect-soup builder that motivated
|
|
278
|
+
the divisor-pitch and half-pixel-centering results.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# DSL Complexity Analysis
|
|
2
|
+
|
|
3
|
+
`@m0saic/dsl` exports lightweight complexity utilities for inspecting the
|
|
4
|
+
structural cost of an m0 string: how expensive is this layout to render, to
|
|
5
|
+
edit, and to subdivide? All functions operate via canonical-string scanning —
|
|
6
|
+
no geometry computation, no full tree parse.
|
|
7
|
+
|
|
8
|
+
> **Source of truth:** [https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/complexity/complexity.ts](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/complexity/complexity.ts).
|
|
9
|
+
|
|
10
|
+
## The public surface: two functions
|
|
11
|
+
|
|
12
|
+
| API | Validates? | Return type | Use when |
|
|
13
|
+
|---|---|---|---|
|
|
14
|
+
| `getComplexityMetricsFast(input)` | No | `ComplexityMetrics` (never null, never throws) | The default — editor, scoring, preflight, hot loops |
|
|
15
|
+
| `getFrameCount(input)` | Yes | `number \| null` (null = invalid input) | Single-metric query with strict input handling |
|
|
16
|
+
|
|
17
|
+
**These are the only complexity exports.** `getComplexityMetrics`,
|
|
18
|
+
`getPassthroughCount`, and `getNodeCount` were **removed** — the barrel comment
|
|
19
|
+
in [https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/complexity/index.ts](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/complexity/index.ts)
|
|
20
|
+
says so explicitly ("callers use `getComplexityMetricsFast`"); `getPrecisionCost`
|
|
21
|
+
is an internal helper, not exported. Every other metric is a **field** on the
|
|
22
|
+
`ComplexityMetrics` result, not a function:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
type ComplexityMetrics = {
|
|
26
|
+
frameCount: number; // rendered leaves (`1`/`F`) → composited layers: the render-cost signal
|
|
27
|
+
passthroughCount: number; // `0`/`>` leaves → implicit donation chains: the editor-cost signal (zero render cost)
|
|
28
|
+
nullCount: number; // `-` leaves — gaps; structurally simple
|
|
29
|
+
groupCount: number; // split containers
|
|
30
|
+
nodeCount: number; // all structural nodes — total tree size (completeness metric)
|
|
31
|
+
precisionCost: number; // maxSplitAny — min-resolution viability, orthogonal to the others
|
|
32
|
+
precision: M0Precision; // maxSplitX / maxSplitY / maxSplitAny
|
|
33
|
+
};
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
One canonicalization, one node-count scan, one precision scan — prefer the
|
|
37
|
+
aggregate whenever you need more than a frame count. `ComplexityMetrics` and
|
|
38
|
+
`M0Precision` live in [`types.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/types.ts).
|
|
39
|
+
|
|
40
|
+
## How the metrics relate
|
|
41
|
+
|
|
42
|
+
| Metric | Render cost | Editor cost | Precision |
|
|
43
|
+
|--------|:-----------:|:-----------:|:---------:|
|
|
44
|
+
| `frameCount` | **primary** | secondary | — |
|
|
45
|
+
| `passthroughCount` | none | **primary** | — |
|
|
46
|
+
| `nodeCount` | correlates | correlates | — |
|
|
47
|
+
| `precisionCost` | — | — | **primary** |
|
|
48
|
+
|
|
49
|
+
Independent dimensions: a layout can be cheap to render but hard to edit (few
|
|
50
|
+
frames, many passthroughs), expensive to render but easy to edit (many frames,
|
|
51
|
+
no passthroughs), or precision-constrained regardless of either.
|
|
52
|
+
|
|
53
|
+
## Two instructive examples
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
Layout A: 3(1,1,1) frameCount=3 passthroughCount=0
|
|
57
|
+
Layout B: 5(0,1,0,1,1) frameCount=3 passthroughCount=2
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Same render cost (3 composited frames); B's passthroughs create implicit
|
|
61
|
+
space-donation chains the editor must visualize and the user must reason about.
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
Nulls: 3(1,-,1) frameCount=2 passthroughCount=0 nullCount=1
|
|
65
|
+
Passthroughs: 3(1,0,1) frameCount=2 passthroughCount=1 nullCount=0
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Nulls consume space as empty gaps — structurally simple. Passthroughs donate
|
|
69
|
+
space forward — same frame count, higher editor complexity.
|
|
70
|
+
|
|
71
|
+
## Scale
|
|
72
|
+
|
|
73
|
+
These metrics only bite at scale: frame count matters ~20k+ (compositing
|
|
74
|
+
pipeline stress), passthroughs ~10k+ (deep donation graphs), precision when
|
|
75
|
+
split factors push the minimum resolution past the target output dims.
|