@motionscript/geography 0.1.0-alpha.6 → 0.1.0-alpha.8

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 (59) hide show
  1. package/CHANGELOG.md +104 -1
  2. package/NOTICE +15 -14
  3. package/README.md +78 -14
  4. package/dist/border/format.d.ts +73 -0
  5. package/dist/border/format.d.ts.map +1 -0
  6. package/dist/border/format.js +68 -0
  7. package/dist/border/format.js.map +1 -0
  8. package/dist/border/geo-border.d.ts +103 -0
  9. package/dist/border/geo-border.d.ts.map +1 -0
  10. package/dist/border/geo-border.js +203 -0
  11. package/dist/border/geo-border.js.map +1 -0
  12. package/dist/border/geometry.d.ts +40 -0
  13. package/dist/border/geometry.d.ts.map +1 -0
  14. package/dist/border/geometry.js +382 -0
  15. package/dist/border/geometry.js.map +1 -0
  16. package/dist/border/index.d.ts +15 -0
  17. package/dist/border/index.d.ts.map +1 -0
  18. package/dist/border/index.js +15 -0
  19. package/dist/border/index.js.map +1 -0
  20. package/dist/border/loader.d.ts +31 -0
  21. package/dist/border/loader.d.ts.map +1 -0
  22. package/dist/border/loader.js +87 -0
  23. package/dist/border/loader.js.map +1 -0
  24. package/dist/border/registry.d.ts +64 -0
  25. package/dist/border/registry.d.ts.map +1 -0
  26. package/dist/border/registry.js +163 -0
  27. package/dist/border/registry.js.map +1 -0
  28. package/dist/border/selection.d.ts +21 -0
  29. package/dist/border/selection.d.ts.map +1 -0
  30. package/dist/border/selection.js +86 -0
  31. package/dist/border/selection.js.map +1 -0
  32. package/dist/border/simplify.d.ts +12 -0
  33. package/dist/border/simplify.d.ts.map +1 -0
  34. package/dist/border/simplify.js +116 -0
  35. package/dist/border/simplify.js.map +1 -0
  36. package/dist/browser/THIRD_PARTY_LICENSES.txt +58 -0
  37. package/dist/browser/index.js +2 -0
  38. package/dist/browser/index.js.map +7 -0
  39. package/dist/browser/manifest.json +23 -0
  40. package/dist/index.d.ts +3 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +3 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/nodes.d.ts +15 -0
  45. package/dist/nodes.d.ts.map +1 -0
  46. package/dist/nodes.js +16 -0
  47. package/dist/nodes.js.map +1 -0
  48. package/package.json +46 -8
  49. package/registry.json +16 -0
  50. package/src/border/format.ts +147 -0
  51. package/src/border/geo-border.ts +268 -0
  52. package/src/border/geometry.ts +463 -0
  53. package/src/border/index.ts +14 -0
  54. package/src/border/loader.ts +97 -0
  55. package/src/border/registry.ts +201 -0
  56. package/src/border/selection.ts +95 -0
  57. package/src/border/simplify.ts +110 -0
  58. package/src/index.ts +2 -0
  59. package/src/nodes.ts +16 -0
package/CHANGELOG.md CHANGED
@@ -1,7 +1,31 @@
1
1
  # @motionscript/geography
2
2
 
3
+ ## 0.1.0-alpha.8
4
+
5
+ ## 0.1.0-alpha.7
6
+
3
7
  ## 0.1.0-alpha.6
4
8
 
9
+ ### Minor Changes
10
+
11
+ - 163b708: Node types are components: one spec object handed to `component2d`, `component3d` or `componentAudio`, with no classes, decorators or JSX.
12
+
13
+ - **Writing a node type.** A component is one object literal: `key`, `state` (literals and `Types`), `defaults`, `commands`, `children`, `allowedChildren`/`allowedParents`, `provide`, `load`, and its tree's own fields (`measure`, `layout`, `render` for 2D). Behaviour comes from modules you spread in and call: `Paint`, `Flow`, `Shapes`, `Typeface`, `TextLayout`, `Aim`, `Material`, `View3D`. ADR 0007 and the custom-node docs cover it.
14
+ - **Building nodes in code** uses calls: `Rect({ fill: 'tomato', children: [Text({ text: 'Hi' })] })`, or `new Rect({...})` for the node itself.
15
+ - **Every built-in and every kit node is a component**, under the same document key. Code that tested `instanceof` a class now uses the component's `.is`, or `instanceof X.node`. Changed members:
16
+ - `SurfacePlot3d.valueAt` and `FunctionPlot.valueAt` are now the functions `surfaceValueAt` and `curveValue`.
17
+ - A system's `coordinates` is replaced by `planeMap`/`axesMap`/`systemMap`.
18
+ - `PieChart.isSelected` is replaced by `isSliceSelected`.
19
+ - `ItemGrid` is a kit module (`ItemGrid.isGrid`, `ItemGrid.define`), not a base class.
20
+ - **The sdk no longer exports the class surface:**
21
+ - the decorators: `node`, `property` and every `*Property`, `command`, `withdraw`, `driveCommand`;
22
+ - the authoring bases: `PaintNode`, `ShapeNode`, `TextBase`, `Mesh3DBase`, `Canvas3DBase` and the `*Base` geometry classes;
23
+ - `asCommand`, `compose`, `drive` and `declarePaints` from `@motionscript/sdk/component`;
24
+ - the `jsx-runtime` and `jsx-dev-runtime` subpaths.
25
+ - `Node`, `Node2D`, `Node3D` and `NodeAudio` remain as types.
26
+ - **Editor metadata leaves the engine.** The kits no longer declare `parentKey`, `forkable`, `layout`, `seed`, `within`, `beside` or `closed`; a host keeps those in its own configs. `describe()` now reports `holds`, the trees a type takes children from, and the placement checks use it, so a 2D viewport of your own that takes 3D children places them.
27
+ - A command called from code with no argument runs, and a refused child is told what its parent takes (`allowedChildren`) or where it goes (`allowedParents`).
28
+
5
29
  ## 0.1.0-alpha.5
6
30
 
7
31
  ### Patch Changes
@@ -29,16 +53,95 @@
29
53
  - `@motionscript/ui` is removed: its `Switch`, `Button`, `Window` and `Cursor` nodes, and the `ControlNode` base class, no longer ship. They are plain nodes over `@motionscript/sdk`, so a project that needs one writes its own and shapes it to taste.
30
54
  - `@motionscript/patterns` is new. It holds the pattern nodes (`LineGridPattern`, `DotGridPattern`, `GridPattern`, `ViewportPattern`) that `@motionscript/core` and `@motionscript/sdk` no longer ship.
31
55
 
56
+ ### Patch Changes
57
+
58
+ - ad28707: Component kit fixes.
59
+
60
+ - Charts: a bar chart maps its columns across the plot panel as laid out (and takes the line chart's top headroom), so a column ends on its marker's grid line. A pie chart's wedges, labels and legend follow `data` and the palette once they are set. A restated value-axis or y/x-axis `range` or `ticks` in `to()` rescales continuously from the axis as drawn, instead of cutting at the midpoint. Without a legend (or without series) the entrance keeps that beat's share, so the call-out and legend land where the slot lays them out. A constant line series gets a scale with width, a `NaN` or infinite cell reads as 0, and tick captions gain decimals when the step is finer than 0.1.
61
+ - Code: the compiled highlighter is cached per style object, so two styles sharing a name no longer colour alike. A `CodeRanges` chain serialises as its ranges, so a stored `highlight` target selects what it did before it was saved.
62
+ - LaTeX: seeking from inside a morph to a time before it restores the source formula instead of leaving the last morph frame.
63
+ - Geo: custom border rings are wound for d3, so a GeoJSON counter-clockwise ring draws as the small region it encloses; a great-circle path between antipodal points runs over the north pole instead of wherever rounding lands.
64
+ - Audio: bottom-aligned waveform bars stand on the floor of the box instead of hanging from the top.
65
+ - `Code` and `Latex` take an optional props bag, so `new Registry({ nodes: NODES })` and `createBrowserEngine({ nodes: NODES })` type-check without a cast.
66
+
67
+ - e818c6e: A `Globe` fades its markers, holds its camera and map to the ranges it advertises, and refuses 3D children. `GeoBorder` documents that `start` and `end` trim the fill too.
68
+
69
+ - A marker's `opacity` was parsed and clamped and then only tested against 0; a marker at 0.5 drew fully opaque. It now reaches the material: instance colours carry no alpha, so markers are drawn together per distinct opacity, and a list of opaque markers is still one draw.
70
+ - `zoom` below 1.02 drew as 1.02, so an `orbitTo` to the advertised minimum of 0.2 appeared to stall. `zoom` is now stored at 1.02 or more and `orbitTo` advertises 1.02. `elevation` is stored within ±89, and `resolution` is advertised as 256 to 2048 and a larger value bakes a 2048-wide map instead of allocating a 16384-wide one.
71
+ - A 3D node parented to a `Globe`, which was validated and never drawn, is now refused. The inherited viewport settings (`showGrid`, `shadows`, `tone`, `exposure`, `post`) stay listed and stay inert, and the kit README says so.
72
+ - The kit README states that the `markers` and `arcs` lists are JSON text and snap rather than tween, and that `GeoBorder`'s `start` and `end` trim the fill and overlay as well as the outline.
73
+
74
+ - 7c07443: `to()` maps a prop's destination once, so a mapper that is not idempotent no longer moves the prop twice as far.
75
+
76
+ - A numeric prop behind a mapper was interpolated between stored values and then written through the setter, which ran the mapper on every step again: with `mapper: (v) => v * 2`, `to({ dbl: 10 })` ended at 40 and was at 20 halfway, and a second tween started from the wrong place. A string that snaps at the end was mapped twice the same way. The destination is now mapped once and every step is stored as it is, which is what a prop with a `tween` already did. A clamp behaves as before for any ease that stays between the two ends.
77
+ - An ease that overshoots (`back`, `elastic`) is no longer held by a mapper alone. A prop that must stay in range says so in its `tween`, as `Globe`'s `elevation` and `zoom` now do, so they still stop at 89° and 1.02 under an overshooting ease.
78
+
32
79
  ## 0.1.0-alpha.3
33
80
 
34
81
  ### Patch Changes
35
82
 
36
- - 98ea74b: `@motionscript/geo-data` packs from a fresh checkout. `data/` is generated rather than committed, and `prepack` only verified it, so a publish from a clean tree, as the release workflow's is, failed. `prepack` now generates `data/` when it is missing, from the Natural Earth sources `sources.lock.json` pins by tag and sha256, and verifies it either way. A downloaded source is cached only once its checksum matches.
83
+ - 59c9944: A subclass that redeclares a `@property` or a `@command()` its base already declares now overrides it. `getPropertyMeta` and `getCommandMeta` kept the base's declaration and dropped the subclass's, so a redeclared default, mapper, tween or editor option, or a command's `args` and `name`, never took effect. The most-derived declaration of each key now wins, whole, in the position the base gave it, and an inherited key the subclass does not redeclare is kept as declared. `Registry.describe`, a node type's command table and a new instance's values all read that one declaration.
84
+
85
+ A bare `Globe` now starts at its own camera and key light (orbit −10, elevation 25, zoom 2.8, ambient 0.55, key 1.9) rather than `Canvas3D`'s (35, 20, 12, 0.45, 2.4), which framed it as a small disc twelve radii away. A bare `Protein` likewise starts at elevation 18, zoom 2.6, ambient 0.55 and key 1.6. `describe("graph3d")` no longer reports `Canvas3D`'s defaults for `orbit`, `elevation` and `zoom`, which a graph derives from `camera.initialPosition`.
37
86
 
38
87
  ## 0.1.0-alpha.2
39
88
 
40
89
  ## 0.1.0-alpha.1
41
90
 
91
+ ### Minor Changes
92
+
93
+ - 59cecba: Build on `@motionscript/sdk` instead of `@motionscript/core`. The peer dependency is now `@motionscript/sdk`, every import goes through it (and `@motionscript/sdk/component` for the component machinery), and the browser build leaves `@motionscript/sdk` an import, so an import map names it once. A component copied with `ms add` imports the SDK too, and its registry item depends on `@motionscript/sdk` rather than `@motionscript/core`. `@motionscript/charts/kit` re-exports `@motionscript/sdk/component` where it re-exported `@motionscript/core/component`.
94
+
42
95
  ## 0.1.0-alpha.0
43
96
 
44
97
  Initial release.
98
+
99
+ ## Before the merge
100
+
101
+ The entries below belong to the data-only `@motionscript/geography` that this package
102
+ absorbed. The kit's own history is above, under its former name `@motionscript/geo`.
103
+
104
+ ### 0.1.0-alpha.7
105
+
106
+ ### 0.1.0-alpha.6
107
+
108
+ ### 0.1.0-alpha.5
109
+
110
+ #### Patch Changes
111
+
112
+ - da6f97a: Every package ships a `NOTICE`, and a browser build lists what it bundles.
113
+
114
+ - Each tarball carries the repository `NOTICE`: the copyright line and the MIT attributions for the noise functions in `skia-render`'s fractal-noise fill (David Hoskins' "Hash without Sine", Inigo Quilez's 2D simplex noise).
115
+ - `@motionscript/canvaskit` ships `THIRD_PARTY_NOTICES.txt` with the licences of the libraries compiled into the binary: FreeType, HarfBuzz, ICU 74, zlib, libpng, Brotli, WOFF2, Emscripten and musl.
116
+ - `dist/browser/manifest.json` gains `thirdParty`, the packages the browser build bundled (`name`, `version`, `license`), and their licence and NOTICE files are written to `dist/browser/THIRD_PARTY_LICENSES.txt`. `@motionscript/browser` bundles mediabunny (MPL-2.0, unmodified; source at https://github.com/Vanilagy/mediabunny), esbuild-wasm and three; `@motionscript/math` bundles MathJax.
117
+
118
+ - 1778355: The published packages carry what a user needs and say what they are.
119
+
120
+ - `@motionscript/code` no longer ships `dist/tests/diff.bench.*`, and `@motionscript/math` no longer ships `dist/tests/harness.*` or `mathjax.setup.*`: test helpers under a `tests` folder are left out of every package's build.
121
+ - `@motionscript/sdk`'s description no longer repeats core's, and `@motionscript/math`'s names the coordinate systems, plots, 3D graphs and network graphs it has as well as formulas.
122
+ - The READMEs of `@motionscript/canvaskit`, `@motionscript/charts`, `@motionscript/geo`, `@motionscript/grids`, `@motionscript/skia-render` and `@motionscript/geography` say what to install, and `@motionscript/sdk`'s example now opens the document in an engine and plays it. Links between packages point at the repository, so they work on npm.
123
+
124
+ ### 0.1.0-alpha.4
125
+
126
+ #### Minor Changes
127
+
128
+ - 3eefc7e: Package reshuffle.
129
+
130
+ - `@motionscript/layout-grid` is now `@motionscript/grids` (`@motionscript/grids/kit`, and `packages/components/grids`), and its `LayoutGrid` base class is `ItemGrid` (`ItemGridProps`, `ITEM_GRID_TYPES`). `RectangularGrid`, `RadialGrid`, `PathGrid` and `SphereGrid` keep their names and document keys. Update the dependency, the imports, and any `@motionscript/layout-grid/kit` specifier a copied component carries.
131
+ - `@motionscript/geo-data` is now `@motionscript/geography`. The data format on disk (`data/v1/…`) is unchanged, so `@motionscript/geo` reads it as before and a host that serves `@motionscript/geo-data/data` serves `@motionscript/geography/data` the same way.
132
+ - `@motionscript/ui` is removed: its `Switch`, `Button`, `Window` and `Cursor` nodes, and the `ControlNode` base class, no longer ship. They are plain nodes over `@motionscript/sdk`, so a project that needs one writes its own and shapes it to taste.
133
+ - `@motionscript/patterns` is new. It holds the pattern nodes (`LineGridPattern`, `DotGridPattern`, `GridPattern`, `ViewportPattern`) that `@motionscript/core` and `@motionscript/sdk` no longer ship.
134
+
135
+ ### 0.1.0-alpha.3
136
+
137
+ #### Patch Changes
138
+
139
+ - 98ea74b: `@motionscript/geo-data` packs from a fresh checkout. `data/` is generated rather than committed, and `prepack` only verified it, so a publish from a clean tree, as the release workflow's is, failed. `prepack` now generates `data/` when it is missing, from the Natural Earth sources `sources.lock.json` pins by tag and sha256, and verifies it either way. A downloaded source is cached only once its checksum matches.
140
+
141
+ ### 0.1.0-alpha.2
142
+
143
+ ### 0.1.0-alpha.1
144
+
145
+ ### 0.1.0-alpha.0
146
+
147
+ Initial release.
package/NOTICE CHANGED
@@ -4,24 +4,25 @@ Copyright 2026 Motionscript LLC
4
4
  This product includes the third-party code below, each part under the MIT
5
5
  License reproduced at the end of this file.
6
6
 
7
- - packages/skia-render/src/fills/fractal-noise.ts: `hash2` is "Hash without
8
- Sine" by David Hoskins (https://www.shadertoy.com/view/4djSRW).
7
+ - packages/renderer/crates/renderer/src/shaders/prim.wgsl (`fn_hash2`):
8
+ "Hash without Sine" by David Hoskins (https://www.shadertoy.com/view/4djSRW).
9
9
  Copyright (c) 2014 David Hoskins.
10
10
 
11
- - packages/skia-render/src/fills/fractal-noise.ts: `simplexNoise` follows
12
- "Noise - simplex - 2D" by Inigo Quilez (https://www.shadertoy.com/view/Msf3WH).
13
- Copyright (c) 2013 Inigo Quilez.
11
+ - packages/renderer/crates/renderer/src/shaders/prim.wgsl (`simplex_noise`): "Noise - simplex - 2D" by Inigo Quilez
12
+ (https://www.shadertoy.com/view/Msf3WH). Copyright (c) 2013 Inigo Quilez.
14
13
 
15
- The project website, which is in the source repository only and in no npm
16
- package, also includes MIT code: its landing-page background
17
- (packages/site/components/landing/GradientBackground.tsx) uses 3D simplex
18
- noise from webgl-noise by Ian McEwan, Ashima Arts
19
- (https://github.com/ashima/webgl-noise). Copyright (C) 2011 by Ashima Arts.
14
+ - packages/renderer/crates/render3d (in @motionscript/renderer's WebAssembly):
15
+ its shaders (src/shaders/mesh.wgsl) port three.js r185's mesh programs and
16
+ src/geometry.rs its primitive generators (https://threejs.org).
17
+ Copyright (c) 2010-2026 three.js authors.
20
18
 
21
- @motionscript/canvaskit is a build of Skia's CanvasKit (BSD-3-Clause,
22
- Copyright (c) 2011 Google Inc.) and carries FreeType, HarfBuzz, ICU, zlib,
23
- libpng, Brotli, WOFF2, Emscripten and musl; their notices are in
24
- packages/canvaskit/THIRD_PARTY_NOTICES.txt.
19
+ @motionscript/renderer's WebAssembly (dist/ms_renderer_bg.wasm) compiles in
20
+ Rust crates under MIT, Apache-2.0, Zlib and Unicode-3.0 terms (wgpu, naga,
21
+ harfrust, read-fonts, kurbo, wasm-bindgen and others); their notices are in
22
+ packages/renderer/THIRD_PARTY_NOTICES.txt.
23
+
24
+ MP3 export loads @mediabunny/mp3-encoder (MPL-2.0), a WebAssembly build of
25
+ the LAME MP3 encoder (LGPL, https://lame.sourceforge.io), on first use.
25
26
 
26
27
  The browser builds under dist/browser/ bundle each package's npm dependencies;
27
28
  dist/browser/THIRD_PARTY_LICENSES.txt in each package lists them with their
package/README.md CHANGED
@@ -1,24 +1,88 @@
1
1
  # @motionscript/geography
2
2
 
3
- Country and first-level subdivision outlines for Motion Script's `GeoBorder`
4
- ([`@motionscript/geo`](https://github.com/motionscript-dev/motionscript/tree/main/packages/components/geo)), as small JSON files keyed by ISO 3166
5
- code. Derived from [Natural Earth](https://www.naturalearthdata.com/) v5.1.2
6
- (public domain); see `CREDITS.md`.
3
+ Geography for [Motion Script](https://motionscript.dev): `GeoBorder`, country and
4
+ state outlines by ISO 3166 code. Hand `NODES` to the engine through your
5
+ project entry, and a document can name `geoBorder`:
7
6
 
8
- The package has no JavaScript entry point. Its `exports` are `./package.json` and
9
- `./data/*`, read by path from `node_modules` or served from a static host. The geo
10
- kit does not depend on it: the two meet only at the file format.
7
+ ```ts
8
+ // src/project.ts
9
+ import { NODES } from '@motionscript/geography';
10
+
11
+ export const nodes = NODES;
12
+ ```
13
+
14
+ `GeoBorder` draws nothing until a data source is registered; see
15
+ [its docs page](https://motionscript.dev/docs/components/geography/geo-border).
16
+
17
+ This package is written against [`@motionscript/sdk`](https://github.com/motionscript-dev/motionscript/tree/main/packages/sdk), which it
18
+ takes as a peer dependency.
11
19
 
12
20
  ## Install
13
21
 
14
22
  ```bash
15
- npm install @motionscript/geography
23
+ npm install @motionscript/geography @motionscript/sdk
16
24
  ```
17
25
 
18
- Or serve `data/` from a static host. Registering it as `GeoBorder`'s data source:
19
- [motionscript.dev/docs](https://motionscript.dev/docs).
26
+ ## GeoBorder
27
+
28
+ - **`start` and `end` trim the fill and the overlay as well as the outline.**
29
+ Each path is cut to the part of `[start, end]` that falls inside it, so a
30
+ filled border drawn on with `end` from 0 to 1 grows as partial polygons, each
31
+ closed by a straight chord, and a stroke-only one grows as a line. There is no
32
+ way to trim the stroke alone.
33
+ - **Fill and stroke are separate geometry.** The fill is each kept polygon; the
34
+ stroke draws each arc once, so a border two selected regions share is stroked
35
+ once, and `stroke.align` is forced to the centre. `clipSelf` is the layout box,
36
+ not the outline.
37
+ - **`detail` never refits.** The files store a simplification bucket per point,
38
+ `detail` is a draw-time threshold over it, and the projection is fitted at full
39
+ detail and cached apart from it, so tweening `detail` does not move the drawing.
40
+ - **`iso` is a list of codes**, as an array or text split on spaces, commas and
41
+ semicolons, upper-cased. A code is `AB` or `AB-XYZ`; one that is not, or that no
42
+ loaded file has, draws nothing and warns once. A bare country code draws the
43
+ country, or at `level: "state"` its first-level subdivisions (minus `remote`
44
+ ones when `frame` is `"mainland"`); a subdivision code is always itself.
45
+ - **Drawing never throws.** A node declares each file it needs as the asset
46
+ `geo:<key>` (gate `render`: layout does not wait for it, but an export loads the
47
+ files before the frame that needs them); a missing or failed file draws nothing.
48
+
49
+ ### Data sources
50
+
51
+ The files are this package's own `data/` (see [The data](#the-data)); the kit
52
+ reads them only through their format
53
+ (`v1/index`, `v1/countries/<ISO2>`, `v1/states/<ISO2>`, where `v1/states/US`
54
+ holds every `US-…` unit). The registry is process-wide, shared by every engine,
55
+ session and node:
56
+
57
+ ```ts
58
+ import { geoRegistry, geoUrlSource } from '@motionscript/geography';
59
+
60
+ geoRegistry.use(geoUrlSource('/geo')); // serves the *contents of* @motionscript/geography/data
61
+ geoRegistry.define({ 'XX-ATLANTIS': { shape: geojsonPolygon, name: 'Atlantis' } });
62
+ ```
63
+
64
+ - **Sources are asked in registration order; the first non-null answer wins.**
65
+ `null` means "not here" and a throw means "could not tell"; neither is cached
66
+ for good, and `loadGeoFile` never rejects. `geoUrlSource` treats HTTP 404 and 400
67
+ as absent. `geoLoaderSource(load)` wraps text, bytes or parsed JSON (for example
68
+ `fs.readFile`). With no source registered nothing loads and `GeoBorder` draws
69
+ nothing.
70
+ - **A defined shape wins over every source** and needs none. It is GeoJSON in
71
+ lon/lat, wound either way round, and is simplified by `detail` like any other.
72
+ - **Decoded files are never freed** while the process lives (`clearGeoFiles`
73
+ exists for tests), and `geoLoader(key)` returns one function per key, so any
74
+ number of nodes declare the same loader.
75
+
76
+ ## The data
77
+
78
+ The package also ships the outlines `GeoBorder` draws: country and first-level
79
+ subdivision outlines keyed by ISO 3166 code, as small JSON files derived from
80
+ [Natural Earth](https://www.naturalearthdata.com/) v5.1.2 (public domain; see
81
+ `CREDITS.md`). They are in `data/` and exported as `./data/*`, read by path from
82
+ `node_modules` or served from a static host. The kit's code does not import
83
+ them.
20
84
 
21
- ## The data is generated, never edited
85
+ ### The data is generated, never edited
22
86
 
23
87
  `data/` and `.cache/` are gitignored. `scripts/generate.mjs` removes `data/v1/`
24
88
  and writes it whole, from three Natural Earth GeoJSON files pinned in
@@ -37,9 +101,9 @@ node --max-old-space-size=8192 scripts/generate.mjs --update-lock # re-pin sou
37
101
 
38
102
  `prepack` (before `npm pack` and `publish`) generates `data/` when it is missing,
39
103
  then runs `verify`; the package has no `build`, `lint` or `typecheck`. The geo kit's
40
- committed fixture is `components/geo/src/border/tests/fixtures/v1/`.
104
+ committed fixture is `components/geography/src/border/tests/fixtures/v1/`.
41
105
 
42
- ## Layout and keys
106
+ ### Layout and keys
43
107
 
44
108
  ```
45
109
  data/v1/index.json every listed region: [code, name, nameNl, parent|null, first 0|1]
@@ -61,7 +125,7 @@ data/v1/states/<prefix>.json every subdivision whose code starts with <pr
61
125
  `remote`, `area` (steradians), `mainland` (polygon indices, or `null` when all),
62
126
  `areas` and `polygons` (rings of arc references; `~i` is arc `i` reversed).
63
127
 
64
- ## Invariants
128
+ ### Invariants
65
129
 
66
130
  - **One topology.** Every country and subdivision is stitched and quantised
67
131
  together (1e6 steps, one `transform` for every file), so a border two regions
@@ -0,0 +1,73 @@
1
+ /** The on-disk shape `@motionscript/geography` writes; see its `generate.mjs`. */
2
+ export declare const GEO_DATA_FORMAT = 1;
3
+ export interface GeoFileData {
4
+ format: number;
5
+ source?: string;
6
+ transform: {
7
+ scale: [number, number];
8
+ translate: [number, number];
9
+ };
10
+ /** Global arc id per local arc — identical across files for a shared border. */
11
+ arcIds: number[];
12
+ /** Per arc, flat `[x0, y0, w0, dx, dy, w, …]`: delta-encoded quantized lon/lat and a weight bucket. */
13
+ arcs: number[][];
14
+ features: GeoFeatureData[];
15
+ }
16
+ export interface GeoFeatureData {
17
+ code: string;
18
+ name: string;
19
+ nameNl?: string;
20
+ parent: string | null;
21
+ first: boolean;
22
+ listed: boolean;
23
+ remote?: boolean;
24
+ area: number;
25
+ mainland: number[] | null;
26
+ areas: number[];
27
+ /** Polygons → rings → arc refs (`~i` is arc `i` reversed). */
28
+ polygons: number[][][];
29
+ }
30
+ /** `[code, name, nameNl, parent, first]` per listed region. */
31
+ export type GeoIndexEntry = [string, string, string, string | null, 0 | 1];
32
+ export interface GeoIndex {
33
+ format: number;
34
+ source?: string;
35
+ regions: GeoIndexEntry[];
36
+ }
37
+ export interface GeoArc {
38
+ /** Identity for de-duplicating a border drawn by two regions, across files. */
39
+ key: number | string;
40
+ /** Interleaved lon/lat, in degrees. */
41
+ points: Float64Array;
42
+ /** Per point: 0 always survives, higher buckets are less significant; see `thresholdBucket`. */
43
+ buckets: Uint8Array;
44
+ }
45
+ export interface GeoRingRef {
46
+ arc: GeoArc;
47
+ reversed: boolean;
48
+ }
49
+ export interface GeoPolygon {
50
+ rings: GeoRingRef[][];
51
+ /** Per ring, in steradians — exterior first, then holes. */
52
+ ringAreas: number[];
53
+ area: number;
54
+ mainland: boolean;
55
+ }
56
+ export interface GeoFeature {
57
+ code: string;
58
+ name: string;
59
+ nameNl: string;
60
+ parent: string | null;
61
+ first: boolean;
62
+ listed: boolean;
63
+ remote: boolean;
64
+ polygons: GeoPolygon[];
65
+ }
66
+ export interface DecodedGeoFile {
67
+ features: Map<string, GeoFeature>;
68
+ }
69
+ export declare function ringCoordinates(ring: readonly GeoRingRef[]): [number, number][];
70
+ /** A ring's area in steradians, whichever way it winds. */
71
+ export declare function ringArea(ring: readonly GeoRingRef[]): number;
72
+ export declare function decodeGeoFile(data: GeoFileData): DecodedGeoFile;
73
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/border/format.ts"],"names":[],"mappings":"AAEA,kFAAkF;AAClF,eAAO,MAAM,eAAe,IAAI,CAAA;AAEhC,MAAM,WAAW,WAAW;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE;QAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAA;IACnE,gFAAgF;IAChF,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,uGAAuG;IACvG,IAAI,EAAE,MAAM,EAAE,EAAE,CAAA;IAChB,QAAQ,EAAE,cAAc,EAAE,CAAA;CAC7B;AAED,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACzB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAA;CACzB;AAED,+DAA+D;AAC/D,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;AAE1E,MAAM,WAAW,QAAQ;IACrB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,aAAa,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,MAAM;IACnB,+EAA+E;IAC/E,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;IACpB,uCAAuC;IACvC,MAAM,EAAE,YAAY,CAAA;IACpB,gGAAgG;IAChG,OAAO,EAAE,UAAU,CAAA;CACtB;AAED,MAAM,WAAW,UAAU;IACvB,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,UAAU;IACvB,KAAK,EAAE,UAAU,EAAE,EAAE,CAAA;IACrB,4DAA4D;IAC5D,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,EAAE,UAAU,EAAE,CAAA;CACzB;AAED,MAAM,WAAW,cAAc;IAC3B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CACpC;AAkBD,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAU/E;AAED,2DAA2D;AAC3D,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,GAAG,MAAM,CAK5D;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,cAAc,CAiC/D"}
@@ -0,0 +1,68 @@
1
+ import { geoArea } from "d3-geo";
2
+ /** The on-disk shape `@motionscript/geography` writes; see its `generate.mjs`. */
3
+ export const GEO_DATA_FORMAT = 1;
4
+ function decodeArc(encoded, key, [kx, ky], [tx, ty]) {
5
+ const count = encoded.length / 3;
6
+ const points = new Float64Array(count * 2);
7
+ const buckets = new Uint8Array(count);
8
+ let x = 0;
9
+ let y = 0;
10
+ for (let i = 0; i < count; i++) {
11
+ x += encoded[i * 3];
12
+ y += encoded[i * 3 + 1];
13
+ points[i * 2] = x * kx + tx;
14
+ points[i * 2 + 1] = y * ky + ty;
15
+ buckets[i] = encoded[i * 3 + 2];
16
+ }
17
+ return { key, points, buckets };
18
+ }
19
+ export function ringCoordinates(ring) {
20
+ const out = [];
21
+ for (const { arc, reversed } of ring) {
22
+ const n = arc.buckets.length;
23
+ for (let j = out.length === 0 ? 0 : 1; j < n; j++) {
24
+ const i = reversed ? n - 1 - j : j;
25
+ out.push([arc.points[i * 2], arc.points[i * 2 + 1]]);
26
+ }
27
+ }
28
+ return out;
29
+ }
30
+ /** A ring's area in steradians, whichever way it winds. */
31
+ export function ringArea(ring) {
32
+ const coordinates = ringCoordinates(ring);
33
+ if (coordinates.length < 4)
34
+ return 0;
35
+ const area = geoArea({ type: "Polygon", coordinates: [coordinates] });
36
+ return area > 2 * Math.PI ? 4 * Math.PI - area : area;
37
+ }
38
+ export function decodeGeoFile(data) {
39
+ if (data.format !== GEO_DATA_FORMAT) {
40
+ throw new Error(`geo data format ${data.format} is not supported (expected ${GEO_DATA_FORMAT})`);
41
+ }
42
+ const arcs = data.arcs.map((encoded, i) => decodeArc(encoded, data.arcIds[i], data.transform.scale, data.transform.translate));
43
+ const ref = (index) => index < 0 ? { arc: arcs[~index], reversed: true } : { arc: arcs[index], reversed: false };
44
+ const features = new Map();
45
+ for (const f of data.features) {
46
+ const mainland = f.mainland ? new Set(f.mainland) : null;
47
+ features.set(f.code, {
48
+ code: f.code,
49
+ name: f.name,
50
+ nameNl: f.nameNl ?? f.name,
51
+ parent: f.parent,
52
+ first: f.first,
53
+ listed: f.listed,
54
+ remote: f.remote === true,
55
+ polygons: f.polygons.map((rings, i) => {
56
+ const resolved = rings.map((ring) => ring.map(ref));
57
+ return {
58
+ rings: resolved,
59
+ ringAreas: resolved.map((ring, r) => (r === 0 ? f.areas[i] : ringArea(ring))),
60
+ area: f.areas[i],
61
+ mainland: mainland === null || mainland.has(i),
62
+ };
63
+ }),
64
+ });
65
+ }
66
+ return { features };
67
+ }
68
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/border/format.ts"],"names":[],"mappings":"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","sourcesContent":["import { geoArea } from \"d3-geo\"\r\n\r\n/** The on-disk shape `@motionscript/geography` writes; see its `generate.mjs`. */\r\nexport const GEO_DATA_FORMAT = 1\r\n\r\nexport interface GeoFileData {\r\n format: number\r\n source?: string\r\n transform: { scale: [number, number]; translate: [number, number] }\r\n /** Global arc id per local arc — identical across files for a shared border. */\r\n arcIds: number[]\r\n /** Per arc, flat `[x0, y0, w0, dx, dy, w, …]`: delta-encoded quantized lon/lat and a weight bucket. */\r\n arcs: number[][]\r\n features: GeoFeatureData[]\r\n}\r\n\r\nexport interface GeoFeatureData {\r\n code: string\r\n name: string\r\n nameNl?: string\r\n parent: string | null\r\n first: boolean\r\n listed: boolean\r\n remote?: boolean\r\n area: number\r\n mainland: number[] | null\r\n areas: number[]\r\n /** Polygons → rings → arc refs (`~i` is arc `i` reversed). */\r\n polygons: number[][][]\r\n}\r\n\r\n/** `[code, name, nameNl, parent, first]` per listed region. */\r\nexport type GeoIndexEntry = [string, string, string, string | null, 0 | 1]\r\n\r\nexport interface GeoIndex {\r\n format: number\r\n source?: string\r\n regions: GeoIndexEntry[]\r\n}\r\n\r\nexport interface GeoArc {\r\n /** Identity for de-duplicating a border drawn by two regions, across files. */\r\n key: number | string\r\n /** Interleaved lon/lat, in degrees. */\r\n points: Float64Array\r\n /** Per point: 0 always survives, higher buckets are less significant; see `thresholdBucket`. */\r\n buckets: Uint8Array\r\n}\r\n\r\nexport interface GeoRingRef {\r\n arc: GeoArc\r\n reversed: boolean\r\n}\r\n\r\nexport interface GeoPolygon {\r\n rings: GeoRingRef[][]\r\n /** Per ring, in steradians — exterior first, then holes. */\r\n ringAreas: number[]\r\n area: number\r\n mainland: boolean\r\n}\r\n\r\nexport interface GeoFeature {\r\n code: string\r\n name: string\r\n nameNl: string\r\n parent: string | null\r\n first: boolean\r\n listed: boolean\r\n remote: boolean\r\n polygons: GeoPolygon[]\r\n}\r\n\r\nexport interface DecodedGeoFile {\r\n features: Map<string, GeoFeature>\r\n}\r\n\r\nfunction decodeArc(encoded: number[], key: number, [kx, ky]: [number, number], [tx, ty]: [number, number]): GeoArc {\r\n const count = encoded.length / 3\r\n const points = new Float64Array(count * 2)\r\n const buckets = new Uint8Array(count)\r\n let x = 0\r\n let y = 0\r\n for (let i = 0; i < count; i++) {\r\n x += encoded[i * 3]\r\n y += encoded[i * 3 + 1]\r\n points[i * 2] = x * kx + tx\r\n points[i * 2 + 1] = y * ky + ty\r\n buckets[i] = encoded[i * 3 + 2]\r\n }\r\n return { key, points, buckets }\r\n}\r\n\r\nexport function ringCoordinates(ring: readonly GeoRingRef[]): [number, number][] {\r\n const out: [number, number][] = []\r\n for (const { arc, reversed } of ring) {\r\n const n = arc.buckets.length\r\n for (let j = out.length === 0 ? 0 : 1; j < n; j++) {\r\n const i = reversed ? n - 1 - j : j\r\n out.push([arc.points[i * 2], arc.points[i * 2 + 1]])\r\n }\r\n }\r\n return out\r\n}\r\n\r\n/** A ring's area in steradians, whichever way it winds. */\r\nexport function ringArea(ring: readonly GeoRingRef[]): number {\r\n const coordinates = ringCoordinates(ring)\r\n if (coordinates.length < 4) return 0\r\n const area = geoArea({ type: \"Polygon\", coordinates: [coordinates] })\r\n return area > 2 * Math.PI ? 4 * Math.PI - area : area\r\n}\r\n\r\nexport function decodeGeoFile(data: GeoFileData): DecodedGeoFile {\r\n if (data.format !== GEO_DATA_FORMAT) {\r\n throw new Error(`geo data format ${data.format} is not supported (expected ${GEO_DATA_FORMAT})`)\r\n }\r\n const arcs = data.arcs.map((encoded, i) =>\r\n decodeArc(encoded, data.arcIds[i], data.transform.scale, data.transform.translate)\r\n )\r\n const ref = (index: number): GeoRingRef =>\r\n index < 0 ? { arc: arcs[~index], reversed: true } : { arc: arcs[index], reversed: false }\r\n\r\n const features = new Map<string, GeoFeature>()\r\n for (const f of data.features) {\r\n const mainland = f.mainland ? new Set(f.mainland) : null\r\n features.set(f.code, {\r\n code: f.code,\r\n name: f.name,\r\n nameNl: f.nameNl ?? f.name,\r\n parent: f.parent,\r\n first: f.first,\r\n listed: f.listed,\r\n remote: f.remote === true,\r\n polygons: f.polygons.map((rings, i) => {\r\n const resolved = rings.map((ring) => ring.map(ref))\r\n return {\r\n rings: resolved,\r\n ringAreas: resolved.map((ring, r) => (r === 0 ? f.areas[i] : ringArea(ring))),\r\n area: f.areas[i],\r\n mainland: mainland === null || mainland.has(i),\r\n }\r\n }),\r\n })\r\n }\r\n return { features }\r\n}\r\n"]}
@@ -0,0 +1,103 @@
1
+ import { type FillResolved, type NodeOf, type Alignment as _Alignment, type Fill as _Fill, type FlowMode as _FlowMode, type FlowResolved as _FlowResolved, type GapSize as _GapSize, type Node2D as _Node2D, type Node2DProps as _Node2DProps, type Shadow as _Shadow, type Stroke as _Stroke, type Vector2 as _Vector2, type ShadowResolved, type ShapeProps, type StrokeResolved, type Type } from "@motionscript/sdk";
2
+ import { type GeoBorders, type GeoProjectionKind } from "./geometry.js";
3
+ import { type GeoFrame, type GeoLevel } from "./selection.js";
4
+ export interface GeoBorderProps extends ShapeProps {
5
+ /** ISO 3166-1 alpha-2 (`"FR"`) or ISO 3166-2 (`"US-CA"`) codes. */
6
+ iso: string | readonly string[];
7
+ level: GeoLevel;
8
+ /** 0–1: 1 is full resolution, lower is a more stylised outline. */
9
+ detail: number;
10
+ projection: GeoProjectionKind;
11
+ frame: GeoFrame;
12
+ borders: GeoBorders;
13
+ }
14
+ /**
15
+ * Country and subdivision outlines by ISO 3166 code, fitted into the node's box.
16
+ *
17
+ * <GeoBorder iso={["FR", "DE"]} detail={0.4} stroke={{ weight: 2, fill: "white" }} />
18
+ *
19
+ * Geometry comes from whatever `geoRegistry` has been given — a URL, a loader,
20
+ * or shapes defined by hand — and loads as a declared asset, so a frame never
21
+ * draws half a country. Fill and stroke are separate geometry: the fill is each
22
+ * region's polygons, the stroke is every border once, which is why a border two
23
+ * selected regions share is not drawn twice and why `stroke.align` does not
24
+ * apply.
25
+ */
26
+ export declare const GeoBorder: import("@motionscript/sdk").Component<"geoBorder", import("@motionscript/sdk").Merge<import("@motionscript/sdk").Box2DState, {
27
+ readonly iso: Type<string | readonly string[], string[], false>;
28
+ readonly level: Type<"country" | "state", "country" | "state", false>;
29
+ readonly detail: Type<number, number, false>;
30
+ readonly projection: Type<"conic" | "mercator" | "equirectangular" | "orthographic", "conic" | "mercator" | "equirectangular" | "orthographic", false>;
31
+ readonly frame: Type<"mainland" | "all", "mainland" | "all", false>;
32
+ readonly borders: Type<"all" | "outline", "all" | "outline", false>;
33
+ readonly flow: Type<_FlowMode, _FlowResolved, false>;
34
+ readonly gap: Type<_GapSize, _GapSize, false>;
35
+ readonly wrap: boolean;
36
+ readonly crossGap: Type<number | undefined, number | undefined, false>;
37
+ readonly columns: Type<number, number, false>;
38
+ readonly align: import("@motionscript/sdk").Attribute<_Alignment, _Vector2>;
39
+ readonly fill: import("@motionscript/sdk").Attribute<_Fill, FillResolved[]>;
40
+ readonly stroke: import("@motionscript/sdk").Attribute<_Stroke, StrokeResolved[]>;
41
+ readonly shadow: import("@motionscript/sdk").Attribute<_Shadow, ShadowResolved[]>;
42
+ readonly overlay: import("@motionscript/sdk").Attribute<_Fill, FillResolved[]>;
43
+ readonly start: Type<number, number, false>;
44
+ readonly end: Type<number, number, false>;
45
+ }>, {
46
+ readonly fillTo: readonly ["fill"];
47
+ readonly strokeTo: readonly ["stroke"];
48
+ readonly shadowTo: readonly ["shadow"];
49
+ readonly overlayTo: readonly ["overlay"];
50
+ readonly progressTo: readonly ["start", "end"];
51
+ readonly flash: {
52
+ readonly portion: Type<number, number, false>;
53
+ };
54
+ }, null, _Node2D<_Node2DProps>, import("@motionscript/sdk").CallProps<import("@motionscript/sdk").Merge<import("@motionscript/sdk").Box2DState, {
55
+ readonly iso: Type<string | readonly string[], string[], false>;
56
+ readonly level: Type<"country" | "state", "country" | "state", false>;
57
+ readonly detail: Type<number, number, false>;
58
+ readonly projection: Type<"conic" | "mercator" | "equirectangular" | "orthographic", "conic" | "mercator" | "equirectangular" | "orthographic", false>;
59
+ readonly frame: Type<"mainland" | "all", "mainland" | "all", false>;
60
+ readonly borders: Type<"all" | "outline", "all" | "outline", false>;
61
+ readonly flow: Type<_FlowMode, _FlowResolved, false>;
62
+ readonly gap: Type<_GapSize, _GapSize, false>;
63
+ readonly wrap: boolean;
64
+ readonly crossGap: Type<number | undefined, number | undefined, false>;
65
+ readonly columns: Type<number, number, false>;
66
+ readonly align: import("@motionscript/sdk").Attribute<_Alignment, _Vector2>;
67
+ readonly fill: import("@motionscript/sdk").Attribute<_Fill, FillResolved[]>;
68
+ readonly stroke: import("@motionscript/sdk").Attribute<_Stroke, StrokeResolved[]>;
69
+ readonly shadow: import("@motionscript/sdk").Attribute<_Shadow, ShadowResolved[]>;
70
+ readonly overlay: import("@motionscript/sdk").Attribute<_Fill, FillResolved[]>;
71
+ readonly start: Type<number, number, false>;
72
+ readonly end: Type<number, number, false>;
73
+ }>, null, import("@motionscript/sdk").BuiltNode<import("@motionscript/sdk").Merge<import("@motionscript/sdk").Box2DState, {
74
+ readonly iso: Type<string | readonly string[], string[], false>;
75
+ readonly level: Type<"country" | "state", "country" | "state", false>;
76
+ readonly detail: Type<number, number, false>;
77
+ readonly projection: Type<"conic" | "mercator" | "equirectangular" | "orthographic", "conic" | "mercator" | "equirectangular" | "orthographic", false>;
78
+ readonly frame: Type<"mainland" | "all", "mainland" | "all", false>;
79
+ readonly borders: Type<"all" | "outline", "all" | "outline", false>;
80
+ readonly flow: Type<_FlowMode, _FlowResolved, false>;
81
+ readonly gap: Type<_GapSize, _GapSize, false>;
82
+ readonly wrap: boolean;
83
+ readonly crossGap: Type<number | undefined, number | undefined, false>;
84
+ readonly columns: Type<number, number, false>;
85
+ readonly align: import("@motionscript/sdk").Attribute<_Alignment, _Vector2>;
86
+ readonly fill: import("@motionscript/sdk").Attribute<_Fill, FillResolved[]>;
87
+ readonly stroke: import("@motionscript/sdk").Attribute<_Stroke, StrokeResolved[]>;
88
+ readonly shadow: import("@motionscript/sdk").Attribute<_Shadow, ShadowResolved[]>;
89
+ readonly overlay: import("@motionscript/sdk").Attribute<_Fill, FillResolved[]>;
90
+ readonly start: Type<number, number, false>;
91
+ readonly end: Type<number, number, false>;
92
+ }>, {
93
+ readonly fillTo: readonly ["fill"];
94
+ readonly strokeTo: readonly ["stroke"];
95
+ readonly shadowTo: readonly ["shadow"];
96
+ readonly overlayTo: readonly ["overlay"];
97
+ readonly progressTo: readonly ["start", "end"];
98
+ readonly flash: {
99
+ readonly portion: Type<number, number, false>;
100
+ };
101
+ }, _Node2D<_Node2DProps>>>>;
102
+ export type GeoBorder = NodeOf<typeof GeoBorder>;
103
+ //# sourceMappingURL=geo-border.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geo-border.d.ts","sourceRoot":"","sources":["../../src/border/geo-border.ts"],"names":[],"mappings":"AAAA,OAAO,EAQH,KAAK,YAAY,EACjB,KAAK,MAAM,EAEX,KAAK,SAAS,IAAI,UAAU,EAC5B,KAAK,IAAI,IAAI,KAAK,EAClB,KAAK,QAAQ,IAAI,SAAS,EAC1B,KAAK,YAAY,IAAI,aAAa,EAClC,KAAK,OAAO,IAAI,QAAQ,EACxB,KAAK,MAAM,IAAI,OAAO,EACtB,KAAK,WAAW,IAAI,YAAY,EAChC,KAAK,MAAM,IAAI,OAAO,EACtB,KAAK,MAAM,IAAI,OAAO,EACtB,KAAK,OAAO,IAAI,QAAQ,EAExB,KAAK,cAAc,EACnB,KAAK,UAAU,EAEf,KAAK,cAAc,EACnB,KAAK,IAAI,EACZ,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAAe,KAAK,UAAU,EAAqC,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAGpH,OAAO,EAAgD,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAA;AAExG,MAAM,WAAW,cAAe,SAAQ,UAAU;IAC9C,mEAAmE;IACnE,GAAG,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;IAC/B,KAAK,EAAE,QAAQ,CAAA;IACf,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,iBAAiB,CAAA;IAC7B,KAAK,EAAE,QAAQ,CAAA;IACf,OAAO,EAAE,UAAU,CAAA;CACtB;AA4ID;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAqEpB,CAAA;AAEF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,SAAS,CAAC,CAAA"}