@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,1453 @@
|
|
|
1
|
+
// The scene: everything a frame draws, as data.
|
|
2
|
+
//
|
|
3
|
+
// `./node.ts` used to hold both halves of drawing — deciding *what* a frame
|
|
4
|
+
// shows and issuing the 2D calls that show it — in one pass of interleaved
|
|
5
|
+
// private methods. This module is the first half, lifted out and made pure,
|
|
6
|
+
// and `./paint.ts` is the second.
|
|
7
|
+
//
|
|
8
|
+
// Three things wanted the split, in the order they mattered:
|
|
9
|
+
//
|
|
10
|
+
// - **A second renderer.** A `<glarea>` draws from instance buffers, not
|
|
11
|
+
// from a `FlowPainter`, but it draws the *same scene*: the same cards in
|
|
12
|
+
// the same order, the same routed edges, the same culling. A description
|
|
13
|
+
// that names no drawing API is what both can read
|
|
14
|
+
// (`docs/prd-flow-gl.md`).
|
|
15
|
+
// - **Measuring.** A frame's cost could not be told apart from a window's:
|
|
16
|
+
// building the scene needs no server, no surface and no display, so
|
|
17
|
+
// `scripts/bench/flow.ts` can price it on its own.
|
|
18
|
+
// - **Reading it.** What the pane draws is now a value you can print.
|
|
19
|
+
//
|
|
20
|
+
// Everything here is in **screen space** — logical pixels relative to the
|
|
21
|
+
// window, the unit `FlowPainter` takes — because that is where culling has
|
|
22
|
+
// to happen and where a routed edge's sample density is decided. The
|
|
23
|
+
// exception is {@link SceneNodeSource.rect}, which is graph space: it is
|
|
24
|
+
// what the handle anchors and the minimap are computed from.
|
|
25
|
+
//
|
|
26
|
+
// Purity is the point, so the live state a gesture holds does not reach in
|
|
27
|
+
// here: the element resolves it into {@link SceneNodeSource} once per paint
|
|
28
|
+
// — a drag's position already applied, a resize's box already substituted —
|
|
29
|
+
// and this module reads that. The one callback left is text measurement,
|
|
30
|
+
// which needs the app's font cache and cannot be a constant.
|
|
31
|
+
import { tint } from 'react-x11/style';
|
|
32
|
+
import { snapToGrid } from './draw.js';
|
|
33
|
+
import { DEFAULT_MARKER_SIZE, EDGE_SLOP, EDGE_STEP_OFFSET, gripPoint, handleAnchor, inflateRect, NODE_DESC_SIZE, NODE_LABEL_SIZE, NODE_PAD_X, NODE_PAD_Y, normalizeMarker, rectsOverlap, RESIZE_GRIP, unionRects, } from './model.js';
|
|
34
|
+
import { edgePath, endAngle, pathBounds, pointAtFraction, startAngle, trimEnd, } from './paths.js';
|
|
35
|
+
/** Below these zooms the pane stops drawing detail nobody could read — the
|
|
36
|
+
* cheapest optimisation there is, and the one that keeps a zoomed-out
|
|
37
|
+
* overview interactive. Exported because the element's hit testing has to
|
|
38
|
+
* agree with what was drawn. */
|
|
39
|
+
export const LABEL_ZOOM = 0.45;
|
|
40
|
+
export const HANDLE_ZOOM = 0.5;
|
|
41
|
+
export const DESC_ZOOM = 0.6;
|
|
42
|
+
/** The grid never draws denser than this on screen, whatever the zoom. */
|
|
43
|
+
export const MIN_GRID_PX = 16;
|
|
44
|
+
/**
|
|
45
|
+
* How far outside its own box a node's ink can land: handles (capped at
|
|
46
|
+
* 6 × 1.4 plus their outline), resize grips, the selection border, a pixel
|
|
47
|
+
* of antialiasing. Damage rects grow by this before they are invalidated,
|
|
48
|
+
* and cull tests grow their target by the same amount — the two must agree,
|
|
49
|
+
* or a moved node leaves crumbs of its old handles behind.
|
|
50
|
+
*/
|
|
51
|
+
export const CULL_MARGIN = 16;
|
|
52
|
+
/**
|
|
53
|
+
* From this many edges on the pane, they are stroked a pen at a time — every
|
|
54
|
+
* run of a pen, and every arrowhead of a colour, as one path — and below
|
|
55
|
+
* it an edge at a time, in the graph's order.
|
|
56
|
+
*
|
|
57
|
+
* A path's mask is its bounding box, so a pen's scattered runs as one path
|
|
58
|
+
* cost a mask the size of the pane where each alone costs its own small
|
|
59
|
+
* one: nineteen long edges criss-crossing a pane measured 634 KB a repaint
|
|
60
|
+
* batched against 97 KB one at a time, and seven hundred measured 3.9 MB
|
|
61
|
+
* one at a time against 1.3 MB batched. And the two do not draw the same
|
|
62
|
+
* pixels where edges overlap: coverage adds inside one path and lays one
|
|
63
|
+
* over the other between two, so a fan-out's shared leg came out as much
|
|
64
|
+
* as 35 levels apart at its edges. So the choice is the pane's, never a
|
|
65
|
+
* pass's — decided from the edges on the pane and kept across the passes
|
|
66
|
+
* of every frame until a repaint of the whole pane takes the other one
|
|
67
|
+
* ({@link SceneInput.batch}).
|
|
68
|
+
*/
|
|
69
|
+
export const EDGE_BATCH = 24;
|
|
70
|
+
/** …and below this many a batched pane goes back, so that a pan across the
|
|
71
|
+
* threshold does not change the way every step. */
|
|
72
|
+
const EDGE_UNBATCH = 16;
|
|
73
|
+
/** Whether `onPane` edges are stroked a pen at a time, given whether they
|
|
74
|
+
* were (`EDGE_BATCH`). */
|
|
75
|
+
export function batchesEdges(onPane, was = false) {
|
|
76
|
+
return onPane >= (was ? EDGE_UNBATCH : EDGE_BATCH);
|
|
77
|
+
}
|
|
78
|
+
const DEFAULT_DASH = [7, 5];
|
|
79
|
+
/** Bounded, so a graph that churns edge ids cannot turn this into a leak. */
|
|
80
|
+
const ROUTE_LIMIT = 20000;
|
|
81
|
+
/**
|
|
82
|
+
* Move a polyline, in place.
|
|
83
|
+
*
|
|
84
|
+
* Addition in place drifts by float rounding — `(x + a) + b` is not always
|
|
85
|
+
* `x + (a + b)` — so a route panned many times sits a few units in the last
|
|
86
|
+
* place off one routed fresh. That is ~1e-14 px a pan, the painter puts
|
|
87
|
+
* every coordinate on the device grid before it draws, and any zoom or node
|
|
88
|
+
* move rebuilds the route from scratch and resets it. Reading through a
|
|
89
|
+
* per-frame copy instead would be exact and would bring back the allocation
|
|
90
|
+
* this cache exists to remove.
|
|
91
|
+
*/
|
|
92
|
+
function shift(points, dx, dy) {
|
|
93
|
+
for (const p of points) {
|
|
94
|
+
p.x += dx;
|
|
95
|
+
p.y += dy;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* What a renderer keeps between frames. Owned by whoever draws — the
|
|
100
|
+
* element, or a GL renderer — and handed to {@link buildScene}, which reads
|
|
101
|
+
* and fills it. Passing none is correct and simply recomputes everything.
|
|
102
|
+
*/
|
|
103
|
+
/**
|
|
104
|
+
* One edge routed from scratch, arrowheads and all — the miss path of
|
|
105
|
+
* {@link SceneCache}, and the whole path for a caller that keeps none.
|
|
106
|
+
*/
|
|
107
|
+
function buildRoute(v, edge, from, to, markerEnd, markerStart, into) {
|
|
108
|
+
const routed = edgeRoute(v, edge, from, to);
|
|
109
|
+
if (!routed)
|
|
110
|
+
return null;
|
|
111
|
+
const markerKey = markerKeyOf(markerEnd, markerStart);
|
|
112
|
+
const loop = edge.source === edge.target;
|
|
113
|
+
const all = routed.points;
|
|
114
|
+
const { endHead, startHead, endTrim } = routeHeads(v, all, markerEnd, markerStart);
|
|
115
|
+
// Copied vertex by vertex, not just sliced. `trimEnd` returns a shallow
|
|
116
|
+
// copy — the same point *objects* as the route, less the last — and a
|
|
117
|
+
// pan moves both arrays in place, so a shared vertex moved twice: the
|
|
118
|
+
// stroke slid away from its own head by the pan's distance. Caught by
|
|
119
|
+
// test/flow-scene.test.ts, which the gesture tests could not have seen.
|
|
120
|
+
const trimmed = endHead
|
|
121
|
+
? trimEnd(all, endTrim).map((p) => ({ x: p.x, y: p.y }))
|
|
122
|
+
: null;
|
|
123
|
+
// Written over the entry this edge already had, where there is one. A
|
|
124
|
+
// zoom misses on every edge of every frame, and allocating a fresh record
|
|
125
|
+
// for each — plus the `Map.set` to file it — cost more than the routing
|
|
126
|
+
// the cache saves: a drag and a zoom both came out ~10% slower than with
|
|
127
|
+
// no cache at all, before this.
|
|
128
|
+
const entry = into ?? {};
|
|
129
|
+
Object.assign(entry, {
|
|
130
|
+
zoom: v.zoom,
|
|
131
|
+
originX: v.x,
|
|
132
|
+
originY: v.y,
|
|
133
|
+
sx: from.rect.x,
|
|
134
|
+
sy: from.rect.y,
|
|
135
|
+
sw: from.rect.width,
|
|
136
|
+
sh: from.rect.height,
|
|
137
|
+
tx: to.rect.x,
|
|
138
|
+
ty: to.rect.y,
|
|
139
|
+
tw: to.rect.width,
|
|
140
|
+
th: to.rect.height,
|
|
141
|
+
sourceSpecs: from.specs,
|
|
142
|
+
targetSpecs: to.specs,
|
|
143
|
+
sourceHandle: edge.sourceHandle ?? null,
|
|
144
|
+
targetHandle: edge.targetHandle ?? null,
|
|
145
|
+
source: edge.source,
|
|
146
|
+
target: edge.target,
|
|
147
|
+
type: edge.type,
|
|
148
|
+
loop,
|
|
149
|
+
markerKey,
|
|
150
|
+
points: all,
|
|
151
|
+
trimmed,
|
|
152
|
+
endHead,
|
|
153
|
+
startHead,
|
|
154
|
+
from: routed.from,
|
|
155
|
+
to: routed.to,
|
|
156
|
+
bounds: withHeads(pathBounds(all), endHead, startHead),
|
|
157
|
+
middle: null,
|
|
158
|
+
});
|
|
159
|
+
return entry;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Where a route's label sits. Walking the route to its middle is the length
|
|
163
|
+
* of every segment twice, and a 2D pan's passes asked it of every labelled
|
|
164
|
+
* edge they reached, every frame; kept, it is two additions a pan. A copy,
|
|
165
|
+
* never one of the route's own points, which the pan moves as well.
|
|
166
|
+
*/
|
|
167
|
+
function middleOf(route) {
|
|
168
|
+
if (route.middle === null) {
|
|
169
|
+
const p = pointAtFraction(route.points, 0.5);
|
|
170
|
+
route.middle = { x: p.x, y: p.y };
|
|
171
|
+
}
|
|
172
|
+
return route.middle;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* An edge's arrowheads on a route, and how much of the route's end the
|
|
176
|
+
* stroke leaves to the one at the end: the one place a head is placed, so
|
|
177
|
+
* what is drawn and what a move claims ({@link edgeInkBox}) cannot disagree.
|
|
178
|
+
*/
|
|
179
|
+
function routeHeads(v, all, markerEnd, markerStart) {
|
|
180
|
+
// The tip stops short of the handle rather than at it: the handle dot is
|
|
181
|
+
// drawn *over* the edges, with the nodes, so an arrow aimed at the
|
|
182
|
+
// handle's centre is an arrow mostly hidden under a white circle.
|
|
183
|
+
const inset = v.zoom >= HANDLE_ZOOM ? handleRadius(v.zoom) + 1 : 1;
|
|
184
|
+
let endHead = null;
|
|
185
|
+
let startHead = null;
|
|
186
|
+
let endTrim = 0;
|
|
187
|
+
if (markerEnd) {
|
|
188
|
+
const size = (markerEnd.size ?? DEFAULT_MARKER_SIZE) * v.zoom;
|
|
189
|
+
const last = all[all.length - 1];
|
|
190
|
+
const angle = endAngle(all);
|
|
191
|
+
// and the stroke stops behind the head, so a filled triangle is a
|
|
192
|
+
// triangle rather than a triangle with a line through it
|
|
193
|
+
endTrim = size * 0.8 + inset;
|
|
194
|
+
endHead = markerPoints({
|
|
195
|
+
x: last.x - Math.cos(angle) * inset,
|
|
196
|
+
y: last.y - Math.sin(angle) * inset,
|
|
197
|
+
}, angle, markerEnd.type, size);
|
|
198
|
+
}
|
|
199
|
+
if (markerStart) {
|
|
200
|
+
const size = (markerStart.size ?? DEFAULT_MARKER_SIZE) * v.zoom;
|
|
201
|
+
const angle = startAngle(all);
|
|
202
|
+
startHead = markerPoints({
|
|
203
|
+
x: all[0].x - Math.cos(angle) * inset,
|
|
204
|
+
y: all[0].y - Math.sin(angle) * inset,
|
|
205
|
+
}, angle, markerStart.type, size);
|
|
206
|
+
}
|
|
207
|
+
return { endHead, startHead, endTrim };
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* A route's box with its arrowheads in it. A head lies along the route's
|
|
211
|
+
* last stretch, but as wide as it is long: where the route runs along its
|
|
212
|
+
* box's side, a wing of it is outside the box — by nine pixels at `zoom` 2.
|
|
213
|
+
*/
|
|
214
|
+
function withHeads(box, endHead, startHead) {
|
|
215
|
+
if (endHead)
|
|
216
|
+
box = unionRects(box, pathBounds(endHead));
|
|
217
|
+
if (startHead)
|
|
218
|
+
box = unionRects(box, pathBounds(startHead));
|
|
219
|
+
return box;
|
|
220
|
+
}
|
|
221
|
+
/** The head an edge ends in. Left out, it is an arrow: a directed graph
|
|
222
|
+
* whose edges do not say which way they point is a set of lines. `null`
|
|
223
|
+
* opts out. */
|
|
224
|
+
function endMarkerOf(edge) {
|
|
225
|
+
return normalizeMarker(edge.markerEnd === undefined ? 'arrowclosed' : edge.markerEnd);
|
|
226
|
+
}
|
|
227
|
+
/** An edge's pen, on screen. */
|
|
228
|
+
function edgeLineWidth(edge, zoom) {
|
|
229
|
+
return Math.max(1, (edge.style?.strokeWidth ?? (edge.selected ? 2 : 1.5)) * zoom);
|
|
230
|
+
}
|
|
231
|
+
/** How far ink reaches from a segment, as a cull asks it: the pen and its
|
|
232
|
+
* join — a miter at the sharpest corner a route takes, or at an
|
|
233
|
+
* arrowhead's tip, stays well inside three widths. Loose on purpose: a
|
|
234
|
+
* cull that keeps an edge too many draws it clipped away. */
|
|
235
|
+
function penReach(lineWidth) {
|
|
236
|
+
return lineWidth * 3 + 2;
|
|
237
|
+
}
|
|
238
|
+
/** How far it reaches in fact, as a claim asks it, where too much is pixels
|
|
239
|
+
* repainted for nothing: an edge and an open head are stroked round-joined
|
|
240
|
+
* and round-capped, so half the pen, and a filled head is not stroked at
|
|
241
|
+
* all — plus the edge pixels and the device grid every point is put on. */
|
|
242
|
+
function inkReach(lineWidth) {
|
|
243
|
+
return lineWidth / 2 + 2;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Everything drawing `edge` along `points` can ink, in the same pixels: the
|
|
247
|
+
* route and its arrowheads, the pen's reach past both, and the label's
|
|
248
|
+
* plate — what a change that moves the edge has to claim. From the heads
|
|
249
|
+
* and the pen {@link buildScene} draws with: a claim that took the route's
|
|
250
|
+
* points alone reached a head's wing only through the margin it was grown
|
|
251
|
+
* by, and a large head, a deep zoom or a wide pen outgrows any margin.
|
|
252
|
+
*
|
|
253
|
+
* `margin` is what the caller grows the box by afterwards, and the pen and
|
|
254
|
+
* the heads count only past it: an ordinary pen and a default arrow stay
|
|
255
|
+
* inside a claim's margin at any zoom a pane allows by default, and adding
|
|
256
|
+
* them again made every move on a small graph claim a fifth more.
|
|
257
|
+
*/
|
|
258
|
+
export function edgeInkBox(v, edge, points, margin = 0) {
|
|
259
|
+
const { endHead, startHead } = routeHeads(v, points, endMarkerOf(edge), normalizeMarker(edge.markerStart));
|
|
260
|
+
// how far ink reaches past the geometry, less the margin: below zero for
|
|
261
|
+
// any pen short of the margin's width
|
|
262
|
+
const past = inkReach(edgeLineWidth(edge, v.zoom)) - margin;
|
|
263
|
+
let box = withLabelPlate(pathBounds(points), edge, v.zoom);
|
|
264
|
+
if (past > 0)
|
|
265
|
+
box = inflateRect(box, past);
|
|
266
|
+
if (endHead)
|
|
267
|
+
box = reachingPast(box, pathBounds(endHead), past);
|
|
268
|
+
if (startHead)
|
|
269
|
+
box = reachingPast(box, pathBounds(startHead), past);
|
|
270
|
+
return box;
|
|
271
|
+
}
|
|
272
|
+
/** `box`, grown on each side until it reaches `by` past `part` — or, where
|
|
273
|
+
* `by` is negative, until it comes within that much of it. */
|
|
274
|
+
function reachingPast(box, part, by) {
|
|
275
|
+
const x = Math.min(box.x, part.x - by);
|
|
276
|
+
const y = Math.min(box.y, part.y - by);
|
|
277
|
+
return {
|
|
278
|
+
x,
|
|
279
|
+
y,
|
|
280
|
+
width: Math.max(box.x + box.width, part.x + part.width + by) - x,
|
|
281
|
+
height: Math.max(box.y + box.height, part.y + part.height + by) - y,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
/** Which arrowheads a route was built for — their shapes and sizes, never
|
|
285
|
+
* their colours, which move nothing. */
|
|
286
|
+
function markerKeyOf(markerEnd, markerStart) {
|
|
287
|
+
return `${markerEnd?.type ?? ''}:${markerEnd?.size ?? ''}|${markerStart?.type ?? ''}:${markerStart?.size ?? ''}`;
|
|
288
|
+
}
|
|
289
|
+
/** A route with nothing kept between frames. */
|
|
290
|
+
export function uncachedRoute(v, edge, from, to, markerEnd, markerStart) {
|
|
291
|
+
return buildRoute(v, edge, from, to, markerEnd, markerStart);
|
|
292
|
+
}
|
|
293
|
+
function sameCull(key, from, all, v, kept, scale) {
|
|
294
|
+
return (key != null &&
|
|
295
|
+
key.from === from &&
|
|
296
|
+
key.all === all &&
|
|
297
|
+
key.vx === v.x &&
|
|
298
|
+
key.vy === v.y &&
|
|
299
|
+
key.zoom === v.zoom &&
|
|
300
|
+
key.kx === kept.x &&
|
|
301
|
+
key.ky === kept.y &&
|
|
302
|
+
key.kw === kept.width &&
|
|
303
|
+
key.kh === kept.height &&
|
|
304
|
+
key.scale === scale);
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* A box an edge's route stays in, grown to hold its label's plate too. The
|
|
308
|
+
* plate is centred on the route's middle and as wide as its text, which can
|
|
309
|
+
* be far wider than the route: a long name on a short edge put a plate on
|
|
310
|
+
* the pane whose edge was culled for being off it, and a node that moved
|
|
311
|
+
* such an edge claimed its route and left the plate's ends behind. Every
|
|
312
|
+
* cull and every claim asks this of every labelled edge, so the plate is
|
|
313
|
+
* bounded rather than measured — an em and a quarter a code unit, wider
|
|
314
|
+
* than any label is set — and grows nothing where no labels are drawn.
|
|
315
|
+
*/
|
|
316
|
+
export function withLabelPlate(box, edge, zoom) {
|
|
317
|
+
if (!edge.label || zoom < LABEL_ZOOM)
|
|
318
|
+
return box;
|
|
319
|
+
const size = Math.max(8, 11 * zoom);
|
|
320
|
+
const dx = (edge.label.length * size * 1.25) / 2 + 5 * zoom + 1;
|
|
321
|
+
const dy = size + 2 * zoom + 1;
|
|
322
|
+
return {
|
|
323
|
+
x: box.x - dx,
|
|
324
|
+
y: box.y - dy,
|
|
325
|
+
width: box.width + dx * 2,
|
|
326
|
+
height: box.height + dy * 2,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
function cullEdges(v, kept, edges, byId, scale) {
|
|
330
|
+
const list = [];
|
|
331
|
+
for (const edge of edges) {
|
|
332
|
+
if (edge.hidden)
|
|
333
|
+
continue;
|
|
334
|
+
const from = byId.get(edge.source);
|
|
335
|
+
const to = byId.get(edge.target);
|
|
336
|
+
if (!from || !to || from.node.hidden || to.node.hidden)
|
|
337
|
+
continue;
|
|
338
|
+
const coarse = withLabelPlate(edgeCoarseBox(v, from, to, scale), edge, v.zoom);
|
|
339
|
+
if (rectsOverlap(coarse, kept))
|
|
340
|
+
list.push({ edge, from, to, coarse });
|
|
341
|
+
}
|
|
342
|
+
return list;
|
|
343
|
+
}
|
|
344
|
+
function cullNodes(v, kept, nodes, scale) {
|
|
345
|
+
const list = [];
|
|
346
|
+
for (const source of nodes) {
|
|
347
|
+
if (source.node.hidden)
|
|
348
|
+
continue;
|
|
349
|
+
const rect = screenRect(v, source.rect, scale);
|
|
350
|
+
// By what the node inks, not by its card: a handle sits on the card's
|
|
351
|
+
// edge and reaches past it, and a node whose card had just left the
|
|
352
|
+
// pane lost the half ring still inside it. The margin every claim and
|
|
353
|
+
// the damage test below grow a node by.
|
|
354
|
+
if (rectsOverlap(inflateRect(rect, CULL_MARGIN), kept)) {
|
|
355
|
+
list.push({ source, rect });
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return list;
|
|
359
|
+
}
|
|
360
|
+
function cullKey(from, all, v, kept, scale) {
|
|
361
|
+
return {
|
|
362
|
+
from,
|
|
363
|
+
all,
|
|
364
|
+
vx: v.x,
|
|
365
|
+
vy: v.y,
|
|
366
|
+
zoom: v.zoom,
|
|
367
|
+
kx: kept.x,
|
|
368
|
+
ky: kept.y,
|
|
369
|
+
kw: kept.width,
|
|
370
|
+
kh: kept.height,
|
|
371
|
+
scale,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
export class SceneCache {
|
|
375
|
+
routes = new Map();
|
|
376
|
+
/** The node index, memoized on the array it was built from: at two
|
|
377
|
+
* thousand nodes, rebuilding this Map every frame was a tenth of the
|
|
378
|
+
* build. */
|
|
379
|
+
index = null;
|
|
380
|
+
indexFor = null;
|
|
381
|
+
/** The edges and nodes on screen, for the passes of one frame. */
|
|
382
|
+
edgeCull = null;
|
|
383
|
+
nodeCull = null;
|
|
384
|
+
/** Everything is derived, so forgetting it is always safe. */
|
|
385
|
+
clear() {
|
|
386
|
+
this.routes.clear();
|
|
387
|
+
this.index = null;
|
|
388
|
+
this.indexFor = null;
|
|
389
|
+
this.edgeCull = null;
|
|
390
|
+
this.nodeCull = null;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* The edges whose coarse box reaches `kept`, with their ends — the list a
|
|
394
|
+
* pass walks, culled once for every pass of a frame. A 2D frame paints its
|
|
395
|
+
* damage as several passes, a pan's strips and corners and furniture, and
|
|
396
|
+
* each one walked every edge in the graph to throw nearly all of them
|
|
397
|
+
* away: at four thousand edges that was most of what a pan frame cost
|
|
398
|
+
* once the drawing had become a copy. Keyed on the arrays themselves, so
|
|
399
|
+
* the element has to hand the same ones to every pass of a frame.
|
|
400
|
+
*/
|
|
401
|
+
edgesOnScreen(v, kept, edges, all, scale) {
|
|
402
|
+
const hit = this.edgeCull;
|
|
403
|
+
if (hit && sameCull(hit.key, edges, all, v, kept, scale))
|
|
404
|
+
return hit.list;
|
|
405
|
+
const list = cullEdges(v, kept, edges, this.byId(all), scale);
|
|
406
|
+
this.edgeCull = { key: cullKey(edges, all, v, kept, scale), list };
|
|
407
|
+
return list;
|
|
408
|
+
}
|
|
409
|
+
/** The nodes whose screen box reaches `kept`, culled once for every pass
|
|
410
|
+
* of a frame the way `edgesOnScreen` culls the edges. */
|
|
411
|
+
nodesOnScreen(v, kept, nodes, scale) {
|
|
412
|
+
const hit = this.nodeCull;
|
|
413
|
+
if (hit && sameCull(hit.key, nodes, null, v, kept, scale))
|
|
414
|
+
return hit.list;
|
|
415
|
+
const list = cullNodes(v, kept, nodes, scale);
|
|
416
|
+
this.nodeCull = { key: cullKey(nodes, null, v, kept, scale), list };
|
|
417
|
+
return list;
|
|
418
|
+
}
|
|
419
|
+
byId(all) {
|
|
420
|
+
if (this.indexFor === all && this.index)
|
|
421
|
+
return this.index;
|
|
422
|
+
const map = new Map();
|
|
423
|
+
for (const source of all)
|
|
424
|
+
map.set(source.node.id, source);
|
|
425
|
+
this.index = map;
|
|
426
|
+
this.indexFor = all;
|
|
427
|
+
return map;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* The route for `edge` at `v`, from the cache where it still holds. With
|
|
431
|
+
* `within`, a cached route whose box, carried to `v`, misses that rect is
|
|
432
|
+
* left where it is and null comes back: the pass has nothing of it to
|
|
433
|
+
* draw, and the pan it was not moved by is added to the next one that
|
|
434
|
+
* reaches it.
|
|
435
|
+
*/
|
|
436
|
+
route(v, edge, from, to, markerEnd, markerStart, within = null) {
|
|
437
|
+
const loop = edge.source === edge.target;
|
|
438
|
+
const markerKey = markerKeyOf(markerEnd, markerStart);
|
|
439
|
+
const hit = this.routes.get(edge.id);
|
|
440
|
+
if (hit &&
|
|
441
|
+
hit.zoom === v.zoom &&
|
|
442
|
+
hit.sx === from.rect.x &&
|
|
443
|
+
hit.sy === from.rect.y &&
|
|
444
|
+
hit.sw === from.rect.width &&
|
|
445
|
+
hit.sh === from.rect.height &&
|
|
446
|
+
hit.tx === to.rect.x &&
|
|
447
|
+
hit.ty === to.rect.y &&
|
|
448
|
+
hit.tw === to.rect.width &&
|
|
449
|
+
hit.th === to.rect.height &&
|
|
450
|
+
hit.sourceSpecs === from.specs &&
|
|
451
|
+
hit.targetSpecs === to.specs &&
|
|
452
|
+
hit.sourceHandle === (edge.sourceHandle ?? null) &&
|
|
453
|
+
hit.targetHandle === (edge.targetHandle ?? null) &&
|
|
454
|
+
hit.source === edge.source &&
|
|
455
|
+
hit.target === edge.target &&
|
|
456
|
+
hit.type === edge.type &&
|
|
457
|
+
hit.loop === loop &&
|
|
458
|
+
hit.markerKey === markerKey) {
|
|
459
|
+
const dx = v.x - hit.originX;
|
|
460
|
+
const dy = v.y - hit.originY;
|
|
461
|
+
if (within) {
|
|
462
|
+
const b = hit.bounds;
|
|
463
|
+
if (!(b.x + dx < within.x + within.width &&
|
|
464
|
+
b.x + dx + b.width > within.x &&
|
|
465
|
+
b.y + dy < within.y + within.height &&
|
|
466
|
+
b.y + dy + b.height > within.y)) {
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (dx !== 0 || dy !== 0) {
|
|
471
|
+
shift(hit.points, dx, dy);
|
|
472
|
+
// `trimmed` is null when it would be `points`, so nothing is moved
|
|
473
|
+
// twice — the bug that aliasing invites here.
|
|
474
|
+
if (hit.trimmed)
|
|
475
|
+
shift(hit.trimmed, dx, dy);
|
|
476
|
+
if (hit.endHead)
|
|
477
|
+
shift(hit.endHead, dx, dy);
|
|
478
|
+
if (hit.startHead)
|
|
479
|
+
shift(hit.startHead, dx, dy);
|
|
480
|
+
hit.bounds.x += dx;
|
|
481
|
+
hit.bounds.y += dy;
|
|
482
|
+
if (hit.middle) {
|
|
483
|
+
hit.middle.x += dx;
|
|
484
|
+
hit.middle.y += dy;
|
|
485
|
+
}
|
|
486
|
+
hit.originX = v.x;
|
|
487
|
+
hit.originY = v.y;
|
|
488
|
+
}
|
|
489
|
+
return hit;
|
|
490
|
+
}
|
|
491
|
+
const entry = buildRoute(v, edge, from, to, markerEnd, markerStart, hit);
|
|
492
|
+
if (!entry) {
|
|
493
|
+
this.routes.delete(edge.id);
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
if (!hit) {
|
|
497
|
+
if (this.routes.size >= ROUTE_LIMIT)
|
|
498
|
+
this.routes.clear();
|
|
499
|
+
this.routes.set(edge.id, entry);
|
|
500
|
+
}
|
|
501
|
+
return entry;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
/** Where a box of the graph sits in the minimap, as it is drawn there — the
|
|
505
|
+
* one formula the pass that draws the minimap and a claim on it share. */
|
|
506
|
+
export function miniMapPlace(map, rect) {
|
|
507
|
+
const ox = map.panel.x + (map.panel.width - map.bounds.width * map.scale) / 2;
|
|
508
|
+
const oy = map.panel.y + (map.panel.height - map.bounds.height * map.scale) / 2;
|
|
509
|
+
return {
|
|
510
|
+
x: ox + (rect.x - map.bounds.x) * map.scale,
|
|
511
|
+
y: oy + (rect.y - map.bounds.y) * map.scale,
|
|
512
|
+
width: Math.max(1, rect.width * map.scale),
|
|
513
|
+
height: Math.max(1, rect.height * map.scale),
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
/** How far a minimap node's fill reaches past its box: its antialiased
|
|
517
|
+
* edge, which a pass has to take in to draw the node's part of a pixel it
|
|
518
|
+
* shares. */
|
|
519
|
+
export const MINIMAP_BLEED = 2;
|
|
520
|
+
// --- geometry -----------------------------------------------------------------
|
|
521
|
+
/**
|
|
522
|
+
* The viewport as the *window* sees it: the pane's own origin folded into
|
|
523
|
+
* the translation.
|
|
524
|
+
*
|
|
525
|
+
* A {@link Viewport} is the pane's — `x`/`y` place the graph relative to the
|
|
526
|
+
* pane's top-left, which is what `setViewport` and `onViewportChange` speak
|
|
527
|
+
* — while everything this module draws and everything the element hit-tests
|
|
528
|
+
* is in window pixels. Every graph-to-window conversion goes through this
|
|
529
|
+
* first. The scene extraction once skipped it, so a pane that did not sit at
|
|
530
|
+
* the window's origin drew and hit-tested shifted by its own offset — and
|
|
531
|
+
* agreed with itself while doing it, which is why only an off-origin test
|
|
532
|
+
* could see it (`test/flow.test.ts`, "off the window origin").
|
|
533
|
+
*/
|
|
534
|
+
export function screenViewport(v, pane) {
|
|
535
|
+
return { x: pane.x + v.x, y: pane.y + v.y, zoom: v.zoom };
|
|
536
|
+
}
|
|
537
|
+
/** Graph point to window, through a {@link screenViewport}. */
|
|
538
|
+
export function toScreen(v, p) {
|
|
539
|
+
return { x: p.x * v.zoom + v.x, y: p.y * v.zoom + v.y };
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* A node's box on screen, put on whole device pixels.
|
|
543
|
+
*
|
|
544
|
+
* Rounded as *edges* rather than as a size, so two nodes that share a column
|
|
545
|
+
* still share it after rounding.
|
|
546
|
+
*/
|
|
547
|
+
export function screenRect(v, rect, scale) {
|
|
548
|
+
// Written out rather than through `toScreen` and a rounding closure: a
|
|
549
|
+
// drag or a pan asks this of every node in the graph each step, to find
|
|
550
|
+
// the few on screen, and the two allocations a call were most of it.
|
|
551
|
+
const px = rect.x * v.zoom + v.x;
|
|
552
|
+
const py = rect.y * v.zoom + v.y;
|
|
553
|
+
const x = snapToGrid(px * scale) / scale;
|
|
554
|
+
const y = snapToGrid(py * scale) / scale;
|
|
555
|
+
return {
|
|
556
|
+
x,
|
|
557
|
+
y,
|
|
558
|
+
width: snapToGrid((px + rect.width * v.zoom) * scale) / scale - x,
|
|
559
|
+
height: snapToGrid((py + rect.height * v.zoom) * scale) / scale - y,
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
/** A handle's drawn radius: it grows with the zoom, but only so far, so a
|
|
563
|
+
* zoomed-out graph still has something to aim at. */
|
|
564
|
+
export function handleRadius(zoom) {
|
|
565
|
+
return Math.min(6, Math.max(2.5, 4.5 * zoom));
|
|
566
|
+
}
|
|
567
|
+
export function sameHandle(a, b) {
|
|
568
|
+
return (a != null &&
|
|
569
|
+
a.nodeId === b.nodeId &&
|
|
570
|
+
(a.id ?? null) === (b.id ?? null) &&
|
|
571
|
+
a.type === b.type &&
|
|
572
|
+
a.position === b.position);
|
|
573
|
+
}
|
|
574
|
+
function intersect(a, b) {
|
|
575
|
+
const x = Math.max(a.x, b.x);
|
|
576
|
+
const y = Math.max(a.y, b.y);
|
|
577
|
+
const right = Math.min(a.x + a.width, b.x + b.width);
|
|
578
|
+
const bottom = Math.min(a.y + a.height, b.y + b.height);
|
|
579
|
+
if (right <= x || bottom <= y)
|
|
580
|
+
return null;
|
|
581
|
+
return { x, y, width: right - x, height: bottom - y };
|
|
582
|
+
}
|
|
583
|
+
function centre(rect) {
|
|
584
|
+
return { x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 };
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* A node's handle anchors, in graph space.
|
|
588
|
+
*
|
|
589
|
+
* Takes the three fields it reads rather than a whole
|
|
590
|
+
* {@link SceneNodeSource}, and that is load-bearing rather than tidy: a
|
|
591
|
+
* source carries `grips`, and which grips a node shows depends on where its
|
|
592
|
+
* handles are — asking for the whole source here is a cycle.
|
|
593
|
+
*/
|
|
594
|
+
export function anchorsOf(source) {
|
|
595
|
+
return source.specs.map((spec) => handleAnchor(source.node.id, source.rect, spec));
|
|
596
|
+
}
|
|
597
|
+
/** An arrowhead's points. */
|
|
598
|
+
function markerPoints(at, angle, type, size) {
|
|
599
|
+
const spread = 0.42; // radians off the shaft — a ~24° half-angle
|
|
600
|
+
const back = {
|
|
601
|
+
x: at.x - Math.cos(angle) * size,
|
|
602
|
+
y: at.y - Math.sin(angle) * size,
|
|
603
|
+
};
|
|
604
|
+
const left = {
|
|
605
|
+
x: at.x - Math.cos(angle - spread) * size,
|
|
606
|
+
y: at.y - Math.sin(angle - spread) * size,
|
|
607
|
+
};
|
|
608
|
+
const right = {
|
|
609
|
+
x: at.x - Math.cos(angle + spread) * size,
|
|
610
|
+
y: at.y - Math.sin(angle + spread) * size,
|
|
611
|
+
};
|
|
612
|
+
return type === 'arrowclosed' ? [at, left, back, right] : [left, at, right];
|
|
613
|
+
}
|
|
614
|
+
// --- the builder --------------------------------------------------------------
|
|
615
|
+
/**
|
|
616
|
+
* The scene for one pass.
|
|
617
|
+
*
|
|
618
|
+
* Pure but for one thing, and it is worth stating plainly: with a
|
|
619
|
+
* {@link SceneCache} the routes it returns are **arrays the cache owns and
|
|
620
|
+
* the next frame will move in place**. A scene is built and drawn inside one
|
|
621
|
+
* paint and nothing keeps it, so the rule is simply that — do not hold a
|
|
622
|
+
* scene past the frame that made it, and do not mutate what it hands back.
|
|
623
|
+
* Without a cache every array is fresh and the rule is vacuous.
|
|
624
|
+
*/
|
|
625
|
+
export function buildScene(input) {
|
|
626
|
+
const { viewport: v, pane, clip, palette } = input;
|
|
627
|
+
const region = clip ? intersect(pane, clip) : pane;
|
|
628
|
+
const scene = {
|
|
629
|
+
viewport: v,
|
|
630
|
+
pane,
|
|
631
|
+
palette,
|
|
632
|
+
region,
|
|
633
|
+
background: region
|
|
634
|
+
? {
|
|
635
|
+
rect: region,
|
|
636
|
+
radius: 0,
|
|
637
|
+
fill: input.paneBackground ?? palette.background,
|
|
638
|
+
}
|
|
639
|
+
: null,
|
|
640
|
+
grid: null,
|
|
641
|
+
edges: [],
|
|
642
|
+
nodes: [],
|
|
643
|
+
connection: null,
|
|
644
|
+
selection: null,
|
|
645
|
+
miniMap: null,
|
|
646
|
+
controls: null,
|
|
647
|
+
animated: false,
|
|
648
|
+
animBox: null,
|
|
649
|
+
batch: input.batch ?? input.batchWas ?? false,
|
|
650
|
+
edgesOnPane: 0,
|
|
651
|
+
};
|
|
652
|
+
if (!region)
|
|
653
|
+
return scene;
|
|
654
|
+
scene.grid = buildGrid(input, region);
|
|
655
|
+
scene.edges = buildEdges(input, scene);
|
|
656
|
+
scene.nodes = buildNodes(input);
|
|
657
|
+
scene.connection = buildConnection(input);
|
|
658
|
+
if (input.selection) {
|
|
659
|
+
scene.selection = {
|
|
660
|
+
rect: input.selection,
|
|
661
|
+
radius: 2,
|
|
662
|
+
fill: palette.selection,
|
|
663
|
+
stroke: palette.accent,
|
|
664
|
+
lineWidth: 1,
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
// Both panels are pinned to a corner, so a pass whose damage misses them
|
|
668
|
+
// skips the walk that builds them — which for the minimap is every node in
|
|
669
|
+
// the graph, not just the ones in view.
|
|
670
|
+
if (input.miniMap && (!clip || rectsOverlap(input.miniMap.panel, clip))) {
|
|
671
|
+
scene.miniMap = buildMiniMap(input, input.miniMap, clip ?? input.miniMap.reach);
|
|
672
|
+
}
|
|
673
|
+
if (input.controls.length > 0 &&
|
|
674
|
+
(!clip || input.controls.some((b) => rectsOverlap(b.rect, clip)))) {
|
|
675
|
+
scene.controls = buildControls(input);
|
|
676
|
+
}
|
|
677
|
+
return scene;
|
|
678
|
+
}
|
|
679
|
+
function buildGrid(input, region) {
|
|
680
|
+
const { background: options, viewport: v, pane, palette } = input;
|
|
681
|
+
if (options.variant === 'none')
|
|
682
|
+
return null;
|
|
683
|
+
let step = options.gap * v.zoom;
|
|
684
|
+
if (!(step > 0))
|
|
685
|
+
return null;
|
|
686
|
+
// Doubling rather than clamping keeps the grid *aligned* to the graph
|
|
687
|
+
// while zooming out: every visible line is still a real one.
|
|
688
|
+
while (step < MIN_GRID_PX)
|
|
689
|
+
step *= 2;
|
|
690
|
+
return {
|
|
691
|
+
variant: options.variant,
|
|
692
|
+
step,
|
|
693
|
+
origin: { x: pane.x + v.x, y: pane.y + v.y },
|
|
694
|
+
size: options.size,
|
|
695
|
+
color: options.color ?? palette.grid,
|
|
696
|
+
region,
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
function indexOf(all) {
|
|
700
|
+
const map = new Map();
|
|
701
|
+
for (const source of all)
|
|
702
|
+
map.set(source.node.id, source);
|
|
703
|
+
return map;
|
|
704
|
+
}
|
|
705
|
+
function buildEdges(input, scene) {
|
|
706
|
+
const { pane, clip, palette, hover, scale } = input;
|
|
707
|
+
const v = screenViewport(input.viewport, pane);
|
|
708
|
+
const kept = input.cull ?? pane;
|
|
709
|
+
const labels = v.zoom >= LABEL_ZOOM;
|
|
710
|
+
const out = [];
|
|
711
|
+
const cache = input.cache;
|
|
712
|
+
let animBox = null;
|
|
713
|
+
// a label's plate, the same for every edge that names none of its own
|
|
714
|
+
let plateFill;
|
|
715
|
+
// Two rejects: one from the nodes alone — the coarse box, culled once for
|
|
716
|
+
// every pass of a frame where there is a cache — and one from the route
|
|
717
|
+
// the edge took.
|
|
718
|
+
const onPane = cache
|
|
719
|
+
? cache.edgesOnScreen(v, kept, input.edges, input.all, scale)
|
|
720
|
+
: cullEdges(v, kept, input.edges, indexOf(input.all), scale);
|
|
721
|
+
scene.edgesOnPane = onPane.length;
|
|
722
|
+
scene.batch = input.batch ?? batchesEdges(onPane.length, input.batchWas);
|
|
723
|
+
for (const { edge, from, to, coarse } of onPane) {
|
|
724
|
+
// Tracked before the damage skip, deliberately: whether the dash timer
|
|
725
|
+
// runs is a question about the viewport, not about what this particular
|
|
726
|
+
// pass repaints — deciding it after the skip is how a drag in one corner
|
|
727
|
+
// would stop the dash marching in the other. And so is the box the ticks
|
|
728
|
+
// repaint, below, for the same reason: a pass over one corner that
|
|
729
|
+
// reached one animated edge made that edge the only one the next tick
|
|
730
|
+
// repainted, and every other dash on screen stopped.
|
|
731
|
+
if (edge.animated)
|
|
732
|
+
scene.animated = true;
|
|
733
|
+
const reached = !clip || rectsOverlap(coarse, clip);
|
|
734
|
+
if (!reached && !edge.animated)
|
|
735
|
+
continue;
|
|
736
|
+
const markerEnd = endMarkerOf(edge);
|
|
737
|
+
const markerStart = normalizeMarker(edge.markerStart);
|
|
738
|
+
const selected = edge.selected ?? false;
|
|
739
|
+
const hovered = hover.edgeId === edge.id;
|
|
740
|
+
const stroke = edge.style?.stroke ??
|
|
741
|
+
(selected ? palette.edgeSelected : hovered ? palette.text : palette.edge);
|
|
742
|
+
const lineWidth = edgeLineWidth(edge, v.zoom);
|
|
743
|
+
const reach = penReach(lineWidth);
|
|
744
|
+
// The route, from the cache where there is one: a pan keeps the zoom, so
|
|
745
|
+
// the cached route is the same curve translated, and re-deriving it —
|
|
746
|
+
// the handles, the curve, the arrowheads — is the work this skips.
|
|
747
|
+
//
|
|
748
|
+
// And a cached route that cannot reach this pass is not moved to it at
|
|
749
|
+
// all. A long edge's coarse box reaches a strip its curve never comes
|
|
750
|
+
// near — the lattice's diagonals reach every corner of the pane — and
|
|
751
|
+
// carrying each such route to the frame, cutting it to the pass and
|
|
752
|
+
// finding it empty was most of what a 2D pan's strips cost. Only where
|
|
753
|
+
// nothing of the edge could land there: its stroke stays within `reach`
|
|
754
|
+
// of its route's box, heads and all; a label's plate is sized by its
|
|
755
|
+
// text, and a marching edge's box is tracked whatever it reaches.
|
|
756
|
+
const within = clip && reached && !edge.animated && !(labels && edge.label)
|
|
757
|
+
? inflateRect(clip, reach)
|
|
758
|
+
: null;
|
|
759
|
+
const routed = cache
|
|
760
|
+
? cache.route(v, edge, from, to, markerEnd, markerStart, within)
|
|
761
|
+
: uncachedRoute(v, edge, from, to, markerEnd, markerStart);
|
|
762
|
+
if (!routed)
|
|
763
|
+
continue;
|
|
764
|
+
const bounds = routed.bounds;
|
|
765
|
+
// the heads are in the box and the pen reaches past it: an edge whose
|
|
766
|
+
// route had just left the pane culled the wing of a head still on it
|
|
767
|
+
if (!rectsOverlap(inflateRect(withLabelPlate(bounds, edge, v.zoom), reach), kept)) {
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
if (edge.animated) {
|
|
771
|
+
// The coarse box carries the bezier's slack, and a tick that repaints
|
|
772
|
+
// slack repaints a card-sized halo of neighbours sixteen times a
|
|
773
|
+
// second — so the box the ticks invalidate comes off the *drawn*
|
|
774
|
+
// geometry.
|
|
775
|
+
const tight = inflateRect(bounds, CULL_MARGIN);
|
|
776
|
+
animBox = animBox ? unionRects(animBox, tight) : tight;
|
|
777
|
+
}
|
|
778
|
+
if (!reached)
|
|
779
|
+
continue;
|
|
780
|
+
// The heads' geometry came with the route; only their ink is this
|
|
781
|
+
// frame's, because selection and hover recolour without moving anything.
|
|
782
|
+
const markers = [];
|
|
783
|
+
if (routed.endHead && markerEnd) {
|
|
784
|
+
markers.push({
|
|
785
|
+
points: routed.endHead,
|
|
786
|
+
filled: markerEnd.type === 'arrowclosed',
|
|
787
|
+
color: markerEnd.color ?? stroke,
|
|
788
|
+
lineWidth,
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
if (routed.startHead && markerStart) {
|
|
792
|
+
markers.push({
|
|
793
|
+
points: routed.startHead,
|
|
794
|
+
filled: markerStart.type === 'arrowclosed',
|
|
795
|
+
color: markerStart.color ?? stroke,
|
|
796
|
+
lineWidth,
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
const dash = edge.style?.dash ?? (edge.animated ? DEFAULT_DASH : undefined);
|
|
800
|
+
const points = routed.trimmed ?? routed.points;
|
|
801
|
+
const item = {
|
|
802
|
+
id: edge.id,
|
|
803
|
+
points,
|
|
804
|
+
stroke,
|
|
805
|
+
lineWidth,
|
|
806
|
+
dash: dash?.map((d) => d * v.zoom),
|
|
807
|
+
dashOffset: edge.animated ? -input.dashPhase * v.zoom : 0,
|
|
808
|
+
animated: edge.animated ?? false,
|
|
809
|
+
markers,
|
|
810
|
+
layer: selected ? 2 : hovered ? 1 : 0,
|
|
811
|
+
};
|
|
812
|
+
// A pass over part of the edge draws the part it reaches: a strip down
|
|
813
|
+
// the pane's edge crossed by a hundred long edges traced every point of
|
|
814
|
+
// every one of them, to keep the handful of segments inside it
|
|
815
|
+
// (`reach`, above).
|
|
816
|
+
//
|
|
817
|
+
// A dashed edge too, with where each run starts along it: the pattern
|
|
818
|
+
// is picked up there through the dash offset (`paintEdges`). Left
|
|
819
|
+
// whole, every marching edge a 2D pan's strip crossed was stroked end
|
|
820
|
+
// to end, and on X11 that is a coverage mask the size of the edge's box
|
|
821
|
+
// uploaded for a strip two pixels wide — 1.1 MB a frame over the
|
|
822
|
+
// stress example's widgets, a pan held to 38 fps by ten dashed edges.
|
|
823
|
+
if (clip) {
|
|
824
|
+
const starts = dash ? [] : undefined;
|
|
825
|
+
const runs = runsReaching(points, clip, reach, starts);
|
|
826
|
+
if (runs) {
|
|
827
|
+
item.runs = runs;
|
|
828
|
+
if (starts)
|
|
829
|
+
item.runStarts = distancesAt(points, starts);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
if (labels && edge.label) {
|
|
833
|
+
const at = middleOf(routed);
|
|
834
|
+
const size = Math.max(8, 11 * v.zoom);
|
|
835
|
+
const metrics = input.measure(edge.label, { size });
|
|
836
|
+
const padX = 5 * v.zoom;
|
|
837
|
+
const padY = 2 * v.zoom;
|
|
838
|
+
item.chip = {
|
|
839
|
+
rect: {
|
|
840
|
+
x: snapToGrid(at.x - metrics.width / 2 - padX),
|
|
841
|
+
y: snapToGrid(at.y - metrics.height / 2 - padY),
|
|
842
|
+
width: Math.round(metrics.width + padX * 2),
|
|
843
|
+
height: Math.round(metrics.height + padY * 2),
|
|
844
|
+
},
|
|
845
|
+
radius: Math.max(2, Math.round(3 * v.zoom)),
|
|
846
|
+
fill: edge.style?.labelBackground ??
|
|
847
|
+
(plateFill ??= tint(palette.background, 0.92)),
|
|
848
|
+
};
|
|
849
|
+
item.label = {
|
|
850
|
+
kind: 'text',
|
|
851
|
+
text: edge.label,
|
|
852
|
+
x: at.x,
|
|
853
|
+
y: at.y,
|
|
854
|
+
size,
|
|
855
|
+
color: edge.style?.labelColor ?? palette.text,
|
|
856
|
+
align: 'center',
|
|
857
|
+
baseline: 'middle',
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
// …and of what is left, only what this pass reaches: an arrowhead or a
|
|
861
|
+
// label plate beside the rect is drawn by the pass that holds it, and an
|
|
862
|
+
// edge with nothing here is not drawn at all.
|
|
863
|
+
if (clip) {
|
|
864
|
+
if (item.chip && !rectsOverlap(item.chip.rect, clip)) {
|
|
865
|
+
item.chip = undefined;
|
|
866
|
+
item.label = undefined;
|
|
867
|
+
}
|
|
868
|
+
if (markers.length > 0) {
|
|
869
|
+
item.markers = markers.filter((m) => rectsOverlap(inflateRect(pointsBox(m.points), reach), clip));
|
|
870
|
+
}
|
|
871
|
+
const stroked = item.runs
|
|
872
|
+
? item.runs.length > 0
|
|
873
|
+
: rectsOverlap(inflateRect(bounds, reach), clip);
|
|
874
|
+
if (!stroked && !item.chip && item.markers.length === 0)
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
out.push(item);
|
|
878
|
+
}
|
|
879
|
+
scene.animBox = animBox;
|
|
880
|
+
return out;
|
|
881
|
+
}
|
|
882
|
+
/** The box around a handful of points. */
|
|
883
|
+
function pointsBox(points) {
|
|
884
|
+
let x0 = Infinity;
|
|
885
|
+
let y0 = Infinity;
|
|
886
|
+
let x1 = -Infinity;
|
|
887
|
+
let y1 = -Infinity;
|
|
888
|
+
for (const p of points) {
|
|
889
|
+
if (p.x < x0)
|
|
890
|
+
x0 = p.x;
|
|
891
|
+
if (p.y < y0)
|
|
892
|
+
y0 = p.y;
|
|
893
|
+
if (p.x > x1)
|
|
894
|
+
x1 = p.x;
|
|
895
|
+
if (p.y > y1)
|
|
896
|
+
y1 = p.y;
|
|
897
|
+
}
|
|
898
|
+
return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* The runs of `points` whose ink can land in `clip`: each maximal run of
|
|
902
|
+
* segments whose box comes within `reach` of it, or null when every segment
|
|
903
|
+
* does and the route is one run already. A run ends at a vertex farther
|
|
904
|
+
* than `reach` from the rect, so the cap and the join the cut replaced are
|
|
905
|
+
* drawn where the rect cannot see them.
|
|
906
|
+
*/
|
|
907
|
+
export function runsReaching(points, clip, reach,
|
|
908
|
+
/** Filled, when given, with the index in `points` each run starts at. */
|
|
909
|
+
starts) {
|
|
910
|
+
const x0 = clip.x - reach;
|
|
911
|
+
const y0 = clip.y - reach;
|
|
912
|
+
const x1 = clip.x + clip.width + reach;
|
|
913
|
+
const y1 = clip.y + clip.height + reach;
|
|
914
|
+
let runs = null;
|
|
915
|
+
let start = -1;
|
|
916
|
+
for (let i = 0; i + 1 < points.length; i++) {
|
|
917
|
+
const a = points[i];
|
|
918
|
+
const b = points[i + 1];
|
|
919
|
+
const inside = (a.x > b.x ? a.x : b.x) >= x0 &&
|
|
920
|
+
(a.x < b.x ? a.x : b.x) <= x1 &&
|
|
921
|
+
(a.y > b.y ? a.y : b.y) >= y0 &&
|
|
922
|
+
(a.y < b.y ? a.y : b.y) <= y1;
|
|
923
|
+
if (inside) {
|
|
924
|
+
if (start < 0)
|
|
925
|
+
start = i;
|
|
926
|
+
}
|
|
927
|
+
else {
|
|
928
|
+
runs ??= [];
|
|
929
|
+
if (start >= 0) {
|
|
930
|
+
runs.push(points.slice(start, i + 1));
|
|
931
|
+
starts?.push(start);
|
|
932
|
+
start = -1;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
if (!runs)
|
|
937
|
+
return null;
|
|
938
|
+
if (start >= 0) {
|
|
939
|
+
runs.push(points.slice(start));
|
|
940
|
+
starts?.push(start);
|
|
941
|
+
}
|
|
942
|
+
return runs;
|
|
943
|
+
}
|
|
944
|
+
/** How far along a polyline each of `indices` is, in its own units. */
|
|
945
|
+
function distancesAt(points, indices) {
|
|
946
|
+
const out = [];
|
|
947
|
+
let at = 0;
|
|
948
|
+
let length = 0;
|
|
949
|
+
for (const index of indices) {
|
|
950
|
+
for (; at < index; at++) {
|
|
951
|
+
length += Math.hypot(points[at + 1].x - points[at].x, points[at + 1].y - points[at].y);
|
|
952
|
+
}
|
|
953
|
+
out.push(length);
|
|
954
|
+
}
|
|
955
|
+
return out;
|
|
956
|
+
}
|
|
957
|
+
/** The box two nodes' edge cannot leave, before it is routed. */
|
|
958
|
+
export function edgeCoarseBox(v, from, to,
|
|
959
|
+
// unused: a coarse box with pixels of slack has no use for the device
|
|
960
|
+
// grid `screenRect` snaps to, and snapping two rects per edge was most of
|
|
961
|
+
// what this cost — every edge, every frame of a drag
|
|
962
|
+
_scale) {
|
|
963
|
+
const z = v.zoom;
|
|
964
|
+
const f = from.rect;
|
|
965
|
+
const t = to.rect;
|
|
966
|
+
const ax = f.x * z + v.x;
|
|
967
|
+
const ay = f.y * z + v.y;
|
|
968
|
+
const aw = f.width * z;
|
|
969
|
+
const ah = f.height * z;
|
|
970
|
+
const bx = t.x * z + v.x;
|
|
971
|
+
const by = t.y * z + v.y;
|
|
972
|
+
const bw = t.width * z;
|
|
973
|
+
const bh = t.height * z;
|
|
974
|
+
const span = Math.hypot(ax + aw / 2 - (bx + bw / 2), ay + ah / 2 - (by + bh / 2));
|
|
975
|
+
const slack = Math.max(5, EDGE_SLOP * z) +
|
|
976
|
+
Math.max(EDGE_STEP_OFFSET * 3 * z, 8 * Math.sqrt(z * span));
|
|
977
|
+
const x = Math.min(ax, bx) - slack;
|
|
978
|
+
const y = Math.min(ay, by) - slack;
|
|
979
|
+
return {
|
|
980
|
+
x,
|
|
981
|
+
y,
|
|
982
|
+
width: Math.max(ax + aw, bx + bw) + slack - x,
|
|
983
|
+
height: Math.max(ay + ah, by + bh) + slack - y,
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
/** Which handles an edge joins, and the polyline between them. */
|
|
987
|
+
export function edgeRoute(v, edge, from, to) {
|
|
988
|
+
const a = endpoint(from, edge.sourceHandle, 'source', centre(to.rect));
|
|
989
|
+
const b = endpoint(to, edge.targetHandle, 'target', centre(from.rect));
|
|
990
|
+
if (!a || !b)
|
|
991
|
+
return null;
|
|
992
|
+
const s = toScreen(v, a);
|
|
993
|
+
const t = toScreen(v, b);
|
|
994
|
+
return {
|
|
995
|
+
points: edgePath(edge.type, { x: s.x, y: s.y, position: a.position }, { x: t.x, y: t.y, position: b.position }, {
|
|
996
|
+
stepOffset: EDGE_STEP_OFFSET * v.zoom,
|
|
997
|
+
radius: 8 * v.zoom,
|
|
998
|
+
scale: v.zoom,
|
|
999
|
+
loop: edge.source === edge.target,
|
|
1000
|
+
}),
|
|
1001
|
+
from: a,
|
|
1002
|
+
to: b,
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
export function endpoint(source, handleId, type, towards) {
|
|
1006
|
+
const anchors = anchorsOf(source);
|
|
1007
|
+
if (handleId != null) {
|
|
1008
|
+
const named = anchors.find((a) => a.id === handleId);
|
|
1009
|
+
if (named)
|
|
1010
|
+
return named;
|
|
1011
|
+
}
|
|
1012
|
+
const typed = anchors.filter((a) => a.type === type);
|
|
1013
|
+
const candidates = typed.length > 0 ? typed : anchors;
|
|
1014
|
+
if (candidates.length === 0)
|
|
1015
|
+
return null;
|
|
1016
|
+
if (candidates.length === 1 || !towards)
|
|
1017
|
+
return candidates[0];
|
|
1018
|
+
let best = candidates[0];
|
|
1019
|
+
let bestDistance = Infinity;
|
|
1020
|
+
for (const anchor of candidates) {
|
|
1021
|
+
const d = Math.hypot(anchor.x - towards.x, anchor.y - towards.y);
|
|
1022
|
+
if (d < bestDistance) {
|
|
1023
|
+
bestDistance = d;
|
|
1024
|
+
best = anchor;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
return best;
|
|
1028
|
+
}
|
|
1029
|
+
/** The node items of a scene alone — what a mounted node's card is painted
|
|
1030
|
+
* from, apart from the rest of the scene (`FlowGraphNode.paintCard`). */
|
|
1031
|
+
export function buildNodeItems(input) {
|
|
1032
|
+
return buildNodes(input);
|
|
1033
|
+
}
|
|
1034
|
+
function buildNodes(input) {
|
|
1035
|
+
const { pane, clip, hover, scale } = input;
|
|
1036
|
+
const v = screenViewport(input.viewport, pane);
|
|
1037
|
+
const kept = input.cull ?? pane;
|
|
1038
|
+
const out = [];
|
|
1039
|
+
for (const { source, rect } of input.cache
|
|
1040
|
+
? input.cache.nodesOnScreen(v, kept, input.nodes, scale)
|
|
1041
|
+
: cullNodes(v, kept, input.nodes, scale)) {
|
|
1042
|
+
// inflated by the margin its handles and grips can ink outside the box —
|
|
1043
|
+
// the same margin every invalidate grew by, so the two agree
|
|
1044
|
+
if (clip && !rectsOverlap(inflateRect(rect, CULL_MARGIN), clip))
|
|
1045
|
+
continue;
|
|
1046
|
+
const selected = source.node.selected ?? false;
|
|
1047
|
+
const hovered = hover.nodeId === source.node.id;
|
|
1048
|
+
const anchors = anchorsOf(source).map((anchor) => {
|
|
1049
|
+
const s = toScreen(v, anchor);
|
|
1050
|
+
return { ...anchor, x: s.x, y: s.y };
|
|
1051
|
+
});
|
|
1052
|
+
const item = {
|
|
1053
|
+
id: source.node.id,
|
|
1054
|
+
rect,
|
|
1055
|
+
selected,
|
|
1056
|
+
hovered,
|
|
1057
|
+
ink: [],
|
|
1058
|
+
handles: [],
|
|
1059
|
+
grips: [],
|
|
1060
|
+
};
|
|
1061
|
+
if (source.type?.paint) {
|
|
1062
|
+
// Never described: a type that draws its own body is drawing whatever
|
|
1063
|
+
// it likes, in an order only it knows.
|
|
1064
|
+
item.custom = { type: source.type, node: source.node, anchors };
|
|
1065
|
+
}
|
|
1066
|
+
else {
|
|
1067
|
+
item.card = buildCard(input, source, rect, selected, hovered);
|
|
1068
|
+
item.ink = buildCardInk(input, source, rect);
|
|
1069
|
+
}
|
|
1070
|
+
if (source.connectable && (v.zoom >= HANDLE_ZOOM || hovered)) {
|
|
1071
|
+
item.handles = buildHandles(input, anchors);
|
|
1072
|
+
}
|
|
1073
|
+
item.grips = buildGrips(input, source, rect);
|
|
1074
|
+
out.push(item);
|
|
1075
|
+
}
|
|
1076
|
+
return out;
|
|
1077
|
+
}
|
|
1078
|
+
function buildCard(input, source, rect, selected, hovered) {
|
|
1079
|
+
const { viewport: v, palette } = input;
|
|
1080
|
+
const style = source.node.style;
|
|
1081
|
+
const radius = Math.round((style?.borderRadius ?? 6) * v.zoom);
|
|
1082
|
+
const border = selected
|
|
1083
|
+
? palette.accent
|
|
1084
|
+
: hovered
|
|
1085
|
+
? tint(palette.accent, 0.55)
|
|
1086
|
+
: (style?.borderColor ?? palette.nodeBorder);
|
|
1087
|
+
const shape = {
|
|
1088
|
+
rect,
|
|
1089
|
+
radius,
|
|
1090
|
+
fill: style?.background ?? palette.nodeBackground,
|
|
1091
|
+
stroke: border,
|
|
1092
|
+
lineWidth: Math.max(1, Math.round((style?.borderWidth ?? (selected ? 2 : 1)) * v.zoom)),
|
|
1093
|
+
};
|
|
1094
|
+
if (!style?.accent)
|
|
1095
|
+
return { shape };
|
|
1096
|
+
return {
|
|
1097
|
+
shape,
|
|
1098
|
+
// Inset past the rounded corners instead of clipped to them: a
|
|
1099
|
+
// non-rectangular clip forfeits ntk's rounded-box fast path for every
|
|
1100
|
+
// fill under it — measured as a pixmap create/free and a trapezoid pass
|
|
1101
|
+
// per card per repaint.
|
|
1102
|
+
accent: {
|
|
1103
|
+
rect: {
|
|
1104
|
+
x: rect.x + Math.max(1, v.zoom),
|
|
1105
|
+
y: rect.y + radius,
|
|
1106
|
+
width: Math.max(2, 3 * v.zoom),
|
|
1107
|
+
height: rect.height - radius * 2,
|
|
1108
|
+
},
|
|
1109
|
+
radius: 0,
|
|
1110
|
+
fill: style.accent,
|
|
1111
|
+
},
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
function buildCardInk(input, source, rect) {
|
|
1115
|
+
const { viewport: v, palette } = input;
|
|
1116
|
+
if (v.zoom < LABEL_ZOOM)
|
|
1117
|
+
return [];
|
|
1118
|
+
const style = source.node.style;
|
|
1119
|
+
const header = source.mounted ? source.header : 0;
|
|
1120
|
+
const data = source.node.data;
|
|
1121
|
+
const label = data?.label ?? source.node.id;
|
|
1122
|
+
const description = data?.description;
|
|
1123
|
+
const color = style?.color ?? palette.text;
|
|
1124
|
+
const padX = NODE_PAD_X * v.zoom;
|
|
1125
|
+
const centreX = rect.x + rect.width / 2;
|
|
1126
|
+
const maxWidth = Math.max(8, rect.width - padX * 2);
|
|
1127
|
+
const showDescription = Boolean(description) && v.zoom >= DESC_ZOOM;
|
|
1128
|
+
if (header > 0) {
|
|
1129
|
+
// The title bar of a node whose body is somebody else's: left-aligned,
|
|
1130
|
+
// with a hairline under it so the strip reads as the thing to grab.
|
|
1131
|
+
const band = header * v.zoom;
|
|
1132
|
+
return [
|
|
1133
|
+
{
|
|
1134
|
+
kind: 'text',
|
|
1135
|
+
text: label,
|
|
1136
|
+
x: rect.x + padX,
|
|
1137
|
+
y: rect.y + band / 2,
|
|
1138
|
+
size: NODE_LABEL_SIZE * v.zoom,
|
|
1139
|
+
weight: 'bold',
|
|
1140
|
+
color,
|
|
1141
|
+
baseline: 'middle',
|
|
1142
|
+
maxWidth,
|
|
1143
|
+
},
|
|
1144
|
+
{
|
|
1145
|
+
kind: 'rule',
|
|
1146
|
+
points: [
|
|
1147
|
+
{ x: rect.x + 1, y: rect.y + band },
|
|
1148
|
+
{ x: rect.x + rect.width - 1, y: rect.y + band },
|
|
1149
|
+
],
|
|
1150
|
+
color: palette.nodeBorder,
|
|
1151
|
+
lineWidth: 1,
|
|
1152
|
+
},
|
|
1153
|
+
];
|
|
1154
|
+
}
|
|
1155
|
+
if (!showDescription) {
|
|
1156
|
+
return [
|
|
1157
|
+
{
|
|
1158
|
+
kind: 'text',
|
|
1159
|
+
text: label,
|
|
1160
|
+
x: centreX,
|
|
1161
|
+
y: rect.y + rect.height / 2,
|
|
1162
|
+
size: NODE_LABEL_SIZE * v.zoom,
|
|
1163
|
+
weight: 'bold',
|
|
1164
|
+
color,
|
|
1165
|
+
align: 'center',
|
|
1166
|
+
baseline: 'middle',
|
|
1167
|
+
maxWidth,
|
|
1168
|
+
},
|
|
1169
|
+
];
|
|
1170
|
+
}
|
|
1171
|
+
const top = rect.y + NODE_PAD_Y * v.zoom;
|
|
1172
|
+
return [
|
|
1173
|
+
{
|
|
1174
|
+
kind: 'text',
|
|
1175
|
+
text: label,
|
|
1176
|
+
x: centreX,
|
|
1177
|
+
y: top,
|
|
1178
|
+
size: NODE_LABEL_SIZE * v.zoom,
|
|
1179
|
+
weight: 'bold',
|
|
1180
|
+
color,
|
|
1181
|
+
align: 'center',
|
|
1182
|
+
maxWidth,
|
|
1183
|
+
},
|
|
1184
|
+
{
|
|
1185
|
+
kind: 'text',
|
|
1186
|
+
text: description,
|
|
1187
|
+
x: centreX,
|
|
1188
|
+
y: top + NODE_LABEL_SIZE * 1.35 * v.zoom,
|
|
1189
|
+
size: NODE_DESC_SIZE * v.zoom,
|
|
1190
|
+
color: palette.dim,
|
|
1191
|
+
align: 'center',
|
|
1192
|
+
maxWidth,
|
|
1193
|
+
},
|
|
1194
|
+
];
|
|
1195
|
+
}
|
|
1196
|
+
function buildHandles(input, anchors) {
|
|
1197
|
+
const { viewport: v, palette, hover, connection } = input;
|
|
1198
|
+
const radius = handleRadius(v.zoom);
|
|
1199
|
+
const lineWidth = Math.max(1, 1.5 * v.zoom);
|
|
1200
|
+
return anchors.map((anchor) => {
|
|
1201
|
+
const active = sameHandle(hover.handle, anchor) ||
|
|
1202
|
+
sameHandle(connection?.to ?? null, anchor) ||
|
|
1203
|
+
sameHandle(connection?.from ?? null, anchor);
|
|
1204
|
+
const handle = {
|
|
1205
|
+
at: { x: anchor.x, y: anchor.y },
|
|
1206
|
+
radius: active ? radius * 1.4 : radius,
|
|
1207
|
+
fill: active ? palette.accent : palette.nodeBackground,
|
|
1208
|
+
stroke: active ? palette.accent : palette.handle,
|
|
1209
|
+
lineWidth,
|
|
1210
|
+
};
|
|
1211
|
+
if (anchor.label && v.zoom >= DESC_ZOOM) {
|
|
1212
|
+
const outside = anchor.position === 'left' || anchor.position === 'top';
|
|
1213
|
+
handle.label = {
|
|
1214
|
+
kind: 'text',
|
|
1215
|
+
text: anchor.label,
|
|
1216
|
+
x: anchor.x +
|
|
1217
|
+
(anchor.position === 'left'
|
|
1218
|
+
? -radius - 4 * v.zoom
|
|
1219
|
+
: radius + 4 * v.zoom),
|
|
1220
|
+
y: anchor.y,
|
|
1221
|
+
size: 10 * v.zoom,
|
|
1222
|
+
color: palette.dim,
|
|
1223
|
+
align: outside ? 'right' : 'left',
|
|
1224
|
+
baseline: 'middle',
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
return handle;
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
function buildGrips(input, source, rect) {
|
|
1231
|
+
if (source.grips.length === 0)
|
|
1232
|
+
return [];
|
|
1233
|
+
const { viewport: v, palette } = input;
|
|
1234
|
+
const size = Math.max(4, RESIZE_GRIP * 2 * v.zoom);
|
|
1235
|
+
return source.grips.map((dir) => {
|
|
1236
|
+
const at = gripPoint(rect, dir);
|
|
1237
|
+
return {
|
|
1238
|
+
rect: {
|
|
1239
|
+
x: at.x - size / 2,
|
|
1240
|
+
y: at.y - size / 2,
|
|
1241
|
+
width: size,
|
|
1242
|
+
height: size,
|
|
1243
|
+
},
|
|
1244
|
+
radius: 1,
|
|
1245
|
+
fill: palette.nodeBackground,
|
|
1246
|
+
stroke: palette.accent,
|
|
1247
|
+
lineWidth: 1.5,
|
|
1248
|
+
};
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* The line a connection gesture draws, routed. Exported because the element
|
|
1253
|
+
* claims damage for it between frames and the two must be the same curve —
|
|
1254
|
+
* a box drawn from a second derivation is a box that leaves crumbs.
|
|
1255
|
+
*/
|
|
1256
|
+
export function connectionPath(v, source) {
|
|
1257
|
+
const from = toScreen(v, source.from);
|
|
1258
|
+
const to = toScreen(v, source.to ?? source.pointer);
|
|
1259
|
+
const toPosition = source.to?.position ??
|
|
1260
|
+
(source.from.position === 'left'
|
|
1261
|
+
? 'right'
|
|
1262
|
+
: source.from.position === 'right'
|
|
1263
|
+
? 'left'
|
|
1264
|
+
: source.from.position === 'top'
|
|
1265
|
+
? 'bottom'
|
|
1266
|
+
: 'top');
|
|
1267
|
+
return edgePath('bezier', { x: from.x, y: from.y, position: source.from.position }, { x: to.x, y: to.y, position: toPosition }, {
|
|
1268
|
+
stepOffset: EDGE_STEP_OFFSET * v.zoom,
|
|
1269
|
+
radius: 8 * v.zoom,
|
|
1270
|
+
scale: v.zoom,
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
function buildConnection(input) {
|
|
1274
|
+
const { palette, connection } = input;
|
|
1275
|
+
if (!connection)
|
|
1276
|
+
return null;
|
|
1277
|
+
const v = screenViewport(input.viewport, input.pane);
|
|
1278
|
+
const to = toScreen(v, connection.to ?? connection.pointer);
|
|
1279
|
+
const invalid = connection.to != null && !connection.valid;
|
|
1280
|
+
const color = invalid ? palette.dim : palette.accent;
|
|
1281
|
+
return {
|
|
1282
|
+
points: connectionPath(v, connection),
|
|
1283
|
+
stroke: color,
|
|
1284
|
+
lineWidth: Math.max(1.5, 2 * v.zoom),
|
|
1285
|
+
dash: connection.to && connection.valid ? undefined : [5 * v.zoom, 4 * v.zoom],
|
|
1286
|
+
tip: { at: to, radius: handleRadius(v.zoom), fill: color },
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
function buildMiniMap(input, map, clip) {
|
|
1290
|
+
const { viewport: v, pane, palette } = input;
|
|
1291
|
+
const ox = map.panel.x + (map.panel.width - map.bounds.width * map.scale) / 2;
|
|
1292
|
+
const oy = map.panel.y + (map.panel.height - map.bounds.height * map.scale) / 2;
|
|
1293
|
+
const place = (p) => ({
|
|
1294
|
+
x: ox + (p.x - map.bounds.x) * map.scale,
|
|
1295
|
+
y: oy + (p.y - map.bounds.y) * map.scale,
|
|
1296
|
+
});
|
|
1297
|
+
// A pass over a corner of the minimap — a node a drag moves claims its
|
|
1298
|
+
// places there and no more — builds the nodes that reach its clip. Every
|
|
1299
|
+
// node is still walked, for its run; only the ones kept are made.
|
|
1300
|
+
const reach = clip ? inflateRect(clip, MINIMAP_BLEED) : null;
|
|
1301
|
+
const colour = map.nodeColor;
|
|
1302
|
+
const nodes = [];
|
|
1303
|
+
let run = 0;
|
|
1304
|
+
let runFill = null;
|
|
1305
|
+
let runStart = 0;
|
|
1306
|
+
let runLength = 0;
|
|
1307
|
+
const closeRun = () => {
|
|
1308
|
+
if (runLength <= 1)
|
|
1309
|
+
return;
|
|
1310
|
+
for (let k = runStart; k < nodes.length; k++)
|
|
1311
|
+
nodes[k].batched = true;
|
|
1312
|
+
};
|
|
1313
|
+
for (const source of input.all) {
|
|
1314
|
+
if (source.node.hidden)
|
|
1315
|
+
continue;
|
|
1316
|
+
const fill = typeof colour === 'function'
|
|
1317
|
+
? colour(source.node)
|
|
1318
|
+
: (colour ??
|
|
1319
|
+
(source.node.selected ? palette.accent : palette.nodeBorder));
|
|
1320
|
+
// a plain square of the run's colour joins it, as `paintPanels` groups
|
|
1321
|
+
if (runLength === 0 || fill == null || fill !== runFill) {
|
|
1322
|
+
closeRun();
|
|
1323
|
+
run++;
|
|
1324
|
+
runFill = fill ?? null;
|
|
1325
|
+
runStart = nodes.length;
|
|
1326
|
+
runLength = 0;
|
|
1327
|
+
}
|
|
1328
|
+
runLength++;
|
|
1329
|
+
// `miniMapPlace`, written out so that a node the pass does not reach
|
|
1330
|
+
// makes nothing
|
|
1331
|
+
const r = source.rect;
|
|
1332
|
+
const x = ox + (r.x - map.bounds.x) * map.scale;
|
|
1333
|
+
const y = oy + (r.y - map.bounds.y) * map.scale;
|
|
1334
|
+
const width = Math.max(1, r.width * map.scale);
|
|
1335
|
+
const height = Math.max(1, r.height * map.scale);
|
|
1336
|
+
if (reach &&
|
|
1337
|
+
(x >= reach.x + reach.width ||
|
|
1338
|
+
reach.x >= x + width ||
|
|
1339
|
+
y >= reach.y + reach.height ||
|
|
1340
|
+
reach.y >= y + height)) {
|
|
1341
|
+
continue;
|
|
1342
|
+
}
|
|
1343
|
+
nodes.push({
|
|
1344
|
+
rect: { x, y, width, height },
|
|
1345
|
+
radius: 0,
|
|
1346
|
+
fill,
|
|
1347
|
+
run,
|
|
1348
|
+
batched: false,
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
closeRun();
|
|
1352
|
+
const view = place({ x: -v.x / v.zoom, y: -v.y / v.zoom });
|
|
1353
|
+
return {
|
|
1354
|
+
panel: {
|
|
1355
|
+
rect: map.panel,
|
|
1356
|
+
radius: 4,
|
|
1357
|
+
fill: tint(palette.surface, 0.92),
|
|
1358
|
+
stroke: palette.surfaceBorder,
|
|
1359
|
+
lineWidth: 1,
|
|
1360
|
+
},
|
|
1361
|
+
nodes,
|
|
1362
|
+
// The viewport, as an outline over a wash on everything outside it —
|
|
1363
|
+
// cheaper than four mask rectangles and reads the same.
|
|
1364
|
+
view: {
|
|
1365
|
+
rect: {
|
|
1366
|
+
x: view.x,
|
|
1367
|
+
y: view.y,
|
|
1368
|
+
width: (pane.width / v.zoom) * map.scale,
|
|
1369
|
+
height: (pane.height / v.zoom) * map.scale,
|
|
1370
|
+
},
|
|
1371
|
+
radius: 2,
|
|
1372
|
+
fill: map.maskColor ?? tint(palette.accent, 0.1),
|
|
1373
|
+
stroke: palette.accent,
|
|
1374
|
+
lineWidth: 1,
|
|
1375
|
+
},
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1378
|
+
function buildControls(input) {
|
|
1379
|
+
const { palette, controls } = input;
|
|
1380
|
+
const first = controls[0].rect;
|
|
1381
|
+
const last = controls[controls.length - 1].rect;
|
|
1382
|
+
const glyphs = [];
|
|
1383
|
+
const rules = [];
|
|
1384
|
+
for (let i = 0; i < controls.length; i++) {
|
|
1385
|
+
const { rect, action } = controls[i];
|
|
1386
|
+
if (i > 0) {
|
|
1387
|
+
rules.push({
|
|
1388
|
+
kind: 'rule',
|
|
1389
|
+
points: [
|
|
1390
|
+
{ x: rect.x + 4, y: rect.y },
|
|
1391
|
+
{ x: rect.x + rect.width - 4, y: rect.y },
|
|
1392
|
+
],
|
|
1393
|
+
color: palette.surfaceBorder,
|
|
1394
|
+
lineWidth: 1,
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
const cx = rect.x + rect.width / 2;
|
|
1398
|
+
const cy = rect.y + rect.height / 2;
|
|
1399
|
+
const arm = 5;
|
|
1400
|
+
const runs = [];
|
|
1401
|
+
if (action === 'in' || action === 'out') {
|
|
1402
|
+
runs.push([
|
|
1403
|
+
{ x: cx - arm, y: cy },
|
|
1404
|
+
{ x: cx + arm, y: cy },
|
|
1405
|
+
]);
|
|
1406
|
+
if (action === 'in') {
|
|
1407
|
+
runs.push([
|
|
1408
|
+
{ x: cx, y: cy - arm },
|
|
1409
|
+
{ x: cx, y: cy + arm },
|
|
1410
|
+
]);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
else {
|
|
1414
|
+
// a frame with its sides opened out: "fit what is there into this"
|
|
1415
|
+
const a = 5;
|
|
1416
|
+
runs.push([
|
|
1417
|
+
{ x: cx - a, y: cy - a + 3 },
|
|
1418
|
+
{ x: cx - a, y: cy - a },
|
|
1419
|
+
{ x: cx - a + 3, y: cy - a },
|
|
1420
|
+
], [
|
|
1421
|
+
{ x: cx + a - 3, y: cy - a },
|
|
1422
|
+
{ x: cx + a, y: cy - a },
|
|
1423
|
+
{ x: cx + a, y: cy - a + 3 },
|
|
1424
|
+
], [
|
|
1425
|
+
{ x: cx + a, y: cy + a - 3 },
|
|
1426
|
+
{ x: cx + a, y: cy + a },
|
|
1427
|
+
{ x: cx + a - 3, y: cy + a },
|
|
1428
|
+
], [
|
|
1429
|
+
{ x: cx - a + 3, y: cy + a },
|
|
1430
|
+
{ x: cx - a, y: cy + a },
|
|
1431
|
+
{ x: cx - a, y: cy + a - 3 },
|
|
1432
|
+
]);
|
|
1433
|
+
}
|
|
1434
|
+
glyphs.push({ runs, color: palette.text, lineWidth: 1.5 });
|
|
1435
|
+
}
|
|
1436
|
+
return {
|
|
1437
|
+
panel: {
|
|
1438
|
+
rect: {
|
|
1439
|
+
x: first.x,
|
|
1440
|
+
y: first.y,
|
|
1441
|
+
width: first.width,
|
|
1442
|
+
height: last.y + last.height - first.y,
|
|
1443
|
+
},
|
|
1444
|
+
radius: 4,
|
|
1445
|
+
fill: tint(palette.surface, 0.94),
|
|
1446
|
+
stroke: palette.surfaceBorder,
|
|
1447
|
+
lineWidth: 1,
|
|
1448
|
+
},
|
|
1449
|
+
rules,
|
|
1450
|
+
glyphs,
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
//# sourceMappingURL=scene.js.map
|