@tileflow/core 0.1.0-alpha.37 → 0.1.0-alpha.39

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 (37) hide show
  1. package/README.md +10 -10
  2. package/dist/3d-BkaJjwfk.d.ts +1083 -0
  3. package/dist/3d.d.ts +1 -0
  4. package/dist/3d.js +12331 -0
  5. package/dist/browser.d.ts +788 -1409
  6. package/dist/browser.js +11457 -9407
  7. package/dist/build.d.ts +4 -3
  8. package/dist/build.js +1135 -203
  9. package/dist/capture.js +4 -1
  10. package/dist/config-reference.json +927 -548
  11. package/dist/{icon-composition-C5X3Q9Ya.d.ts → icon-composition-Ivgoxz8d.d.ts} +172 -5
  12. package/dist/index.d.ts +174 -5
  13. package/dist/index.js +1797 -330
  14. package/dist/{manifest-types-jrBdBrES.d.ts → manifest-types-DRy2980g.d.ts} +5 -2
  15. package/dist/manifest.d.ts +2 -2
  16. package/dist/manifest.js +4 -1
  17. package/dist/native-profile.d.ts +4 -0
  18. package/dist/native-profile.js +2 -0
  19. package/dist/native.js +11 -1
  20. package/dist/runtime.d.ts +1 -1
  21. package/dist/runtime.js +11 -1
  22. package/dist/worker/camera-tile-worker.global.js +20 -0
  23. package/docs/application-tile-reading.md +5 -2
  24. package/docs/authoring-model.md +8 -0
  25. package/docs/buildings-3d.md +80 -12
  26. package/docs/compiled-style-performance.md +11 -2
  27. package/docs/data-is-separate-from-design.md +6 -3
  28. package/docs/globe-atmosphere.md +3 -0
  29. package/docs/native-artifact-profile.md +2 -2
  30. package/docs/nautical-marks.md +147 -39
  31. package/docs/public-api-and-browser-subpath.md +7 -2
  32. package/docs/street-lights.md +5 -1
  33. package/docs/street-sky.md +153 -0
  34. package/docs/sun-and-light.md +4 -2
  35. package/docs/trees-3d.md +11 -6
  36. package/package.json +6 -1
  37. package/dist/worker/buildings-3d-worker.global.js +0 -1
@@ -1,10 +1,12 @@
1
1
  # Nautical marks
2
2
 
3
3
  The `nautical` module draws navigation marks from a `nautical-v1` vector source: buoys, beacons,
4
- lighthouses, lights, rocks and other hazards, wrecks, reefs, navigation areas, routes, cables,
5
- harbours, ports, and a restrained indication of where a provider publishes marks. It is reference
6
- cartography for general-purpose maps. It is not a nautical chart, and a map that draws it always
7
- shows a "Not for navigation" notice.
4
+ lighthouses, lights that flash in their rhythm, rocks and other hazards, wrecks, reefs, navigation
5
+ areas, routes, submarine and overhead cables, bridges with their clearances, harbours, marinas,
6
+ berths, small-craft facilities, inland notice boards, waterway distance marks, ports, and a
7
+ restrained indication of where a provider publishes marks. It is reference cartography for
8
+ general-purpose maps. It is not a nautical chart, and a map that draws it always shows a "Not for
9
+ navigation" notice.
8
10
 
9
11
  ## Enable Nautical on a map
10
12
 
@@ -100,19 +102,25 @@ displayed.
100
102
  Every feature carries `provider`, `provenance`, `edition`, `coverage`, `licence`, and `min_zoom`,
101
103
  the first zoom at which the feature appears. `rank` (1 is the most important feature of its layer)
102
104
  and `name` are optional. Colours are S-57 colour names, and multi-valued colours and categories are
103
- comma-separated in source order, for example `black,yellow`. `tileflowNauticalV1Schema` lists the
104
- field names, layers, and geometry types.
105
-
106
- | Source layer | Geometry | Fields the module reads |
107
- | ----------------- | -------------------------- | -------------------------------------------------------- |
108
- | `aid` | Point | `class`, `subclass`, `category`, `color`, `rank`, `name` |
109
- | `light` | Point | `class` (`major` or `minor`), `color`, `label`, `rank` |
110
- | `hazard` | Point, LineString, Polygon | `class`, `water_level`, `name` |
111
- | `wreck` | Point, Polygon | `class`, `name` |
112
- | `reef` | Polygon | `name` |
113
- | `navigation_area` | Point, LineString, Polygon | `class`, `size`, `rank`, `name` |
114
- | `coverage` | Polygon | `families` |
115
- | `sounding` | Point | `depth` |
105
+ comma-separated in source order, for example `black,yellow`. Clearances, depths, and heights are in
106
+ metres. `tileflowNauticalV1Schema` lists the field names, layers, geometry types, and known
107
+ providers: `openstreetmap` (coverage class `community`), `nga-list-of-lights` (`list`), and the
108
+ `chart` providers `noaa-office-of-coast-survey` and `usace-ienc` (U.S. Army Corps of Engineers
109
+ Inland ENCs). The source's TileJSON owns every provider credit.
110
+
111
+ | Source layer | Geometry | Fields the module reads |
112
+ | ----------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------ |
113
+ | `aid` | Point | `class`, `subclass`, `category`, `color`, `rank`, `name`, `information`, `distance`, `distance_unit` |
114
+ | `light` | Point | `class` (`major` or `minor`), `color`, `label`, `rank`, `character`, `group`, `period_s`, `sequence` |
115
+ | `hazard` | Point, LineString, Polygon | `class`, `water_level`, `name` |
116
+ | `wreck` | Point, Polygon | `class`, `name` |
117
+ | `reef` | Polygon | `name` |
118
+ | `navigation_area` | Point, LineString, Polygon | `class`, `subclass`, `size`, `rank`, `name`, `vertical_clearance` and its `_closed`, `_open`, and `_safe` variants |
119
+ | `coverage` | Polygon | `families` |
120
+ | `sounding` | Point | `depth` |
121
+
122
+ The Nautical source promotes each light's `provenance` as its feature ID
123
+ (`promoteId: {light: 'provenance'}`), so lights can be animated with MapLibre feature state.
116
124
 
117
125
  Every layer filters on `min_zoom`, so a compatible source that keeps features in lower-zoom tiles
118
126
  still shows each one from its own first zoom. Point groups place a lower `rank` first.
@@ -122,26 +130,42 @@ still shows each one from its own first zoom. Point groups place a lower `rank`
122
130
  Each group is a style option of `nautical(...)`, with the same marker, symbol, area, and line style
123
131
  objects as other modules.
124
132
 
125
- | Option | Draws | Default symbology |
126
- | ----------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------- |
127
- | `buoys` | `aid` buoys, light floats, and light vessels | Circle in the first colour, ringed with the second |
128
- | `beacons` | `aid` beacons and daymarks | Small circle in the first colour |
129
- | `lighthouses` | `aid` lighthouses | Ink circle and name |
130
- | `aids` | Other `aid` classes, such as moorings, fog signals, and racons | Small hollow circle |
131
- | `lights` | `light` points, one per sector | Flare in the light's first colour and its chart `label` |
132
- | `hazards` | `hazard` points | `+` for rocks, `*` for rocks that show; circled obstacles |
133
- | `hazardAreas` | `hazard` polygons; the outline also strokes linear hazards | Accent wash with a dotted outline |
134
- | `wrecks` | `wreck` points | `Wk`, in the accent colour when dangerous |
135
- | `wreckAreas` | `wreck` polygons | Ink wash with a dashed outline |
136
- | `reefs` | `reef` polygons | Sand wash with a dotted outline |
137
- | `navigationAreas` | `navigation_area` polygons | Dashed outlines; accent for operational areas |
138
- | `routes` | `navigation_area` lines of class `route` and `traffic-separation` | Dashed line; accent for traffic separation |
139
- | `cables` | `navigation_area` lines of class `cable`, `pipeline`, and their area classes | Fine dashed accent line |
140
- | `harbours` | `navigation_area` points of class `harbour` | Ink circle and name |
141
- | `ports` | `navigation_area` points of class `port` | Ink circle and name sized by `size`, from low zooms |
142
- | `coverage` | `coverage` polygons that cover every family | Faint tint at low zooms only |
143
- | `soundings` | `sounding` points | Depth figure |
144
- | `labels` | Names inside hazard, navigation-area, reef, and wreck polygons; coverage opt-in | Collision-aware text |
133
+ | Option | Draws | Default symbology |
134
+ | ----------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------- |
135
+ | `buoys` | `aid` buoys, light floats, and light vessels | Circle in the first colour, ringed with the second |
136
+ | `beacons` | `aid` beacons and daymarks | Small circle in the first colour |
137
+ | `lighthouses` | `aid` lighthouses | Ink circle and name |
138
+ | `aids` | Other `aid` classes, such as moorings, fog signals, and racons | Small hollow circle |
139
+ | `lights` | `light` points, one per sector | Flare in the light's first colour and its chart `label` |
140
+ | `hazards` | `hazard` points | `+` for rocks, `*` for rocks that show; circled obstacles |
141
+ | `hazardAreas` | `hazard` polygons; the outline also strokes linear hazards | Accent wash with a dotted outline |
142
+ | `wrecks` | `wreck` points | `Wk`, in the accent colour when dangerous |
143
+ | `wreckAreas` | `wreck` polygons | Ink wash with a dashed outline |
144
+ | `reefs` | `reef` polygons | Sand wash with a dotted outline |
145
+ | `navigationAreas` | `navigation_area` polygons | Dashed outlines; accent for operational areas |
146
+ | `routes` | `navigation_area` lines of class `route` and `traffic-separation` | Dashed line; accent for traffic separation |
147
+ | `cables` | `navigation_area` lines of class `cable`, `pipeline`, and their area classes | Fine dashed accent line |
148
+ | `overheadCables` | `navigation_area` lines of class `overhead-cable` | Dash-dot accent line |
149
+ | `bridges` | `navigation_area` bridge polygons; the outline also strokes bridge lines | Land-coloured deck with an ink outline |
150
+ | `berths` | `navigation_area` berth polygons; the outline also strokes berth lines | Thin ink outline, wider and darker for `guest` berths |
151
+ | `harbourAreas` | `navigation_area` harbour and marina polygons | Faint ink wash with a solid outline |
152
+ | `harbours` | `navigation_area` harbour points, and a symbol inside harbour polygons | Ink circle and name; the marina token for marinas |
153
+ | `facilities` | `navigation_area` facility points, polygons, and line centres, from zoom 13 | Hollow circle; facility icons from the theme |
154
+ | `notices` | `aid` notice boards, from zoom 12 | Circle in the board's function colour and `information` |
155
+ | `distanceMarks` | `aid` distance marks, from zoom 11 | Ink dot and distance with its unit, fainter if not posted |
156
+ | `ports` | `navigation_area` points of class `port` | Ink circle and name sized by `size`, from low zooms |
157
+ | `coverage` | `coverage` polygons that cover every family | Faint tint at low zooms only |
158
+ | `soundings` | `sounding` points | Depth figure |
159
+ | `labels` | Names inside polygons, bridge and overhead-cable clearances; coverage opt-in | Collision-aware text |
160
+
161
+ Bridge, berth, facility, and harbour polygons are drawn by their own groups rather than as
162
+ navigation areas. `labels.bridges` shows `clearance 4.5 m`, or `closed 3.6 m · open 12 m` for an
163
+ opening bridge, at bridge points, inside bridge polygons, and at the centre of bridge lines, from
164
+ zoom 13. `labels.overheadCables` shows `safe clearance 28.5 m`, or the plain clearance, at cable
165
+ points and along cable lines. Distance marks read `12 km`, `5 hm`, `300 m`, `12 mi`, or `4.5 NM`
166
+ from zoom 13. Notice boards follow the CEVNI colours: red for prohibitions, regulations, and
167
+ restrictions, green for recommendations, and blue for information; their `information`, such as a
168
+ speed limit, appears from zoom 14. Board orientation and impact are not drawn.
145
169
 
146
170
  Operational areas are restricted, military, anchorage, anchor-berth, dumping-ground, and
147
171
  traffic-separation areas. Marks keep their conventional colours in every theme; a white light is
@@ -173,6 +197,77 @@ export default defineMap({
173
197
  });
174
198
  ```
175
199
 
200
+ ## Flashing lights
201
+
202
+ In React, Vue, Svelte, and the development preview, every light with a rhythm flashes: its flare or
203
+ circle draws at its styled opacity while the light shines and at 15 % of it during an eclipse.
204
+ Fixed lights, lights without a usable characteristic, reduced motion, capture, native renderers, and
205
+ static images show every light steadily, exactly as it is styled. Set `animateLights: false` to keep
206
+ a map's lights steady everywhere:
207
+
208
+ <!-- docs:check -->
209
+
210
+ ```ts
211
+ import {defineMap, nautical} from '@tileflow/core';
212
+ import {streets} from '@tileflow/maps';
213
+
214
+ export default defineMap({
215
+ id: 'harbour-guide',
216
+ version: 1,
217
+ extends: streets,
218
+ marine: 'nautical',
219
+ modules: {nautical: nautical({animateLights: false})},
220
+ });
221
+ ```
222
+
223
+ A light flashes by its published `sequence` when it has one: light and eclipse durations in
224
+ seconds, eclipses in parentheses, for example `0.5+(1.5)+0.5+(4.5)`. Otherwise
225
+ `tileflowLightRhythm(properties)` derives a conventional rhythm from `character`, `group`, and
226
+ `period_s`; `tileflowLightRhythmConventions` holds the values below.
227
+
228
+ | Character | Rhythm |
229
+ | ------------------------------------- | ------------------------------------------------------------------------------------------------------ |
230
+ | `Fl` | Flashes of `min(1 s, 0.3 × period ÷ flashes)`; gaps within a group at most twice the flash |
231
+ | `Fl` with a group such as `2+1` | Groups separated by twice the inner gap; the rest of the period is the final eclipse |
232
+ | `LFl` | The same layout with 2 s flashes, shorter only when the period cannot hold them |
233
+ | `Q`, `VQ`, `UQ` | 60, 120, and 240 flashes a minute, lit for 30 % of each; a group of `n` flashes then eclipses |
234
+ | `IQ`, `IVQ`, `IUQ` | Quick flashes for 60 % of the period, then an eclipse |
235
+ | `Iso` | Equal light and eclipse |
236
+ | `Oc` | Eclipses totalling 25 % of the period, laid out like `Fl`, so the light is always longer |
237
+ | `Mo` | The Morse letter in `group`: 0.5 s dots, three-unit dashes, one-unit gaps, then the rest of the period |
238
+ | `FFl`, `F+LFl` | A fixed light at half intensity with full flashes, or long flashes |
239
+ | `Q+LFl`, `VQ+LFl`, `UQ+LFl`, `Fl+LFl` | The group (six quick flashes by default), one 2 s long flash, then the eclipse |
240
+ | `Oc+Fl` | Its occulting part |
241
+ | `Al.<character>`, `Al` | Each lit phase shows the next colour of `color`; `Al` alone shows each colour for an equal share |
242
+ | `F`, unknown, or missing period | Steady; grouped `Q` and `VQ` cardinal marks without a period use the IALA periods |
243
+
244
+ `tileflowLightStateAt(rhythm, seconds)` returns the intensity, colour index, and time to the next
245
+ change at any moment. Lights with the same rhythm flash together.
246
+
247
+ React, Vue, Svelte, the development preview, and standalone capture call
248
+ `attachTileflowNauticalLights(map)` from `@tileflow/core/browser`. It reads the light layers the
249
+ style declares in `metadata['tileflow:nautical-lights']`, and writes each rendered light's
250
+ `tileflowLightIntensity` and `tileflowLightColour` feature state only when its phase changes. It runs
251
+ one timer for the next change, and none while the page is hidden, while no light layer is visible at
252
+ the current zoom, or under reduced motion. A direct MapLibre integration attaches it after
253
+ constructing the map:
254
+
255
+ ```ts
256
+ import maplibregl from 'maplibre-gl';
257
+ import {attachTileflowNauticalLights, attachTileflowNotices} from '@tileflow/core/browser';
258
+
259
+ const map = new maplibregl.Map({
260
+ container: 'map',
261
+ style: '/tileflow/styles/harbour-guide/light.json',
262
+ });
263
+ attachTileflowNotices(map);
264
+ const detach = attachTileflowNauticalLights(map);
265
+ // Call detach() to return every light to steady; removing the map also detaches it.
266
+ ```
267
+
268
+ `steadyTileflowNauticalLights(style)` returns a copy that draws every light steadily without feature
269
+ state; native artifact preparation applies it automatically.
270
+
176
271
  ## Chart symbols from the theme
177
272
 
178
273
  A theme that declares the image tokens below replaces a point group's circle marker with that icon.
@@ -190,11 +285,24 @@ Buoy variants fall back to `nautical.buoy`. `tileflowNauticalImageRoles` lists t
190
285
  | `nautical.aid` | `aids` |
191
286
  | `nautical.wreck` | `wrecks` |
192
287
  | `nautical.harbour` | `harbours` |
288
+ | `nautical.marina` | Harbours whose `subclass` lists a marina |
193
289
  | `nautical.port` | `ports` |
290
+ | `nautical.notice` | `notices` |
291
+ | `nautical.notice.<role>` | Notices of that function |
292
+ | `nautical.distance-mark` | `distanceMarks` |
293
+ | `nautical.facility` | `facilities` |
294
+ | `nautical.facility.<cat>` | Facilities listing that category |
295
+
296
+ Notice roles are `prohibition`, `regulation`, `restriction`, `recommendation`, and `information`.
297
+ Facility categories are `fuel-station`, `slipway`, `boat-hoist`, `pump-out`, `water-tap`,
298
+ `visitor-berth` (also used for `visitors-mooring`), and `boatyard`: a facility listing several shows
299
+ the first in that order (`tileflowNauticalFacilityIcons`), and the others show `nautical.facility`.
300
+ Every icon expression selects among the declared image names only.
194
301
 
195
302
  Buoys, beacons, other aids, and lights colour their icon with the mark's own colour, so declare
196
303
  them as [SDF icons](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/native-icons.md)
197
304
  that share one representation within each group. The defaults size icons for the SDF profile,
198
- whose drawing occupies the central 12 of 24 pixels; set `icon.size` for other artwork. Soundings
199
- declares these tokens with its own buoy, light, lighthouse, wreck, and harbour symbols and keeps
305
+ whose drawing occupies the central 12 of 24 pixels; set `icon.size` for other artwork. Notice
306
+ boards take their function colour. Soundings declares these tokens with its own buoy, light,
307
+ lighthouse, wreck, harbour, marina, notice-board, distance-mark, and facility symbols and keeps
200
308
  beacons as coloured dots.
@@ -79,6 +79,7 @@ import {
79
79
  attachTileflowAtmosphere,
80
80
  attachTileflowFairUseNotice,
81
81
  attachTileflowMapLifecycle,
82
+ attachTileflowNauticalLights,
82
83
  attachTileflowNotices,
83
84
  createTileflowSessionStarter,
84
85
  createTileflowTransformRequest,
@@ -86,9 +87,13 @@ import {
86
87
  } from '@tileflow/core/browser';
87
88
  ```
88
89
 
90
+ `@tileflow/core/3d` holds only `createTileflowMap3D` and the 3D renderers it mounts, so an
91
+ application can load them with a dynamic import when a style declares a 3D scene; see
92
+ [3D buildings](buildings-3d.md#the-maps-3d-scene). The browser entry exports the same function.
93
+
89
94
  `attachTileflowNotices(map)` renders the notices a compiled style declares, such as Nautical's
90
- "Not for navigation", as a compact map control; see
91
- [Nautical marks](nautical-marks.md). The browser entry is not re-exported from the root. It is SSR-safe to import, has no MapLibre
95
+ "Not for navigation", as a compact map control, and `attachTileflowNauticalLights(map)` flashes
96
+ Nautical lights in their rhythm; see [Nautical marks](nautical-marks.md). The browser entry is not re-exported from the root. It is SSR-safe to import, has no MapLibre
92
97
  runtime dependency, and reads no browser global during module evaluation. See the
93
98
  [framework browser runtime contract](https://github.com/tileflow/tileflow-sdk/blob/main/docs/contracts/framework-browser-runtime.md) and the
94
99
  [cartographic authoring contract](https://github.com/tileflow/tileflow-sdk/blob/main/docs/contracts/cartographic-authoring.md). Exact field
@@ -9,6 +9,9 @@ the lights switch on one by one; they go out as day comes.
9
9
  Street lights are off by default in the SDK and in Streets. A compiled map must explicitly set
10
10
  `roads.streetLights.enabled: true` before the renderer or preview will show lamp glows.
11
11
 
12
+ React, Vue and Svelte maps light a map that enables street lights automatically, as part of its
13
+ [3D scene](buildings-3d.md#the-maps-3d-scene); the 2D view has no street lights.
14
+
12
15
  It is a custom WebGL2 layer for MapLibre GL JS, drawn above the map's buildings and below its
13
16
  labels. When `createTileflowBuildings3D` raises the buildings, they hide the lamps behind them. It
14
17
  needs WebGL2 and the Mercator projection, and shines from zoom 14 by default.
@@ -114,7 +117,8 @@ by default, at most 64) bound where and how far the lights shine, `beforeId` pla
114
117
  another one, and `transformRequest` applies the map's request rules to the tiles it reads.
115
118
 
116
119
  Layers that read the same source share each tile's download, so the street lights and the 3D
117
- buildings fetch a tile once between them.
120
+ buildings fetch a tile once between them. The lamps are placed in the worker the page's 3D layers
121
+ share, which decodes each tile once for all of them.
118
122
 
119
123
  ## Limits
120
124
 
@@ -0,0 +1,153 @@
1
+ # Street sky
2
+
3
+ A map's `sky` paints the sky above the horizon when the camera tilts up the street, in either
4
+ projection: blue by day, warm at dusk, dark with stars at night. Install `@tileflow/core` and
5
+ `@tileflow/maps` and use a browser adapter from the same compatible SDK release. Streets declares a
6
+ sky in every theme; the example extends it and keeps its sky.
7
+
8
+ <!-- docs:check -->
9
+
10
+ ```ts
11
+ import {defineMap} from '@tileflow/core';
12
+ import {streets} from '@tileflow/maps';
13
+
14
+ export default defineMap({
15
+ id: 'street-level',
16
+ version: 1,
17
+ extends: streets,
18
+ view: {center: [-3.6904, 40.4493], zoom: 17, bearing: 8, pitch: 80},
19
+ });
20
+ ```
21
+
22
+ The view looks north along Paseo de la Castellana in Madrid, with the sky above the skyline.
23
+ Coordinates are longitude, then latitude.
24
+
25
+ ## Camera pitch
26
+
27
+ React, Vue, Svelte, the Dev preview, and browser capture create maps with a maximum pitch of 85
28
+ degrees, `tileflowMaxPitch`, instead of MapLibre's default of 60. Every portable view already admits
29
+ pitch 0–85. Pass `mapOptions: {maxPitch}` to lower the limit for one map. A map constructed directly
30
+ with MapLibre keeps MapLibre's default unless it sets `maxPitch: tileflowMaxPitch` itself.
31
+
32
+ The React Native surface still uses the native engines' 60-degree limit.
33
+
34
+ ## Configure the sky
35
+
36
+ `sky: true` uses the defaults. An options object replaces an inherited sky atomically, and
37
+ `sky: false` removes it.
38
+
39
+ | Option | Default | Meaning |
40
+ | --------------- | --------- | ----------------------------------------------------------------- |
41
+ | `color` | `#5c9ce6` | The sky overhead |
42
+ | `horizonColor` | `#dcebf7` | The pale band along the horizon, also 3D terrain's distance fog |
43
+ | `horizonBlend` | `0.6` | How far up the band reaches, as a share of half the view's height |
44
+ | `glowColor` | `#ffc68a` | The light round a low sun, which glows on the horizon towards it |
45
+ | `starIntensity` | `0.6` | The brightest the stars shine, from 0 (none) to 1 |
46
+
47
+ Colors take `token.color(...)`, color operations, or `fixed(color, {reason})`, and must resolve to
48
+ opaque colors. Numbers take `token.number(...)`, `fixed(number, {reason})`, or a number, and must
49
+ resolve to 0–1. Unknown properties are rejected. Because tokens resolve per theme, one declaration
50
+ gives each theme its own sky. Streets uses its themes' `sky.color`, `sky.horizon`,
51
+ `sky.horizonBlend`, and `sky.glow` tokens:
52
+
53
+ ```ts
54
+ sky: {
55
+ color: token.color('sky.color'),
56
+ horizonColor: token.color('sky.horizon'),
57
+ horizonBlend: token.number('sky.horizonBlend'),
58
+ glowColor: token.color('sky.glow'),
59
+ starIntensity: 0.6,
60
+ },
61
+ ```
62
+
63
+ A map that replaces Streets' themes supplies those tokens or declares its own `sky`.
64
+
65
+ ## A sky that follows the sun
66
+
67
+ When an application places the sun with the theme controller's `setSun`, by direction or by the
68
+ real sun of a date and place, the sky follows its height continuously instead of staying on the
69
+ theme's preset. Above about 14 degrees the sky is the day theme's; it warms into the dawn or dusk
70
+ theme's sky as the sun nears the horizon, using dawn while the sun stands in the eastern half of
71
+ the sky and dusk in the western half; it holds that sky within two degrees of sunrise or sunset,
72
+ and it darkens into the night theme's sky as twilight ends, about ten degrees below the horizon.
73
+ The themes come from the map's `timeOfDay` family of the theme on screen, and every colour is
74
+ resolved from that theme's own tokens, so a Monochrome family stays grey and a client's own
75
+ family keeps its palette. A family with only one twilight uses it at both ends of the day; a theme
76
+ outside a family, or a family without a day and a night theme, keeps its own sky.
77
+ `setSun(undefined)` returns the sky to the themes. The map's colours on the ground stay those of
78
+ the theme or blend on screen; pair `setSun` with `setBlend` to move them too. See
79
+ [sun and light](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/sun-and-light.md).
80
+
81
+ <!-- docs:check -->
82
+
83
+ ```ts
84
+ import type {TileflowThemeController} from '@tileflow/core/browser';
85
+
86
+ export function sunsetOverMadrid(controller: TileflowThemeController) {
87
+ // 19:15 in Madrid on 21 October 2026: the sun stands 1.5 degrees above the western horizon.
88
+ return controller.setSun({date: Date.UTC(2026, 9, 21, 17, 15), location: [-3.69, 40.45]});
89
+ }
90
+ ```
91
+
92
+ ## Glow, sun and stars
93
+
94
+ A low sun glows on the horizon towards it, in the theme's `glowColor` or, for a sun an application
95
+ placed, in the glow of its hour. The glow is strongest as the sun touches the horizon, fades by
96
+ mid-morning, and is gone once the sun is about nine degrees below. While the sun is low and above
97
+ the horizon, its disc shows too. The sun is the one the theme controller shows, so its
98
+ disc stands where the 3D scene's shadows say it is; without a controller, the theme's own sun.
99
+
100
+ Stars come out as the sky darkens: a night sky shows them, a day or dusk sky does not, and a
101
+ time-of-day blend or a placed sun moving into night brings them in with the dark. They sit on a
102
+ fixed dome, so they turn with the camera's bearing and pitch but not with its position.
103
+
104
+ The glow, the sun, and the stars draw beneath every map layer and only where nothing else has
105
+ drawn, so buildings and trees stand in front of them. With 3D terrain enabled, they are not
106
+ drawn.
107
+
108
+ ## Distance haze
109
+
110
+ Above 45 degrees of pitch, the far city fades into the sky's horizon color, so streets meet the sky
111
+ instead of ending in a sharp edge. The haze starts at the camera's distance to the map's center and
112
+ is complete where MapLibre stops drawing the ground, about 6.7 times farther, where the sky starts in
113
+ the same color. It comes in between 45 and 60 degrees, follows the current sky (theme blends
114
+ and a sky that follows the sun included), and leaves the globe alone. Place and POI labels above the
115
+ 3D layers stay clear.
116
+
117
+ Tileflow's 3D buildings stand on the tiles round the camera, up to a limit of 25 by default, which
118
+ end well before the horizon. Where that ring ends nearer than the horizon, the haze fits itself to
119
+ it: it is nine-tenths complete at the nearest point where the ring's far side shows across the view,
120
+ and complete soon after, never nearer than three camera distances for the nine-tenths, so a renderer
121
+ held to few tiles does not pull the haze in. Buildings on the ring's far side sink into the ground
122
+ over its last fifth, and nothing past the ring or past the complete haze is drawn. Buildings on the
123
+ near side of a ring held short by the tile limit keep standing.
124
+
125
+ The haze is drawn twice, from one model: Tileflow's 3D buildings, trees, and placed models haze
126
+ each pixel by its depth, and a band over the flat map, just under the first 3D layer, hazes the
127
+ flat ground by the depth its screen row has. Road names and one-way arrows that a style draws on the
128
+ ground before its 3D layers fade with the ground, and so do distant 3D shadows, contact shade, and
129
+ night floodlight. A style without a sky, or whose sky has no opaque horizon color, has no haze.
130
+
131
+ ## Browser integration
132
+
133
+ The compiler emits MapLibre `sky` in every concrete style and versioned `metadata['tileflow:sky']`
134
+ with the star brightness, the glow, and, for a theme in a time-of-day family, the family's skies;
135
+ `readTileflowSky` reads it and `tileflowSkyAtSun` mixes the sky of a sun's height. MapLibre draws the sky itself, so raw MapLibre consumers see it. On a
136
+ globe, the sky blends into MapLibre's atmosphere at world zooms; when a map also declares
137
+ `atmosphere`, the sky's colors replace the atmosphere's sky colors while its space background and
138
+ globe stars stay.
139
+
140
+ React, Vue, Svelte, and standalone browser capture draw the glow, the sun, the stars, and the flat
141
+ map's haze through `attachTileflowAtmosphere(map)`. A direct MapLibre integration calls that
142
+ function, or `attachTileflowSky(map)` or `attachTileflowSkyHaze(map)` from `@tileflow/core/browser`
143
+ for one of them alone, after constructing the map; each returns a cleanup function. The sky is the
144
+ custom layer `tileflow-sky` at the bottom of the layer stack, and the haze the custom layer
145
+ `tileflow-sky-haze` under the first 3D layer, or the first label layer without one. Tileflow's 3D
146
+ layers haze themselves. A sky that follows the sun needs the theme controller, which writes the
147
+ sky's colours; raw MapLibre consumers see each theme's own sky.
148
+
149
+ MapLibre applies its own fog only to 3D terrain; with 3D terrain enabled, the flat haze band is not
150
+ drawn and the terrain's fog takes its place. Other layers drawn by an application, such as
151
+ `fill-extrusion` layers, stay sharp. Native artifacts remove the sky during preparation and record
152
+ the removal; see the
153
+ [native artifact profile](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/native-artifact-profile.md).
@@ -106,8 +106,10 @@ custom layer draws; the map redraws whenever the sun changes.
106
106
 
107
107
  `setSun` places the sun by direction, or by the real sun of a date over a place given as
108
108
  `[longitude, latitude]`, the map's centre when it is left out. The themes keep the sun's colours,
109
- strengths and shadow strength; only its direction changes. `setSun(undefined)` returns it to the
110
- themes. This example assumes an existing MapLibre map and a controller for it:
109
+ strengths and shadow strength; only its direction changes. A map with a
110
+ [street sky](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/street-sky.md)
111
+ also takes the sky of the sun's hour from its time-of-day themes. `setSun(undefined)` returns both
112
+ to the themes. This example assumes an existing MapLibre map and a controller for it:
111
113
 
112
114
  <!-- docs:check -->
113
115
 
package/docs/trees-3d.md CHANGED
@@ -20,7 +20,8 @@ for its models unless an extension explicitly chooses `flat` or `auto`.
20
20
  ## Visibility and representation
21
21
 
22
22
  Use `createTileflowVegetation` for application controls. React, Vue and Svelte mount it automatically
23
- in interactive maps. The map chooses `vegetation.mode` independently of visibility:
23
+ in interactive maps, as part of the map's [3D scene](buildings-3d.md#the-maps-3d-scene). The map
24
+ chooses `vegetation.mode` independently of visibility:
24
25
 
25
26
  | Mode | 2D view | 3D view |
26
27
  | -------------- | ------------------- | ------------------- |
@@ -28,7 +29,8 @@ in interactive maps. The map chooses `vegetation.mode` independently of visibili
28
29
  | `flat` | Flat crowns/circles | Flat crowns/circles |
29
30
  | `auto` | Flat crowns/circles | Model trees |
30
31
 
31
- `viewMode` is explicit application state (`2d` by default), not a pitch threshold. Tilting the camera
32
+ `viewMode` is explicit application state, not a pitch threshold: `2d` by default for
33
+ `createTileflowVegetation`, and `3d` by default in the framework components and `createTileflowMap3D`. Tilting the camera
32
34
  does not change representation. Visibility defaults to `vegetation.visible ?? true`; OFF hides every
33
35
  representation. Turning trees off releases model buffers and tile readers. Controls apply immediately,
34
36
  without a grow/sink animation, and do not change building appearance or lighting.
@@ -70,7 +72,7 @@ export default defineMap({
70
72
  Use `refine({visible: false})` for initially hidden vegetation that can be enabled at runtime.
71
73
  Use `vegetation: disable()` to remove the module entirely; a runtime toggle cannot restore a module
72
74
  that is absent from the compiled map. Browser framework components accept `vegetation={false}` and
73
- `viewMode="3d"` (Vue: `:vegetation="false"`, `view-mode="3d"`). Image mode uses the compiled static
75
+ `viewMode="2d"` (Vue: `:vegetation="false"`, `view-mode="2d"`). Image mode uses the compiled static
74
76
  fallback and does not apply these interactive controls.
75
77
 
76
78
  ## Animate trees with the low-level renderer
@@ -260,7 +262,7 @@ Shadow direction stops lowering at 15° above the horizon; the actual scene ligh
260
262
 
261
263
  ## Options
262
264
 
263
- `minzoom` (15 by default, fading to full coverage at 15.25 like buildings) and `maxTiles` (25 by default, at most 64) bound where and how far trees
265
+ `minzoom` (15 by default, fading to full coverage at 15.25) and `maxTiles` (25 by default, at most 64) bound where and how far trees
264
266
  stand; trees thin out and fade with distance from the map's centre, and near trees take more detail
265
267
  than far ones. `canopySource` names a vector source with canopy points when they are not in the
266
268
  map's World source. `beforeId` places the layer before another one; `transformRequest` applies the
@@ -270,9 +272,12 @@ tile's download.
270
272
  ## Cost
271
273
 
272
274
  A near tree has 60 to 120 triangles and a far one about 20, and each form is one instanced draw per
273
- tile. The shared shadow map concentrates resolution on the view when its public camera methods
275
+ tile. Trees are placed in the worker the page's 3D layers share, which decodes each tile once for
276
+ all of them, nearest tiles first. The shared shadow map concentrates resolution on the view when its public camera methods
274
277
  provide a safe planar fit. It is drawn again when that focused area, the light, visible tree
275
- selection, fading or geometry changes. Unchanged views reuse it. Over the Champ de Mars in Paris,
278
+ selection, fading or geometry changes. Unchanged views reuse it. While the camera moves, the trees'
279
+ shadows follow their selection and fading in steps of a sixteenth of a tile, and exactly again once
280
+ it stops. Over the Champ de Mars in Paris,
276
281
  where OpenStreetMap maps every tree, about 13,000 trees stand in a pitched view at zoom 16.6.
277
282
 
278
283
  ## Limits
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tileflow/core",
3
- "version": "0.1.0-alpha.37",
3
+ "version": "0.1.0-alpha.39",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Typed Tileflow config and MapLibre style compiler.",
6
6
  "type": "module",
@@ -35,6 +35,11 @@
35
35
  "import": "./dist/index.js",
36
36
  "default": "./dist/index.js"
37
37
  },
38
+ "./3d": {
39
+ "types": "./dist/3d.d.ts",
40
+ "import": "./dist/3d.js",
41
+ "default": "./dist/3d.js"
42
+ },
38
43
  "./browser": {
39
44
  "types": "./dist/browser.d.ts",
40
45
  "import": "./dist/browser.js",
@@ -1 +0,0 @@
1
- "use strict";(()=>{var Re=new Set,De=!1;function Ue(e,n,t=2){let s=n&&n.length,o=s?n[0]*t:e.length;Re.size&&Re.clear();let r=bn(e,0,o,t,!0),u=[];if(!r||r.next===r.prev)return u;let i=0,l=0,c=0;if(s&&(r=Pn(e,n,r,t)),e.length>80*t){i=e[0],l=e[1];let a=i,f=l;for(let h=t;h<o;h+=t){let m=e[h],b=e[h+1];m<i&&(i=m),b<l&&(l=b),m>a&&(a=m),b>f&&(f=b)}c=Math.max(a-i,f-l),c=c!==0?32767/c:0}return We(r,u,i,l,c),u}function bn(e,n,t,s,o){let r=null;if(o===Xn(e,n,t,s)>0)for(let u=n;u<t;u+=s)r=mn(u/s|0,e[u],e[u+1],r);else for(let u=t-s;u>=n;u-=s)r=mn(u/s|0,e[u],e[u+1],r);return r&&Ae(r,r.next)&&(Te(r),r=r.next),r}function ae(e,n=e){let t=n===e,s=e,o;do o=!1,s!==s.next&&(Re.size===0||!Re.has(s))&&(Ae(s,s.next)||P(s.prev,s,s.next)===0)?((t||s===n)&&(n=s.prev),De=!0,Te(s),s=s.prev,o=!0):(t||s!==n)&&(s=s.next,o=!t);while(o||s!==n);return n}function We(e,n,t,s,o){o&&jn(e,t,s,o);let r=e,u=!1;for(;e.prev!==e.next;){let i=e.prev,l=e.next;if(P(i,e,l)<0&&(o?Bn(e,t,s,o):kn(e))){n.push(i.i,e.i,l.i),Te(e),e=l,r=l;continue}if(e=l,e===r){if(De=!1,e=ae(e),De){r=e;continue}if(!u){e=Fn(e,n),r=e,u=!0;continue}_n(e,n,t,s,o);break}}}function kn(e){let n=e.prev,t=e,s=e.next,o=n.x,r=t.x,u=s.x,i=n.y,l=t.y,c=s.y,a=Math.min(o,r,u),f=Math.min(i,l,c),h=Math.max(o,r,u),m=Math.max(i,l,c),b=s.next;for(;b!==n;){if(b.x>=a&&b.x<=h&&b.y>=f&&b.y<=m&&!(o===b.x&&i===b.y)&&Ne(o,i,r,l,u,c,b.x,b.y)&&P(b.prev,b,b.next)>=0)return!1;b=b.next}return!0}function Bn(e,n,t,s){let o=e.prev,r=e,u=e.next,i=o.x,l=r.x,c=u.x,a=o.y,f=r.y,h=u.y,m=Math.min(i,l,c),b=Math.min(a,f,h),d=Math.max(i,l,c),x=Math.max(a,f,h),U=$e(m,b,n,t,s),k=$e(d,x,n,t,s),M=e.prevZ;for(;M&&M.z>=U;){if(M.x>=m&&M.x<=d&&M.y>=b&&M.y<=x&&M!==u&&!(i===M.x&&a===M.y)&&Ne(i,a,l,f,c,h,M.x,M.y)&&P(M.prev,M,M.next)>=0)return!1;M=M.prevZ}let y=e.nextZ;for(;y&&y.z<=k;){if(y.x>=m&&y.x<=d&&y.y>=b&&y.y<=x&&y!==u&&!(i===y.x&&a===y.y)&&Ne(i,a,l,f,c,h,y.x,y.y)&&P(y.prev,y,y.next)>=0)return!1;y=y.nextZ}return!0}function Fn(e,n){let t=e,s=!1;do{let o=t.prev,r=t.next.next;yn(o,t,t.next,r,!1)&&Ie(o,r)&&Ie(r,o)&&(n.push(o.i,t.i,r.i),Te(t),Te(t.next),t=e=r,s=!0),t=t.next}while(t!==e);return s?ae(t):t}function _n(e,n,t,s,o){let r=e;do{let u=r.next.next;for(;u!==r.prev;){if(r.i!==u.i&&Nn(r,u)){let i=pn(r,u);r=ae(r,r.next),i=ae(i,i.next),We(r,n,t,s,o),We(i,n,t,s,o);return}u=u.next}r=r.next}while(r!==e)}var Ke=!1;function Pn(e,n,t,s){let o=[];for(let r=0,u=n.length;r<u;r++){let i=n[r]*s,l=r<u-1?n[r+1]*s:e.length,c=bn(e,i,l,s,!1);c===c.next&&Re.add(c),o.push(Hn(c))}o.sort(On),zn(e.length/s,n.length),dn(t,t),Ke=!0;for(let r=0;r<o.length;r++)t=Ln(o[r],t);return Ke=!1,ae(t)}function On(e,n){return e.x-n.x||e.y-n.y||(e.next.y-e.y)/(e.next.x-e.x)-(n.next.y-n.y)/(n.next.x-n.x)}function Ln(e,n){let t=Sn(e,n);if(!t)return n;let s=pn(t,e),o=s.next;return dn(t,o.next),ae(s,s.next),ae(t,t.next)}var xn=16,_=new Float64Array(0),He=0,Je=[],Qe=[];function zn(e,n){let t=Math.ceil((e+2*n)/xn)+n+2;_.length<t*4&&(_=new Float64Array(t*4)),He=0}function dn(e,n){let t=e;do{let s=He++;Je[s]=t;let o=t.x,r=t.y,u=t.x,i=t.y,l=0;do{let a=t.next;t.z=s,a.x<o&&(o=a.x),a.x>u&&(u=a.x),a.y<r&&(r=a.y),a.y>i&&(i=a.y),t=a}while(++l<xn&&t!==n);Qe[s]=t;let c=s*4;_[c]=o,_[c+1]=r,_[c+2]=u,_[c+3]=i}while(t!==n)}function Cn(e,n){let t=e.z*4;n.x<_[t]&&(_[t]=n.x),n.y<_[t+1]&&(_[t+1]=n.y),n.x>_[t+2]&&(_[t+2]=n.x),n.y>_[t+3]&&(_[t+3]=n.y)}function fn(e){let n=Qe[e];for(;n.prev.next!==n;)n=n.next;return Qe[e]=n,n}function hn(e){let n=Je[e];for(;n.prev.next!==n;)n=n.next;return Je[e]=n,n}function Sn(e,n){let t=n,s=e.x,o=e.y,r=-1/0,u;if(Ae(e,t))return t;for(let h=0,m=0;h<He;h++,m+=4){if(o<_[m+1]||o>_[m+3]||_[m]>s||_[m+2]<=r)continue;let b=fn(h);t=hn(h);do{if(t.prev.next===t){if(Ae(e,t.next))return t.next;if(o<=t.y&&o>=t.next.y&&t.next.y!==t.y){let d=t.x+(o-t.y)*(t.next.x-t.x)/(t.next.y-t.y);if(d<=s&&d>r&&(r=d,u=t.x<t.next.x?t:t.next,d===s))return u}}t=t.next}while(t!==b)}if(!u)return null;let i=u.x,l=u.y,c=Math.min(o,l),a=Math.max(o,l),f=1/0;for(let h=0,m=0;h<He;h++,m+=4){if(_[m+2]<i||_[m]>s||_[m+3]<c||_[m+1]>a)continue;let b=fn(h);t=hn(h);do{if(t.prev.next===t&&s>=t.x&&t.x>=i&&s!==t.x&&Ne(o<l?s:r,o,i,l,o<l?r:s,o,t.x,t.y)){let d=Math.abs(o-t.y)/(s-t.x);(Ie(t,e)||t.y===o&&t.next.y===o&&t.next.x>s)&&(d<f||d===f&&(t.x>u.x||t.x===u.x&&Zn(u,t)))&&(u=t,f=d)}t=t.next}while(t!==b)}return u}function Zn(e,n){return P(e.prev,e,n.prev)<0&&P(n.next,e,e.next)<0}var J=[],de=[],ce=new Uint32Array(0),we=new Uint32Array(0),ve=new Uint32Array(256);function jn(e,n,t,s){let o=e,r=0;do o.z=$e(o.x,o.y,n,t,s),J[r++]=o,o=o.next;while(o!==e);En(r);let u=null;for(let i=0;i<r;i++){let l=J[i];l.prevZ=u,u&&(u.nextZ=l),u=l}u.nextZ=null,J.fill(null,0,r),de.fill(null,0,r)}function En(e){if(e<=32){for(let n=1;n<e;n++){let t=J[n],s=t.z,o=n-1;for(;o>=0&&J[o].z>s;)J[o+1]=J[o],o--;J[o+1]=t}return}ce.length<e&&(ce=new Uint32Array(e),we=new Uint32Array(e),de=new Array(e));for(let n=0;n<e;n++)ce[n]=J[n].z;je(e,J,ce,de,we,0),je(e,de,we,J,ce,8),je(e,J,ce,de,we,16),je(e,de,we,J,ce,24)}function je(e,n,t,s,o,r){ve.fill(0);for(let i=0;i<e;i++)ve[t[i]>>>r&255]++;let u=0;for(let i=0;i<256;i++){let l=ve[i];ve[i]=u,u+=l}for(let i=0;i<e;i++){let l=t[i],c=ve[l>>>r&255]++;s[c]=n[i],o[c]=l}}function $e(e,n,t,s,o){return e=(e-t)*o|0,n=(n-s)*o|0,e=(e|e<<8)&16711935,e=(e|e<<4)&252645135,e=(e|e<<2)&858993459,e=(e|e<<1)&1431655765,n=(n|n<<8)&16711935,n=(n|n<<4)&252645135,n=(n|n<<2)&858993459,n=(n|n<<1)&1431655765,e|n<<1}function Hn(e){let n=e,t=e;do(n.x<t.x||n.x===t.x&&n.y<t.y)&&(t=n),n=n.next;while(n!==e);return t}function Ne(e,n,t,s,o,r,u,i){return(o-u)*(n-i)>=(e-u)*(r-i)&&(e-u)*(s-i)>=(t-u)*(n-i)&&(t-u)*(r-i)>=(o-u)*(s-i)}function Nn(e,n){let t=Ae(e,n)&&P(e.prev,e,e.next)>0&&P(n.prev,n,n.next)>0;return e.next.i!==n.i&&(t||Ie(e,n)&&Ie(n,e)&&(P(e.prev,e,n.prev)!==0||P(e,n.prev,n)!==0))&&!Vn(e,n)&&(t||Yn(e,n))}function P(e,n,t){return(n.y-e.y)*(t.x-n.x)-(n.x-e.x)*(t.y-n.y)}function Ae(e,n){return e.x===n.x&&e.y===n.y}function yn(e,n,t,s,o=!0){let r=P(e,n,t),u=P(e,n,s),i=P(t,s,e),l=P(t,s,n);return(r>0&&u<0||r<0&&u>0)&&(i>0&&l<0||i<0&&l>0)?!0:o?!!(r===0&&Ee(e,t,n)||u===0&&Ee(e,s,n)||i===0&&Ee(t,e,s)||l===0&&Ee(t,n,s)):!1}function Ee(e,n,t){return n.x<=Math.max(e.x,t.x)&&n.x>=Math.min(e.x,t.x)&&n.y<=Math.max(e.y,t.y)&&n.y>=Math.min(e.y,t.y)}function Vn(e,n){let t=Math.min(e.x,n.x),s=Math.max(e.x,n.x),o=Math.min(e.y,n.y),r=Math.max(e.y,n.y),u=e;do{let i=u.next;if(u.x>s&&i.x>s||u.x<t&&i.x<t||u.y>r&&i.y>r||u.y<o&&i.y<o){u=i;continue}if(u.i!==e.i&&i.i!==e.i&&u.i!==n.i&&i.i!==n.i&&yn(u,i,e,n))return!0;u=i}while(u!==e);return!1}function Ie(e,n){return P(e.prev,e,e.next)<0?P(e,n,e.next)>=0&&P(e,e.prev,n)>=0:P(e,n,e.prev)<0||P(e,e.next,n)<0}function Yn(e,n){let t=e,s=!1,o=(e.x+n.x)/2,r=(e.y+n.y)/2;do{let u=t.next;t.y>r!=u.y>r&&o<(u.x-t.x)*(r-t.y)/(u.y-t.y)+t.x&&(s=!s),t=u}while(t!==e);return s}function pn(e,n){let t=qe(e.i,e.x,e.y),s=qe(n.i,n.x,n.y),o=e.next,r=n.prev;return e.next=n,n.prev=e,t.next=o,o.prev=t,s.next=t,t.prev=s,r.next=s,s.prev=r,s}function mn(e,n,t,s){let o=qe(e,n,t);return s?(o.next=s.next,o.prev=s,s.next.prev=o,s.next=o):(o.prev=o,o.next=o),o}function Te(e){e.next.prev=e.prev,e.prev.next=e.next,e.prevZ&&(e.prevZ.nextZ=e.nextZ),e.nextZ&&(e.nextZ.prevZ=e.prevZ),Ke&&Cn(e.prev,e.next)}function qe(e,n,t){return{i:e,x:n,y:t,prev:null,next:null,z:0,prevZ:null,nextZ:null}}function Xn(e,n,t,s){let o=0;for(let r=n,u=t-s;r<t;r+=s)o+=(e[u]-e[r])*(e[r+1]+e[u+1]),u=r;return o}var Gn=.7,Dn=Math.tan(Math.PI/6),Wn=Math.tan(35*Math.PI/180),Kn=Math.tan(Math.PI/18),ke=.65,Be=.4;function vn(e,n,t,s,o,r,u){let i=String(t.roof_shape??"");if(!i||i==="flat"||i==="sawtooth"||et(e,u))return;let l=$n(e);if(!l||Math.abs(en(e))<Gn*4*l.halfU*l.halfV)return;let c=l.u,a=l.v,f=l.halfU*r,h=l.halfV*r;f<h&&([c,a,f,h]=[a,c,h,f]);let m=wn(t.roof_direction),b=m===void 0?void 0:[Math.sin(m*Math.PI/180),-Math.cos(m*Math.PI/180)];(b!==void 0?Math.abs(b[0]*c[0]+b[1]*c[1])>Math.abs(b[0]*a[0]+b[1]*a[1]):t.roof_orientation==="across")&&i!=="skillion"&&([c,a,f,h]=[a,c,h,f]);let x=Qn(i,f,h,b&&[Mn(b,c),Mn(b,a)]);if(!x)return;let U=Math.max(0,o-s),k=wn(t.roof_height),M,y;if(k!==void 0&&k>0)y=o,M=Math.max(s+Math.min(2,U/2),o-k);else{let p=Math.min(Jn(i,h),U*.6,30);t.height_source==="height"?(y=o,M=o-p):(M=o,y=o+p)}let I=y-M;if(!(I>=.3))return;let[R,V]=l.centre,B=(p,A)=>{let w=(p-R)*r,g=(A-V)*r;return[w*c[0]+g*c[1],w*a[0]+g*a[1]]},j=(p,A)=>[R+(p*c[0]+A*a[0])/r,V+(p*c[1]+A*a[1])/r],Q=(p,A)=>{let w=1/0;for(let[g,N,E]of x)w=Math.min(w,g*p+N*A+E);return Math.max(0,w)},$=(p,A)=>M+I*Q(...B(p,A)),L=[];for(let p=0;p+1<n.length;p+=2)L.push(...B(n[p],n[p+1]));en(L)<0&&nt(L);let z=1.01,q=[-f*z,-h*z,f*z,-h*z,f*z,h*z,-f*z,h*z],Y=[];for(let[p,A]of x.entries()){let w=q;for(let[F,H]of x.entries()){if(F===p)continue;let C=A[0]-H[0],X=A[1]-H[1],ne=A[2]-H[2];if(Math.abs(C)+Math.abs(X)+Math.abs(ne)<1e-9){F<p&&(w=[]);continue}if(w=gn(w,C,X,ne),w.length<6)break}if(w.length<6)continue;let g=L,N=w.length/2;for(let F=0;F<N&&g.length>=6;F+=1){let[H,C]=[w[F*2],w[F*2+1]],[X,ne]=[w[(F+1)%N*2],w[(F+1)%N*2+1]];g=gn(g,ne-C,H-X,-(ne-C)*H-(H-X)*C)}if(g.length<6||Math.abs(en(g))<1e-6)continue;let E=[];for(let F=0;F<g.length;F+=2)E.push(...j(g[F],g[F+1]));let se=Ue(E);if(se.length===0)continue;let ee=I*A[0],oe=I*A[1],T=ee*c[0]+oe*a[0],v=ee*c[1]+oe*a[1],O=Math.hypot(T,v,1);Y.push({ring:E,triangles:se,normal:[-T/O,-v/O]})}if(Y.length!==0)return{eaves:M,ridge:y,faces:Y,heightAt:$,breaks(p,A,w,g){let[N,E]=B(p,A),[se,ee]=B(w,g),oe=[];for(let T=0;T<x.length;T+=1)for(let v=T+1;v<x.length;v+=1){let O=(he,S)=>(x[T][0]-x[v][0])*he+(x[T][1]-x[v][1])*S+(x[T][2]-x[v][2]),F=O(N,E),H=O(se,ee);if(F===H||F*H>0)continue;let C=F/(F-H);if(C<=1e-4||C>=1-1e-4)continue;let X=N+(se-N)*C,ne=E+(ee-E)*C;x[T][0]*X+x[T][1]*ne+x[T][2]<=Q(X,ne)+1e-6&&oe.push(C)}return[...new Set(oe.map(T=>Math.round(T*1e6)/1e6))].sort((T,v)=>T-v)}}}function Jn(e,n){switch(e){case"skillion":return 2*n*Kn;case"pyramidal":return n*Wn;case"gambrel":case"mansard":return n*.8;case"dome":case"onion":case"round":return n;default:return n*Dn}}function Qn(e,n,t,s){let o=n,r=t,u=[[0,-1/r,1],[0,1/r,1]],i=c=>c.flatMap(a=>[1,-1].flatMap(f=>{let h=a==="v"?r:o,m=ke/(Be*r)*f,b=(1-ke)/((1-Be)*r)*f,d=x=>a==="v"?[0,-x]:[-x,0];return[[...d(m),ke/(Be*r)*h],[...d(b),ke+(1-ke)/((1-Be)*r)*(h-Be*r)]]})),l=[.99,.8,.5,.2];switch(e){case"gabled":return u;case"saltbox":{let c=r/3;return[[0,-1/(r-c),1+c/(r-c)],[0,1/(r+c),1-c/(r+c)]]}case"hipped":return[...u,[-1/r,0,o/r],[1/r,0,o/r]];case"half_hipped":return[...u,[-1/r,0,.5+o/r],[1/r,0,.5+o/r]];case"pyramidal":return[...u,[-1/o,0,1],[1/o,0,1]];case"gambrel":return i(["v"]);case"mansard":return i(["v","u"]);case"skillion":{let[c,a]=s??[0,1],f=Math.abs(c)*o+Math.abs(a)*r;return f>0?[[-c/(2*f),-a/(2*f),.5]]:void 0}case"round":return[[0,0,1],...l.flatMap(c=>{let a=Math.sqrt(1-c*c);return[1,-1].map(f=>[0,-f*c/(r*a),1/a])})];case"dome":case"onion":return[[0,0,1],...l.flatMap(c=>{let a=Math.sqrt(1-c*c);return Array.from({length:12},(f,h)=>{let m=h*Math.PI/6;return[-c*Math.cos(m)/(o*a),-c*Math.sin(m)/(r*a),1/a]})})];default:return}}function gn(e,n,t,s){let o=e.length/2,r=[];for(let u=0;u<o;u+=1){let i=e[u*2],l=e[u*2+1],c=e[(u+1)%o*2],a=e[(u+1)%o*2+1],f=n*i+t*l+s,h=n*c+t*a+s;if(f<=0&&r.push(i,l),f<0&&h>0||f>0&&h<0){let m=f/(f-h);r.push(i+(c-i)*m,l+(a-l)*m)}}return r}function $n(e){let n=qn(e);if(n.length<3)return;let t,s=1/0;for(let o=0;o<n.length;o+=1){let[r,u]=n[o],[i,l]=n[(o+1)%n.length],c=Math.hypot(i-r,l-u);if(c===0)continue;let a=[(i-r)/c,(l-u)/c],f=[-a[1],a[0]],[h,m,b,d]=[1/0,-1/0,1/0,-1/0];for(let[U,k]of n){let M=U*a[0]+k*a[1],y=U*f[0]+k*f[1];h=Math.min(h,M),m=Math.max(m,M),b=Math.min(b,y),d=Math.max(d,y)}let x=(m-h)*(d-b);if(x<s){s=x;let U=(h+m)/2,k=(b+d)/2;t={centre:[U*a[0]+k*f[0],U*a[1]+k*f[1]],u:a,v:f,halfU:(m-h)/2,halfV:(d-b)/2}}}return t&&t.halfU>0&&t.halfV>0?t:void 0}function qn(e){let n=[];for(let r=0;r+1<e.length;r+=2)n.push([e[r],e[r+1]]);n.sort((r,u)=>r[0]-u[0]||r[1]-u[1]);let t=(r,u,i)=>(u[0]-r[0])*(i[1]-r[1])-(u[1]-r[1])*(i[0]-r[0]),s=[];for(let r of n){for(;s.length>=2&&t(s.at(-2),s.at(-1),r)<=0;)s.pop();s.push(r)}let o=[];for(let r of n.reverse()){for(;o.length>=2&&t(o.at(-2),o.at(-1),r)<=0;)o.pop();o.push(r)}return[...s.slice(0,-1),...o.slice(0,-1)]}function et(e,n){let[t,s,o,r]=[1/0,-1/0,1/0,-1/0];for(let i=0;i+1<e.length;i+=2)t=Math.min(t,e[i]),s=Math.max(s,e[i]),o=Math.min(o,e[i+1]),r=Math.max(r,e[i+1]);let u=e.length/2;for(let i=0;i<u;i+=1){let l=e[i*2],c=e[i*2+1],a=e[(i+1)%u*2],f=e[(i+1)%u*2+1];if(t<0&&l===t&&a===t||s>n&&l===s&&a===s||o<0&&c===o&&f===o||r>n&&c===r&&f===r)return!0}return!1}function en(e){let n=0,t=e.length/2;for(let s=0;s<t;s+=1){let o=(s+1)%t;n+=e[s*2]*e[o*2+1]-e[o*2]*e[s*2+1]}return n/2}function nt(e){let n=[];for(let t=0;t<e.length;t+=2)n.push([e[t],e[t+1]]);n.reverse(),e.splice(0,e.length,...n.flat())}function Mn(e,n){return e[0]*n[0]+e[1]*n[1]}function wn(e){let n=typeof e=="string"?Number(e):e;return typeof n=="number"&&Number.isFinite(n)?n:void 0}var Mt=Object.freeze({surface:0,secondary:1,glass:2,accent:3,roof:4}),wt=Object.freeze({unclassified:0,wall:1,door:2,roof:3,window:4,lamp:5,logo:6}),vt=Object.freeze([1,1,4,1,3]),Rt=Object.freeze({levels:3,vertices:2e5,triangles:2e5,footprintPoints:4096,radius:2e3,height:1e3,models:512,bytes:16*1024*1024});function nn(e,n,t){for(let s of e){let o=!1;for(let r=0,u=s.length-2;r<s.length;u=r,r+=2){let i=s[r],l=s[r+1],c=s[u],a=s[u+1];l>t!=a>t&&n<(c-i)*(t-l)/(a-l)+i&&(o=!o)}if(o)return!0}return!1}function In(e,n,t){for(let s of n){let[o,r,u,i]=An(s);for(let l of e){let[c,a,f,h]=An(l);if(!(c>u-t||f<o+t||a>i-t||h<r+t)&&(Rn(l,s,t)||Rn(s,l,t)))return!0}}return!1}function Rn(e,n,t){let s=(o,r)=>nn([n],o,r)&&ot(n,o,r)>t;for(let o=0;o+1<e.length;o+=2)if(s(e[o],e[o+1]))return!0;for(let o=0,r=e.length-2;o<e.length;r=o,o+=2){let[u,i,l,c]=[e[r],e[r+1],e[o],e[o+1]],a=[0,1];for(let f=0,h=n.length-2;f<n.length;h=f,f+=2){let m=tt(u,i,l,c,n[h],n[h+1],n[f],n[f+1]);m!==void 0&&a.push(m)}if(a.length!==2){a.sort((f,h)=>f-h);for(let f=1;f<a.length;f+=1){let h=(a[f-1]+a[f])/2;if(s(u+(l-u)*h,i+(c-i)*h))return!0}}}return!1}function tt(e,n,t,s,o,r,u,i){let l=t-e,c=s-n,a=u-o,f=i-r,h=l*f-c*a;if(h===0)return;let m=((o-e)*f-(r-n)*a)/h,b=((o-e)*c-(r-n)*l)/h;return m>0&&m<1&&b>=0&&b<=1?m:void 0}function ot(e,n,t){let s=1/0;for(let o=0,r=e.length-2;o<e.length;r=o,o+=2){let[u,i,l,c]=[e[r],e[r+1],e[o],e[o+1]],a=(l-u)**2+(c-i)**2,f=a>0?Math.max(0,Math.min(1,((n-u)*(l-u)+(t-i)*(c-i))/a)):0;s=Math.min(s,Math.hypot(n-(u+(l-u)*f),t-(i+(c-i)*f)))}return s}function An(e){let[n,t,s,o]=[1/0,1/0,-1/0,-1/0];for(let r=0;r+1<e.length;r+=2)n=Math.min(n,e[r]),s=Math.max(s,e[r]),t=Math.min(t,e[r+1]),o=Math.max(o,e[r+1]);return[n,t,s,o]}var te=8192,tn=28,rt={commercial:1,active:2,destination:3},st={residential:1,...Object.fromEntries(["civic","commercial","education","entertainment","medical","transportation"].map(e=>[e,2])),...Object.fromEntries(["agricultural","industrial","military","outbuilding","religious","service"].map(e=>[e,3]))},it={dining:1,entertainment:2,nightlife:3},on={...Object.fromEntries(["alcoholic_beverage_venue","bar","beer","biergarten","brewery","casino","cocktail_bar","dance_club","karaoke","lounge","music_venue","night_club","nightclub","pub","wine_bar"].map(e=>[e,3])),...Object.fromEntries(["arts_centre","bowling_alley","cinema","comedy_club","concert_hall","movie_theater","opera","performing_arts_venue","theater","theatre","theatre_venue"].map(e=>[e,2])),...Object.fromEntries(["casual_eatery","fast_food","fast_food_restaurant","food_court","restaurant"].map(e=>[e,1]))},ut=6,lt=.5,ct=.5;function Un(e,n){let t=te/e.extent,s=n.tileMetres/te,o=n.defaultHeight??5,r=Number.isFinite(n.edgeRadius)?Math.max(0,Math.min(1,n.edgeRadius))/s:0,u=new sn,i=new Ve,l=new Ve,c=[],a=[],f=0,h=mt(e,n);for(let[y,I]of e.features.entries()){if(I.type!=="Polygon"||c.length>=65535)continue;let R=I.properties,V=R.hide_3d;if(V===!0||V===1||V==="1"||V==="true")continue;let B=Math.min(6e3,Tn(R.render_height)??o),j=Math.min(B,Tn(R.render_min_height)??0),Q=rt[String(R.building_tone??"")]??0,$=st[String(R.class??"")]??0,L=h.get(y)??0,z=c.length,q=!1;for(let Y of bt(I.geometry,t)){let p=Y.map(dt).filter(v=>v.length>=6);if(p.length===0||Ye(p[0])===0)continue;let[A,w]=xt(p[0]);if(n.masks?.length&&(nn(n.masks,A,w)||In(p,n.masks,ct/s)))continue;let g={cell:Math.min(7,Math.max(0,Math.floor(w/1024)))*8+Math.min(7,Math.max(0,Math.floor(A/1024))),bounds:[1/0,1/0,-1/0,-1/0,B+.1],roofsFrom:i.length,roofsCount:0,wallsFrom:l.length,wallsCount:0};for(let v of p)for(let O=0;O<v.length;O+=2)g.bounds[0]=Math.min(g.bounds[0],v[O]-1),g.bounds[1]=Math.min(g.bounds[1],v[O+1]-1),g.bounds[2]=Math.max(g.bounds[2],v[O]+1),g.bounds[3]=Math.max(g.bounds[3],v[O+1]+1);q=!0,f=Math.max(f,B);let N=Number.isSafeInteger(I.id),E=rn(N?I.id*.0731:A*.0731+w*.19137+z*1.618),se=!N&&p[0].some(v=>v<=0||v>=te)?rn(B*7.31+j*3.17+Q):E,ee={base:j,top:B,jitter:se,seed:E,tone:Q,group:$,use:L,cx:A,cy:w,index:z},oe=p.map((v,O)=>ht(v,O,r,n.cornerAngle));p=oe.map(({ring:v})=>v);let T=Y.length===1&&p.length===1?vn(Y[0],p[0],R,j,B,s,te):void 0;T?(f=Math.max(f,T.ridge),g.bounds[4]=Math.max(g.bounds[4],T.ridge+.1),ft(T,ee,u,i)):at(p,ee,u,i);for(let[v,O]of p.entries()){let F=Ye(O)>0,H=v===0?F?-1:1:F?1:-1,C=O.length/2,X=S=>{let Z=(S%C+C)%C;return[O[Z*2],O[Z*2+1]]},ne=S=>{let[Z,W]=X(S-1),[G,D]=X(S),[ie,me]=X(S+1);if(fe(Z,W,G,D)||fe(G,D,ie,me))return!1;let be=Math.hypot(G-Z,D-W),ue=Math.hypot(ie-G,me-D);if(!be||!ue)return!1;let Fe=-(D-W)/be*H,_e=(G-Z)/be*H;return(Fe*(ie-G)+_e*(me-D))/ue>lt},Xe=(S,Z)=>T?T.heightAt(S,Z):B,he=0;for(let S=0;S<C;S+=1){let[Z,W]=X(S),[G,D]=X(S+1),ie=Math.hypot(G-Z,D-W);if(ie===0||fe(Z,W,G,D))continue;let me=-(D-W)/ie*H,be=(G-Z)/ie*H,ue=oe[v].normals[S]??[me,be],Fe=oe[v].normals[(S+1)%C]??[me,be],_e=K=>{let pe=ue[0]+(Fe[0]-ue[0])*K,ge=ue[1]+(Fe[1]-ue[1])*K,le=Math.hypot(pe,ge)||1;return[pe/le,ge/le]},re=[0,...T?T.breaks(Z,W,G,D):[],1];for(let K=0;K+1<re.length;K+=1){let pe=K===0,ge=K+2===re.length,le=Z+(G-Z)*re[K],Pe=W+(D-W)*re[K],Oe=Z+(G-Z)*re[K+1],Le=W+(D-W)*re[K+1],Me=Math.hypot(Oe-le,Le-Pe)/te*n.tileMetres,ze=(pe&&ne(S)?1:0)|(ge&&ne(S+1)?2:0)|(v>0?4:0),Ce=(pe&&un(Z,W)?1:0)|(ge&&un(G,D)?2:0),Ge=he+Me,Se=_e(re[K]),Ze=_e(re[K+1]),cn=Xe(le,Pe),an=Xe(Oe,Le),xe=u.write(ee,le,Pe,0,Se[0],Se[1],1,he,ze,Me,0,Ce,cn);u.write(ee,Oe,Le,0,Ze[0],Ze[1],1,Ge,ze,Me,1,Ce,an),u.write(ee,le,Pe,1,Se[0],Se[1],1,he,ze,Me,0,Ce,cn),u.write(ee,Oe,Le,1,Ze[0],Ze[1],1,Ge,ze,Me,1,Ce,an),l.push(xe,xe+1,xe+2,xe+1,xe+3,xe+2),he=Ge}}}g.roofsCount=i.length-g.roofsFrom,g.wallsCount=l.length-g.wallsFrom,a.push(g)}q&&c.push(R)}if(u.count===0)return;let m=new Uint32Array(i.length+l.length),b=[],d=0,x=i.length,U=i.values(),k=l.values();a.sort((y,I)=>y.cell-I.cell);let M=-1;for(let y of a){y.cell!==M&&(M=y.cell,b.push({bounds:[...y.bounds],roofsFrom:d,roofsCount:0,wallsFrom:x,wallsCount:0}));let I=b.at(-1);for(let R=0;R<5;R+=1)I.bounds[R]=R<2?Math.min(I.bounds[R],y.bounds[R]):Math.max(I.bounds[R],y.bounds[R]);m.set(U.subarray(y.roofsFrom,y.roofsFrom+y.roofsCount),d),m.set(k.subarray(y.wallsFrom,y.wallsFrom+y.wallsCount),x),d+=y.roofsCount,x+=y.wallsCount,I.roofsCount+=y.roofsCount,I.wallsCount+=y.wallsCount}return{groups:b,vertices:u.bytes(),indices:m,wallsFrom:i.length,top:f,features:c}}function at(e,n,t,s){let o=[],r=[];for(let l of e)r.push(o.length/2),o.push(...l);r.push(o.length/2);let u=(l,c)=>{let a=r.findIndex((d,x)=>l>=d&&l<r[x+1]),[f,h]=[r[a],r[a+1]-1];if(c<f||c>h)return!1;let[m,b]=l<c?[l,c]:[c,l];return b-m!==1&&!(m===f&&b===h)?!1:!fe(o[l*2],o[l*2+1],o[c*2],o[c*2+1])},i=Ue(o,r.slice(1,-1),2);for(let l=0;l+2<i.length;l+=3){let c=[i[l],i[l+1],i[l+2]],a=0;for(let f=0;f<3;f+=1)u(c[(f+1)%3],c[(f+2)%3])&&(a|=1<<f);for(let[f,h]of c.entries())s.push(t.write(n,o[h*2],o[h*2+1],1,0,0,0,0,0,0,f,a))}}function ft(e,n,t,s){for(let o of e.faces){let r=o.ring,u=r.length/2,i=(l,c)=>{let[a,f]=l<c?[l,c]:[c,l];return f-a!==1&&!(a===0&&f===u-1)?!1:!fe(r[l*2],r[l*2+1],r[c*2],r[c*2+1])};for(let l=0;l+2<o.triangles.length;l+=3){let c=[o.triangles[l],o.triangles[l+1],o.triangles[l+2]],a=0;for(let f=0;f<3;f+=1)i(c[(f+1)%3],c[(f+2)%3])&&(a|=1<<f);for(let[f,h]of c.entries()){let m=r[h*2],b=r[h*2+1];s.push(t.write(n,m,b,1,o.normal[0],o.normal[1],0,0,0,0,f,a,e.heightAt(m,b)))}}}}function ht(e,n,t,s=Math.PI/6){if(t<1)return{ring:e,normals:[]};let o=e.length/2,r=Ye(e)>0,u=n===0?r?-1:1:r?1:-1,i=[],l=[],c=(a,f,h)=>{a=ye(a),f=ye(f),!(i.at(-2)===a&&i.at(-1)===f)&&(i.push(a,f),l.push(h))};for(let a=0;a<o;a+=1){let f=(a+o-1)%o,h=(a+1)%o,[m,b]=[e[f*2],e[f*2+1]],[d,x]=[e[a*2],e[a*2+1]],[U,k]=[e[h*2],e[h*2+1]],M=Math.hypot(d-m,x-b),y=Math.hypot(U-d,k-x);if(!M||!y||un(d,x)||fe(m,b,d,x)||fe(d,x,U,k)){c(d,x);continue}let[I,R]=[(d-m)/M,(x-b)/M],[V,B]=[(U-d)/y,(k-x)/y],j=Math.atan2(I*B-R*V,I*V+R*B);if(j*u>=-.08||Math.abs(j)>Math.PI*.85){c(d,x);continue}let Q=Math.tan(Math.abs(j)/2),$=Math.min(t*Q,M*.2,y*.2),L=$/Q;if(L<1){c(d,x);continue}let[z,q]=[-R*u,I*u],[Y,p]=[d-I*$-z*L,x-R*$-q*L],A=Math.atan2(q,z),w=Math.max(s<=Math.PI/6?2:1,Math.ceil(Math.abs(j)/Math.max(Math.PI/6,Math.min(Math.PI/3,s))));for(let g=0;g<=w;g+=1){let N=A+j*g/w,E=[Math.cos(N),Math.sin(N)];c(Y+E[0]*L,p+E[1]*L,E)}}return i[0]===i.at(-2)&&i[1]===i.at(-1)&&(i.splice(-2),l.pop()),i.length>=6?{ring:i,normals:l}:{ring:e,normals:[]}}var sn=class{#e=new ArrayBuffer(4096*tn);#n=new DataView(this.#e);count=0;write(n,t,s,o,r,u,i,l,c,a,f=0,h=0,m=n.top){let b=tn;if((this.count+1)*b>this.#e.byteLength){let U=new ArrayBuffer(this.#e.byteLength*2);new Uint8Array(U).set(new Uint8Array(this.#e)),this.#e=U,this.#n=new DataView(U)}let d=this.#n,x=this.count*b;return d.setUint16(x,ye(t),!0),d.setUint16(x+2,ye(s),!0),d.setUint16(x+4,Math.min(65535,Math.round(n.base*10)),!0),d.setUint16(x+6,Math.min(65535,Math.max(0,Math.round(m*10))),!0),d.setInt8(x+8,Math.round(r*127)),d.setInt8(x+9,Math.round(u*127)),d.setUint8(x+10,o),d.setUint8(x+11,i),d.setUint16(x+12,Math.round(l*10)%65536,!0),d.setUint8(x+14,Math.round(n.jitter*255)),d.setUint8(x+15,Math.round(n.seed*255)),d.setUint16(x+16,ye(n.cx),!0),d.setUint16(x+18,ye(n.cy),!0),d.setUint8(x+20,n.tone+4*n.group),d.setUint8(x+21,n.use),d.setUint8(x+22,c),d.setUint8(x+23,Math.min(255,Math.round(a))),d.setUint16(x+24,n.index,!0),d.setUint8(x+26,f),d.setUint8(x+27,h),this.count+=1,this.count-1}bytes(){return new Uint8Array(this.#e.slice(0,this.count*tn))}},Ve=class{#e=new Uint32Array(8192);length=0;push(...n){if(this.length+n.length>this.#e.length){let t=new Uint32Array(Math.max(this.length+n.length,this.#e.length*2));t.set(this.#e),this.#e=t}for(let t of n)this.#e[this.length++]=t}values(){return this.#e.subarray(0,this.length)}};function mt(e,n){let t=new Map;if(e.features.some(i=>i.properties.night_use!==void 0)){for(let[i,l]of e.features.entries()){let c=it[String(l.properties.night_use??"")];c&&t.set(i,c)}return t}let s=n.places;if(!s||s.features.length===0)return t;let o=ut/n.tileMetres*e.extent,r=[];for(let[i,l]of e.features.entries()){if(l.type!=="Polygon")continue;let c=l.properties.hide_3d;if(c===!0||c===1||c==="1"||c==="true")continue;let a=[1/0,1/0,-1/0,-1/0];for(let f of l.geometry)for(let h=0;h+1<f.length;h+=2)a[0]=Math.min(a[0],f[h]),a[1]=Math.min(a[1],f[h+1]),a[2]=Math.max(a[2],f[h]),a[3]=Math.max(a[3],f[h+1]);r.push({index:i,rings:l.geometry,box:a})}let u=e.extent/s.extent;for(let i of s.features){if(i.type!=="Point")continue;let l=i.properties,c=on[String(l.type??"")]??on[String(l.subclass??"")]??on[String(l.class??"")];if(c)for(let a of i.geometry)for(let f=0;f+1<a.length;f+=2){let h=a[f]*u,m=a[f+1]*u,b,d,x=o;for(let k of r){let[M,y,I,R]=k.box;if(h<M-o||h>I+o||m<y-o||m>R+o)continue;let V=!1;for(let B of k.rings)for(let j=0,Q=B.length-2;j+1<B.length;Q=j,j+=2){let $=B[Q],L=B[Q+1],z=B[j],q=B[j+1];q>m!=L>m&&h<($-z)*(m-q)/(L-q)+z&&(V=!V);let Y=z-$,p=q-L,A=Y*Y+p*p,w=A?Math.min(1,Math.max(0,((h-$)*Y+(m-L)*p)/A)):0,g=Math.hypot(h-$-w*Y,m-L-w*p);g<x&&(x=g,d=k.index)}if(V){b=k.index;break}}let U=b??d;U!==void 0&&t.set(U,Math.max(t.get(U)??0,c))}}return t}function ye(e){return Math.min(te,Math.max(0,Math.round(e)))}function Tn(e){let n=Number(e);return Number.isFinite(n)&&n>0?n:void 0}function bt(e,n){let t=[],s;for(let o of e){let r=[];for(let i=0;i+1<o.length;i+=2){let l=o[i]*n,c=o[i+1]*n;r.length&&l===r.at(-2)&&c===r.at(-1)||r.push(l,c)}if(r.length>=6&&r[0]===r.at(-2)&&r[1]===r.at(-1)&&(r.length-=2),r.length<6)continue;let u=Math.sign(Ye(r));u&&(s??=u,u===s||t.length===0?t.push([r]):t.at(-1).push(r))}return t}function Ye(e){let n=0,t=e.length/2;for(let s=0;s<t;s+=1){let o=(s+1)%t;n+=e[s*2]*e[o*2+1]-e[o*2]*e[s*2+1]}return n}function xt(e){let n=0,t=0,s=e.length/2;for(let o=0;o<s;o+=1)n+=e[o*2],t+=e[o*2+1];return[n/s,t/s]}function un(e,n){return e<=0||n<=0||e>=te||n>=te}function fe(e,n,t,s){let o=te;return e<=0&&t<=0||e>=o&&t>=o||n<=0&&s<=0||n>=o&&s>=o}function dt(e){let n=te,t=[...e],s=[[0,0,o=>o>=0],[0,n,o=>o<=n],[1,0,o=>o>=0],[1,n,o=>o<=n]];for(let[o,r,u]of s){let i=[],l=t.length/2;for(let c=0;c<l;c+=1){let a=[t[c*2],t[c*2+1]],f=[t[(c+1)%l*2],t[(c+1)%l*2+1]],h=u(a[o]),m=u(f[o]);if(h&&i.push(a[0],a[1]),h!==m){let b=(r-a[o])/(f[o]-a[o]);i.push(a[0]+(f[0]-a[0])*b,a[1]+(f[1]-a[1])*b)}}if(t=i,t.length<6)return[]}return t}function rn(e){let n=Math.sin(e*12.9898+78.233)*43758.5453;return n-Math.floor(n)}var ln=globalThis;ln.onmessage=({data:e})=>{try{let n=Un(e.layer,e.options);ln.postMessage({mesh:n},n?[n.vertices.buffer,n.indices.buffer]:[])}catch(n){ln.postMessage({error:String(n)})}};})();