@react-x11/components 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +105 -38
- package/dist/charts/node.d.ts +12 -2
- package/dist/charts/node.d.ts.map +1 -1
- package/dist/charts/node.js +50 -16
- package/dist/charts/node.js.map +1 -1
- package/dist/charts/render.d.ts +12 -3
- package/dist/charts/render.d.ts.map +1 -1
- package/dist/charts/render.js +94 -25
- package/dist/charts/render.js.map +1 -1
- package/dist/code/index.d.ts.map +1 -1
- package/dist/code/index.js +3 -1
- package/dist/code/index.js.map +1 -1
- package/dist/code-editor/completion.d.ts.map +1 -1
- package/dist/code-editor/completion.js +3 -2
- package/dist/code-editor/completion.js.map +1 -1
- package/dist/code-editor/index.d.ts.map +1 -1
- package/dist/code-editor/index.js +19 -0
- package/dist/code-editor/index.js.map +1 -1
- package/dist/code-editor/node.d.ts +177 -7
- package/dist/code-editor/node.d.ts.map +1 -1
- package/dist/code-editor/node.js +1329 -164
- package/dist/code-editor/node.js.map +1 -1
- package/dist/code-language/languages/glsl.d.ts.map +1 -1
- package/dist/code-language/languages/glsl.js +10 -2
- package/dist/code-language/languages/glsl.js.map +1 -1
- package/dist/code-language/languages/javascript.d.ts.map +1 -1
- package/dist/code-language/languages/javascript.js +18 -3
- package/dist/code-language/languages/javascript.js.map +1 -1
- package/dist/code-language/languages/json.d.ts.map +1 -1
- package/dist/code-language/languages/json.js +10 -2
- package/dist/code-language/languages/json.js.map +1 -1
- package/dist/code-language/languages/shell.d.ts.map +1 -1
- package/dist/code-language/languages/shell.js +10 -2
- package/dist/code-language/languages/shell.js.map +1 -1
- package/dist/code-language/languages/sql.d.ts.map +1 -1
- package/dist/code-language/languages/sql.js +20 -2
- package/dist/code-language/languages/sql.js.map +1 -1
- package/dist/code-language/stream.d.ts +22 -0
- package/dist/code-language/stream.d.ts.map +1 -1
- package/dist/code-language/stream.js +259 -9
- package/dist/code-language/stream.js.map +1 -1
- package/dist/code-language/types.d.ts +5 -4
- package/dist/code-language/types.d.ts.map +1 -1
- package/dist/codeblock/index.d.ts +13 -2
- package/dist/codeblock/index.d.ts.map +1 -1
- package/dist/codeblock/index.js +20 -3
- package/dist/codeblock/index.js.map +1 -1
- package/dist/embed/client.d.ts +48 -3
- package/dist/embed/client.d.ts.map +1 -1
- package/dist/embed/client.js +79 -5
- package/dist/embed/client.js.map +1 -1
- package/dist/embed/index.d.ts +1 -1
- package/dist/embed/index.d.ts.map +1 -1
- package/dist/embed/index.js +1 -1
- package/dist/embed/index.js.map +1 -1
- package/dist/flow/draw.d.ts +50 -0
- package/dist/flow/draw.d.ts.map +1 -1
- package/dist/flow/draw.js +132 -33
- package/dist/flow/draw.js.map +1 -1
- package/dist/flow/gl/color.d.ts +13 -0
- package/dist/flow/gl/color.d.ts.map +1 -0
- package/dist/flow/gl/color.js +82 -0
- package/dist/flow/gl/color.js.map +1 -0
- package/dist/flow/gl/index.d.ts +60 -0
- package/dist/flow/gl/index.d.ts.map +1 -0
- package/dist/flow/gl/index.js +283 -0
- package/dist/flow/gl/index.js.map +1 -0
- package/dist/flow/gl/pack.d.ts +133 -0
- package/dist/flow/gl/pack.d.ts.map +1 -0
- package/dist/flow/gl/pack.js +408 -0
- package/dist/flow/gl/pack.js.map +1 -0
- package/dist/flow/gl/renderer.d.ts +95 -0
- package/dist/flow/gl/renderer.d.ts.map +1 -0
- package/dist/flow/gl/renderer.js +459 -0
- package/dist/flow/gl/renderer.js.map +1 -0
- package/dist/flow/gl/shaders.d.ts +79 -0
- package/dist/flow/gl/shaders.d.ts.map +1 -0
- package/dist/flow/gl/shaders.js +346 -0
- package/dist/flow/gl/shaders.js.map +1 -0
- package/dist/flow/gl/text.d.ts +318 -0
- package/dist/flow/gl/text.d.ts.map +1 -0
- package/dist/flow/gl/text.js +1103 -0
- package/dist/flow/gl/text.js.map +1 -0
- package/dist/flow/index.d.ts +1 -1
- package/dist/flow/index.d.ts.map +1 -1
- package/dist/flow/index.js +619 -64
- package/dist/flow/index.js.map +1 -1
- package/dist/flow/node.d.ts +621 -72
- package/dist/flow/node.d.ts.map +1 -1
- package/dist/flow/node.js +2253 -1018
- package/dist/flow/node.js.map +1 -1
- package/dist/flow/paint.d.ts +35 -0
- package/dist/flow/paint.d.ts.map +1 -0
- package/dist/flow/paint.js +393 -0
- package/dist/flow/paint.js.map +1 -0
- package/dist/flow/paths.d.ts +14 -0
- package/dist/flow/paths.d.ts.map +1 -1
- package/dist/flow/paths.js +45 -19
- package/dist/flow/paths.js.map +1 -1
- package/dist/flow/scene.d.ts +584 -0
- package/dist/flow/scene.d.ts.map +1 -0
- package/dist/flow/scene.js +1453 -0
- package/dist/flow/scene.js.map +1 -0
- package/dist/flow/types.d.ts +111 -1
- package/dist/flow/types.d.ts.map +1 -1
- package/dist/html/controls.d.ts +28 -0
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js +52 -2
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/calc.d.ts +14 -0
- package/dist/html/css/calc.d.ts.map +1 -0
- package/dist/html/css/calc.js +344 -0
- package/dist/html/css/calc.js.map +1 -0
- package/dist/html/css/cascade.d.ts +240 -6
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +1327 -66
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/color.d.ts +23 -0
- package/dist/html/css/color.d.ts.map +1 -0
- package/dist/html/css/color.js +740 -0
- package/dist/html/css/color.js.map +1 -0
- package/dist/html/css/content.d.ts +92 -0
- package/dist/html/css/content.d.ts.map +1 -0
- package/dist/html/css/content.js +388 -0
- package/dist/html/css/content.js.map +1 -0
- package/dist/html/css/counter-styles.d.ts +76 -0
- package/dist/html/css/counter-styles.d.ts.map +1 -0
- package/dist/html/css/counter-styles.js +893 -0
- package/dist/html/css/counter-styles.js.map +1 -0
- package/dist/html/css/decode.d.ts +12 -0
- package/dist/html/css/decode.d.ts.map +1 -0
- package/dist/html/css/decode.js +116 -0
- package/dist/html/css/decode.js.map +1 -0
- package/dist/html/css/parse.d.ts +110 -10
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +1473 -180
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/style.d.ts +498 -15
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +3698 -206
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/ua.d.ts.map +1 -1
- package/dist/html/css/ua.js +57 -11
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/css/values.d.ts +37 -3
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js +122 -48
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/css/vars.d.ts +52 -0
- package/dist/html/css/vars.d.ts.map +1 -0
- package/dist/html/css/vars.js +358 -0
- package/dist/html/css/vars.js.map +1 -0
- package/dist/html/dom.d.ts +30 -4
- package/dist/html/dom.d.ts.map +1 -1
- package/dist/html/dom.js +278 -17
- package/dist/html/dom.js.map +1 -1
- package/dist/html/fonts.d.ts +106 -0
- package/dist/html/fonts.d.ts.map +1 -0
- package/dist/html/fonts.js +565 -0
- package/dist/html/fonts.js.map +1 -0
- package/dist/html/gif.d.ts +15 -0
- package/dist/html/gif.d.ts.map +1 -0
- package/dist/html/gif.js +229 -0
- package/dist/html/gif.js.map +1 -0
- package/dist/html/index.d.ts +50 -11
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +115 -9
- package/dist/html/index.js.map +1 -1
- package/dist/html/layout/block.d.ts +226 -3
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +2937 -197
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +340 -17
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +1714 -182
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/cache.d.ts +58 -0
- package/dist/html/layout/cache.d.ts.map +1 -0
- package/dist/html/layout/cache.js +224 -0
- package/dist/html/layout/cache.js.map +1 -0
- package/dist/html/layout/collapse.d.ts +45 -0
- package/dist/html/layout/collapse.d.ts.map +1 -0
- package/dist/html/layout/collapse.js +246 -0
- package/dist/html/layout/collapse.js.map +1 -0
- package/dist/html/layout/css-grid.d.ts +7 -0
- package/dist/html/layout/css-grid.d.ts.map +1 -0
- package/dist/html/layout/css-grid.js +900 -0
- package/dist/html/layout/css-grid.js.map +1 -0
- package/dist/html/layout/flex.d.ts.map +1 -1
- package/dist/html/layout/flex.js +1025 -89
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/floats.d.ts +45 -6
- package/dist/html/layout/floats.d.ts.map +1 -1
- package/dist/html/layout/floats.js +113 -14
- package/dist/html/layout/floats.js.map +1 -1
- package/dist/html/layout/grid-lines.d.ts +39 -0
- package/dist/html/layout/grid-lines.d.ts.map +1 -0
- package/dist/html/layout/grid-lines.js +119 -0
- package/dist/html/layout/grid-lines.js.map +1 -0
- package/dist/html/layout/grid.d.ts +53 -0
- package/dist/html/layout/grid.d.ts.map +1 -0
- package/dist/html/layout/grid.js +153 -0
- package/dist/html/layout/grid.js.map +1 -0
- package/dist/html/layout/inline.d.ts +151 -1
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +3375 -174
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/layout/shaping.d.ts +6 -0
- package/dist/html/layout/shaping.d.ts.map +1 -0
- package/dist/html/layout/shaping.js +100 -0
- package/dist/html/layout/shaping.js.map +1 -0
- package/dist/html/layout/table.d.ts +3 -0
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +681 -175
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/layout/tracks.d.ts +44 -0
- package/dist/html/layout/tracks.d.ts.map +1 -0
- package/dist/html/layout/tracks.js +349 -0
- package/dist/html/layout/tracks.js.map +1 -0
- package/dist/html/node.d.ts +172 -5
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +1336 -103
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +92 -3
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +3503 -141
- package/dist/html/paint.js.map +1 -1
- package/dist/html/resources.d.ts +60 -14
- package/dist/html/resources.d.ts.map +1 -1
- package/dist/html/resources.js +122 -36
- package/dist/html/resources.js.map +1 -1
- package/dist/html/surfaces.d.ts +23 -0
- package/dist/html/surfaces.d.ts.map +1 -0
- package/dist/html/surfaces.js +68 -0
- package/dist/html/surfaces.js.map +1 -0
- package/dist/html/svg.d.ts +80 -0
- package/dist/html/svg.d.ts.map +1 -0
- package/dist/html/svg.js +435 -0
- package/dist/html/svg.js.map +1 -0
- package/dist/html/url.d.ts +23 -0
- package/dist/html/url.d.ts.map +1 -0
- package/dist/html/url.js +80 -0
- package/dist/html/url.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/collate.d.ts +3 -0
- package/dist/internal/collate.d.ts.map +1 -0
- package/dist/internal/collate.js +11 -0
- package/dist/internal/collate.js.map +1 -0
- package/dist/{markdown → internal/markdown}/ast.d.ts +23 -0
- package/dist/internal/markdown/ast.d.ts.map +1 -0
- package/dist/internal/markdown/ast.js +22 -0
- package/dist/internal/markdown/ast.js.map +1 -0
- package/dist/internal/markdown/parse.d.ts.map +1 -0
- package/dist/{markdown → internal/markdown}/parse.js +221 -30
- package/dist/internal/markdown/parse.js.map +1 -0
- package/dist/internal/markdown/stringify.d.ts +4 -0
- package/dist/internal/markdown/stringify.d.ts.map +1 -0
- package/dist/internal/markdown/stringify.js +684 -0
- package/dist/internal/markdown/stringify.js.map +1 -0
- package/dist/internal/markdown/tags.d.ts.map +1 -0
- package/dist/internal/markdown/tags.js.map +1 -0
- package/dist/internal/prewarm.d.ts +18 -0
- package/dist/internal/prewarm.d.ts.map +1 -0
- package/dist/internal/prewarm.js +42 -0
- package/dist/internal/prewarm.js.map +1 -0
- package/dist/internal/scroll.d.ts +3 -2
- package/dist/internal/scroll.d.ts.map +1 -1
- package/dist/internal/scroll.js +1 -0
- package/dist/internal/scroll.js.map +1 -1
- package/dist/internal/sdf.d.ts +20 -0
- package/dist/internal/sdf.d.ts.map +1 -0
- package/dist/internal/sdf.js +216 -0
- package/dist/internal/sdf.js.map +1 -0
- package/dist/internal/splice.d.ts +12 -0
- package/dist/internal/splice.d.ts.map +1 -0
- package/dist/internal/splice.js +23 -0
- package/dist/internal/splice.js.map +1 -0
- package/dist/internal/window.d.ts +12 -0
- package/dist/internal/window.d.ts.map +1 -1
- package/dist/internal/window.js +23 -6
- package/dist/internal/window.js.map +1 -1
- package/dist/maps/anchors.d.ts +103 -0
- package/dist/maps/anchors.d.ts.map +1 -0
- package/dist/maps/anchors.js +790 -0
- package/dist/maps/anchors.js.map +1 -0
- package/dist/maps/controller.d.ts +340 -0
- package/dist/maps/controller.d.ts.map +1 -0
- package/dist/maps/controller.js +822 -0
- package/dist/maps/controller.js.map +1 -0
- package/dist/maps/gl/buckets.d.ts +99 -0
- package/dist/maps/gl/buckets.d.ts.map +1 -0
- package/dist/maps/gl/buckets.js +365 -0
- package/dist/maps/gl/buckets.js.map +1 -0
- package/dist/maps/gl/build-worker.d.ts +15 -0
- package/dist/maps/gl/build-worker.d.ts.map +1 -0
- package/dist/maps/gl/build-worker.js +44 -0
- package/dist/maps/gl/build-worker.js.map +1 -0
- package/dist/maps/gl/color.d.ts +8 -0
- package/dist/maps/gl/color.d.ts.map +1 -0
- package/dist/maps/gl/color.js +57 -0
- package/dist/maps/gl/color.js.map +1 -0
- package/dist/maps/gl/cover.d.ts +70 -0
- package/dist/maps/gl/cover.d.ts.map +1 -0
- package/dist/maps/gl/cover.js +137 -0
- package/dist/maps/gl/cover.js.map +1 -0
- package/dist/maps/gl/index.d.ts +3 -0
- package/dist/maps/gl/index.d.ts.map +1 -0
- package/dist/maps/gl/index.js +20 -0
- package/dist/maps/gl/index.js.map +1 -0
- package/dist/maps/gl/markers.d.ts +28 -0
- package/dist/maps/gl/markers.d.ts.map +1 -0
- package/dist/maps/gl/markers.js +60 -0
- package/dist/maps/gl/markers.js.map +1 -0
- package/dist/maps/gl/overlays.d.ts +68 -0
- package/dist/maps/gl/overlays.d.ts.map +1 -0
- package/dist/maps/gl/overlays.js +300 -0
- package/dist/maps/gl/overlays.js.map +1 -0
- package/dist/maps/gl/pane.d.ts +12 -0
- package/dist/maps/gl/pane.d.ts.map +1 -0
- package/dist/maps/gl/pane.js +37 -0
- package/dist/maps/gl/pane.js.map +1 -0
- package/dist/maps/gl/placement.d.ts +124 -0
- package/dist/maps/gl/placement.d.ts.map +1 -0
- package/dist/maps/gl/placement.js +642 -0
- package/dist/maps/gl/placement.js.map +1 -0
- package/dist/maps/gl/renderer.d.ts +384 -0
- package/dist/maps/gl/renderer.d.ts.map +1 -0
- package/dist/maps/gl/renderer.js +1184 -0
- package/dist/maps/gl/renderer.js.map +1 -0
- package/dist/maps/gl/shaders.d.ts +121 -0
- package/dist/maps/gl/shaders.d.ts.map +1 -0
- package/dist/maps/gl/shaders.js +453 -0
- package/dist/maps/gl/shaders.js.map +1 -0
- package/dist/maps/gl/store.d.ts +125 -0
- package/dist/maps/gl/store.d.ts.map +1 -0
- package/dist/maps/gl/store.js +512 -0
- package/dist/maps/gl/store.js.map +1 -0
- package/dist/maps/gl/text.d.ts +283 -0
- package/dist/maps/gl/text.d.ts.map +1 -0
- package/dist/maps/gl/text.js +698 -0
- package/dist/maps/gl/text.js.map +1 -0
- package/dist/maps/gl/view.d.ts +345 -0
- package/dist/maps/gl/view.d.ts.map +1 -0
- package/dist/maps/gl/view.js +1209 -0
- package/dist/maps/gl/view.js.map +1 -0
- package/dist/maps/icons.d.ts +32 -0
- package/dist/maps/icons.d.ts.map +1 -0
- package/dist/maps/icons.js +151 -0
- package/dist/maps/icons.js.map +1 -0
- package/dist/maps/index.d.ts +3 -1
- package/dist/maps/index.d.ts.map +1 -1
- package/dist/maps/index.js +218 -52
- package/dist/maps/index.js.map +1 -1
- package/dist/maps/labels.d.ts +91 -18
- package/dist/maps/labels.d.ts.map +1 -1
- package/dist/maps/labels.js +376 -140
- package/dist/maps/labels.js.map +1 -1
- package/dist/maps/node.d.ts +179 -56
- package/dist/maps/node.d.ts.map +1 -1
- package/dist/maps/node.js +663 -537
- package/dist/maps/node.js.map +1 -1
- package/dist/maps/overlay.d.ts +72 -9
- package/dist/maps/overlay.d.ts.map +1 -1
- package/dist/maps/overlay.js +108 -28
- package/dist/maps/overlay.js.map +1 -1
- package/dist/maps/paint.d.ts +3 -0
- package/dist/maps/paint.d.ts.map +1 -1
- package/dist/maps/paint.js.map +1 -1
- package/dist/maps/proj.d.ts +62 -0
- package/dist/maps/proj.d.ts.map +1 -1
- package/dist/maps/proj.js +101 -0
- package/dist/maps/proj.js.map +1 -1
- package/dist/maps/renderer.d.ts +69 -0
- package/dist/maps/renderer.d.ts.map +1 -0
- package/dist/maps/renderer.js +66 -0
- package/dist/maps/renderer.js.map +1 -0
- package/dist/maps/sources.d.ts +30 -6
- package/dist/maps/sources.d.ts.map +1 -1
- package/dist/maps/sources.js +22 -3
- package/dist/maps/sources.js.map +1 -1
- package/dist/maps/style.d.ts +35 -0
- package/dist/maps/style.d.ts.map +1 -1
- package/dist/maps/style.js.map +1 -1
- package/dist/maps/styles.d.ts +12 -0
- package/dist/maps/styles.d.ts.map +1 -1
- package/dist/maps/styles.js +120 -6
- package/dist/maps/styles.js.map +1 -1
- package/dist/maps/theme.d.ts +19 -0
- package/dist/maps/theme.d.ts.map +1 -0
- package/dist/maps/theme.js +44 -0
- package/dist/maps/theme.js.map +1 -0
- package/dist/maps/tiles.d.ts +147 -27
- package/dist/maps/tiles.d.ts.map +1 -1
- package/dist/maps/tiles.js +221 -54
- package/dist/maps/tiles.js.map +1 -1
- package/dist/maps/types.d.ts +213 -21
- package/dist/maps/types.d.ts.map +1 -1
- package/dist/markdown/expressions.d.ts +1 -1
- package/dist/markdown/expressions.d.ts.map +1 -1
- package/dist/markdown/expressions.js.map +1 -1
- package/dist/markdown/index.d.ts +7 -6
- package/dist/markdown/index.d.ts.map +1 -1
- package/dist/markdown/index.js +121 -50
- package/dist/markdown/index.js.map +1 -1
- package/dist/markdown/spans.d.ts +1 -1
- package/dist/markdown/spans.d.ts.map +1 -1
- package/dist/markdown/spans.js +6 -0
- package/dist/markdown/spans.js.map +1 -1
- package/dist/media-player/index.d.ts +11 -3
- package/dist/media-player/index.d.ts.map +1 -1
- package/dist/media-player/index.js +13 -2
- package/dist/media-player/index.js.map +1 -1
- package/dist/qml/containers.d.ts.map +1 -1
- package/dist/qml/containers.js +6 -1
- package/dist/qml/containers.js.map +1 -1
- package/dist/qml/interaction.d.ts.map +1 -1
- package/dist/qml/interaction.js +38 -5
- package/dist/qml/interaction.js.map +1 -1
- package/dist/qml/layouts.d.ts.map +1 -1
- package/dist/qml/layouts.js +21 -6
- package/dist/qml/layouts.js.map +1 -1
- package/dist/qml/qtquick.d.ts.map +1 -1
- package/dist/qml/qtquick.js +9 -2
- package/dist/qml/qtquick.js.map +1 -1
- package/dist/qml/react.d.ts.map +1 -1
- package/dist/qml/react.js +9 -7
- package/dist/qml/react.js.map +1 -1
- package/dist/reorder/clock.d.ts +7 -0
- package/dist/reorder/clock.d.ts.map +1 -0
- package/dist/reorder/clock.js +26 -0
- package/dist/reorder/clock.js.map +1 -0
- package/dist/reorder/index.d.ts +25 -3
- package/dist/reorder/index.d.ts.map +1 -1
- package/dist/reorder/index.js +64 -19
- package/dist/reorder/index.js.map +1 -1
- package/dist/rich-text-editor/clipboard.d.ts +23 -0
- package/dist/rich-text-editor/clipboard.d.ts.map +1 -0
- package/dist/rich-text-editor/clipboard.js +307 -0
- package/dist/rich-text-editor/clipboard.js.map +1 -0
- package/dist/rich-text-editor/collab.d.ts +32 -0
- package/dist/rich-text-editor/collab.d.ts.map +1 -0
- package/dist/rich-text-editor/collab.js +55 -0
- package/dist/rich-text-editor/collab.js.map +1 -0
- package/dist/rich-text-editor/commands.d.ts +50 -0
- package/dist/rich-text-editor/commands.d.ts.map +1 -0
- package/dist/rich-text-editor/commands.js +376 -0
- package/dist/rich-text-editor/commands.js.map +1 -0
- package/dist/rich-text-editor/drag.d.ts +68 -0
- package/dist/rich-text-editor/drag.d.ts.map +1 -0
- package/dist/rich-text-editor/drag.js +148 -0
- package/dist/rich-text-editor/drag.js.map +1 -0
- package/dist/rich-text-editor/html.d.ts +145 -0
- package/dist/rich-text-editor/html.d.ts.map +1 -0
- package/dist/rich-text-editor/html.js +516 -0
- package/dist/rich-text-editor/html.js.map +1 -0
- package/dist/rich-text-editor/index.d.ts +252 -0
- package/dist/rich-text-editor/index.d.ts.map +1 -0
- package/dist/rich-text-editor/index.js +856 -0
- package/dist/rich-text-editor/index.js.map +1 -0
- package/dist/rich-text-editor/inline.d.ts +94 -0
- package/dist/rich-text-editor/inline.d.ts.map +1 -0
- package/dist/rich-text-editor/inline.js +252 -0
- package/dist/rich-text-editor/inline.js.map +1 -0
- package/dist/rich-text-editor/keymap.d.ts +71 -0
- package/dist/rich-text-editor/keymap.d.ts.map +1 -0
- package/dist/rich-text-editor/keymap.js +394 -0
- package/dist/rich-text-editor/keymap.js.map +1 -0
- package/dist/rich-text-editor/keys.d.ts +40 -0
- package/dist/rich-text-editor/keys.d.ts.map +1 -0
- package/dist/rich-text-editor/keys.js +202 -0
- package/dist/rich-text-editor/keys.js.map +1 -0
- package/dist/rich-text-editor/look.d.ts +71 -0
- package/dist/rich-text-editor/look.d.ts.map +1 -0
- package/dist/rich-text-editor/look.js +342 -0
- package/dist/rich-text-editor/look.js.map +1 -0
- package/dist/rich-text-editor/markdown.d.ts +18 -0
- package/dist/rich-text-editor/markdown.d.ts.map +1 -0
- package/dist/rich-text-editor/markdown.js +496 -0
- package/dist/rich-text-editor/markdown.js.map +1 -0
- package/dist/rich-text-editor/marks.d.ts +9 -0
- package/dist/rich-text-editor/marks.d.ts.map +1 -0
- package/dist/rich-text-editor/marks.js +83 -0
- package/dist/rich-text-editor/marks.js.map +1 -0
- package/dist/rich-text-editor/nodes.d.ts +135 -0
- package/dist/rich-text-editor/nodes.d.ts.map +1 -0
- package/dist/rich-text-editor/nodes.js +397 -0
- package/dist/rich-text-editor/nodes.js.map +1 -0
- package/dist/rich-text-editor/render.d.ts +68 -0
- package/dist/rich-text-editor/render.d.ts.map +1 -0
- package/dist/rich-text-editor/render.js +518 -0
- package/dist/rich-text-editor/render.js.map +1 -0
- package/dist/rich-text-editor/schema.d.ts +16 -0
- package/dist/rich-text-editor/schema.d.ts.map +1 -0
- package/dist/rich-text-editor/schema.js +386 -0
- package/dist/rich-text-editor/schema.js.map +1 -0
- package/dist/rich-text-editor/store.d.ts +11 -0
- package/dist/rich-text-editor/store.d.ts.map +1 -0
- package/dist/rich-text-editor/store.js +43 -0
- package/dist/rich-text-editor/store.js.map +1 -0
- package/dist/rich-text-editor/suggest.d.ts +127 -0
- package/dist/rich-text-editor/suggest.d.ts.map +1 -0
- package/dist/rich-text-editor/suggest.js +0 -0
- package/dist/rich-text-editor/suggest.js.map +1 -0
- package/dist/rich-text-editor/tables.d.ts +41 -0
- package/dist/rich-text-editor/tables.d.ts.map +1 -0
- package/dist/rich-text-editor/tables.js +204 -0
- package/dist/rich-text-editor/tables.js.map +1 -0
- package/dist/rich-text-editor/toolbar.d.ts +58 -0
- package/dist/rich-text-editor/toolbar.d.ts.map +1 -0
- package/dist/rich-text-editor/toolbar.js +548 -0
- package/dist/rich-text-editor/toolbar.js.map +1 -0
- package/dist/rich-text-editor/view.d.ts +368 -0
- package/dist/rich-text-editor/view.d.ts.map +1 -0
- package/dist/rich-text-editor/view.js +1968 -0
- package/dist/rich-text-editor/view.js.map +1 -0
- package/dist/rich-text-editor/virtual.d.ts +54 -0
- package/dist/rich-text-editor/virtual.d.ts.map +1 -0
- package/dist/rich-text-editor/virtual.js +279 -0
- package/dist/rich-text-editor/virtual.js.map +1 -0
- package/dist/richtext/node.d.ts +71 -4
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +233 -28
- package/dist/richtext/node.js.map +1 -1
- package/dist/richtext/runs.d.ts +13 -8
- package/dist/richtext/runs.d.ts.map +1 -1
- package/dist/richtext/runs.js +18 -8
- package/dist/richtext/runs.js.map +1 -1
- package/dist/table/index.d.ts.map +1 -1
- package/dist/table/index.js +23 -0
- package/dist/table/index.js.map +1 -1
- package/dist/table/rows.d.ts +3 -2
- package/dist/table/rows.d.ts.map +1 -1
- package/dist/table/rows.js +20 -6
- package/dist/table/rows.js.map +1 -1
- package/dist/terminal/backends.d.ts +4 -1
- package/dist/terminal/backends.d.ts.map +1 -1
- package/dist/terminal/backends.js.map +1 -1
- package/dist/terminal/index.d.ts +18 -7
- package/dist/terminal/index.d.ts.map +1 -1
- package/dist/terminal/index.js +32 -11
- package/dist/terminal/index.js.map +1 -1
- package/dist/terminal/vt/pty.d.ts +13 -2
- package/dist/terminal/vt/pty.d.ts.map +1 -1
- package/dist/terminal/vt/pty.js +18 -3
- package/dist/terminal/vt/pty.js.map +1 -1
- package/dist/terminal-output/index.d.ts.map +1 -1
- package/dist/terminal-output/index.js +6 -1
- package/dist/terminal-output/index.js.map +1 -1
- package/dist/three/canvas.d.ts.map +1 -1
- package/dist/three/canvas.js +85 -7
- package/dist/three/canvas.js.map +1 -1
- package/dist/three/events.d.ts +22 -4
- package/dist/three/events.d.ts.map +1 -1
- package/dist/three/events.js +34 -4
- package/dist/three/events.js.map +1 -1
- package/dist/tree/index.d.ts.map +1 -1
- package/dist/tree/index.js +17 -0
- package/dist/tree/index.js.map +1 -1
- package/package.json +59 -31
- package/src/charts/node.ts +68 -28
- package/src/charts/render.ts +120 -30
- package/src/code/index.ts +3 -1
- package/src/code-editor/completion.ts +3 -2
- package/src/code-editor/index.ts +19 -0
- package/src/code-editor/node.ts +1574 -184
- package/src/code-language/languages/glsl.ts +12 -2
- package/src/code-language/languages/javascript.ts +21 -4
- package/src/code-language/languages/json.ts +12 -2
- package/src/code-language/languages/shell.ts +12 -2
- package/src/code-language/languages/sql.ts +22 -2
- package/src/code-language/stream.ts +257 -17
- package/src/code-language/types.ts +5 -4
- package/src/codeblock/index.ts +20 -3
- package/src/embed/client.ts +102 -8
- package/src/embed/index.ts +2 -0
- package/src/flow/draw.ts +167 -41
- package/src/flow/gl/color.ts +85 -0
- package/src/flow/gl/index.ts +342 -0
- package/src/flow/gl/pack.ts +585 -0
- package/src/flow/gl/renderer.ts +679 -0
- package/src/flow/gl/shaders.ts +371 -0
- package/src/flow/gl/text.ts +1295 -0
- package/src/flow/index.ts +794 -67
- package/src/flow/node.ts +2496 -1256
- package/src/flow/paint.ts +511 -0
- package/src/flow/paths.ts +80 -15
- package/src/flow/scene.ts +2199 -0
- package/src/flow/types.ts +113 -1
- package/src/html/controls.ts +73 -2
- package/src/html/css/calc.ts +385 -0
- package/src/html/css/cascade.ts +1403 -62
- package/src/html/css/color.ts +755 -0
- package/src/html/css/content.ts +417 -0
- package/src/html/css/counter-styles.ts +973 -0
- package/src/html/css/decode.ts +130 -0
- package/src/html/css/parse.ts +1514 -160
- package/src/html/css/style.ts +4102 -222
- package/src/html/css/ua.ts +58 -12
- package/src/html/css/values.ts +150 -50
- package/src/html/css/vars.ts +348 -0
- package/src/html/dom.ts +284 -17
- package/src/html/fonts.ts +641 -0
- package/src/html/gif.ts +233 -0
- package/src/html/index.ts +180 -20
- package/src/html/layout/block.ts +3456 -230
- package/src/html/layout/boxes.ts +2100 -190
- package/src/html/layout/cache.ts +289 -0
- package/src/html/layout/collapse.ts +317 -0
- package/src/html/layout/css-grid.ts +1063 -0
- package/src/html/layout/flex.ts +1115 -106
- package/src/html/layout/floats.ts +128 -14
- package/src/html/layout/grid-lines.ts +141 -0
- package/src/html/layout/grid.ts +181 -0
- package/src/html/layout/inline.ts +3943 -192
- package/src/html/layout/shaping.ts +140 -0
- package/src/html/layout/table.ts +696 -186
- package/src/html/layout/tracks.ts +437 -0
- package/src/html/node.ts +1486 -121
- package/src/html/paint.ts +4293 -169
- package/src/html/resources.ts +161 -50
- package/src/html/surfaces.ts +81 -0
- package/src/html/svg.ts +493 -0
- package/src/html/url.ts +86 -0
- package/src/index.ts +4 -0
- package/src/internal/collate.ts +12 -0
- package/src/{markdown → internal/markdown}/ast.ts +35 -7
- package/src/{markdown → internal/markdown}/parse.ts +288 -35
- package/src/internal/markdown/stringify.ts +772 -0
- package/src/internal/prewarm.ts +45 -0
- package/src/internal/scroll.ts +5 -3
- package/src/internal/sdf.ts +227 -0
- package/src/internal/splice.ts +25 -0
- package/src/internal/window.ts +36 -5
- package/src/maps/anchors.ts +913 -0
- package/src/maps/controller.ts +1084 -0
- package/src/maps/gl/buckets.ts +461 -0
- package/src/maps/gl/build-worker.ts +66 -0
- package/src/maps/gl/color.ts +62 -0
- package/src/maps/gl/cover.ts +205 -0
- package/src/maps/gl/index.ts +23 -0
- package/src/maps/gl/markers.ts +83 -0
- package/src/maps/gl/overlays.ts +379 -0
- package/src/maps/gl/pane.ts +42 -0
- package/src/maps/gl/placement.ts +968 -0
- package/src/maps/gl/renderer.ts +1660 -0
- package/src/maps/gl/shaders.ts +489 -0
- package/src/maps/gl/store.ts +615 -0
- package/src/maps/gl/text.ts +868 -0
- package/src/maps/gl/view.ts +1489 -0
- package/src/maps/icons.ts +183 -0
- package/src/maps/index.ts +274 -81
- package/src/maps/labels.ts +457 -144
- package/src/maps/node.ts +832 -665
- package/src/maps/overlay.ts +148 -43
- package/src/maps/paint.ts +3 -0
- package/src/maps/proj.ts +118 -0
- package/src/maps/renderer.ts +155 -0
- package/src/maps/sources.ts +43 -6
- package/src/maps/style.ts +32 -0
- package/src/maps/styles.ts +215 -4
- package/src/maps/theme.ts +51 -0
- package/src/maps/tiles.ts +253 -62
- package/src/maps/types.ts +213 -21
- package/src/markdown/expressions.ts +5 -1
- package/src/markdown/index.ts +163 -78
- package/src/markdown/spans.ts +6 -1
- package/src/media-player/index.ts +18 -4
- package/src/qml/containers.tsx +6 -1
- package/src/qml/interaction.tsx +42 -5
- package/src/qml/layouts.tsx +21 -6
- package/src/qml/qtquick.tsx +9 -2
- package/src/qml/react.tsx +7 -5
- package/src/reorder/clock.ts +27 -0
- package/src/reorder/index.ts +100 -27
- package/src/rich-text-editor/clipboard.ts +439 -0
- package/src/rich-text-editor/collab.ts +91 -0
- package/src/rich-text-editor/commands.ts +448 -0
- package/src/rich-text-editor/dom-globals.d.ts +23 -0
- package/src/rich-text-editor/drag.ts +211 -0
- package/src/rich-text-editor/html.ts +619 -0
- package/src/rich-text-editor/index.ts +1364 -0
- package/src/rich-text-editor/inline.ts +345 -0
- package/src/rich-text-editor/keymap.ts +510 -0
- package/src/rich-text-editor/keys.ts +228 -0
- package/src/rich-text-editor/look.ts +427 -0
- package/src/rich-text-editor/markdown.ts +653 -0
- package/src/rich-text-editor/marks.ts +101 -0
- package/src/rich-text-editor/nodes.ts +497 -0
- package/src/rich-text-editor/render.ts +837 -0
- package/src/rich-text-editor/schema.ts +431 -0
- package/src/rich-text-editor/store.ts +39 -0
- package/src/rich-text-editor/suggest.ts +597 -0
- package/src/rich-text-editor/tables.ts +272 -0
- package/src/rich-text-editor/toolbar.ts +724 -0
- package/src/rich-text-editor/view.ts +2287 -0
- package/src/rich-text-editor/virtual.ts +372 -0
- package/src/richtext/node.ts +294 -44
- package/src/richtext/runs.ts +23 -12
- package/src/table/index.ts +29 -0
- package/src/table/rows.ts +18 -8
- package/src/terminal/backends.ts +4 -1
- package/src/terminal/index.ts +47 -15
- package/src/terminal/vt/pty.ts +16 -2
- package/src/terminal-output/index.ts +6 -1
- package/src/three/canvas.ts +102 -8
- package/src/three/events.ts +49 -9
- package/src/tree/index.ts +19 -0
- package/dist/markdown/ast.d.ts.map +0 -1
- package/dist/markdown/ast.js +0 -17
- package/dist/markdown/ast.js.map +0 -1
- package/dist/markdown/parse.d.ts.map +0 -1
- package/dist/markdown/parse.js.map +0 -1
- package/dist/markdown/tags.d.ts.map +0 -1
- package/dist/markdown/tags.js.map +0 -1
- /package/dist/{markdown → internal/markdown}/parse.d.ts +0 -0
- /package/dist/{markdown → internal/markdown}/tags.d.ts +0 -0
- /package/dist/{markdown → internal/markdown}/tags.js +0 -0
- /package/src/{markdown → internal/markdown}/tags.ts +0 -0
|
@@ -0,0 +1,1489 @@
|
|
|
1
|
+
// The GL renderer as `<Map>` mounts it: a pane (`./pane.ts`) with a
|
|
2
|
+
// `<glarea>` in it, and every frame drawn into the surface from vector tiles
|
|
3
|
+
// — the style, and over it the labels.
|
|
4
|
+
//
|
|
5
|
+
// **The camera is not this renderer's.** It is the map's controller's
|
|
6
|
+
// (`../controller.ts`), which the retained renderer answers to as well, so a
|
|
7
|
+
// fallback in the middle of a pan keeps the camera and the handle. What this
|
|
8
|
+
// file adds is what a camera move costs here: a frame. A pan step is two
|
|
9
|
+
// numbers on the controller and `requestFrame()`, with no render and no
|
|
10
|
+
// commit in the loop — which matters more here than for the retained
|
|
11
|
+
// renderer, because every frame here is a full redraw and the budget for one
|
|
12
|
+
// is the whole frame, not a strip.
|
|
13
|
+
//
|
|
14
|
+
// **Input reaches the map through the pane's handlers**, which a press, a
|
|
15
|
+
// drag and a wheel over the surface bubble up to on both backends — the
|
|
16
|
+
// pointer over a `<glarea>` is the tree's (react-x11#545) — and which end at
|
|
17
|
+
// the controller methods the retained renderer's element calls.
|
|
18
|
+
//
|
|
19
|
+
// **A failure is `<Map>`'s to handle.** A surface that will not come up, a
|
|
20
|
+
// connection with no direct GL, a context or a shader that will not build, a
|
|
21
|
+
// throw from a frame: each is reported once through `onFailure`, and `<Map>`
|
|
22
|
+
// moves an `'auto'` map to the retained renderer or tells a `'gl'` one
|
|
23
|
+
// through `onError`. Nothing here falls back on its own.
|
|
24
|
+
import React, { useEffect, useMemo } from 'react';
|
|
25
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
26
|
+
import { useTheme } from 'react-x11';
|
|
27
|
+
import type { Style } from 'react-x11/style';
|
|
28
|
+
|
|
29
|
+
import { scaleOf } from '../../internal/units.js';
|
|
30
|
+
import type { MapController, MapPointerInput, MapView } from '../controller.js';
|
|
31
|
+
import {
|
|
32
|
+
ATTRIBUTION_OPACITY,
|
|
33
|
+
ATTRIBUTION_SIZE,
|
|
34
|
+
attributionLayout,
|
|
35
|
+
} from '../overlay.js';
|
|
36
|
+
import type { MapOverlay, OverlayPalette } from '../overlay.js';
|
|
37
|
+
import { prepareStyle } from '../paint.js';
|
|
38
|
+
import type { PreparedStyle } from '../paint.js';
|
|
39
|
+
import {
|
|
40
|
+
DEFAULT_TILE_SIZE,
|
|
41
|
+
project,
|
|
42
|
+
transformFor,
|
|
43
|
+
worldSize,
|
|
44
|
+
} from '../proj.js';
|
|
45
|
+
import type { MapCamera, ScreenRect } from '../proj.js';
|
|
46
|
+
import { attributionOf, pyramidOf } from '../sources.js';
|
|
47
|
+
import type { MapSource } from '../sources.js';
|
|
48
|
+
import type { MapStyle } from '../style.js';
|
|
49
|
+
import { defaultStyleFor, isDarkTheme, overlayPalette } from '../theme.js';
|
|
50
|
+
import type { MapFrameStats, MapProps } from '../types.js';
|
|
51
|
+
import { parseColor, premultiplied } from './color.js';
|
|
52
|
+
import type { Rgba } from './color.js';
|
|
53
|
+
import { renderCover } from './cover.js';
|
|
54
|
+
import type { CoverResult } from './cover.js';
|
|
55
|
+
import { MarkerBatcher } from './markers.js';
|
|
56
|
+
import {
|
|
57
|
+
buildOverlayBucket,
|
|
58
|
+
overlayRegion,
|
|
59
|
+
regionHolds,
|
|
60
|
+
regionPlacement,
|
|
61
|
+
} from './overlays.js';
|
|
62
|
+
import type { OverlayBucket } from './overlays.js';
|
|
63
|
+
import { GL_PANE } from './pane.js';
|
|
64
|
+
import { LABEL_INSTANCE, LabelPlacer } from './placement.js';
|
|
65
|
+
import type { LabelBatch, PlacementFrame } from './placement.js';
|
|
66
|
+
import { GlMapRenderer, gatesAt } from './renderer.js';
|
|
67
|
+
import type {
|
|
68
|
+
AttributionDraw,
|
|
69
|
+
FadeFrame,
|
|
70
|
+
OverlayDraw,
|
|
71
|
+
RenderFrame,
|
|
72
|
+
RenderTile,
|
|
73
|
+
} from './renderer.js';
|
|
74
|
+
import { GlTileStore } from './store.js';
|
|
75
|
+
import { LabelAtlas, SurfaceTextEngine, fieldBase } from './text.js';
|
|
76
|
+
|
|
77
|
+
const h = React.createElement;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Adaptive quality's rungs, cheapest last. Each leaves out more than the
|
|
81
|
+
* one before it, in the order that costs the picture least:
|
|
82
|
+
*
|
|
83
|
+
* 1. **The fill edge pass** — the half-pixel line that antialiases every
|
|
84
|
+
* polygon. A fifth of a dense frame, measured, and a staircase edge is
|
|
85
|
+
* invisible on a map in motion.
|
|
86
|
+
* 2. **The style's newest detail layers** — those whose `minZoom` is within
|
|
87
|
+
* `detail` levels of the zoom: buildings and service roads first, then
|
|
88
|
+
* sites, minor roads. The style already says which layers are details,
|
|
89
|
+
* by bringing them in last.
|
|
90
|
+
* 3. **A coarser tile level** — the parent level's tiles, drawn at this
|
|
91
|
+
* zoom: generalized geometry and fewer features, which is what a low-zoom
|
|
92
|
+
* frame (all land and water polygons) is made cheaper by.
|
|
93
|
+
*/
|
|
94
|
+
export const QUALITY_LADDER: readonly {
|
|
95
|
+
edges: boolean;
|
|
96
|
+
detail: number;
|
|
97
|
+
coarser: number;
|
|
98
|
+
}[] = [
|
|
99
|
+
{ edges: true, detail: 0, coarser: 0 },
|
|
100
|
+
{ edges: false, detail: 0, coarser: 0 },
|
|
101
|
+
{ edges: false, detail: 1, coarser: 0 },
|
|
102
|
+
{ edges: false, detail: 2, coarser: 0 },
|
|
103
|
+
{ edges: false, detail: 2, coarser: 1 },
|
|
104
|
+
{ edges: false, detail: 3, coarser: 2 },
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
type Rung = (typeof QUALITY_LADDER)[number];
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The rung a moving frame is drawn at: the lowest predicted to fit the
|
|
111
|
+
* budget, and a rung up only once that one is predicted well inside it —
|
|
112
|
+
* so a frame that sits at the budget does not flicker between two rungs.
|
|
113
|
+
*/
|
|
114
|
+
export function chooseQuality(
|
|
115
|
+
current: number,
|
|
116
|
+
predict: (rung: number) => number,
|
|
117
|
+
budgetMs: number,
|
|
118
|
+
): number {
|
|
119
|
+
const top = QUALITY_LADDER.length - 1;
|
|
120
|
+
let rung = Math.max(0, Math.min(current, top));
|
|
121
|
+
while (rung < top && predict(rung) > budgetMs) rung++;
|
|
122
|
+
while (rung > 0 && predict(rung - 1) <= budgetMs * 0.75) rung--;
|
|
123
|
+
return rung;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Milliseconds per segment instance, learned from frames it measured. It
|
|
128
|
+
* starts at what this renderer measured on an M1 Pro (3.7 M instances in
|
|
129
|
+
* 8.5 ms) and moves toward each measured frame, so a slower GPU is priced
|
|
130
|
+
* right within a few frames of the first gesture.
|
|
131
|
+
*/
|
|
132
|
+
class CostModel {
|
|
133
|
+
msPerInstance = 2.4e-6;
|
|
134
|
+
overheadMs = 0.4;
|
|
135
|
+
|
|
136
|
+
predict(instances: number): number {
|
|
137
|
+
return this.overheadMs + instances * this.msPerInstance;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
learn(instances: number, ms: number): void {
|
|
141
|
+
// A near-empty frame is all overhead and says nothing about the slope.
|
|
142
|
+
if (instances < 50_000) return;
|
|
143
|
+
const slope = Math.max(0, ms - this.overheadMs) / instances;
|
|
144
|
+
this.msPerInstance += 0.25 * (slope - this.msPerInstance);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
interface AreaNode {
|
|
149
|
+
requestFrame?(): void;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
interface PaneNode {
|
|
153
|
+
focus?(): void;
|
|
154
|
+
contentBox?(): { x: number; y: number; width: number; height: number };
|
|
155
|
+
notifyA11ySceneChanged?(): void;
|
|
156
|
+
resolvedTextStyle?(): { family?: string };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
interface DrawInfo {
|
|
160
|
+
width: number;
|
|
161
|
+
height: number;
|
|
162
|
+
node: object;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
interface SyntheticPointer {
|
|
166
|
+
x: number;
|
|
167
|
+
y: number;
|
|
168
|
+
button?: number;
|
|
169
|
+
detail?: number;
|
|
170
|
+
shiftKey?: boolean;
|
|
171
|
+
ctrlKey?: boolean;
|
|
172
|
+
altKey?: boolean;
|
|
173
|
+
metaKey?: boolean;
|
|
174
|
+
deltaY?: number;
|
|
175
|
+
/** Whether the device measured the scroll rather than clicking it — a
|
|
176
|
+
* touchpad's valuators against a wheel's notches. */
|
|
177
|
+
smooth?: boolean;
|
|
178
|
+
keysym?: number;
|
|
179
|
+
capturePointer?(): void;
|
|
180
|
+
preventDefault?(): void;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** How far outside the pane tiles are kept warm, in logical pixels — the
|
|
184
|
+
* retained renderer's ring, so an ordinary flick has its tiles. */
|
|
185
|
+
const COVER_PADDING = 256;
|
|
186
|
+
/** How long a fade waits for the new level's tiles before starting anyway. */
|
|
187
|
+
const FADE_WAIT_MS = 400;
|
|
188
|
+
const DEFAULT_BUDGET_MS = 12;
|
|
189
|
+
/** Milliseconds a frame may spend building tiles' buckets on this thread;
|
|
190
|
+
* a frame always builds at least one waiting tile. */
|
|
191
|
+
const BUILD_BUDGET_MS = 6;
|
|
192
|
+
/** Milliseconds a frame may spend measuring strings it has not set before
|
|
193
|
+
* — the one label cost that can be large (the first strings on X11 load
|
|
194
|
+
* fonts and rasterize glyphs in JS). Placement's remainder waits a frame. */
|
|
195
|
+
const TEXT_BUDGET_MOVING_MS = 1;
|
|
196
|
+
/** Milliseconds a frame may spend making label fields (`text.ts`), about
|
|
197
|
+
* 0.2 ms each, when there are any to make. */
|
|
198
|
+
const FIELD_BUDGET_MS = 4;
|
|
199
|
+
/** With `labelsWhileMoving`: milliseconds a frame may spend setting the
|
|
200
|
+
* text of names it has just placed, so they draw in it. */
|
|
201
|
+
const LIVE_TEXT_BUDGET_MS = 8;
|
|
202
|
+
const TEXT_BUDGET_SETTLED_MS = 4;
|
|
203
|
+
/**
|
|
204
|
+
* How often a moving view is placed again, in milliseconds — every other
|
|
205
|
+
* frame or so. The labels shown follow the map every frame regardless; what
|
|
206
|
+
* waits a frame is a name coming into view, and what that buys is the frame
|
|
207
|
+
* that would have placed *and* measured *and* uploaded at once, which is
|
|
208
|
+
* what the late frames of a labelled pan were made of.
|
|
209
|
+
*/
|
|
210
|
+
const PLACE_INTERVAL_MS = 25;
|
|
211
|
+
/** How long the zoom must hold still before labels not already shown are
|
|
212
|
+
* placed (see `PlacementFrame.admit`). Under the controller's settle
|
|
213
|
+
* window, so the frame that marks the camera settled is one that admits
|
|
214
|
+
* them. */
|
|
215
|
+
const ZOOM_QUIET_MS = 120;
|
|
216
|
+
/** Sources taken off the map whose tiles are kept for a switch back. */
|
|
217
|
+
const INACTIVE_STORES = 2;
|
|
218
|
+
const NO_RASTER: MapFrameStats['draw'] = {
|
|
219
|
+
features: 0,
|
|
220
|
+
vertices: 0,
|
|
221
|
+
decimated: 0,
|
|
222
|
+
culled: 0,
|
|
223
|
+
batches: 0,
|
|
224
|
+
};
|
|
225
|
+
/** A theme colour that does not parse falls back to these. */
|
|
226
|
+
const BLACK: Rgba = [0, 0, 0, 1];
|
|
227
|
+
const WHITE: Rgba = [1, 1, 1, 1];
|
|
228
|
+
|
|
229
|
+
const INDIRECT =
|
|
230
|
+
'@react-x11/components maps: this <glarea> draws through indirect GLX, ' +
|
|
231
|
+
'which has no shaders — the GL renderer needs the direct backend, which ' +
|
|
232
|
+
"on X11 is createRoot({ glPolicy: 'auto' }) and a DRI3 or Apple-DRI server";
|
|
233
|
+
|
|
234
|
+
const globals = globalThis as unknown as {
|
|
235
|
+
performance?: { now(): number };
|
|
236
|
+
setTimeout(fn: () => void, ms: number): unknown;
|
|
237
|
+
clearTimeout(handle: unknown): void;
|
|
238
|
+
process?: { env?: Record<string, string | undefined> };
|
|
239
|
+
console?: { warn(message: string): void };
|
|
240
|
+
};
|
|
241
|
+
const now = (): number => globals.performance?.now() ?? Date.now();
|
|
242
|
+
|
|
243
|
+
const EMPTY_PANE: ScreenRect = { x: 0, y: 0, width: 0, height: 0 };
|
|
244
|
+
const EMPTY_COVER: CoverResult = {
|
|
245
|
+
tiles: [],
|
|
246
|
+
missing: [],
|
|
247
|
+
level: 0,
|
|
248
|
+
own: 0,
|
|
249
|
+
ancestors: 0,
|
|
250
|
+
descendants: 0,
|
|
251
|
+
inView: [],
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
/** Which levels a frame shows: one, or two while the second fades in. */
|
|
255
|
+
export interface LevelPlan {
|
|
256
|
+
base: CoverResult;
|
|
257
|
+
next: CoverResult | null;
|
|
258
|
+
alpha: number;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* The level fade: which level the frame is drawn from, and the one fading
|
|
263
|
+
* in over it.
|
|
264
|
+
*
|
|
265
|
+
* A new level is waited for — briefly, until every tile of it in view has
|
|
266
|
+
* answered — so the fade shows it arriving rather than a stand-in for it;
|
|
267
|
+
* then it fades in over the level it replaces, with an ease at both ends.
|
|
268
|
+
* A level more than one step away is not faded (the view it would fade
|
|
269
|
+
* from is sixteen times the tiles), and a change of target halfway
|
|
270
|
+
* through a fade takes the half-shown level as the one it fades from.
|
|
271
|
+
*
|
|
272
|
+
* What it cannot carry is a layer the style starts or stops drawing, which
|
|
273
|
+
* is {@link GateFader}'s: see there for why that is a separate thing and
|
|
274
|
+
* not a scene of its own.
|
|
275
|
+
*/
|
|
276
|
+
export class LevelFader {
|
|
277
|
+
/** The level the frame is drawn from, once one has been drawn. */
|
|
278
|
+
shown: number | null = null;
|
|
279
|
+
private _fade: {
|
|
280
|
+
to: number;
|
|
281
|
+
start: number | null;
|
|
282
|
+
waitSince: number;
|
|
283
|
+
} | null = null;
|
|
284
|
+
|
|
285
|
+
/** Whether a fade is running or waiting to start. */
|
|
286
|
+
get fading(): boolean {
|
|
287
|
+
return this._fade !== null;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
plan(
|
|
291
|
+
target: CoverResult,
|
|
292
|
+
cover: (level: number) => CoverResult,
|
|
293
|
+
options: { at: number; fadeMs: number },
|
|
294
|
+
): LevelPlan {
|
|
295
|
+
const { at, fadeMs } = options;
|
|
296
|
+
const to = target.level;
|
|
297
|
+
const plain = { base: target, next: null, alpha: 0 };
|
|
298
|
+
if (fadeMs <= 0 || this.shown === null || Math.abs(to - this.shown) > 1) {
|
|
299
|
+
this.shown = to;
|
|
300
|
+
this._fade = null;
|
|
301
|
+
return plain;
|
|
302
|
+
}
|
|
303
|
+
if (to === this.shown) {
|
|
304
|
+
this._fade = null;
|
|
305
|
+
return plain;
|
|
306
|
+
}
|
|
307
|
+
let fade = this._fade;
|
|
308
|
+
if (!fade || fade.to !== to) {
|
|
309
|
+
if (fade && fade.start !== null && (at - fade.start) / fadeMs >= 0.5) {
|
|
310
|
+
this.shown = fade.to;
|
|
311
|
+
}
|
|
312
|
+
if (this.shown === to) {
|
|
313
|
+
this._fade = null;
|
|
314
|
+
return plain;
|
|
315
|
+
}
|
|
316
|
+
fade = this._fade = { to, start: null, waitSince: at };
|
|
317
|
+
}
|
|
318
|
+
const base = cover(this.shown);
|
|
319
|
+
if (fade.start === null) {
|
|
320
|
+
if (target.missing.length > 0 && at - fade.waitSince < FADE_WAIT_MS) {
|
|
321
|
+
return { base, next: null, alpha: 0 };
|
|
322
|
+
}
|
|
323
|
+
fade.start = at;
|
|
324
|
+
}
|
|
325
|
+
const p = (at - fade.start) / fadeMs;
|
|
326
|
+
if (p >= 1) {
|
|
327
|
+
this.shown = to;
|
|
328
|
+
this._fade = null;
|
|
329
|
+
return plain;
|
|
330
|
+
}
|
|
331
|
+
return { base, next: target, alpha: p * p * (3 - 2 * p) };
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* The zoom gate's fade: how much of each style layer to draw, as the camera
|
|
337
|
+
* zooms past the `minZoom` and `maxZoom` the style gives them.
|
|
338
|
+
*
|
|
339
|
+
* **Why this is not a scene.** A level fade cross-fades two whole pictures,
|
|
340
|
+
* and a picture is the natural unit for a change of level. A layer ending
|
|
341
|
+
* is not: `minZoom` is a zoom, the pyramid changes level at a different
|
|
342
|
+
* zoom, and the two flips land on different frames — so carrying the gate
|
|
343
|
+
* as a second scene means a fade retargeted in the middle of another one,
|
|
344
|
+
* which can only either snap to the arriving scene or restart from the
|
|
345
|
+
* outgoing one. Both are a step in the picture, and stepping is what the
|
|
346
|
+
* fade is for. That is what made buildings vanish for one wheel notch and
|
|
347
|
+
* come back for the next. An opacity per layer has no such conflict: each
|
|
348
|
+
* layer's number moves on its own, through any number of level fades, and
|
|
349
|
+
* both scenes of a level fade draw with the same numbers — so the one
|
|
350
|
+
* thing an opacity ramp is bad at, a map dissolving layer by layer, cannot
|
|
351
|
+
* happen here, because only the layers actually crossing a gate move.
|
|
352
|
+
*/
|
|
353
|
+
export class GateFader {
|
|
354
|
+
/** Per style layer, in the prepared style's order. */
|
|
355
|
+
private _alpha: number[] = [];
|
|
356
|
+
private _at: number | null = null;
|
|
357
|
+
private _fading = false;
|
|
358
|
+
|
|
359
|
+
/** Whether any layer is between the gate's ends — a frame to ask for. */
|
|
360
|
+
get fading(): boolean {
|
|
361
|
+
return this._fading;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* The frame's `layerAlpha`, or null for "what the gate says" — which is
|
|
366
|
+
* every frame where nothing is crossing one, and every frame at all with
|
|
367
|
+
* the fade turned off.
|
|
368
|
+
*/
|
|
369
|
+
step(
|
|
370
|
+
style: PreparedStyle,
|
|
371
|
+
options: {
|
|
372
|
+
at: number;
|
|
373
|
+
fadeMs: number;
|
|
374
|
+
zoom: number;
|
|
375
|
+
/** Adaptive quality's, from the rung this frame is drawn at. */
|
|
376
|
+
detail: number;
|
|
377
|
+
},
|
|
378
|
+
): readonly number[] | null {
|
|
379
|
+
const { at, fadeMs, zoom, detail } = options;
|
|
380
|
+
const targets = gatesAt(style, zoom, detail);
|
|
381
|
+
// The first frame, a restyle that changes the layer count, and the fade
|
|
382
|
+
// turned off: the gate, with nothing to advance.
|
|
383
|
+
const first = this._at === null || this._alpha.length !== targets.length;
|
|
384
|
+
if (fadeMs <= 0 || first) {
|
|
385
|
+
this._alpha = targets;
|
|
386
|
+
this._at = at;
|
|
387
|
+
this._fading = false;
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
const step = (at - (this._at ?? at)) / fadeMs;
|
|
391
|
+
this._at = at;
|
|
392
|
+
let fading = false;
|
|
393
|
+
let moved = false;
|
|
394
|
+
for (let i = 0; i < targets.length; i++) {
|
|
395
|
+
const to = targets[i];
|
|
396
|
+
const from = this._alpha[i];
|
|
397
|
+
if (from !== to) {
|
|
398
|
+
const next =
|
|
399
|
+
to > from ? Math.min(to, from + step) : Math.max(to, from - step);
|
|
400
|
+
this._alpha[i] = next;
|
|
401
|
+
if (next !== to) fading = true;
|
|
402
|
+
}
|
|
403
|
+
if (this._alpha[i] !== to) moved = true;
|
|
404
|
+
}
|
|
405
|
+
this._fading = fading;
|
|
406
|
+
return moved ? this._alpha : null;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/** One source on the map, as a frame found it. */
|
|
411
|
+
interface SourceCover {
|
|
412
|
+
source: MapSource;
|
|
413
|
+
store: GlTileStore;
|
|
414
|
+
pyramid: { minZoom: number; maxZoom: number; tileSize: number };
|
|
415
|
+
target: CoverResult;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/** What `<Map>` hands the GL renderer. */
|
|
419
|
+
export interface GlMapPaneProps {
|
|
420
|
+
/** The map's controller: its camera, its gestures and its handle. */
|
|
421
|
+
controller: MapController;
|
|
422
|
+
/** Everything `<Map>` was given. */
|
|
423
|
+
map: MapProps;
|
|
424
|
+
/** GL failed — the surface, the context, a shader, a frame. Called once;
|
|
425
|
+
* what happens next is `<Map>`'s decision. */
|
|
426
|
+
onFailure: (error: Error) => void;
|
|
427
|
+
style?: Style;
|
|
428
|
+
role?: string;
|
|
429
|
+
'aria-label'?: string;
|
|
430
|
+
'data-testname'?: string;
|
|
431
|
+
/** Drawn above the surface, as core draws a `<glarea>`'s children — given
|
|
432
|
+
* only where `useSupports('glOverlay')` says this connection does. */
|
|
433
|
+
children?: ReactNode;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* The driver: everything a frame is, between the controller's camera and
|
|
438
|
+
* the renderer. Exported for the tests that drive it against a recording
|
|
439
|
+
* GL table — there is no other way to see a whole frame's decisions, and
|
|
440
|
+
* this host cannot render one (indirect GLX has no shaders).
|
|
441
|
+
*/
|
|
442
|
+
export class GlMapDriver implements MapView {
|
|
443
|
+
props: GlMapPaneProps;
|
|
444
|
+
theme: unknown = null;
|
|
445
|
+
private readonly _controller: MapController;
|
|
446
|
+
private _pane: PaneNode | null = null;
|
|
447
|
+
private _area: AreaNode | null = null;
|
|
448
|
+
private _renderer: GlMapRenderer | null = null;
|
|
449
|
+
private _gl: unknown = null;
|
|
450
|
+
private _failed = false;
|
|
451
|
+
/** A store per source object — the retained cache's rule: a source is its
|
|
452
|
+
* own cache, whatever its id. */
|
|
453
|
+
private readonly _stores = new Map<MapSource, GlTileStore>();
|
|
454
|
+
/** Sources taken off the map, oldest first, whose tiles are kept. */
|
|
455
|
+
private _inactive: MapSource[] = [];
|
|
456
|
+
private _style: MapStyle | null = null;
|
|
457
|
+
private _prepared: PreparedStyle | null = null;
|
|
458
|
+
private _committed: MapProps | null = null;
|
|
459
|
+
private _lastEnd: number | null = null;
|
|
460
|
+
private _drawnZoom: number | null = null;
|
|
461
|
+
/** When the zoom last changed — which holds new labels back. */
|
|
462
|
+
private _zoomedAt = -Infinity;
|
|
463
|
+
private _rung = 0;
|
|
464
|
+
private readonly _cost = new CostModel();
|
|
465
|
+
private readonly _fader = new LevelFader();
|
|
466
|
+
private readonly _gates = new GateFader();
|
|
467
|
+
private readonly _placer = new LabelPlacer();
|
|
468
|
+
/** Places the labels of the view a glide stops at, and shows nothing:
|
|
469
|
+
* what it asks the atlas for is measured and set while the glide is
|
|
470
|
+
* still being drawn. */
|
|
471
|
+
private readonly _scout = new LabelPlacer();
|
|
472
|
+
/** The destination the scout last placed, and whether that placement
|
|
473
|
+
* measured everything it offered — a frame's text budget can run out
|
|
474
|
+
* first, and then the next frame places it again. */
|
|
475
|
+
private _scoutedFor = '';
|
|
476
|
+
private _scoutDone = false;
|
|
477
|
+
private readonly _markers = new MarkerBatcher();
|
|
478
|
+
/** The overlays' bucket, and what it was built from. */
|
|
479
|
+
private _overlay: {
|
|
480
|
+
bucket: OverlayBucket;
|
|
481
|
+
overlays: readonly MapOverlay[];
|
|
482
|
+
accent: string;
|
|
483
|
+
} | null = null;
|
|
484
|
+
private _atlas: LabelAtlas | null = null;
|
|
485
|
+
private _atlasKey = '';
|
|
486
|
+
/** No labels — the app has no fonts, or placing them failed — and no
|
|
487
|
+
* asking again every frame. */
|
|
488
|
+
private _noText = false;
|
|
489
|
+
/** The attribution's one instance. */
|
|
490
|
+
private readonly _attributionText = new Float32Array(LABEL_INSTANCE);
|
|
491
|
+
/** An attribution a frame could not draw yet — its string not measured,
|
|
492
|
+
* or its field not in the texture — and the size it is drawn at. */
|
|
493
|
+
private _attributionWanted: { text: string; size: number } | null = null;
|
|
494
|
+
/** When labels were last placed. */
|
|
495
|
+
private _placedAt = -Infinity;
|
|
496
|
+
private _pumpTimer: unknown = null;
|
|
497
|
+
private _stats: MapFrameStats | null = null;
|
|
498
|
+
private _announced = false;
|
|
499
|
+
private _paneStyle: {
|
|
500
|
+
base: Style | undefined;
|
|
501
|
+
background: string;
|
|
502
|
+
value: Style;
|
|
503
|
+
} | null = null;
|
|
504
|
+
|
|
505
|
+
constructor(controller: MapController, props: GlMapPaneProps) {
|
|
506
|
+
this._controller = controller;
|
|
507
|
+
this.props = props;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// --- the controller's view ----------------------------------------------------
|
|
511
|
+
|
|
512
|
+
/** The pane in the window's logical pixels: its box's, which is current
|
|
513
|
+
* before the first frame and on a surface that never draws at all. */
|
|
514
|
+
pane(): ScreenRect {
|
|
515
|
+
const box = this._pane?.contentBox?.();
|
|
516
|
+
if (!box) return EMPTY_PANE;
|
|
517
|
+
const s = scaleOf(this._pane);
|
|
518
|
+
return {
|
|
519
|
+
x: box.x / s,
|
|
520
|
+
y: box.y / s,
|
|
521
|
+
width: box.width / s,
|
|
522
|
+
height: box.height / s,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
scale(): number {
|
|
527
|
+
return scaleOf(this._pane);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/** Any camera move is one frame — there is no blit to prefer. */
|
|
531
|
+
moved(): void {
|
|
532
|
+
this.request();
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/** Still: the next frame draws everything adaptive quality left out. */
|
|
536
|
+
settled(): void {
|
|
537
|
+
this.request();
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/** `refresh()`: the style read again, and every tile's buckets and label
|
|
541
|
+
* anchors built again from its bytes. */
|
|
542
|
+
refresh(): void {
|
|
543
|
+
this._style = null;
|
|
544
|
+
this._prepared = null;
|
|
545
|
+
for (const store of this._stores.values()) store.rebuild();
|
|
546
|
+
this._placer.reset();
|
|
547
|
+
this.request();
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
stats(): MapFrameStats | null {
|
|
551
|
+
return this._stats;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
focus(): void {
|
|
555
|
+
this._pane?.focus?.();
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
readonly request = (): void => {
|
|
559
|
+
this._area?.requestFrame?.();
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
attach(): void {
|
|
563
|
+
this._controller.attach(this);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
readonly paneRef = (node: PaneNode | null): void => {
|
|
567
|
+
this._pane = node;
|
|
568
|
+
if (node) this._controller.attach(this);
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
readonly areaRef = (node: AreaNode | null): void => {
|
|
572
|
+
this._area = node;
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
/** A commit landed: what it changed that a frame shows asks for one. */
|
|
576
|
+
committed(): void {
|
|
577
|
+
const map = this.props.map;
|
|
578
|
+
const before = this._committed;
|
|
579
|
+
this._committed = map;
|
|
580
|
+
if (!before) return;
|
|
581
|
+
if (map.markers !== before.markers) {
|
|
582
|
+
this._pane?.notifyA11ySceneChanged?.();
|
|
583
|
+
}
|
|
584
|
+
if (
|
|
585
|
+
map.camera !== before.camera ||
|
|
586
|
+
map.markers !== before.markers ||
|
|
587
|
+
map.overlays !== before.overlays ||
|
|
588
|
+
map.sources !== before.sources ||
|
|
589
|
+
map.mapStyle !== before.mapStyle ||
|
|
590
|
+
map.attribution !== before.attribution ||
|
|
591
|
+
map.antialias !== before.antialias ||
|
|
592
|
+
map.fillRule !== before.fillRule ||
|
|
593
|
+
map.levelFade !== before.levelFade ||
|
|
594
|
+
map.adaptive !== before.adaptive
|
|
595
|
+
) {
|
|
596
|
+
this.request();
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/** The style's background: what the pane shows under the surface, and
|
|
601
|
+
* what the surface is cleared to. */
|
|
602
|
+
background(): string {
|
|
603
|
+
const style =
|
|
604
|
+
this.props.map.mapStyle ?? defaultStyleFor(isDarkTheme(this.theme));
|
|
605
|
+
return style.background ?? '#ffffff';
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/** The pane's style, the same object for as long as nothing in it changes
|
|
609
|
+
* — a new one every render would be a damage claim every render. */
|
|
610
|
+
paneStyle(base: Style | undefined, background: string): Style {
|
|
611
|
+
const cached = this._paneStyle;
|
|
612
|
+
if (cached && cached.base === base && cached.background === background) {
|
|
613
|
+
return cached.value;
|
|
614
|
+
}
|
|
615
|
+
const value = [base, { backgroundColor: background }] as unknown as Style;
|
|
616
|
+
this._paneStyle = { base, background, value };
|
|
617
|
+
return value;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// --- input --------------------------------------------------------------------
|
|
621
|
+
|
|
622
|
+
private _local(ev: { x: number; y: number }): { x: number; y: number } {
|
|
623
|
+
const pane = this.pane();
|
|
624
|
+
return { x: ev.x - pane.x, y: ev.y - pane.y };
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
private _input(ev: SyntheticPointer): MapPointerInput {
|
|
628
|
+
const p = this._local(ev);
|
|
629
|
+
return {
|
|
630
|
+
x: p.x,
|
|
631
|
+
y: p.y,
|
|
632
|
+
button: ev.button,
|
|
633
|
+
detail: ev.detail,
|
|
634
|
+
shiftKey: ev.shiftKey,
|
|
635
|
+
ctrlKey: ev.ctrlKey,
|
|
636
|
+
altKey: ev.altKey,
|
|
637
|
+
metaKey: ev.metaKey,
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/** The pane's handlers — the whole of the map's input, on both backends. */
|
|
642
|
+
readonly handlers = {
|
|
643
|
+
onMouseDown: (ev: SyntheticPointer): void => {
|
|
644
|
+
if (this._controller.pointerDown(this._input(ev)) === 'pan') {
|
|
645
|
+
this._pane?.focus?.();
|
|
646
|
+
}
|
|
647
|
+
ev.capturePointer?.();
|
|
648
|
+
},
|
|
649
|
+
onMouseMove: (ev: SyntheticPointer): void => {
|
|
650
|
+
const input = this._input(ev);
|
|
651
|
+
if (this._controller.dragging) this._controller.pointerDrag(input);
|
|
652
|
+
else this._controller.pointerMove(input);
|
|
653
|
+
},
|
|
654
|
+
onMouseUp: (ev: SyntheticPointer): void => {
|
|
655
|
+
this._controller.pointerUp(this._input(ev));
|
|
656
|
+
},
|
|
657
|
+
onMouseLeave: (): void => this._controller.pointerLeave(),
|
|
658
|
+
onWheel: (ev: SyntheticPointer): void => {
|
|
659
|
+
if (
|
|
660
|
+
this._controller.wheel(
|
|
661
|
+
this._local(ev),
|
|
662
|
+
ev.deltaY ?? 0,
|
|
663
|
+
ev.smooth === true,
|
|
664
|
+
)
|
|
665
|
+
) {
|
|
666
|
+
ev.preventDefault?.();
|
|
667
|
+
}
|
|
668
|
+
},
|
|
669
|
+
onKeyDown: (ev: SyntheticPointer): void => {
|
|
670
|
+
if (this._controller.keyDown(ev.keysym ?? 0, ev.shiftKey ?? false)) {
|
|
671
|
+
ev.preventDefault?.();
|
|
672
|
+
}
|
|
673
|
+
},
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
// --- failure ------------------------------------------------------------------
|
|
677
|
+
|
|
678
|
+
readonly surfaceError = (error: Error): void => {
|
|
679
|
+
this._fail(error);
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
private _fail(error: unknown): void {
|
|
683
|
+
if (this._failed) return;
|
|
684
|
+
this._failed = true;
|
|
685
|
+
try {
|
|
686
|
+
this._renderer?.dispose();
|
|
687
|
+
} catch {
|
|
688
|
+
// A context that failed may not delete what it made either.
|
|
689
|
+
}
|
|
690
|
+
this._renderer = null;
|
|
691
|
+
this.props.onFailure(
|
|
692
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// --- the frame ------------------------------------------------------------------
|
|
697
|
+
|
|
698
|
+
readonly draw = (gl: unknown, info: DrawInfo): void => {
|
|
699
|
+
if (this._failed) return;
|
|
700
|
+
try {
|
|
701
|
+
if ((gl as { backend?: string }).backend !== 'direct') {
|
|
702
|
+
throw new Error(INDIRECT);
|
|
703
|
+
}
|
|
704
|
+
const controller = this._controller;
|
|
705
|
+
// A fit asked for before layout had a size lands in the first frame
|
|
706
|
+
// that has one, and this frame draws it.
|
|
707
|
+
controller.beginFrame();
|
|
708
|
+
controller.painting = true;
|
|
709
|
+
try {
|
|
710
|
+
this._frame(gl, info);
|
|
711
|
+
} finally {
|
|
712
|
+
controller.painting = false;
|
|
713
|
+
// `onFrame` ran inside that, and a camera animation drives itself
|
|
714
|
+
// from it — so whatever it asked for is scheduled here.
|
|
715
|
+
controller.paintEnded();
|
|
716
|
+
}
|
|
717
|
+
} catch (error) {
|
|
718
|
+
// Core hands an `onDraw` throw to `<glarea onError>` and nothing else;
|
|
719
|
+
// caught here, it is the map's failure, reported the one way.
|
|
720
|
+
this._fail(error);
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
private _styleNow(): { style: MapStyle; prepared: PreparedStyle } {
|
|
725
|
+
const style =
|
|
726
|
+
this.props.map.mapStyle ?? defaultStyleFor(isDarkTheme(this.theme));
|
|
727
|
+
if (style !== this._style || !this._prepared) {
|
|
728
|
+
this._style = style;
|
|
729
|
+
this._prepared = prepareStyle(style);
|
|
730
|
+
for (const store of this._stores.values()) {
|
|
731
|
+
store.setStyle(this._prepared);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
return { style, prepared: this._prepared };
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/** A store for every source on the map, in order; the stores of sources
|
|
738
|
+
* taken off it stop loading and keep their tiles, a couple of them. */
|
|
739
|
+
private _sourcesNow(
|
|
740
|
+
prepared: PreparedStyle,
|
|
741
|
+
): { source: MapSource; store: GlTileStore }[] {
|
|
742
|
+
const map = this.props.map;
|
|
743
|
+
const sources = map.sources ?? [];
|
|
744
|
+
const out: { source: MapSource; store: GlTileStore }[] = [];
|
|
745
|
+
sources.forEach((source, index) => {
|
|
746
|
+
let store = this._stores.get(source);
|
|
747
|
+
if (!store) {
|
|
748
|
+
store = new GlTileStore({
|
|
749
|
+
source,
|
|
750
|
+
sourceId: source.id ?? `source-${index}`,
|
|
751
|
+
prepared,
|
|
752
|
+
onChange: this.request,
|
|
753
|
+
onError: (error, tile) => this.props.map.onTileError?.(error, tile),
|
|
754
|
+
workers: map.buildWorkers ?? 0,
|
|
755
|
+
});
|
|
756
|
+
this._stores.set(source, store);
|
|
757
|
+
}
|
|
758
|
+
out.push({ source, store });
|
|
759
|
+
});
|
|
760
|
+
this._inactive = this._inactive.filter((s) => !sources.includes(s));
|
|
761
|
+
for (const [source, store] of this._stores) {
|
|
762
|
+
if (sources.includes(source) || this._inactive.includes(source)) {
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
store.abortLoads();
|
|
766
|
+
this._inactive.push(source);
|
|
767
|
+
}
|
|
768
|
+
while (this._inactive.length > INACTIVE_STORES) {
|
|
769
|
+
const old = this._inactive.shift()!;
|
|
770
|
+
this._stores.get(old)?.dispose((data) => this._renderer?.release(data));
|
|
771
|
+
this._stores.delete(old);
|
|
772
|
+
}
|
|
773
|
+
return out;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* How long a moving frame may take, or null for "as long as it likes".
|
|
778
|
+
*
|
|
779
|
+
* **On unless turned off.** A renderer with no budget spends whatever a
|
|
780
|
+
* frame costs and asks for the next one on the following compositor tick
|
|
781
|
+
* regardless, so at the expensive end of a zoom sweep it takes the whole
|
|
782
|
+
* thread — and the app around it stops getting its timers. Measured on a
|
|
783
|
+
* fly across ten levels: without a budget the loop went a second at a time
|
|
784
|
+
* without running a `setTimeout`, and nothing inside it was slow, because
|
|
785
|
+
* the cost was spread over a hundred frames rather than spent in one call.
|
|
786
|
+
* With the budget, none. An embedding app should not have to know that to
|
|
787
|
+
* keep its own clock.
|
|
788
|
+
*
|
|
789
|
+
* What it costs is detail while the camera moves, and only while it moves:
|
|
790
|
+
* the frame after it settles draws everything again.
|
|
791
|
+
*/
|
|
792
|
+
private _budget(): number | null {
|
|
793
|
+
const adaptive = this.props.map.adaptive;
|
|
794
|
+
if (adaptive === false) return null;
|
|
795
|
+
return typeof adaptive === 'object'
|
|
796
|
+
? (adaptive.budgetMs ?? DEFAULT_BUDGET_MS)
|
|
797
|
+
: DEFAULT_BUDGET_MS;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
private _frame(gl: unknown, info: DrawInfo): void {
|
|
801
|
+
const map = this.props.map;
|
|
802
|
+
const controller = this._controller;
|
|
803
|
+
const scale = scaleOf(info.node);
|
|
804
|
+
if (gl !== this._gl) {
|
|
805
|
+
// A new context — the surface was recreated — owns none of the old
|
|
806
|
+
// one's buffers, so everything is uploaded again from the buckets the
|
|
807
|
+
// stores still hold.
|
|
808
|
+
this._renderer = null;
|
|
809
|
+
this._gl = gl;
|
|
810
|
+
}
|
|
811
|
+
this._renderer ??= new GlMapRenderer(gl, {
|
|
812
|
+
antialias: map.antialias,
|
|
813
|
+
fillRule: map.fillRule,
|
|
814
|
+
});
|
|
815
|
+
const renderer = this._renderer;
|
|
816
|
+
renderer.antialias = map.antialias ?? true;
|
|
817
|
+
renderer.fillRule = map.fillRule ?? 'nonzero';
|
|
818
|
+
const { style, prepared } = this._styleNow();
|
|
819
|
+
const camera = controller.camera();
|
|
820
|
+
if (this._drawnZoom !== null && this._drawnZoom !== camera.zoom) {
|
|
821
|
+
this._zoomedAt = now();
|
|
822
|
+
}
|
|
823
|
+
this._drawnZoom = camera.zoom;
|
|
824
|
+
const pane = { width: info.width / scale, height: info.height / scale };
|
|
825
|
+
const at = now();
|
|
826
|
+
const moving = controller.moving;
|
|
827
|
+
const budget = this._budget();
|
|
828
|
+
|
|
829
|
+
// Every source's tiles: built as far as the budget goes, then asked
|
|
830
|
+
// for — the view, and the ring around it that keeps a pan's tiles warm.
|
|
831
|
+
const sources = this._sourcesNow(prepared);
|
|
832
|
+
const buildStart = now();
|
|
833
|
+
const deadline = buildStart + BUILD_BUDGET_MS;
|
|
834
|
+
let more = false;
|
|
835
|
+
for (const { store } of sources) {
|
|
836
|
+
store.tick();
|
|
837
|
+
if (store.pump(deadline)) more = true;
|
|
838
|
+
}
|
|
839
|
+
const buildMs = now() - buildStart;
|
|
840
|
+
const covers: SourceCover[] = sources.map(({ source, store }) => {
|
|
841
|
+
const pyramid = pyramidOf(source);
|
|
842
|
+
const target = renderCover(camera, pane, scale, pyramid, store, {
|
|
843
|
+
padding: COVER_PADDING,
|
|
844
|
+
});
|
|
845
|
+
store.want(target.missing);
|
|
846
|
+
return { source, store, pyramid, target };
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
// Where a wheel's glide stops, while one is running: its tiles are
|
|
850
|
+
// asked for after the view's own, so the view the glide lands on is
|
|
851
|
+
// loaded — and its labels set, below — by the time it gets there.
|
|
852
|
+
const destination = moving ? controller.destination() : null;
|
|
853
|
+
const ahead = destination
|
|
854
|
+
? covers.map((c) => {
|
|
855
|
+
const cover = renderCover(
|
|
856
|
+
destination,
|
|
857
|
+
pane,
|
|
858
|
+
scale,
|
|
859
|
+
c.pyramid,
|
|
860
|
+
c.store,
|
|
861
|
+
);
|
|
862
|
+
c.store.want(cover.missing);
|
|
863
|
+
return cover;
|
|
864
|
+
})
|
|
865
|
+
: null;
|
|
866
|
+
|
|
867
|
+
// The level fade and adaptive quality are the first source's — the
|
|
868
|
+
// basemap's; a pyramid over it is drawn at its own level throughout.
|
|
869
|
+
const primary = covers[0] ?? null;
|
|
870
|
+
const coverAt = (level: number): CoverResult =>
|
|
871
|
+
primary
|
|
872
|
+
? renderCover(camera, pane, scale, primary.pyramid, primary.store, {
|
|
873
|
+
level,
|
|
874
|
+
})
|
|
875
|
+
: EMPTY_COVER;
|
|
876
|
+
const fadeMs = map.levelFade ?? 0;
|
|
877
|
+
const plan: LevelPlan = primary
|
|
878
|
+
? this._fader.plan(primary.target, coverAt, { at, fadeMs })
|
|
879
|
+
: { base: EMPTY_COVER, next: null, alpha: 0 };
|
|
880
|
+
if (primary && plan.base !== primary.target) {
|
|
881
|
+
primary.store.want(plan.base.missing);
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
const frameOf = (tiles: RenderTile[][], rung: Rung): RenderFrame => ({
|
|
885
|
+
width: info.width,
|
|
886
|
+
height: info.height,
|
|
887
|
+
zoom: camera.zoom,
|
|
888
|
+
scale,
|
|
889
|
+
style: prepared,
|
|
890
|
+
background: style.background,
|
|
891
|
+
sources: tiles,
|
|
892
|
+
detail: rung.detail,
|
|
893
|
+
edges: rung.edges ? undefined : false,
|
|
894
|
+
});
|
|
895
|
+
// The frame at a rung: the plan's levels with that rung's detail left
|
|
896
|
+
// out — and, when no fade is running, a coarser level for the base.
|
|
897
|
+
const build = (
|
|
898
|
+
r: number,
|
|
899
|
+
): { frame: RenderFrame; fade: FadeFrame | null } => {
|
|
900
|
+
const q = QUALITY_LADDER[r];
|
|
901
|
+
const base: RenderTile[][] = [];
|
|
902
|
+
const arriving: RenderTile[][] = [];
|
|
903
|
+
covers.forEach((c, i) => {
|
|
904
|
+
let own = i === 0 ? plan.base : c.target;
|
|
905
|
+
if (i === 0 && q.coarser > 0 && !plan.next) {
|
|
906
|
+
const coarser = Math.max(c.pyramid.minZoom, own.level - q.coarser);
|
|
907
|
+
if (coarser < own.level) own = coverAt(coarser);
|
|
908
|
+
}
|
|
909
|
+
base.push(own.tiles);
|
|
910
|
+
arriving.push(i === 0 && plan.next ? plan.next.tiles : c.target.tiles);
|
|
911
|
+
});
|
|
912
|
+
return {
|
|
913
|
+
frame: frameOf(base, q),
|
|
914
|
+
fade: plan.next
|
|
915
|
+
? { frame: frameOf(arriving, q), alpha: plan.alpha }
|
|
916
|
+
: null,
|
|
917
|
+
};
|
|
918
|
+
};
|
|
919
|
+
let rung = 0;
|
|
920
|
+
let predictedMs = 0;
|
|
921
|
+
// An overview of the view: its ancestors every other level up to the
|
|
922
|
+
// pyramid's root, asked for after the view's own tiles and looked up
|
|
923
|
+
// every frame, so they stay loaded. A view is a speck at those levels,
|
|
924
|
+
// so this is a tile or two a level. It is what a fast zoom out lands
|
|
925
|
+
// on: a trackpad's momentum can cross fifteen levels in a quarter of a
|
|
926
|
+
// second, and every level it passes that has no tile draws the style's
|
|
927
|
+
// background alone — a frame of one flat colour, labels gone with the
|
|
928
|
+
// tiles, where a coarser tile would have drawn the place, blurred. With
|
|
929
|
+
// one level of it in reach two levels up, a hole is always filled from
|
|
930
|
+
// above (`renderCover`'s ancestors reach eight). The level just above
|
|
931
|
+
// the view comes first: it is also what the ladder's last rungs draw.
|
|
932
|
+
if (primary) {
|
|
933
|
+
const top = primary.pyramid.minZoom;
|
|
934
|
+
for (
|
|
935
|
+
let level = primary.target.level - 1;
|
|
936
|
+
level >= top;
|
|
937
|
+
level = level - 2 < top && level > top ? top : level - 2
|
|
938
|
+
) {
|
|
939
|
+
primary.store.want(coverAt(level).missing);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
if (budget !== null && moving && primary) {
|
|
943
|
+
const predict = (r: number): number => {
|
|
944
|
+
const built = build(r);
|
|
945
|
+
const work =
|
|
946
|
+
renderer.estimate(built.frame) +
|
|
947
|
+
(built.fade ? renderer.estimate(built.fade.frame) : 0);
|
|
948
|
+
return this._cost.predict(work);
|
|
949
|
+
};
|
|
950
|
+
rung = chooseQuality(this._rung, predict, budget);
|
|
951
|
+
predictedMs = predict(rung);
|
|
952
|
+
}
|
|
953
|
+
this._rung = rung;
|
|
954
|
+
const { frame, fade } = build(rung);
|
|
955
|
+
|
|
956
|
+
// A layer crossing a gate — the style's `minZoom`, or this rung's
|
|
957
|
+
// `detail` — is ramped rather than cut, and both of a level fade's
|
|
958
|
+
// scenes draw it at the same ramp. Decided after the rung, because the
|
|
959
|
+
// rung is one of the gates: a moving frame that drops the style's
|
|
960
|
+
// newest layers to make its budget would otherwise take them out in
|
|
961
|
+
// one frame and hand them back in one more when the camera settled,
|
|
962
|
+
// which is a flicker on exactly the layers a zoom is looking at.
|
|
963
|
+
const layerAlpha = this._gates.step(prepared, {
|
|
964
|
+
at,
|
|
965
|
+
fadeMs,
|
|
966
|
+
zoom: camera.zoom,
|
|
967
|
+
detail: QUALITY_LADDER[rung].detail,
|
|
968
|
+
});
|
|
969
|
+
if (layerAlpha) {
|
|
970
|
+
frame.layerAlpha = layerAlpha;
|
|
971
|
+
if (fade) fade.frame.layerAlpha = layerAlpha;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// The text: the labels, and the attribution, set in the same atlas.
|
|
975
|
+
let labels: LabelBatch | null = null;
|
|
976
|
+
let attribution: AttributionDraw | null = null;
|
|
977
|
+
let labelMs = 0;
|
|
978
|
+
if (!this._noText) {
|
|
979
|
+
const started = now();
|
|
980
|
+
try {
|
|
981
|
+
const atlas = this._atlasFor(info.node, style, scale);
|
|
982
|
+
if (atlas) {
|
|
983
|
+
// Live labels measure at the settled budget while moving too:
|
|
984
|
+
// what they admit mid-zoom has to be measured mid-zoom.
|
|
985
|
+
const live = map.labelsWhileMoving === true;
|
|
986
|
+
const textDeadline =
|
|
987
|
+
now() +
|
|
988
|
+
(moving && !live ? TEXT_BUDGET_MOVING_MS : TEXT_BUDGET_SETTLED_MS);
|
|
989
|
+
atlas.beginFrame(textDeadline);
|
|
990
|
+
// The attribution before the names: it is one string, measured
|
|
991
|
+
// once, and a frame whose budget went on names would leave it
|
|
992
|
+
// for a later one — for as long as names keep arriving.
|
|
993
|
+
attribution = this._attributionFor(atlas, frame);
|
|
994
|
+
labels = this._labelsFor(atlas, frame, fade, at, moving);
|
|
995
|
+
if (destination && ahead) {
|
|
996
|
+
this._scoutFor(atlas, frame, destination, ahead, at);
|
|
997
|
+
} else if (this._scoutedFor !== '') {
|
|
998
|
+
this._scout.reset();
|
|
999
|
+
this._scoutedFor = '';
|
|
1000
|
+
}
|
|
1001
|
+
// The fields of strings already read back, on a budget of their
|
|
1002
|
+
// own: the text budget moving is 1 ms, five fields, and a jump
|
|
1003
|
+
// somewhere new reads back sixty names at once — they came in
|
|
1004
|
+
// over two hundred milliseconds that way, against none for the
|
|
1005
|
+
// coverage rasters fields replaced. This frame's draw uploads
|
|
1006
|
+
// what it makes.
|
|
1007
|
+
atlas.makeFields(Math.max(textDeadline, now() + FIELD_BUDGET_MS));
|
|
1008
|
+
this._pumpSoon();
|
|
1009
|
+
}
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
// Labels are the map's to lose, not the frame's: a failure placing
|
|
1012
|
+
// them turns them off, once and said, and the map draws on.
|
|
1013
|
+
this._noText = true;
|
|
1014
|
+
this._atlas?.dispose();
|
|
1015
|
+
this._atlas = null;
|
|
1016
|
+
if (globals.process?.env?.NODE_ENV !== 'production') {
|
|
1017
|
+
globals.console?.warn(
|
|
1018
|
+
`@react-x11/components: <Map> stopped drawing labels: ${String(error)}`,
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
labelMs = now() - started;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
// The overlays and the markers, laid out on the same world as the
|
|
1026
|
+
// tiles under them.
|
|
1027
|
+
const palette = overlayPalette(this.theme);
|
|
1028
|
+
const overlays = this._overlaysFor(
|
|
1029
|
+
renderer,
|
|
1030
|
+
camera,
|
|
1031
|
+
pane,
|
|
1032
|
+
scale,
|
|
1033
|
+
palette,
|
|
1034
|
+
info,
|
|
1035
|
+
);
|
|
1036
|
+
const markers = map.markers?.length
|
|
1037
|
+
? this._markers.batch(
|
|
1038
|
+
map.markers,
|
|
1039
|
+
transformFor(camera, pane, DEFAULT_TILE_SIZE),
|
|
1040
|
+
pane,
|
|
1041
|
+
scale,
|
|
1042
|
+
palette,
|
|
1043
|
+
)
|
|
1044
|
+
: null;
|
|
1045
|
+
|
|
1046
|
+
// Adaptive quality prices moving frames with a rate it learns from
|
|
1047
|
+
// frames it times — and it times only settled ones. Timing a frame means
|
|
1048
|
+
// `glFinish`, which holds this thread until the GPU is done and, on the
|
|
1049
|
+
// Cocoa backend, until the surface is: a moving frame cannot afford
|
|
1050
|
+
// that, and what it reads is not the frame's cost either. Timed on every
|
|
1051
|
+
// moving frame, the cheapest rungs read 16–52 ms of what is a few
|
|
1052
|
+
// milliseconds of drawing, and the model learned from that put nearly
|
|
1053
|
+
// every frame on the coarsest rung. A settled frame is drawn once and
|
|
1054
|
+
// animates nothing, so it can afford two finishes: one to drain what
|
|
1055
|
+
// came before it, and one to time it alone. It still errs slow — a lone
|
|
1056
|
+
// frame after a pause meets a GPU that has clocked down — which errs on
|
|
1057
|
+
// the side of frame rate.
|
|
1058
|
+
const calibrate = budget !== null && !moving;
|
|
1059
|
+
const finish = (): void => (gl as { finish?(): void }).finish?.();
|
|
1060
|
+
if (calibrate) finish();
|
|
1061
|
+
const drawStart = now();
|
|
1062
|
+
const stats = renderer.render(frame, {
|
|
1063
|
+
fade,
|
|
1064
|
+
labels,
|
|
1065
|
+
overlays,
|
|
1066
|
+
markers,
|
|
1067
|
+
attribution,
|
|
1068
|
+
});
|
|
1069
|
+
let measuredMs = 0;
|
|
1070
|
+
if (calibrate) {
|
|
1071
|
+
finish();
|
|
1072
|
+
measuredMs = now() - drawStart;
|
|
1073
|
+
// An upload is not drawing, and would teach the model it was.
|
|
1074
|
+
if (stats.uploads === 0) this._cost.learn(stats.instances, measuredMs);
|
|
1075
|
+
}
|
|
1076
|
+
map.onAfterDraw?.(gl, { width: info.width, height: info.height });
|
|
1077
|
+
for (const { store } of sources) {
|
|
1078
|
+
store.evict((data) => renderer.release(data));
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
const end = now();
|
|
1082
|
+
const interval = this._lastEnd === null ? 0 : end - this._lastEnd;
|
|
1083
|
+
this._lastEnd = end;
|
|
1084
|
+
let tiles = 0;
|
|
1085
|
+
let ready = 0;
|
|
1086
|
+
let fromAncestor = 0;
|
|
1087
|
+
let fromDescendant = 0;
|
|
1088
|
+
let errors = 0;
|
|
1089
|
+
let building = 0;
|
|
1090
|
+
let loading = 0;
|
|
1091
|
+
for (const c of covers) {
|
|
1092
|
+
tiles += c.target.inView.length;
|
|
1093
|
+
ready += c.target.own;
|
|
1094
|
+
fromAncestor += c.target.ancestors;
|
|
1095
|
+
fromDescendant += c.target.descendants;
|
|
1096
|
+
errors += c.store.failedAmong(c.target.inView);
|
|
1097
|
+
building += c.store.building;
|
|
1098
|
+
loading += c.store.loading;
|
|
1099
|
+
}
|
|
1100
|
+
const level = primary?.target.level ?? 0;
|
|
1101
|
+
this._stats = {
|
|
1102
|
+
renderer: 'gl',
|
|
1103
|
+
// Nothing rasterized, no surfaces held, and every frame whole.
|
|
1104
|
+
rasterMs: 0,
|
|
1105
|
+
surfaceBytes: 0,
|
|
1106
|
+
damage: null,
|
|
1107
|
+
draw: NO_RASTER,
|
|
1108
|
+
drawMs: stats.cpuMs + labelMs,
|
|
1109
|
+
tiles,
|
|
1110
|
+
ready,
|
|
1111
|
+
fromAncestor,
|
|
1112
|
+
fromDescendant,
|
|
1113
|
+
pending: building,
|
|
1114
|
+
restyling: false,
|
|
1115
|
+
labels: stats.labels,
|
|
1116
|
+
errors,
|
|
1117
|
+
gl: {
|
|
1118
|
+
interval,
|
|
1119
|
+
cpuMs: stats.cpuMs,
|
|
1120
|
+
buildMs,
|
|
1121
|
+
building,
|
|
1122
|
+
loading,
|
|
1123
|
+
gpuBytes: renderer.gpuBytes,
|
|
1124
|
+
instances: stats.instances,
|
|
1125
|
+
drawCalls: stats.drawCalls,
|
|
1126
|
+
uploads: stats.uploads,
|
|
1127
|
+
uploadBytes: stats.uploadBytes,
|
|
1128
|
+
offscreen: stats.offscreen,
|
|
1129
|
+
level,
|
|
1130
|
+
shownLevel: this._fader.shown ?? level,
|
|
1131
|
+
fade: stats.fade,
|
|
1132
|
+
quality: rung,
|
|
1133
|
+
predictedMs,
|
|
1134
|
+
measuredMs,
|
|
1135
|
+
moving,
|
|
1136
|
+
labelCandidates: labels ? this._placer.stats.candidates : 0,
|
|
1137
|
+
labelsPlaced: labels ? this._placer.stats.placed : 0,
|
|
1138
|
+
labelMs,
|
|
1139
|
+
textPending: this._atlas?.pending ?? false,
|
|
1140
|
+
},
|
|
1141
|
+
};
|
|
1142
|
+
// A label still fading, strings this frame had no budget to measure,
|
|
1143
|
+
// fields still to be made, or fields still to go into the texture want
|
|
1144
|
+
// the next frame; a batch in flight asks for one itself when it lands.
|
|
1145
|
+
const labelling =
|
|
1146
|
+
labels !== null &&
|
|
1147
|
+
(this._placer.animating ||
|
|
1148
|
+
this._atlas?.starved === true ||
|
|
1149
|
+
this._atlas?.fielding === true ||
|
|
1150
|
+
this._atlas?.uploading === true);
|
|
1151
|
+
// An attribution this frame could not draw may have gone into the
|
|
1152
|
+
// texture during it — the upload happens as the labels draw, after the
|
|
1153
|
+
// frame chose what to draw — and then it is the next frame's.
|
|
1154
|
+
const wanted = this._attributionWanted;
|
|
1155
|
+
const attributing =
|
|
1156
|
+
wanted !== null && this._atlas?.entry(wanted.text) != null;
|
|
1157
|
+
if (
|
|
1158
|
+
more ||
|
|
1159
|
+
this._fader.fading ||
|
|
1160
|
+
this._gates.fading ||
|
|
1161
|
+
labelling ||
|
|
1162
|
+
attributing
|
|
1163
|
+
)
|
|
1164
|
+
this.request();
|
|
1165
|
+
if (!this._announced) {
|
|
1166
|
+
this._announced = true;
|
|
1167
|
+
this._pane?.notifyA11ySceneChanged?.();
|
|
1168
|
+
}
|
|
1169
|
+
map.onFrame?.(this._stats);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
/**
|
|
1173
|
+
* The overlays' bucket, placed for this frame: the one there is while the
|
|
1174
|
+
* overlays are the same array, the theme's accent the same colour and
|
|
1175
|
+
* the view inside the bucket's region, and a new one around the view
|
|
1176
|
+
* otherwise.
|
|
1177
|
+
*/
|
|
1178
|
+
private _overlaysFor(
|
|
1179
|
+
renderer: GlMapRenderer,
|
|
1180
|
+
camera: MapCamera,
|
|
1181
|
+
pane: { width: number; height: number },
|
|
1182
|
+
scale: number,
|
|
1183
|
+
palette: OverlayPalette,
|
|
1184
|
+
info: DrawInfo,
|
|
1185
|
+
): OverlayDraw | null {
|
|
1186
|
+
const overlays = this.props.map.overlays;
|
|
1187
|
+
let current = this._overlay;
|
|
1188
|
+
if (!overlays || overlays.length === 0) {
|
|
1189
|
+
if (current) renderer.release(current.bucket.data);
|
|
1190
|
+
this._overlay = null;
|
|
1191
|
+
return null;
|
|
1192
|
+
}
|
|
1193
|
+
if (
|
|
1194
|
+
!current ||
|
|
1195
|
+
current.overlays !== overlays ||
|
|
1196
|
+
current.accent !== palette.accent ||
|
|
1197
|
+
!regionHolds(current.bucket.region, camera, pane, scale)
|
|
1198
|
+
) {
|
|
1199
|
+
if (current) renderer.release(current.bucket.data);
|
|
1200
|
+
current = this._overlay = {
|
|
1201
|
+
bucket: buildOverlayBucket(
|
|
1202
|
+
overlays,
|
|
1203
|
+
overlayRegion(camera, pane, scale),
|
|
1204
|
+
palette,
|
|
1205
|
+
),
|
|
1206
|
+
overlays,
|
|
1207
|
+
accent: palette.accent,
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
const { bucket } = current;
|
|
1211
|
+
return {
|
|
1212
|
+
data: bucket.data,
|
|
1213
|
+
passes: bucket.passes,
|
|
1214
|
+
...regionPlacement(bucket.region, camera, info.width, info.height, scale),
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
/**
|
|
1219
|
+
* The atlas every string on the map is set in — the labels, and the
|
|
1220
|
+
* attribution. Made on the first frame, from the app the surface belongs
|
|
1221
|
+
* to, and again when the face or the scale changes; null, and not asked
|
|
1222
|
+
* for again, when the app has no fonts.
|
|
1223
|
+
*/
|
|
1224
|
+
private _atlasFor(
|
|
1225
|
+
node: object,
|
|
1226
|
+
style: MapStyle,
|
|
1227
|
+
scale: number,
|
|
1228
|
+
): LabelAtlas | null {
|
|
1229
|
+
const family =
|
|
1230
|
+
style.fontFamily ??
|
|
1231
|
+
this._pane?.resolvedTextStyle?.().family ??
|
|
1232
|
+
'sans-serif';
|
|
1233
|
+
const key = `${family}|${scale}`;
|
|
1234
|
+
if (this._atlas && this._atlasKey === key) return this._atlas;
|
|
1235
|
+
this._atlas?.dispose();
|
|
1236
|
+
this._atlas = null;
|
|
1237
|
+
const engine = SurfaceTextEngine.forApp(
|
|
1238
|
+
(node as { app?: unknown }).app,
|
|
1239
|
+
family,
|
|
1240
|
+
);
|
|
1241
|
+
if (!engine) {
|
|
1242
|
+
this._noText = true;
|
|
1243
|
+
return null;
|
|
1244
|
+
}
|
|
1245
|
+
// Fields set at a base size that follows the display's scale, so a
|
|
1246
|
+
// label on a 2x panel is sampled from a field twice as fine.
|
|
1247
|
+
this._atlas = new LabelAtlas(engine, { base: fieldBase(scale) });
|
|
1248
|
+
this._atlas.onChange = this.request;
|
|
1249
|
+
this._atlasKey = key;
|
|
1250
|
+
this._placer.reset();
|
|
1251
|
+
return this._atlas;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
/**
|
|
1255
|
+
* The frame's labels: placed over the tiles of the level being shown —
|
|
1256
|
+
* the arriving one, once a fade is past halfway — and batched for the
|
|
1257
|
+
* renderer.
|
|
1258
|
+
*/
|
|
1259
|
+
private _labelsFor(
|
|
1260
|
+
atlas: LabelAtlas,
|
|
1261
|
+
frame: RenderFrame,
|
|
1262
|
+
fade: FadeFrame | null,
|
|
1263
|
+
at: number,
|
|
1264
|
+
moving: boolean,
|
|
1265
|
+
): LabelBatch {
|
|
1266
|
+
const live = this.props.map.labelsWhileMoving === true;
|
|
1267
|
+
const from = fade && fade.alpha >= 0.5 ? fade.frame : frame;
|
|
1268
|
+
const centre = project(this._controller.camera().center);
|
|
1269
|
+
const placement: PlacementFrame = {
|
|
1270
|
+
width: frame.width,
|
|
1271
|
+
height: frame.height,
|
|
1272
|
+
scale: frame.scale,
|
|
1273
|
+
zoom: frame.zoom,
|
|
1274
|
+
style: frame.style,
|
|
1275
|
+
tiles: from.sources.flat(),
|
|
1276
|
+
centerX: centre.x,
|
|
1277
|
+
centerY: centre.y,
|
|
1278
|
+
// The projection's world, which the cover lays every source out on.
|
|
1279
|
+
world: worldSize(frame.zoom, DEFAULT_TILE_SIZE) * frame.scale,
|
|
1280
|
+
now: at,
|
|
1281
|
+
// Live: every name the view offers, at every step of a zoom.
|
|
1282
|
+
admit: live || at - this._zoomedAt >= ZOOM_QUIET_MS,
|
|
1283
|
+
};
|
|
1284
|
+
if (live || !moving || at - this._placedAt >= PLACE_INTERVAL_MS) {
|
|
1285
|
+
this._placer.place(placement, atlas);
|
|
1286
|
+
this._placedAt = at;
|
|
1287
|
+
}
|
|
1288
|
+
// Live: what placement asked for is set now, before the batch, so a
|
|
1289
|
+
// name placed in this frame is drawn in it — its field goes into the
|
|
1290
|
+
// texture ahead of the cap, before this frame's labels draw.
|
|
1291
|
+
if (live) atlas.setNow(now() + LIVE_TEXT_BUDGET_MS);
|
|
1292
|
+
return this._placer.batch(placement, atlas);
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
/**
|
|
1296
|
+
* Render ahead: place the labels of the view a glide stops at, so that
|
|
1297
|
+
* every string it will show is measured and asked of the atlas while the
|
|
1298
|
+
* glide's own frames are drawn. Nothing it places is drawn — the frame's
|
|
1299
|
+
* placer places the destination again when it gets there, and finds every
|
|
1300
|
+
* box measured and every field in the texture, so the labels fade in
|
|
1301
|
+
* when the view settles rather than a readback and an upload after.
|
|
1302
|
+
*
|
|
1303
|
+
* A fresh placement each time (the scout keeps no labels between runs;
|
|
1304
|
+
* holding a place is for what is on screen), and not every frame: when
|
|
1305
|
+
* the destination moves — another notch — or more of its tiles land, or
|
|
1306
|
+
* the last placement ran out of the frame's text budget before it had
|
|
1307
|
+
* measured everything. It measures out of that budget, after the frame's
|
|
1308
|
+
* own labels.
|
|
1309
|
+
*/
|
|
1310
|
+
private _scoutFor(
|
|
1311
|
+
atlas: LabelAtlas,
|
|
1312
|
+
frame: RenderFrame,
|
|
1313
|
+
destination: MapCamera,
|
|
1314
|
+
covers: readonly CoverResult[],
|
|
1315
|
+
at: number,
|
|
1316
|
+
): void {
|
|
1317
|
+
// The destination is recomputed from each frame's camera, so its last
|
|
1318
|
+
// digits wander; to a millionth of a degree it is the same place. And
|
|
1319
|
+
// it is a different question once more of its tiles are in hand.
|
|
1320
|
+
let held = 0;
|
|
1321
|
+
for (const c of covers) held += c.own;
|
|
1322
|
+
const key = `${destination.zoom}|${destination.center.lon.toFixed(6)}|${destination.center.lat.toFixed(6)}|${held}`;
|
|
1323
|
+
if (key === this._scoutedFor && this._scoutDone) return;
|
|
1324
|
+
this._scoutedFor = key;
|
|
1325
|
+
const centre = project(destination.center);
|
|
1326
|
+
this._scout.reset();
|
|
1327
|
+
this._scout.place(
|
|
1328
|
+
{
|
|
1329
|
+
width: frame.width,
|
|
1330
|
+
height: frame.height,
|
|
1331
|
+
scale: frame.scale,
|
|
1332
|
+
zoom: destination.zoom,
|
|
1333
|
+
style: frame.style,
|
|
1334
|
+
tiles: covers.flatMap((c) => c.tiles),
|
|
1335
|
+
centerX: centre.x,
|
|
1336
|
+
centerY: centre.y,
|
|
1337
|
+
world: worldSize(destination.zoom, DEFAULT_TILE_SIZE) * frame.scale,
|
|
1338
|
+
now: at,
|
|
1339
|
+
admit: true,
|
|
1340
|
+
},
|
|
1341
|
+
atlas,
|
|
1342
|
+
);
|
|
1343
|
+
this._scoutDone = !atlas.starved;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
/**
|
|
1347
|
+
* The attribution, as the retained renderer draws it: the same box on the
|
|
1348
|
+
* same pixels (`attributionLayout`), the text in the labels' face, from
|
|
1349
|
+
* the labels' atlas. Null while there is none to draw — `attribution=""`,
|
|
1350
|
+
* or no source that names one — and until its field is in the texture,
|
|
1351
|
+
* so the box and its text arrive together.
|
|
1352
|
+
*/
|
|
1353
|
+
private _attributionFor(
|
|
1354
|
+
atlas: LabelAtlas,
|
|
1355
|
+
frame: RenderFrame,
|
|
1356
|
+
): AttributionDraw | null {
|
|
1357
|
+
this._attributionWanted = null;
|
|
1358
|
+
const map = this.props.map;
|
|
1359
|
+
const text = attributionOf(map.attribution, map.sources ?? []);
|
|
1360
|
+
if (!text) return null;
|
|
1361
|
+
const s = frame.scale;
|
|
1362
|
+
const size = ATTRIBUTION_SIZE * s;
|
|
1363
|
+
const box = atlas.measure(text, size);
|
|
1364
|
+
const entry = box ? atlas.entry(text) : null;
|
|
1365
|
+
if (!box || !entry) {
|
|
1366
|
+
this._attributionWanted = { text, size };
|
|
1367
|
+
return null;
|
|
1368
|
+
}
|
|
1369
|
+
const at = attributionLayout(
|
|
1370
|
+
{ width: box.width / s, height: box.height / s },
|
|
1371
|
+
{ x: 0, y: 0, width: frame.width / s, height: frame.height / s },
|
|
1372
|
+
s,
|
|
1373
|
+
);
|
|
1374
|
+
const palette = overlayPalette(this.theme);
|
|
1375
|
+
// The string sits `pad` texels inside its field, the field is drawn
|
|
1376
|
+
// `k` device pixels a texel, and a level label's quad is centred on its
|
|
1377
|
+
// anchor: this anchor puts the string's corner on the layout's.
|
|
1378
|
+
const k = size / atlas.base;
|
|
1379
|
+
const d = this._attributionText;
|
|
1380
|
+
d[0] = at.x + (entry.width / 2 - atlas.pad) * k;
|
|
1381
|
+
d[1] = at.y + (entry.height / 2 - atlas.pad) * k;
|
|
1382
|
+
d[2] = 1;
|
|
1383
|
+
d[3] = 0;
|
|
1384
|
+
d[4] = entry.x;
|
|
1385
|
+
d[5] = entry.y;
|
|
1386
|
+
d[6] = entry.width;
|
|
1387
|
+
d[7] = entry.height;
|
|
1388
|
+
d.set(premultiplied(parseColor(palette.text) ?? BLACK), 8);
|
|
1389
|
+
d.fill(0, 12, 17); // no halo: the box is what it reads against
|
|
1390
|
+
d[17] = 1;
|
|
1391
|
+
d[18] = k;
|
|
1392
|
+
return {
|
|
1393
|
+
box: at.box,
|
|
1394
|
+
boxColor: premultiplied(
|
|
1395
|
+
parseColor(palette.background) ?? WHITE,
|
|
1396
|
+
ATTRIBUTION_OPACITY,
|
|
1397
|
+
),
|
|
1398
|
+
text: { atlas, instances: d, count: 1 },
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
/**
|
|
1403
|
+
* Rasterize what placement asked for — after this frame rather than in
|
|
1404
|
+
* it. Setting a batch of strings is milliseconds of this thread, and the
|
|
1405
|
+
* gap between two frames is where that costs no frame anything.
|
|
1406
|
+
*/
|
|
1407
|
+
private _pumpSoon(): void {
|
|
1408
|
+
if (this._pumpTimer !== null || !this._atlas?.pending) return;
|
|
1409
|
+
this._pumpTimer = globals.setTimeout(() => {
|
|
1410
|
+
this._pumpTimer = null;
|
|
1411
|
+
this._atlas?.pump();
|
|
1412
|
+
}, 0);
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
/**
|
|
1416
|
+
* Let go of everything GL: the stores, the atlas, the renderer — all of
|
|
1417
|
+
* it made again, lazily, by the next frame if there is one. React's
|
|
1418
|
+
* strict-mode double effect is the case where there is.
|
|
1419
|
+
*/
|
|
1420
|
+
dispose(): void {
|
|
1421
|
+
this._controller.detach(this);
|
|
1422
|
+
if (this._pumpTimer !== null) globals.clearTimeout(this._pumpTimer);
|
|
1423
|
+
this._pumpTimer = null;
|
|
1424
|
+
this._atlas?.dispose();
|
|
1425
|
+
this._atlas = null;
|
|
1426
|
+
this._atlasKey = '';
|
|
1427
|
+
for (const store of this._stores.values()) {
|
|
1428
|
+
store.dispose((data) => this._renderer?.release(data));
|
|
1429
|
+
}
|
|
1430
|
+
this._stores.clear();
|
|
1431
|
+
this._inactive = [];
|
|
1432
|
+
// Its buffers go with the renderer's.
|
|
1433
|
+
this._overlay = null;
|
|
1434
|
+
try {
|
|
1435
|
+
this._renderer?.dispose();
|
|
1436
|
+
} catch {
|
|
1437
|
+
// The surface may be gone already, and its context with it.
|
|
1438
|
+
}
|
|
1439
|
+
this._renderer = null;
|
|
1440
|
+
this._gl = null;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
/**
|
|
1445
|
+
* The GL half of `<Map>`: a pane with a `<glarea>` in it, drawn every frame
|
|
1446
|
+
* from vector tiles. Mounted by `<Map>` when it chooses GL; not exported
|
|
1447
|
+
* from the package on its own.
|
|
1448
|
+
*/
|
|
1449
|
+
export function GlMapPane(props: GlMapPaneProps): ReactElement {
|
|
1450
|
+
const theme = useTheme();
|
|
1451
|
+
const driver = useMemo(
|
|
1452
|
+
() => new GlMapDriver(props.controller, props),
|
|
1453
|
+
[props.controller],
|
|
1454
|
+
);
|
|
1455
|
+
driver.props = props;
|
|
1456
|
+
driver.theme = theme;
|
|
1457
|
+
useEffect(() => {
|
|
1458
|
+
driver.attach();
|
|
1459
|
+
return () => driver.dispose();
|
|
1460
|
+
}, [driver]);
|
|
1461
|
+
useEffect(() => driver.committed());
|
|
1462
|
+
const background = driver.background();
|
|
1463
|
+
return h(
|
|
1464
|
+
GL_PANE,
|
|
1465
|
+
{
|
|
1466
|
+
ref: driver.paneRef,
|
|
1467
|
+
mapController: props.controller,
|
|
1468
|
+
style: driver.paneStyle(props.style, background),
|
|
1469
|
+
role: props.role,
|
|
1470
|
+
'aria-label': props['aria-label'],
|
|
1471
|
+
'data-testname': props['data-testname'],
|
|
1472
|
+
...driver.handlers,
|
|
1473
|
+
},
|
|
1474
|
+
h(
|
|
1475
|
+
'glarea',
|
|
1476
|
+
{
|
|
1477
|
+
ref: driver.areaRef,
|
|
1478
|
+
style: FILL,
|
|
1479
|
+
clearColor: background,
|
|
1480
|
+
frameLoop: 'demand',
|
|
1481
|
+
onDraw: driver.draw,
|
|
1482
|
+
onError: driver.surfaceError,
|
|
1483
|
+
},
|
|
1484
|
+
props.children,
|
|
1485
|
+
),
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
const FILL = { flexGrow: 1 };
|