@react-x11/components 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/README.md +95 -34
  2. package/dist/charts/node.d.ts +1 -1
  3. package/dist/charts/node.d.ts.map +1 -1
  4. package/dist/charts/node.js +6 -2
  5. package/dist/charts/node.js.map +1 -1
  6. package/dist/embed/client.d.ts +48 -3
  7. package/dist/embed/client.d.ts.map +1 -1
  8. package/dist/embed/client.js +79 -5
  9. package/dist/embed/client.js.map +1 -1
  10. package/dist/embed/index.d.ts +1 -1
  11. package/dist/embed/index.d.ts.map +1 -1
  12. package/dist/embed/index.js +1 -1
  13. package/dist/embed/index.js.map +1 -1
  14. package/dist/html/node.d.ts +3 -1
  15. package/dist/html/node.d.ts.map +1 -1
  16. package/dist/html/node.js +7 -5
  17. package/dist/html/node.js.map +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +1 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/{markdown → internal/markdown}/ast.d.ts +12 -0
  23. package/dist/internal/markdown/ast.d.ts.map +1 -0
  24. package/dist/internal/markdown/ast.js +22 -0
  25. package/dist/internal/markdown/ast.js.map +1 -0
  26. package/dist/internal/markdown/parse.d.ts.map +1 -0
  27. package/dist/{markdown → internal/markdown}/parse.js +112 -22
  28. package/dist/internal/markdown/parse.js.map +1 -0
  29. package/dist/internal/markdown/stringify.d.ts +4 -0
  30. package/dist/internal/markdown/stringify.d.ts.map +1 -0
  31. package/dist/internal/markdown/stringify.js +684 -0
  32. package/dist/internal/markdown/stringify.js.map +1 -0
  33. package/dist/internal/markdown/tags.d.ts.map +1 -0
  34. package/dist/internal/markdown/tags.js.map +1 -0
  35. package/dist/internal/scroll.d.ts +3 -2
  36. package/dist/internal/scroll.d.ts.map +1 -1
  37. package/dist/internal/scroll.js +1 -0
  38. package/dist/internal/scroll.js.map +1 -1
  39. package/dist/maps/anchors.d.ts +98 -0
  40. package/dist/maps/anchors.d.ts.map +1 -0
  41. package/dist/maps/anchors.js +531 -0
  42. package/dist/maps/anchors.js.map +1 -0
  43. package/dist/maps/controller.d.ts +283 -0
  44. package/dist/maps/controller.d.ts.map +1 -0
  45. package/dist/maps/controller.js +743 -0
  46. package/dist/maps/controller.js.map +1 -0
  47. package/dist/maps/gl/buckets.d.ts +95 -0
  48. package/dist/maps/gl/buckets.d.ts.map +1 -0
  49. package/dist/maps/gl/buckets.js +362 -0
  50. package/dist/maps/gl/buckets.js.map +1 -0
  51. package/dist/maps/gl/build-worker.d.ts +13 -0
  52. package/dist/maps/gl/build-worker.d.ts.map +1 -0
  53. package/dist/maps/gl/build-worker.js +44 -0
  54. package/dist/maps/gl/build-worker.js.map +1 -0
  55. package/dist/maps/gl/color.d.ts +8 -0
  56. package/dist/maps/gl/color.d.ts.map +1 -0
  57. package/dist/maps/gl/color.js +57 -0
  58. package/dist/maps/gl/color.js.map +1 -0
  59. package/dist/maps/gl/cover.d.ts +51 -0
  60. package/dist/maps/gl/cover.d.ts.map +1 -0
  61. package/dist/maps/gl/cover.js +124 -0
  62. package/dist/maps/gl/cover.js.map +1 -0
  63. package/dist/maps/gl/index.d.ts +3 -0
  64. package/dist/maps/gl/index.d.ts.map +1 -0
  65. package/dist/maps/gl/index.js +20 -0
  66. package/dist/maps/gl/index.js.map +1 -0
  67. package/dist/maps/gl/markers.d.ts +28 -0
  68. package/dist/maps/gl/markers.d.ts.map +1 -0
  69. package/dist/maps/gl/markers.js +60 -0
  70. package/dist/maps/gl/markers.js.map +1 -0
  71. package/dist/maps/gl/overlays.d.ts +68 -0
  72. package/dist/maps/gl/overlays.d.ts.map +1 -0
  73. package/dist/maps/gl/overlays.js +300 -0
  74. package/dist/maps/gl/overlays.js.map +1 -0
  75. package/dist/maps/gl/pane.d.ts +12 -0
  76. package/dist/maps/gl/pane.d.ts.map +1 -0
  77. package/dist/maps/gl/pane.js +37 -0
  78. package/dist/maps/gl/pane.js.map +1 -0
  79. package/dist/maps/gl/placement.d.ts +111 -0
  80. package/dist/maps/gl/placement.d.ts.map +1 -0
  81. package/dist/maps/gl/placement.js +567 -0
  82. package/dist/maps/gl/placement.js.map +1 -0
  83. package/dist/maps/gl/renderer.d.ts +352 -0
  84. package/dist/maps/gl/renderer.d.ts.map +1 -0
  85. package/dist/maps/gl/renderer.js +1127 -0
  86. package/dist/maps/gl/renderer.js.map +1 -0
  87. package/dist/maps/gl/shaders.d.ts +120 -0
  88. package/dist/maps/gl/shaders.d.ts.map +1 -0
  89. package/dist/maps/gl/shaders.js +451 -0
  90. package/dist/maps/gl/shaders.js.map +1 -0
  91. package/dist/maps/gl/store.d.ts +115 -0
  92. package/dist/maps/gl/store.d.ts.map +1 -0
  93. package/dist/maps/gl/store.js +469 -0
  94. package/dist/maps/gl/store.js.map +1 -0
  95. package/dist/maps/gl/text.d.ts +154 -0
  96. package/dist/maps/gl/text.d.ts.map +1 -0
  97. package/dist/maps/gl/text.js +382 -0
  98. package/dist/maps/gl/text.js.map +1 -0
  99. package/dist/maps/gl/view.d.ts +54 -0
  100. package/dist/maps/gl/view.d.ts.map +1 -0
  101. package/dist/maps/gl/view.js +974 -0
  102. package/dist/maps/gl/view.js.map +1 -0
  103. package/dist/maps/index.d.ts +1 -1
  104. package/dist/maps/index.d.ts.map +1 -1
  105. package/dist/maps/index.js +216 -52
  106. package/dist/maps/index.js.map +1 -1
  107. package/dist/maps/labels.d.ts +37 -16
  108. package/dist/maps/labels.d.ts.map +1 -1
  109. package/dist/maps/labels.js +188 -119
  110. package/dist/maps/labels.js.map +1 -1
  111. package/dist/maps/node.d.ts +162 -56
  112. package/dist/maps/node.d.ts.map +1 -1
  113. package/dist/maps/node.js +607 -515
  114. package/dist/maps/node.js.map +1 -1
  115. package/dist/maps/overlay.d.ts +72 -9
  116. package/dist/maps/overlay.d.ts.map +1 -1
  117. package/dist/maps/overlay.js +108 -28
  118. package/dist/maps/overlay.js.map +1 -1
  119. package/dist/maps/paint.d.ts +3 -0
  120. package/dist/maps/paint.d.ts.map +1 -1
  121. package/dist/maps/paint.js.map +1 -1
  122. package/dist/maps/proj.d.ts +14 -0
  123. package/dist/maps/proj.d.ts.map +1 -1
  124. package/dist/maps/proj.js +28 -0
  125. package/dist/maps/proj.js.map +1 -1
  126. package/dist/maps/renderer.d.ts +69 -0
  127. package/dist/maps/renderer.d.ts.map +1 -0
  128. package/dist/maps/renderer.js +66 -0
  129. package/dist/maps/renderer.js.map +1 -0
  130. package/dist/maps/sources.d.ts +25 -3
  131. package/dist/maps/sources.d.ts.map +1 -1
  132. package/dist/maps/sources.js +17 -0
  133. package/dist/maps/sources.js.map +1 -1
  134. package/dist/maps/theme.d.ts +19 -0
  135. package/dist/maps/theme.d.ts.map +1 -0
  136. package/dist/maps/theme.js +44 -0
  137. package/dist/maps/theme.js.map +1 -0
  138. package/dist/maps/tiles.d.ts +117 -17
  139. package/dist/maps/tiles.d.ts.map +1 -1
  140. package/dist/maps/tiles.js +163 -30
  141. package/dist/maps/tiles.js.map +1 -1
  142. package/dist/maps/types.d.ts +188 -20
  143. package/dist/maps/types.d.ts.map +1 -1
  144. package/dist/markdown/expressions.d.ts +1 -1
  145. package/dist/markdown/expressions.d.ts.map +1 -1
  146. package/dist/markdown/expressions.js.map +1 -1
  147. package/dist/markdown/index.d.ts +7 -6
  148. package/dist/markdown/index.d.ts.map +1 -1
  149. package/dist/markdown/index.js +9 -7
  150. package/dist/markdown/index.js.map +1 -1
  151. package/dist/markdown/spans.d.ts +1 -1
  152. package/dist/markdown/spans.d.ts.map +1 -1
  153. package/dist/media-player/index.d.ts +11 -3
  154. package/dist/media-player/index.d.ts.map +1 -1
  155. package/dist/media-player/index.js +13 -2
  156. package/dist/media-player/index.js.map +1 -1
  157. package/dist/rich-text-editor/clipboard.d.ts +23 -0
  158. package/dist/rich-text-editor/clipboard.d.ts.map +1 -0
  159. package/dist/rich-text-editor/clipboard.js +307 -0
  160. package/dist/rich-text-editor/clipboard.js.map +1 -0
  161. package/dist/rich-text-editor/collab.d.ts +32 -0
  162. package/dist/rich-text-editor/collab.d.ts.map +1 -0
  163. package/dist/rich-text-editor/collab.js +55 -0
  164. package/dist/rich-text-editor/collab.js.map +1 -0
  165. package/dist/rich-text-editor/commands.d.ts +50 -0
  166. package/dist/rich-text-editor/commands.d.ts.map +1 -0
  167. package/dist/rich-text-editor/commands.js +376 -0
  168. package/dist/rich-text-editor/commands.js.map +1 -0
  169. package/dist/rich-text-editor/drag.d.ts +68 -0
  170. package/dist/rich-text-editor/drag.d.ts.map +1 -0
  171. package/dist/rich-text-editor/drag.js +148 -0
  172. package/dist/rich-text-editor/drag.js.map +1 -0
  173. package/dist/rich-text-editor/html.d.ts +145 -0
  174. package/dist/rich-text-editor/html.d.ts.map +1 -0
  175. package/dist/rich-text-editor/html.js +516 -0
  176. package/dist/rich-text-editor/html.js.map +1 -0
  177. package/dist/rich-text-editor/index.d.ts +263 -0
  178. package/dist/rich-text-editor/index.d.ts.map +1 -0
  179. package/dist/rich-text-editor/index.js +822 -0
  180. package/dist/rich-text-editor/index.js.map +1 -0
  181. package/dist/rich-text-editor/inline.d.ts +94 -0
  182. package/dist/rich-text-editor/inline.d.ts.map +1 -0
  183. package/dist/rich-text-editor/inline.js +252 -0
  184. package/dist/rich-text-editor/inline.js.map +1 -0
  185. package/dist/rich-text-editor/keymap.d.ts +71 -0
  186. package/dist/rich-text-editor/keymap.d.ts.map +1 -0
  187. package/dist/rich-text-editor/keymap.js +393 -0
  188. package/dist/rich-text-editor/keymap.js.map +1 -0
  189. package/dist/rich-text-editor/keys.d.ts +29 -0
  190. package/dist/rich-text-editor/keys.d.ts.map +1 -0
  191. package/dist/rich-text-editor/keys.js +93 -0
  192. package/dist/rich-text-editor/keys.js.map +1 -0
  193. package/dist/rich-text-editor/look.d.ts +71 -0
  194. package/dist/rich-text-editor/look.d.ts.map +1 -0
  195. package/dist/rich-text-editor/look.js +342 -0
  196. package/dist/rich-text-editor/look.js.map +1 -0
  197. package/dist/rich-text-editor/markdown.d.ts +18 -0
  198. package/dist/rich-text-editor/markdown.d.ts.map +1 -0
  199. package/dist/rich-text-editor/markdown.js +496 -0
  200. package/dist/rich-text-editor/markdown.js.map +1 -0
  201. package/dist/rich-text-editor/nodes.d.ts +135 -0
  202. package/dist/rich-text-editor/nodes.d.ts.map +1 -0
  203. package/dist/rich-text-editor/nodes.js +397 -0
  204. package/dist/rich-text-editor/nodes.js.map +1 -0
  205. package/dist/rich-text-editor/render.d.ts +68 -0
  206. package/dist/rich-text-editor/render.d.ts.map +1 -0
  207. package/dist/rich-text-editor/render.js +500 -0
  208. package/dist/rich-text-editor/render.js.map +1 -0
  209. package/dist/rich-text-editor/schema.d.ts +16 -0
  210. package/dist/rich-text-editor/schema.d.ts.map +1 -0
  211. package/dist/rich-text-editor/schema.js +386 -0
  212. package/dist/rich-text-editor/schema.js.map +1 -0
  213. package/dist/rich-text-editor/store.d.ts +11 -0
  214. package/dist/rich-text-editor/store.d.ts.map +1 -0
  215. package/dist/rich-text-editor/store.js +43 -0
  216. package/dist/rich-text-editor/store.js.map +1 -0
  217. package/dist/rich-text-editor/suggest.d.ts +127 -0
  218. package/dist/rich-text-editor/suggest.d.ts.map +1 -0
  219. package/dist/rich-text-editor/suggest.js +0 -0
  220. package/dist/rich-text-editor/suggest.js.map +1 -0
  221. package/dist/rich-text-editor/tables.d.ts +41 -0
  222. package/dist/rich-text-editor/tables.d.ts.map +1 -0
  223. package/dist/rich-text-editor/tables.js +204 -0
  224. package/dist/rich-text-editor/tables.js.map +1 -0
  225. package/dist/rich-text-editor/toolbar.d.ts +58 -0
  226. package/dist/rich-text-editor/toolbar.d.ts.map +1 -0
  227. package/dist/rich-text-editor/toolbar.js +548 -0
  228. package/dist/rich-text-editor/toolbar.js.map +1 -0
  229. package/dist/rich-text-editor/view.d.ts +368 -0
  230. package/dist/rich-text-editor/view.d.ts.map +1 -0
  231. package/dist/rich-text-editor/view.js +1968 -0
  232. package/dist/rich-text-editor/view.js.map +1 -0
  233. package/dist/rich-text-editor/virtual.d.ts +54 -0
  234. package/dist/rich-text-editor/virtual.d.ts.map +1 -0
  235. package/dist/rich-text-editor/virtual.js +262 -0
  236. package/dist/rich-text-editor/virtual.js.map +1 -0
  237. package/dist/richtext/node.d.ts +25 -2
  238. package/dist/richtext/node.d.ts.map +1 -1
  239. package/dist/richtext/node.js +46 -24
  240. package/dist/richtext/node.js.map +1 -1
  241. package/dist/terminal/backends.d.ts +4 -1
  242. package/dist/terminal/backends.d.ts.map +1 -1
  243. package/dist/terminal/backends.js.map +1 -1
  244. package/dist/terminal/index.d.ts +18 -7
  245. package/dist/terminal/index.d.ts.map +1 -1
  246. package/dist/terminal/index.js +28 -11
  247. package/dist/terminal/index.js.map +1 -1
  248. package/package.json +23 -4
  249. package/src/charts/node.ts +6 -2
  250. package/src/embed/client.ts +102 -8
  251. package/src/embed/index.ts +2 -0
  252. package/src/html/node.ts +7 -5
  253. package/src/index.ts +2 -0
  254. package/src/{markdown → internal/markdown}/ast.ts +24 -7
  255. package/src/{markdown → internal/markdown}/parse.ts +134 -24
  256. package/src/internal/markdown/stringify.ts +772 -0
  257. package/src/internal/scroll.ts +5 -3
  258. package/src/maps/anchors.ts +607 -0
  259. package/src/maps/controller.ts +999 -0
  260. package/src/maps/gl/buckets.ts +457 -0
  261. package/src/maps/gl/build-worker.ts +55 -0
  262. package/src/maps/gl/color.ts +62 -0
  263. package/src/maps/gl/cover.ts +170 -0
  264. package/src/maps/gl/index.ts +23 -0
  265. package/src/maps/gl/markers.ts +83 -0
  266. package/src/maps/gl/overlays.ts +379 -0
  267. package/src/maps/gl/pane.ts +42 -0
  268. package/src/maps/gl/placement.ts +816 -0
  269. package/src/maps/gl/renderer.ts +1576 -0
  270. package/src/maps/gl/shaders.ts +486 -0
  271. package/src/maps/gl/store.ts +566 -0
  272. package/src/maps/gl/text.ts +495 -0
  273. package/src/maps/gl/view.ts +1224 -0
  274. package/src/maps/index.ts +270 -81
  275. package/src/maps/labels.ts +214 -121
  276. package/src/maps/node.ts +763 -629
  277. package/src/maps/overlay.ts +148 -43
  278. package/src/maps/paint.ts +3 -0
  279. package/src/maps/proj.ts +31 -0
  280. package/src/maps/renderer.ts +155 -0
  281. package/src/maps/sources.ts +38 -3
  282. package/src/maps/theme.ts +51 -0
  283. package/src/maps/tiles.ts +191 -33
  284. package/src/maps/types.ts +188 -20
  285. package/src/markdown/expressions.ts +5 -1
  286. package/src/markdown/index.ts +14 -11
  287. package/src/markdown/spans.ts +1 -1
  288. package/src/media-player/index.ts +18 -4
  289. package/src/rich-text-editor/clipboard.ts +439 -0
  290. package/src/rich-text-editor/collab.ts +91 -0
  291. package/src/rich-text-editor/commands.ts +448 -0
  292. package/src/rich-text-editor/dom-globals.d.ts +23 -0
  293. package/src/rich-text-editor/drag.ts +211 -0
  294. package/src/rich-text-editor/html.ts +619 -0
  295. package/src/rich-text-editor/index.ts +1331 -0
  296. package/src/rich-text-editor/inline.ts +345 -0
  297. package/src/rich-text-editor/keymap.ts +510 -0
  298. package/src/rich-text-editor/keys.ts +119 -0
  299. package/src/rich-text-editor/look.ts +427 -0
  300. package/src/rich-text-editor/markdown.ts +653 -0
  301. package/src/rich-text-editor/nodes.ts +497 -0
  302. package/src/rich-text-editor/render.ts +800 -0
  303. package/src/rich-text-editor/schema.ts +431 -0
  304. package/src/rich-text-editor/store.ts +39 -0
  305. package/src/rich-text-editor/suggest.ts +597 -0
  306. package/src/rich-text-editor/tables.ts +272 -0
  307. package/src/rich-text-editor/toolbar.ts +724 -0
  308. package/src/rich-text-editor/view.ts +2287 -0
  309. package/src/rich-text-editor/virtual.ts +347 -0
  310. package/src/richtext/node.ts +59 -30
  311. package/src/terminal/backends.ts +4 -1
  312. package/src/terminal/index.ts +43 -15
  313. package/dist/markdown/ast.d.ts.map +0 -1
  314. package/dist/markdown/ast.js +0 -17
  315. package/dist/markdown/ast.js.map +0 -1
  316. package/dist/markdown/parse.d.ts.map +0 -1
  317. package/dist/markdown/parse.js.map +0 -1
  318. package/dist/markdown/tags.d.ts.map +0 -1
  319. package/dist/markdown/tags.js.map +0 -1
  320. /package/dist/{markdown → internal/markdown}/parse.d.ts +0 -0
  321. /package/dist/{markdown → internal/markdown}/tags.d.ts +0 -0
  322. /package/dist/{markdown → internal/markdown}/tags.js +0 -0
  323. /package/src/{markdown → internal/markdown}/tags.ts +0 -0
@@ -0,0 +1,457 @@
1
+ // A tile's features as GPU-ready streams — the GL renderer's whole CPU side.
2
+ //
3
+ // The retained renderer (`../paint.ts`) turns a tile into *pixels*, once per
4
+ // zoom level and style, and composites the pixels; this turns it into
5
+ // *geometry*, once per tile, and draws the geometry every frame. That is the
6
+ // entire difference, and it is why nothing here knows about a camera, a
7
+ // scale or a style colour: a bucket is valid for every zoom, every pan,
8
+ // every display scale and every palette, so a style change is a uniform
9
+ // write and a fractional zoom is a matrix.
10
+ //
11
+ // Two streams, both made of 8-byte **records**, and one idea behind both:
12
+ // the GPU draws *segment i* from records `i` and `i + 1` of a stream, as one
13
+ // instance — so a polyline of `k` points is `k` consecutive records and
14
+ // costs nothing to "tessellate". There is no extrusion, no join geometry and
15
+ // no triangulation on the CPU at all: the vertex shader widens a segment into
16
+ // a quad and the fragment shader rounds its ends, so joins, caps and
17
+ // antialiasing come out of one distance function (see `./shaders.ts`).
18
+ //
19
+ // - **line** — `x:int16, y:int16, dist:float32`. `dist` is how far along
20
+ // its polyline the point is, in tile units, for dashes.
21
+ // - **fill** — `x:int16, y:int16, ax:int16, ay:int16`. Every ring is closed
22
+ // explicitly and each record carries its ring's first point as `a`, so
23
+ // segment `i` with `a` is a triangle of the ring's fan. Drawn into the
24
+ // stencil buffer, a fan per ring is a polygon with its holes — no
25
+ // triangulator, the same non-zero rule the retained renderer fills with.
26
+ // The same records drawn as segments are the polygon's outline.
27
+ //
28
+ // Between two polylines (or rings) sits one {@link BREAK} record. The two
29
+ // segments that touch it collapse in the vertex shader, which is cheaper
30
+ // than the index buffer that would otherwise say where every part ends.
31
+ //
32
+ // Coordinates are normalised to {@link TILE_EXTENT} because `extent` is a
33
+ // per-*layer* field that real tiles vary (Shortbread cuts `streets`, `land`,
34
+ // `ocean` and `water_polygons` at 2048 and everything else at 4096), and a
35
+ // per-draw extent would be one more uniform on the hottest loop in the
36
+ // renderer for no benefit.
37
+ import { GeomType, GeometryBuffer } from '../mvt.js';
38
+ import type { FeatureCursor, VectorTile } from '../mvt.js';
39
+ import type { PreparedLayer, PreparedStyle } from '../paint.js';
40
+ import type { MapStyleLayer } from '../style.js';
41
+ import { buildTileLabels } from '../anchors.js';
42
+ import type { GlLabelData } from '../anchors.js';
43
+
44
+ /** Tile units across a tile, after normalisation. */
45
+ export const TILE_EXTENT = 4096;
46
+
47
+ /** The int16 that marks the end of a polyline or ring. Real coordinates are
48
+ * clamped to one above it. */
49
+ export const BREAK = -32768;
50
+
51
+ /** Bytes per record, in either stream. */
52
+ export const RECORD_BYTES = 8;
53
+
54
+ /** A `circle` layer's points are line-stream records too, one per point —
55
+ * a disc each — with a sentinel after them. */
56
+ export type DrawKind = 'fill' | 'line' | 'circle';
57
+
58
+ /** What one style layer draws from one tile. */
59
+ export interface LayerDraw {
60
+ /** Index into {@link PreparedStyle.layers}. */
61
+ layer: number;
62
+ kind: DrawKind;
63
+ /**
64
+ * `[firstRecord, recordCount]` pairs into the kind's stream. A range of
65
+ * `n` records is `n - 1` segment instances; the last record of every
66
+ * range is a {@link BREAK}, so the instance that would join a range to
67
+ * whatever follows it is a degenerate one.
68
+ */
69
+ ranges: number[];
70
+ }
71
+
72
+ /** What building a tile's buckets cost and produced. */
73
+ export interface BucketStats {
74
+ features: number;
75
+ /** Records written to the line stream, sentinels included. */
76
+ lineRecords: number;
77
+ /** Records written to the fill stream, closing points and sentinels
78
+ * included. */
79
+ fillRecords: number;
80
+ /** Membership groups — distinct sets of style layers a feature belongs
81
+ * to. What decides how many draw ranges a layer needs. */
82
+ groups: number;
83
+ ms: number;
84
+ }
85
+
86
+ /** One tile, ready for upload. Plain typed arrays and a small draw list, so
87
+ * it can be built on a worker and transferred without a copy. */
88
+ export interface GlTileData {
89
+ /** The line stream: `lineRecords` records over one buffer. */
90
+ line: ArrayBuffer;
91
+ lineRecords: number;
92
+ /** The fill stream: `fillRecords` records. */
93
+ fill: ArrayBuffer;
94
+ fillRecords: number;
95
+ /** Indexed by style layer; a hole where this tile has nothing to draw. */
96
+ draws: (LayerDraw | undefined)[];
97
+ /** Where the tile's labels could go — see `../anchors.ts`. */
98
+ labels?: GlLabelData;
99
+ /** A raster tile's image — RGBA, not premultiplied, as a source decodes
100
+ * it — in place of streams: its own texture, and no style. */
101
+ raster?: { width: number; height: number; pixels: Uint8Array };
102
+ stats: BucketStats;
103
+ }
104
+
105
+ /** A raster tile as the store keeps it: its image, and nothing to build. */
106
+ export function rasterTileData(
107
+ width: number,
108
+ height: number,
109
+ pixels: Uint8Array,
110
+ ): GlTileData {
111
+ return {
112
+ line: new ArrayBuffer(0),
113
+ lineRecords: 0,
114
+ fill: new ArrayBuffer(0),
115
+ fillRecords: 0,
116
+ draws: [],
117
+ raster: { width, height, pixels },
118
+ stats: { features: 0, lineRecords: 0, fillRecords: 0, groups: 0, ms: 0 },
119
+ };
120
+ }
121
+
122
+ /** Whether a style layer is one this renderer draws from a tile at all. */
123
+ function drawable(
124
+ layer: MapStyleLayer,
125
+ ): layer is Extract<MapStyleLayer, { type: DrawKind }> {
126
+ // Symbol layers are labels, which are placed per frame and not bucketed.
127
+ return (
128
+ (layer.type === 'fill' ||
129
+ layer.type === 'line' ||
130
+ layer.type === 'circle') &&
131
+ layer.visible !== false
132
+ );
133
+ }
134
+
135
+ /** `paint.ts`'s rule: a `line` layer takes polygons too — stroking a ring is
136
+ * what a style asking for an outline means — a `fill` only polygons, and a
137
+ * `circle` only points. */
138
+ function takes(type: DrawKind, geometry: GeomType): boolean {
139
+ if (type === 'circle') return geometry === GeomType.Point;
140
+ return type === 'fill'
141
+ ? geometry === GeomType.Polygon
142
+ : geometry === GeomType.LineString || geometry === GeomType.Polygon;
143
+ }
144
+
145
+ /** Points, a record each, for a circle layer's discs. */
146
+ function emitPoints(
147
+ line: Stream,
148
+ coords: Int32Array,
149
+ from: number,
150
+ to: number,
151
+ k: number,
152
+ ): void {
153
+ line.reserve(to - from);
154
+ for (let i = from; i < to; i++) {
155
+ const at = line.count;
156
+ line.i16[at * 4] = coord(coords[i * 2], k);
157
+ line.i16[at * 4 + 1] = coord(coords[i * 2 + 1], k);
158
+ line.f32[at * 2 + 1] = 0;
159
+ line.count++;
160
+ }
161
+ }
162
+
163
+ const globals = globalThis as { performance?: { now(): number } };
164
+ const now = (): number => globals.performance?.now() ?? Date.now();
165
+
166
+ /** A growable stream of 8-byte records, viewed both ways. */
167
+ export class Stream {
168
+ bytes: ArrayBuffer;
169
+ i16: Int16Array;
170
+ f32: Float32Array;
171
+ count = 0;
172
+
173
+ constructor(records: number) {
174
+ this.bytes = new ArrayBuffer(records * RECORD_BYTES);
175
+ this.i16 = new Int16Array(this.bytes);
176
+ this.f32 = new Float32Array(this.bytes);
177
+ }
178
+
179
+ reserve(more: number): void {
180
+ const want = (this.count + more) * RECORD_BYTES;
181
+ if (want <= this.bytes.byteLength) return;
182
+ let size = this.bytes.byteLength || RECORD_BYTES * 64;
183
+ while (size < want) size *= 2;
184
+ const next = new ArrayBuffer(size);
185
+ new Uint8Array(next).set(
186
+ new Uint8Array(this.bytes, 0, this.count * RECORD_BYTES),
187
+ );
188
+ this.bytes = next;
189
+ this.i16 = new Int16Array(next);
190
+ this.f32 = new Float32Array(next);
191
+ }
192
+
193
+ /** The records written, trimmed to size — what is kept and uploaded. */
194
+ take(): ArrayBuffer {
195
+ return this.bytes.slice(0, this.count * RECORD_BYTES);
196
+ }
197
+ }
198
+
199
+ /** A tile coordinate, normalised and kept clear of the sentinel. */
200
+ function coord(v: number, k: number): number {
201
+ const s = Math.round(v * k);
202
+ return s < -32767 ? -32767 : s > 32767 ? 32767 : s;
203
+ }
204
+
205
+ /** A line stream's sentinel. The caller has reserved the record. */
206
+ export function pushBreak(line: Stream): void {
207
+ const at = line.count * 4;
208
+ line.i16[at] = BREAK;
209
+ line.i16[at + 1] = BREAK;
210
+ line.f32[line.count * 2 + 1] = 0;
211
+ line.count++;
212
+ }
213
+
214
+ /** A fill stream's sentinel. The caller has reserved the record. */
215
+ export function pushFillBreak(fill: Stream): void {
216
+ const at = fill.count * 4;
217
+ fill.i16[at] = BREAK;
218
+ fill.i16[at + 1] = BREAK;
219
+ fill.i16[at + 2] = BREAK;
220
+ fill.i16[at + 3] = BREAK;
221
+ fill.count++;
222
+ }
223
+
224
+ /** One part as a polyline: its points, then (for a ring) its first point
225
+ * again, then a sentinel. */
226
+ function emitLine(
227
+ line: Stream,
228
+ coords: Int32Array,
229
+ from: number,
230
+ to: number,
231
+ k: number,
232
+ closed: boolean,
233
+ ): void {
234
+ const count = to - from;
235
+ if (count < 2) return;
236
+ line.reserve(count + 2);
237
+ const i16 = line.i16;
238
+ const f32 = line.f32;
239
+ let dist = 0;
240
+ let px = coord(coords[from * 2], k);
241
+ let py = coord(coords[from * 2 + 1], k);
242
+ const firstX = px;
243
+ const firstY = py;
244
+ let at = line.count;
245
+ i16[at * 4] = px;
246
+ i16[at * 4 + 1] = py;
247
+ f32[at * 2 + 1] = 0;
248
+ at++;
249
+ for (let i = from + 1; i < to; i++) {
250
+ const x = coord(coords[i * 2], k);
251
+ const y = coord(coords[i * 2 + 1], k);
252
+ dist += Math.hypot(x - px, y - py);
253
+ i16[at * 4] = x;
254
+ i16[at * 4 + 1] = y;
255
+ f32[at * 2 + 1] = dist;
256
+ at++;
257
+ px = x;
258
+ py = y;
259
+ }
260
+ if (closed && (px !== firstX || py !== firstY)) {
261
+ dist += Math.hypot(firstX - px, firstY - py);
262
+ i16[at * 4] = firstX;
263
+ i16[at * 4 + 1] = firstY;
264
+ f32[at * 2 + 1] = dist;
265
+ at++;
266
+ }
267
+ line.count = at;
268
+ pushBreak(line);
269
+ }
270
+
271
+ /** One ring as a fan: its points, closed, each carrying the ring's first
272
+ * point as the fan's apex, then a sentinel. */
273
+ function emitRing(
274
+ fill: Stream,
275
+ coords: Int32Array,
276
+ from: number,
277
+ to: number,
278
+ k: number,
279
+ ): void {
280
+ const count = to - from;
281
+ if (count < 3) return;
282
+ fill.reserve(count + 2);
283
+ const i16 = fill.i16;
284
+ const ax = coord(coords[from * 2], k);
285
+ const ay = coord(coords[from * 2 + 1], k);
286
+ let at = fill.count * 4;
287
+ for (let i = from; i < to; i++) {
288
+ i16[at] = coord(coords[i * 2], k);
289
+ i16[at + 1] = coord(coords[i * 2 + 1], k);
290
+ i16[at + 2] = ax;
291
+ i16[at + 3] = ay;
292
+ at += 4;
293
+ }
294
+ i16[at] = ax;
295
+ i16[at + 1] = ay;
296
+ i16[at + 2] = ax;
297
+ i16[at + 3] = ay;
298
+ fill.count += count + 1;
299
+ pushFillBreak(fill);
300
+ }
301
+
302
+ interface Group {
303
+ mask: number;
304
+ features: number[];
305
+ fill: [number, number];
306
+ line: [number, number];
307
+ }
308
+
309
+ /**
310
+ * Build a tile's buckets for a style.
311
+ *
312
+ * The style is walked in the same **runs** the retained painter uses —
313
+ * consecutive style layers over one source layer — and each run's features
314
+ * are read once. The new part is what happens to a feature's answer: every
315
+ * feature gets a *membership mask*, the set of the run's layers that select
316
+ * it, and features are written grouped by mask. A road network is fourteen
317
+ * style layers over `streets` whose casing and fill select exactly the same
318
+ * roads, so `motorway-casing` and `motorway` share one group and one copy of
319
+ * the geometry: two draws over the same records, with a different width and
320
+ * colour. Without the grouping every feature would be copied once per layer
321
+ * that draws it — twice for every road — and without it being contiguous
322
+ * a layer would be one draw per feature.
323
+ */
324
+ export function buildTileBuckets(
325
+ tile: VectorTile,
326
+ prepared: PreparedStyle,
327
+ ): GlTileData {
328
+ const started = now();
329
+ const line = new Stream(1024);
330
+ const fill = new Stream(1024);
331
+ const buffer = new GeometryBuffer();
332
+ const draws: (LayerDraw | undefined)[] = new Array(prepared.layers.length);
333
+ let features = 0;
334
+ let groupCount = 0;
335
+ let cursor: FeatureCursor | null = null;
336
+
337
+ for (const run of prepared.runs) {
338
+ const source = tile.layers.get(run.sourceLayer);
339
+ if (!source || source.length === 0) continue;
340
+ const active: { index: number; prepared: PreparedLayer; type: DrawKind }[] =
341
+ [];
342
+ for (let i = run.from; i < run.to; i++) {
343
+ const layer = prepared.layers[i].layer;
344
+ if (drawable(layer)) {
345
+ active.push({
346
+ index: i,
347
+ prepared: prepared.layers[i],
348
+ type: layer.type,
349
+ });
350
+ }
351
+ }
352
+ // A mask is a 32-bit integer. A run longer than that is not one any
353
+ // style has been written with; its tail draws nothing rather than
354
+ // aliasing onto the head.
355
+ if (active.length > 31) active.length = 31;
356
+ if (active.length === 0) continue;
357
+ const k = TILE_EXTENT / source.extent;
358
+
359
+ cursor = source.feature(0);
360
+ const byMask = new Map<number, Group>();
361
+ const order: Group[] = [];
362
+ for (let f = 0; f < source.length; f++) {
363
+ source.seek(f, cursor);
364
+ let mask = 0;
365
+ for (let j = 0; j < active.length; j++) {
366
+ if (!takes(active[j].type, cursor.type)) continue;
367
+ if (!active[j].prepared.filter(cursor)) continue;
368
+ mask |= 1 << j;
369
+ }
370
+ if (mask === 0) continue;
371
+ let group = byMask.get(mask);
372
+ if (!group) {
373
+ group = { mask, features: [], fill: [0, 0], line: [0, 0] };
374
+ byMask.set(mask, group);
375
+ order.push(group);
376
+ }
377
+ group.features.push(f);
378
+ }
379
+
380
+ for (const group of order) {
381
+ let needFill = false;
382
+ let needLine = false;
383
+ let needPoints = false;
384
+ for (let j = 0; j < active.length; j++) {
385
+ if (!(group.mask & (1 << j))) continue;
386
+ if (active[j].type === 'fill') needFill = true;
387
+ else if (active[j].type === 'circle') needPoints = true;
388
+ else needLine = true;
389
+ }
390
+ const fillFrom = fill.count;
391
+ const lineFrom = line.count;
392
+ for (const f of group.features) {
393
+ source.seek(f, cursor);
394
+ cursor.readGeometry(buffer);
395
+ if (buffer.parts === 0) continue;
396
+ features++;
397
+ const polygon = cursor.type === GeomType.Polygon;
398
+ const points = cursor.type === GeomType.Point;
399
+ for (let part = 0; part < buffer.parts; part++) {
400
+ const from = buffer.starts[part];
401
+ const to = buffer.starts[part + 1];
402
+ if (points) {
403
+ if (needPoints) emitPoints(line, buffer.coords, from, to, k);
404
+ continue;
405
+ }
406
+ if (needFill && polygon) emitRing(fill, buffer.coords, from, to, k);
407
+ if (needLine) emitLine(line, buffer.coords, from, to, k, polygon);
408
+ }
409
+ }
410
+ // A group's points end in a sentinel like a polyline does, so the
411
+ // instance count of their range is the number of points.
412
+ if (needPoints && line.count > lineFrom) {
413
+ line.reserve(1);
414
+ pushBreak(line);
415
+ }
416
+ group.fill = [fillFrom, fill.count - fillFrom];
417
+ group.line = [lineFrom, line.count - lineFrom];
418
+ }
419
+ groupCount += order.length;
420
+
421
+ for (let j = 0; j < active.length; j++) {
422
+ const { index, type } = active[j];
423
+ const ranges: number[] = [];
424
+ for (const group of order) {
425
+ if (!(group.mask & (1 << j))) continue;
426
+ const [first, count] = type === 'fill' ? group.fill : group.line;
427
+ if (count < 2) continue;
428
+ const last = ranges.length - 2;
429
+ // Adjacent groups coalesce into one range, so a layer is one draw
430
+ // whenever the groups it spans were written next to each other.
431
+ if (last >= 0 && ranges[last] + ranges[last + 1] === first) {
432
+ ranges[last + 1] += count;
433
+ } else {
434
+ ranges.push(first, count);
435
+ }
436
+ }
437
+ if (ranges.length > 0)
438
+ draws[index] = { layer: index, kind: type, ranges };
439
+ }
440
+ }
441
+
442
+ return {
443
+ line: line.take(),
444
+ lineRecords: line.count,
445
+ fill: fill.take(),
446
+ fillRecords: fill.count,
447
+ draws,
448
+ labels: buildTileLabels(tile, prepared, TILE_EXTENT),
449
+ stats: {
450
+ features,
451
+ lineRecords: line.count,
452
+ fillRecords: fill.count,
453
+ groups: groupCount,
454
+ ms: now() - started,
455
+ },
456
+ };
457
+ }
@@ -0,0 +1,55 @@
1
+ // A worker thread's half of `GlTileStore`: tile bytes in, buckets out.
2
+ //
3
+ // Loaded only as a `Worker` entry and never imported, so the listener below
4
+ // is not an import-time side effect of the package — no bundle that follows
5
+ // imports can reach this file. What crosses the thread boundary is the point
6
+ // of the bucket format: a tile arrives as its own bytes (copied, a few
7
+ // hundred kilobytes) and leaves as two ArrayBuffers that are *transferred*,
8
+ // so the main thread's whole cost for a tile is the upload.
9
+ //
10
+ // `node:worker_threads` through a variable specifier, as `src/embed/host.ts`
11
+ // reaches `node:net`: `src/` compiles with `types: []`, and a literal
12
+ // specifier would make every build depend on Node's type declarations.
13
+ import { prepareStyle } from '../paint.js';
14
+ import type { PreparedStyle } from '../paint.js';
15
+ import { parseVectorTile } from '../sources.js';
16
+ import type { MapStyleLayer } from '../style.js';
17
+ import { buildTileBuckets } from './buckets.js';
18
+
19
+ /** What the store sends. */
20
+ export type BuildRequest =
21
+ | { type: 'style'; id: number; layers: MapStyleLayer[] }
22
+ | { type: 'build'; job: number; style: number; bytes: Uint8Array };
23
+
24
+ interface Port {
25
+ on(event: 'message', listener: (message: unknown) => void): void;
26
+ postMessage(message: unknown, transfer?: ArrayBuffer[]): void;
27
+ }
28
+
29
+ const WORKER_THREADS = 'node:worker_threads';
30
+ const { parentPort } = (await import(WORKER_THREADS)) as {
31
+ parentPort: Port | null;
32
+ };
33
+
34
+ /** Compiled filters per style id. Two are kept: the store may still have a
35
+ * build in flight for the style it is leaving. */
36
+ const styles = new Map<number, PreparedStyle>();
37
+
38
+ parentPort?.on('message', (message) => {
39
+ const request = message as BuildRequest;
40
+ if (request.type === 'style') {
41
+ styles.set(request.id, prepareStyle({ layers: request.layers }));
42
+ for (const id of styles.keys()) if (id < request.id - 1) styles.delete(id);
43
+ return;
44
+ }
45
+ try {
46
+ const prepared = styles.get(request.style);
47
+ if (!prepared) throw new Error(`style ${request.style} was never sent`);
48
+ const data = buildTileBuckets(parseVectorTile(request.bytes), prepared);
49
+ const transfer = [data.line, data.fill];
50
+ if (data.labels) transfer.push(data.labels.anchors.buffer as ArrayBuffer);
51
+ parentPort.postMessage({ job: request.job, data }, transfer);
52
+ } catch (error) {
53
+ parentPort.postMessage({ job: request.job, error: String(error) });
54
+ }
55
+ });
@@ -0,0 +1,62 @@
1
+ // CSS colours as the floats GL takes.
2
+ //
3
+ // A local parser rather than ntk's `cssColorStraight`: that one is exported
4
+ // by ntk at runtime but not declared by `react-x11/ntk`'s types, and a map
5
+ // style is written in the three forms below in practice — the default
6
+ // palettes use nothing else. An unknown colour is `null`, and the layer that
7
+ // asked for it draws nothing rather than drawing black.
8
+
9
+ /** Straight (unassociated) `[r, g, b, a]`, each in `0..1`. */
10
+ export type Rgba = readonly [number, number, number, number];
11
+
12
+ const NAMED: Record<string, Rgba> = {
13
+ transparent: [0, 0, 0, 0],
14
+ black: [0, 0, 0, 1],
15
+ white: [1, 1, 1, 1],
16
+ };
17
+
18
+ function hex(value: string): Rgba | null {
19
+ const h = value.slice(1);
20
+ if (!/^[0-9a-fA-F]+$/.test(h)) return null;
21
+ if (h.length === 3 || h.length === 4) {
22
+ const c = (i: number) => parseInt(h[i] + h[i], 16) / 255;
23
+ return [c(0), c(1), c(2), h.length === 4 ? c(3) : 1];
24
+ }
25
+ if (h.length === 6 || h.length === 8) {
26
+ const c = (i: number) => parseInt(h.slice(i * 2, i * 2 + 2), 16) / 255;
27
+ return [c(0), c(1), c(2), h.length === 8 ? c(3) : 1];
28
+ }
29
+ return null;
30
+ }
31
+
32
+ function functional(value: string): Rgba | null {
33
+ const m = /^rgba?\(([^)]*)\)$/i.exec(value);
34
+ if (!m) return null;
35
+ const parts = m[1].split(/[\s,/]+/).filter(Boolean);
36
+ if (parts.length < 3) return null;
37
+ const channel = (p: string) =>
38
+ p.endsWith('%') ? parseFloat(p) / 100 : parseFloat(p) / 255;
39
+ const alpha = (p: string | undefined) =>
40
+ p === undefined ? 1 : p.endsWith('%') ? parseFloat(p) / 100 : parseFloat(p);
41
+ const out: Rgba = [
42
+ channel(parts[0]),
43
+ channel(parts[1]),
44
+ channel(parts[2]),
45
+ alpha(parts[3]),
46
+ ];
47
+ return out.every((c) => Number.isFinite(c)) ? out : null;
48
+ }
49
+
50
+ /** Parse `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`, `rgb()` and `rgba()`. */
51
+ export function parseColor(value: string): Rgba | null {
52
+ const v = value.trim();
53
+ if (v.startsWith('#')) return hex(v);
54
+ return NAMED[v.toLowerCase()] ?? functional(v);
55
+ }
56
+
57
+ /** Premultiplied, with an extra opacity folded in — what the renderer's
58
+ * `ONE, ONE_MINUS_SRC_ALPHA` blend wants. */
59
+ export function premultiplied(color: Rgba, opacity = 1): Rgba {
60
+ const a = color[3] * opacity;
61
+ return [color[0] * a, color[1] * a, color[2] * a, a];
62
+ }