@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.
- package/CHANGELOG.md +104 -1
- package/NOTICE +15 -14
- package/README.md +78 -14
- package/dist/border/format.d.ts +73 -0
- package/dist/border/format.d.ts.map +1 -0
- package/dist/border/format.js +68 -0
- package/dist/border/format.js.map +1 -0
- package/dist/border/geo-border.d.ts +103 -0
- package/dist/border/geo-border.d.ts.map +1 -0
- package/dist/border/geo-border.js +203 -0
- package/dist/border/geo-border.js.map +1 -0
- package/dist/border/geometry.d.ts +40 -0
- package/dist/border/geometry.d.ts.map +1 -0
- package/dist/border/geometry.js +382 -0
- package/dist/border/geometry.js.map +1 -0
- package/dist/border/index.d.ts +15 -0
- package/dist/border/index.d.ts.map +1 -0
- package/dist/border/index.js +15 -0
- package/dist/border/index.js.map +1 -0
- package/dist/border/loader.d.ts +31 -0
- package/dist/border/loader.d.ts.map +1 -0
- package/dist/border/loader.js +87 -0
- package/dist/border/loader.js.map +1 -0
- package/dist/border/registry.d.ts +64 -0
- package/dist/border/registry.d.ts.map +1 -0
- package/dist/border/registry.js +163 -0
- package/dist/border/registry.js.map +1 -0
- package/dist/border/selection.d.ts +21 -0
- package/dist/border/selection.d.ts.map +1 -0
- package/dist/border/selection.js +86 -0
- package/dist/border/selection.js.map +1 -0
- package/dist/border/simplify.d.ts +12 -0
- package/dist/border/simplify.d.ts.map +1 -0
- package/dist/border/simplify.js +116 -0
- package/dist/border/simplify.js.map +1 -0
- package/dist/browser/THIRD_PARTY_LICENSES.txt +58 -0
- package/dist/browser/index.js +2 -0
- package/dist/browser/index.js.map +7 -0
- package/dist/browser/manifest.json +23 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/nodes.d.ts +15 -0
- package/dist/nodes.d.ts.map +1 -0
- package/dist/nodes.js +16 -0
- package/dist/nodes.js.map +1 -0
- package/package.json +46 -8
- package/registry.json +16 -0
- package/src/border/format.ts +147 -0
- package/src/border/geo-border.ts +268 -0
- package/src/border/geometry.ts +463 -0
- package/src/border/index.ts +14 -0
- package/src/border/loader.ts +97 -0
- package/src/border/registry.ts +201 -0
- package/src/border/selection.ts +95 -0
- package/src/border/simplify.ts +110 -0
- package/src/index.ts +2 -0
- 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
|
-
-
|
|
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/
|
|
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/
|
|
12
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
packages/
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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/
|
|
104
|
+
committed fixture is `components/geography/src/border/tests/fixtures/v1/`.
|
|
41
105
|
|
|
42
|
-
|
|
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
|
-
|
|
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"}
|