samcan 0.0.2 → 0.1.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/{license → LICENSE} +3 -1
- package/README.md +3 -0
- package/dist/camera/index.cjs +370 -0
- package/dist/camera/index.d.cts +54 -0
- package/dist/camera/index.d.ts +54 -0
- package/dist/camera/index.js +70 -0
- package/dist/document/index.cjs +735 -0
- package/dist/document/index.d.cts +174 -0
- package/dist/document/index.d.ts +174 -0
- package/dist/document/index.js +161 -0
- package/dist/editor/index.cjs +5007 -0
- package/dist/editor/index.d.cts +374 -0
- package/dist/editor/index.d.ts +374 -0
- package/dist/editor/index.js +2401 -0
- package/dist/engine/index.cjs +3752 -0
- package/dist/engine/index.d.cts +183 -0
- package/dist/engine/index.d.ts +183 -0
- package/dist/engine/index.js +111 -0
- package/dist/index.cjs +8899 -0
- package/dist/index.d.cts +962 -0
- package/dist/index.d.ts +962 -46
- package/dist/index.js +1008 -49
- package/dist/math/index.cjs +2230 -0
- package/dist/math/index.d.cts +302 -0
- package/dist/math/index.d.ts +302 -0
- package/dist/math/index.js +1109 -0
- package/dist/renderer/index.cjs +1865 -0
- package/dist/renderer/index.d.cts +91 -0
- package/dist/renderer/index.d.ts +91 -0
- package/dist/renderer/index.js +89 -0
- package/dist/shared/chunk-35pkr0zs.js +110 -0
- package/dist/shared/chunk-7sr7q84y.js +80 -0
- package/dist/shared/chunk-87399bz7.js +89 -0
- package/dist/shared/chunk-8ynnz57m.js +129 -0
- package/dist/shared/chunk-d6gxvapt.js +500 -0
- package/dist/shared/chunk-hewznwbd.js +305 -0
- package/dist/shared/chunk-jdvrd5tv.js +623 -0
- package/dist/shared/chunk-rvag1j46.js +461 -0
- package/dist/shared/chunk-rzxyjwk8.js +397 -0
- package/dist/shared/chunk-s3qdcmcq.js +1392 -0
- package/dist/shared/chunk-v53jprrn.js +68 -0
- package/dist/shared/chunk-wa3772hp.js +117 -0
- package/dist/spatial/index.cjs +954 -0
- package/dist/spatial/index.d.cts +136 -0
- package/dist/spatial/index.d.ts +136 -0
- package/dist/spatial/index.js +301 -0
- package/dist/stroke/index.cjs +1010 -0
- package/dist/stroke/index.d.cts +143 -0
- package/dist/stroke/index.d.ts +143 -0
- package/dist/stroke/index.js +343 -0
- package/package.json +122 -58
- package/dist/bundle.js +0 -5888
- package/dist/core/animation/animationruntime.d.ts +0 -238
- package/dist/core/animation/animationruntime.d.ts.map +0 -1
- package/dist/core/animation/animationruntime.js +0 -530
- package/dist/core/animation/animationruntime.js.map +0 -1
- package/dist/core/animation/animationstate.d.ts +0 -85
- package/dist/core/animation/animationstate.d.ts.map +0 -1
- package/dist/core/animation/animationstate.js +0 -119
- package/dist/core/animation/animationstate.js.map +0 -1
- package/dist/core/animation/animationtrack.d.ts +0 -55
- package/dist/core/animation/animationtrack.d.ts.map +0 -1
- package/dist/core/animation/animationtrack.js +0 -185
- package/dist/core/animation/animationtrack.js.map +0 -1
- package/dist/core/animation/easing.d.ts +0 -80
- package/dist/core/animation/easing.d.ts.map +0 -1
- package/dist/core/animation/easing.js +0 -126
- package/dist/core/animation/easing.js.map +0 -1
- package/dist/core/animation/index.d.ts +0 -11
- package/dist/core/animation/index.d.ts.map +0 -1
- package/dist/core/animation/index.js +0 -10
- package/dist/core/animation/index.js.map +0 -1
- package/dist/core/animation/interpolator.d.ts +0 -82
- package/dist/core/animation/interpolator.d.ts.map +0 -1
- package/dist/core/animation/interpolator.js +0 -108
- package/dist/core/animation/interpolator.js.map +0 -1
- package/dist/core/animation/keyframe.d.ts +0 -52
- package/dist/core/animation/keyframe.d.ts.map +0 -1
- package/dist/core/animation/keyframe.js +0 -65
- package/dist/core/animation/keyframe.js.map +0 -1
- package/dist/core/animation/logger.d.ts +0 -8
- package/dist/core/animation/logger.d.ts.map +0 -1
- package/dist/core/animation/logger.js +0 -20
- package/dist/core/animation/logger.js.map +0 -1
- package/dist/core/animation/statemachine.d.ts +0 -178
- package/dist/core/animation/statemachine.d.ts.map +0 -1
- package/dist/core/animation/statemachine.js +0 -378
- package/dist/core/animation/statemachine.js.map +0 -1
- package/dist/core/animation/statetransition.d.ts +0 -142
- package/dist/core/animation/statetransition.d.ts.map +0 -1
- package/dist/core/animation/statetransition.js +0 -189
- package/dist/core/animation/statetransition.js.map +0 -1
- package/dist/core/animation/timeline.d.ts +0 -62
- package/dist/core/animation/timeline.d.ts.map +0 -1
- package/dist/core/animation/timeline.js +0 -102
- package/dist/core/animation/timeline.js.map +0 -1
- package/dist/core/api.d.ts +0 -245
- package/dist/core/api.d.ts.map +0 -1
- package/dist/core/api.js +0 -369
- package/dist/core/api.js.map +0 -1
- package/dist/core/asset/assetmanager.d.ts +0 -196
- package/dist/core/asset/assetmanager.d.ts.map +0 -1
- package/dist/core/asset/assetmanager.js +0 -502
- package/dist/core/asset/assetmanager.js.map +0 -1
- package/dist/core/asset/index.d.ts +0 -3
- package/dist/core/asset/index.d.ts.map +0 -1
- package/dist/core/asset/index.js +0 -3
- package/dist/core/asset/index.js.map +0 -1
- package/dist/core/asset/types.d.ts +0 -36
- package/dist/core/asset/types.d.ts.map +0 -1
- package/dist/core/asset/types.js +0 -1
- package/dist/core/asset/types.js.map +0 -1
- package/dist/core/command/basecommand.d.ts +0 -29
- package/dist/core/command/basecommand.d.ts.map +0 -1
- package/dist/core/command/basecommand.js +0 -36
- package/dist/core/command/basecommand.js.map +0 -1
- package/dist/core/command/command.d.ts +0 -55
- package/dist/core/command/command.d.ts.map +0 -1
- package/dist/core/command/command.js +0 -1
- package/dist/core/command/command.js.map +0 -1
- package/dist/core/command/commandhistory.d.ts +0 -76
- package/dist/core/command/commandhistory.d.ts.map +0 -1
- package/dist/core/command/commandhistory.js +0 -122
- package/dist/core/command/commandhistory.js.map +0 -1
- package/dist/core/command/editorcommands.d.ts +0 -108
- package/dist/core/command/editorcommands.d.ts.map +0 -1
- package/dist/core/command/editorcommands.js +0 -274
- package/dist/core/command/editorcommands.js.map +0 -1
- package/dist/core/command/index.d.ts +0 -5
- package/dist/core/command/index.d.ts.map +0 -1
- package/dist/core/command/index.js +0 -5
- package/dist/core/command/index.js.map +0 -1
- package/dist/core/editor/events/emitter.d.ts +0 -45
- package/dist/core/editor/events/emitter.d.ts.map +0 -1
- package/dist/core/editor/events/emitter.js +0 -88
- package/dist/core/editor/events/emitter.js.map +0 -1
- package/dist/core/error/animationerror.d.ts +0 -27
- package/dist/core/error/animationerror.d.ts.map +0 -1
- package/dist/core/error/animationerror.js +0 -41
- package/dist/core/error/animationerror.js.map +0 -1
- package/dist/core/error/asseterror.d.ts +0 -32
- package/dist/core/error/asseterror.d.ts.map +0 -1
- package/dist/core/error/asseterror.js +0 -48
- package/dist/core/error/asseterror.js.map +0 -1
- package/dist/core/error/index.d.ts +0 -7
- package/dist/core/error/index.d.ts.map +0 -1
- package/dist/core/error/index.js +0 -7
- package/dist/core/error/index.js.map +0 -1
- package/dist/core/error/pluginerror.d.ts +0 -22
- package/dist/core/error/pluginerror.d.ts.map +0 -1
- package/dist/core/error/pluginerror.js +0 -33
- package/dist/core/error/pluginerror.js.map +0 -1
- package/dist/core/error/renderererror.d.ts +0 -26
- package/dist/core/error/renderererror.d.ts.map +0 -1
- package/dist/core/error/renderererror.js +0 -40
- package/dist/core/error/renderererror.js.map +0 -1
- package/dist/core/error/samcanerror.d.ts +0 -72
- package/dist/core/error/samcanerror.d.ts.map +0 -1
- package/dist/core/error/samcanerror.js +0 -108
- package/dist/core/error/samcanerror.js.map +0 -1
- package/dist/core/error/serializationerror.d.ts +0 -31
- package/dist/core/error/serializationerror.d.ts.map +0 -1
- package/dist/core/error/serializationerror.js +0 -46
- package/dist/core/error/serializationerror.js.map +0 -1
- package/dist/core/index.d.ts +0 -12
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -12
- package/dist/core/index.js.map +0 -1
- package/dist/core/math/color.d.ts +0 -56
- package/dist/core/math/color.d.ts.map +0 -1
- package/dist/core/math/color.js +0 -125
- package/dist/core/math/color.js.map +0 -1
- package/dist/core/math/index.d.ts +0 -14
- package/dist/core/math/index.d.ts.map +0 -1
- package/dist/core/math/index.js +0 -14
- package/dist/core/math/index.js.map +0 -1
- package/dist/core/math/matrix.d.ts +0 -101
- package/dist/core/math/matrix.d.ts.map +0 -1
- package/dist/core/math/matrix.js +0 -177
- package/dist/core/math/matrix.js.map +0 -1
- package/dist/core/math/paint.d.ts +0 -104
- package/dist/core/math/paint.d.ts.map +0 -1
- package/dist/core/math/paint.js +0 -204
- package/dist/core/math/paint.js.map +0 -1
- package/dist/core/math/path/index.d.ts +0 -94
- package/dist/core/math/path/index.d.ts.map +0 -1
- package/dist/core/math/path/index.js +0 -180
- package/dist/core/math/path/index.js.map +0 -1
- package/dist/core/math/path/operations.d.ts +0 -38
- package/dist/core/math/path/operations.d.ts.map +0 -1
- package/dist/core/math/path/operations.js +0 -149
- package/dist/core/math/path/operations.js.map +0 -1
- package/dist/core/math/pool.d.ts +0 -54
- package/dist/core/math/pool.d.ts.map +0 -1
- package/dist/core/math/pool.js +0 -97
- package/dist/core/math/pool.js.map +0 -1
- package/dist/core/math/pools.d.ts +0 -29
- package/dist/core/math/pools.d.ts.map +0 -1
- package/dist/core/math/pools.js +0 -50
- package/dist/core/math/pools.js.map +0 -1
- package/dist/core/math/rectangle.d.ts +0 -68
- package/dist/core/math/rectangle.d.ts.map +0 -1
- package/dist/core/math/rectangle.js +0 -124
- package/dist/core/math/rectangle.js.map +0 -1
- package/dist/core/math/utils.d.ts +0 -68
- package/dist/core/math/utils.d.ts.map +0 -1
- package/dist/core/math/utils.js +0 -110
- package/dist/core/math/utils.js.map +0 -1
- package/dist/core/math/vector2.d.ts +0 -85
- package/dist/core/math/vector2.d.ts.map +0 -1
- package/dist/core/math/vector2.js +0 -134
- package/dist/core/math/vector2.js.map +0 -1
- package/dist/core/plugin/animationcontroller.d.ts +0 -43
- package/dist/core/plugin/animationcontroller.d.ts.map +0 -1
- package/dist/core/plugin/animationcontroller.js +0 -11
- package/dist/core/plugin/animationcontroller.js.map +0 -1
- package/dist/core/plugin/index.d.ts +0 -6
- package/dist/core/plugin/index.d.ts.map +0 -1
- package/dist/core/plugin/index.js +0 -4
- package/dist/core/plugin/index.js.map +0 -1
- package/dist/core/plugin/plugin.d.ts +0 -53
- package/dist/core/plugin/plugin.d.ts.map +0 -1
- package/dist/core/plugin/plugin.js +0 -29
- package/dist/core/plugin/plugin.js.map +0 -1
- package/dist/core/plugin/pluginregistry.d.ts +0 -71
- package/dist/core/plugin/pluginregistry.d.ts.map +0 -1
- package/dist/core/plugin/pluginregistry.js +0 -143
- package/dist/core/plugin/pluginregistry.js.map +0 -1
- package/dist/core/renderer/batchmanager.d.ts +0 -68
- package/dist/core/renderer/batchmanager.d.ts.map +0 -1
- package/dist/core/renderer/batchmanager.js +0 -82
- package/dist/core/renderer/batchmanager.js.map +0 -1
- package/dist/core/renderer/canvas2drenderer.d.ts +0 -139
- package/dist/core/renderer/canvas2drenderer.d.ts.map +0 -1
- package/dist/core/renderer/canvas2drenderer.js +0 -499
- package/dist/core/renderer/canvas2drenderer.js.map +0 -1
- package/dist/core/renderer/dirtyregionmanager.d.ts +0 -54
- package/dist/core/renderer/dirtyregionmanager.d.ts.map +0 -1
- package/dist/core/renderer/dirtyregionmanager.js +0 -129
- package/dist/core/renderer/dirtyregionmanager.js.map +0 -1
- package/dist/core/renderer/index.d.ts +0 -8
- package/dist/core/renderer/index.d.ts.map +0 -1
- package/dist/core/renderer/index.js +0 -6
- package/dist/core/renderer/index.js.map +0 -1
- package/dist/core/renderer/renderer.d.ts +0 -154
- package/dist/core/renderer/renderer.d.ts.map +0 -1
- package/dist/core/renderer/renderer.js +0 -1
- package/dist/core/renderer/renderer.js.map +0 -1
- package/dist/core/renderer/rendererfactory.d.ts +0 -66
- package/dist/core/renderer/rendererfactory.d.ts.map +0 -1
- package/dist/core/renderer/rendererfactory.js +0 -219
- package/dist/core/renderer/rendererfactory.js.map +0 -1
- package/dist/core/renderer/webglrenderer.d.ts +0 -185
- package/dist/core/renderer/webglrenderer.d.ts.map +0 -1
- package/dist/core/renderer/webglrenderer.js +0 -1007
- package/dist/core/renderer/webglrenderer.js.map +0 -1
- package/dist/core/scene/index.d.ts +0 -4
- package/dist/core/scene/index.d.ts.map +0 -1
- package/dist/core/scene/index.js +0 -4
- package/dist/core/scene/index.js.map +0 -1
- package/dist/core/scene/node.d.ts +0 -162
- package/dist/core/scene/node.d.ts.map +0 -1
- package/dist/core/scene/node.js +0 -402
- package/dist/core/scene/node.js.map +0 -1
- package/dist/core/scene/nodes/artboard.d.ts +0 -42
- package/dist/core/scene/nodes/artboard.d.ts.map +0 -1
- package/dist/core/scene/nodes/artboard.js +0 -64
- package/dist/core/scene/nodes/artboard.js.map +0 -1
- package/dist/core/scene/nodes/groupnode.d.ts +0 -10
- package/dist/core/scene/nodes/groupnode.d.ts.map +0 -1
- package/dist/core/scene/nodes/groupnode.js +0 -12
- package/dist/core/scene/nodes/groupnode.js.map +0 -1
- package/dist/core/scene/nodes/imagenode.d.ts +0 -38
- package/dist/core/scene/nodes/imagenode.d.ts.map +0 -1
- package/dist/core/scene/nodes/imagenode.js +0 -77
- package/dist/core/scene/nodes/imagenode.js.map +0 -1
- package/dist/core/scene/nodes/index.d.ts +0 -5
- package/dist/core/scene/nodes/index.d.ts.map +0 -1
- package/dist/core/scene/nodes/index.js +0 -5
- package/dist/core/scene/nodes/index.js.map +0 -1
- package/dist/core/scene/nodes/shapenode.d.ts +0 -76
- package/dist/core/scene/nodes/shapenode.d.ts.map +0 -1
- package/dist/core/scene/nodes/shapenode.js +0 -212
- package/dist/core/scene/nodes/shapenode.js.map +0 -1
- package/dist/core/scene/transform.d.ts +0 -27
- package/dist/core/scene/transform.d.ts.map +0 -1
- package/dist/core/scene/transform.js +0 -52
- package/dist/core/scene/transform.js.map +0 -1
- package/dist/core/serialization/index.d.ts +0 -3
- package/dist/core/serialization/index.d.ts.map +0 -1
- package/dist/core/serialization/index.js +0 -2
- package/dist/core/serialization/index.js.map +0 -1
- package/dist/core/serialization/serializer.d.ts +0 -323
- package/dist/core/serialization/serializer.d.ts.map +0 -1
- package/dist/core/serialization/serializer.js +0 -1173
- package/dist/core/serialization/serializer.js.map +0 -1
- package/dist/core/serialization/types.d.ts +0 -242
- package/dist/core/serialization/types.d.ts.map +0 -1
- package/dist/core/serialization/types.js +0 -1
- package/dist/core/serialization/types.js.map +0 -1
- package/dist/core/timing/clock.d.ts +0 -43
- package/dist/core/timing/clock.d.ts.map +0 -1
- package/dist/core/timing/clock.js +0 -78
- package/dist/core/timing/clock.js.map +0 -1
- package/dist/core/timing/index.d.ts +0 -3
- package/dist/core/timing/index.d.ts.map +0 -1
- package/dist/core/timing/index.js +0 -3
- package/dist/core/timing/index.js.map +0 -1
- package/dist/core/timing/scheduler.d.ts +0 -72
- package/dist/core/timing/scheduler.d.ts.map +0 -1
- package/dist/core/timing/scheduler.js +0 -163
- package/dist/core/timing/scheduler.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/wrapper/react/index.d.ts +0 -5
- package/dist/wrapper/react/index.d.ts.map +0 -1
- package/dist/wrapper/react/index.js +0 -3
- package/dist/wrapper/react/index.js.map +0 -1
- package/dist/wrapper/react/samcan-player.d.ts +0 -26
- package/dist/wrapper/react/samcan-player.d.ts.map +0 -1
- package/dist/wrapper/react/samcan-player.js +0 -19
- package/dist/wrapper/react/samcan-player.js.map +0 -1
- package/dist/wrapper/react/use-samcan-player.d.ts +0 -33
- package/dist/wrapper/react/use-samcan-player.d.ts.map +0 -1
- package/dist/wrapper/react/use-samcan-player.js +0 -65
- package/dist/wrapper/react/use-samcan-player.js.map +0 -1
- package/readme.md +0 -96
|
@@ -0,0 +1,3752 @@
|
|
|
1
|
+
var import_node_module = require("node:module");
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
7
|
+
var __toCommonJS = (from) => {
|
|
8
|
+
var entry = __moduleCache.get(from), desc;
|
|
9
|
+
if (entry)
|
|
10
|
+
return entry;
|
|
11
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
13
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
14
|
+
get: () => from[key],
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
}));
|
|
17
|
+
__moduleCache.set(from, entry);
|
|
18
|
+
return entry;
|
|
19
|
+
};
|
|
20
|
+
var __export = (target, all) => {
|
|
21
|
+
for (var name in all)
|
|
22
|
+
__defProp(target, name, {
|
|
23
|
+
get: all[name],
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
set: (newValue) => all[name] = () => newValue
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// engine/index.ts
|
|
31
|
+
var exports_engine = {};
|
|
32
|
+
__export(exports_engine, {
|
|
33
|
+
zoom_engine: () => zoom_engine,
|
|
34
|
+
zoom_at_engine: () => zoom_at_engine,
|
|
35
|
+
world_to_screen_engine: () => world_to_screen_engine,
|
|
36
|
+
visible_bounds_world_engine: () => visible_bounds_world_engine,
|
|
37
|
+
set_document_engine: () => set_document_engine,
|
|
38
|
+
set_camera_engine: () => set_camera_engine,
|
|
39
|
+
set_background_image_engine: () => set_background_image_engine,
|
|
40
|
+
set_background_engine: () => set_background_engine,
|
|
41
|
+
screen_to_world_engine: () => screen_to_world_engine,
|
|
42
|
+
resize_engine: () => resize_engine,
|
|
43
|
+
render_engine_with_overlay: () => render_engine_with_overlay,
|
|
44
|
+
render_engine: () => render_engine,
|
|
45
|
+
pan_engine: () => pan_engine,
|
|
46
|
+
dispose_engine: () => dispose_engine,
|
|
47
|
+
create_engine: () => create_engine
|
|
48
|
+
});
|
|
49
|
+
module.exports = __toCommonJS(exports_engine);
|
|
50
|
+
|
|
51
|
+
// camera/camera.ts
|
|
52
|
+
function create_camera(x = 0, y = 0, zoom = 1, rotation = 0) {
|
|
53
|
+
return [x, y, zoom, rotation];
|
|
54
|
+
}
|
|
55
|
+
function clone_camera(c) {
|
|
56
|
+
return [c[0], c[1], c[2], c[3]];
|
|
57
|
+
}
|
|
58
|
+
function copy_camera(c, out) {
|
|
59
|
+
out[0] = c[0];
|
|
60
|
+
out[1] = c[1];
|
|
61
|
+
out[2] = c[2];
|
|
62
|
+
out[3] = c[3];
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
function set_camera(x, y, zoom, rotation, out) {
|
|
66
|
+
out[0] = x;
|
|
67
|
+
out[1] = y;
|
|
68
|
+
out[2] = zoom;
|
|
69
|
+
out[3] = rotation;
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
function x_of_camera(c) {
|
|
73
|
+
return c[0];
|
|
74
|
+
}
|
|
75
|
+
function y_of_camera(c) {
|
|
76
|
+
return c[1];
|
|
77
|
+
}
|
|
78
|
+
function zoom_of_camera(c) {
|
|
79
|
+
return c[2];
|
|
80
|
+
}
|
|
81
|
+
function rotation_of_camera(c) {
|
|
82
|
+
return c[3];
|
|
83
|
+
}
|
|
84
|
+
function position_of_camera(c, out) {
|
|
85
|
+
out[0] = c[0];
|
|
86
|
+
out[1] = c[1];
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
function pan_camera(c, dx, dy, out) {
|
|
90
|
+
out[0] = c[0] + dx;
|
|
91
|
+
out[1] = c[1] + dy;
|
|
92
|
+
out[2] = c[2];
|
|
93
|
+
out[3] = c[3];
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
function zoom_camera(c, factor, out) {
|
|
97
|
+
out[0] = c[0];
|
|
98
|
+
out[1] = c[1];
|
|
99
|
+
out[2] = c[2] * factor;
|
|
100
|
+
out[3] = c[3];
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
function zoom_at_camera(c, factor, anchor, out) {
|
|
104
|
+
const world_dx = anchor.world_x - c[0];
|
|
105
|
+
const world_dy = anchor.world_y - c[1];
|
|
106
|
+
const new_zoom = c[2] * factor;
|
|
107
|
+
const new_world_x = anchor.world_x - world_dx / factor;
|
|
108
|
+
const new_world_y = anchor.world_y - world_dy / factor;
|
|
109
|
+
out[0] = new_world_x;
|
|
110
|
+
out[1] = new_world_y;
|
|
111
|
+
out[2] = new_zoom;
|
|
112
|
+
out[3] = c[3];
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
115
|
+
function rotate_camera(c, angle, out) {
|
|
116
|
+
out[0] = c[0];
|
|
117
|
+
out[1] = c[1];
|
|
118
|
+
out[2] = c[2];
|
|
119
|
+
out[3] = c[3] + angle;
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
function clamp_camera(c, limits, out) {
|
|
123
|
+
const clamped_zoom = Math.max(limits.min_zoom, Math.min(limits.max_zoom, c[2]));
|
|
124
|
+
const clamped_x = Math.max(limits.min_x, Math.min(limits.max_x, c[0]));
|
|
125
|
+
const clamped_y = Math.max(limits.min_y, Math.min(limits.max_y, c[1]));
|
|
126
|
+
out[0] = clamped_x;
|
|
127
|
+
out[1] = clamped_y;
|
|
128
|
+
out[2] = clamped_zoom;
|
|
129
|
+
out[3] = c[3];
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
function lerp_camera(a, b, t, out) {
|
|
133
|
+
const clamped_t = Math.max(0, Math.min(1, t));
|
|
134
|
+
out[0] = a[0] + clamped_t * (b[0] - a[0]);
|
|
135
|
+
out[1] = a[1] + clamped_t * (b[1] - a[1]);
|
|
136
|
+
out[2] = a[2] + clamped_t * (b[2] - a[2]);
|
|
137
|
+
let rotation_diff = b[3] - a[3];
|
|
138
|
+
while (rotation_diff > Math.PI) {
|
|
139
|
+
rotation_diff = rotation_diff - 2 * Math.PI;
|
|
140
|
+
}
|
|
141
|
+
while (rotation_diff < -Math.PI) {
|
|
142
|
+
rotation_diff = rotation_diff + 2 * Math.PI;
|
|
143
|
+
}
|
|
144
|
+
out[3] = a[3] + clamped_t * rotation_diff;
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
function smooth_damp_camera(current, target, velocity, smooth_time, delta_time, out) {
|
|
148
|
+
const omega = 2 / Math.max(0.0001, smooth_time);
|
|
149
|
+
const x = omega * delta_time;
|
|
150
|
+
const exp = 1 / (1 + x + 0.48 * x * x + 0.235 * x * x * x);
|
|
151
|
+
const delta_x = current[0] - target[0];
|
|
152
|
+
const delta_y = current[1] - target[1];
|
|
153
|
+
const delta_zoom = current[2] - target[2];
|
|
154
|
+
let rotation_diff = target[3] - current[3];
|
|
155
|
+
while (rotation_diff > Math.PI) {
|
|
156
|
+
rotation_diff = rotation_diff - 2 * Math.PI;
|
|
157
|
+
}
|
|
158
|
+
while (rotation_diff < -Math.PI) {
|
|
159
|
+
rotation_diff = rotation_diff + 2 * Math.PI;
|
|
160
|
+
}
|
|
161
|
+
const delta_rotation = -rotation_diff;
|
|
162
|
+
const temp_x = (velocity[0] + omega * delta_x) * delta_time;
|
|
163
|
+
const temp_y = (velocity[1] + omega * delta_y) * delta_time;
|
|
164
|
+
const temp_zoom = (velocity[2] + omega * delta_zoom) * delta_time;
|
|
165
|
+
const temp_rotation = (velocity[3] + omega * delta_rotation) * delta_time;
|
|
166
|
+
const new_velocity_x = (velocity[0] - omega * temp_x) * exp;
|
|
167
|
+
const new_velocity_y = (velocity[1] - omega * temp_y) * exp;
|
|
168
|
+
const new_velocity_zoom = (velocity[2] - omega * temp_zoom) * exp;
|
|
169
|
+
const new_velocity_rotation = (velocity[3] - omega * temp_rotation) * exp;
|
|
170
|
+
velocity[0] = new_velocity_x;
|
|
171
|
+
velocity[1] = new_velocity_y;
|
|
172
|
+
velocity[2] = new_velocity_zoom;
|
|
173
|
+
velocity[3] = new_velocity_rotation;
|
|
174
|
+
const result_x = target[0] + (delta_x + temp_x) * exp;
|
|
175
|
+
const result_y = target[1] + (delta_y + temp_y) * exp;
|
|
176
|
+
const result_zoom = target[2] + (delta_zoom + temp_zoom) * exp;
|
|
177
|
+
const result_rotation = target[3] + (delta_rotation + temp_rotation) * exp;
|
|
178
|
+
out[0] = result_x;
|
|
179
|
+
out[1] = result_y;
|
|
180
|
+
out[2] = result_zoom;
|
|
181
|
+
out[3] = result_rotation;
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
function equals_camera(c1, c2, epsilon = 0.0001) {
|
|
185
|
+
return Math.abs(c1[0] - c2[0]) < epsilon && Math.abs(c1[1] - c2[1]) < epsilon && Math.abs(c1[2] - c2[2]) < epsilon && Math.abs(c1[3] - c2[3]) < epsilon;
|
|
186
|
+
}
|
|
187
|
+
function is_identity_camera(c) {
|
|
188
|
+
return c[0] === 0 && c[1] === 0 && c[2] === 1 && c[3] === 0;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// document/layer.ts
|
|
192
|
+
function create_layer(id, name, visible = true, locked = false, opacity = 1) {
|
|
193
|
+
return {
|
|
194
|
+
id,
|
|
195
|
+
name,
|
|
196
|
+
visible,
|
|
197
|
+
locked,
|
|
198
|
+
opacity: Math.max(0, Math.min(1, opacity)),
|
|
199
|
+
element_ids: []
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function clone_layer(l) {
|
|
203
|
+
return {
|
|
204
|
+
id: l.id,
|
|
205
|
+
name: l.name,
|
|
206
|
+
visible: l.visible,
|
|
207
|
+
locked: l.locked,
|
|
208
|
+
opacity: l.opacity,
|
|
209
|
+
element_ids: [...l.element_ids]
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function id_of_layer(l) {
|
|
213
|
+
return l.id;
|
|
214
|
+
}
|
|
215
|
+
function name_of_layer(l) {
|
|
216
|
+
return l.name;
|
|
217
|
+
}
|
|
218
|
+
function is_visible_layer(l) {
|
|
219
|
+
return l.visible;
|
|
220
|
+
}
|
|
221
|
+
function is_locked_layer(l) {
|
|
222
|
+
return l.locked;
|
|
223
|
+
}
|
|
224
|
+
function opacity_of_layer(l) {
|
|
225
|
+
return l.opacity;
|
|
226
|
+
}
|
|
227
|
+
function element_ids_of_layer(l) {
|
|
228
|
+
return l.element_ids;
|
|
229
|
+
}
|
|
230
|
+
function element_count_of_layer(l) {
|
|
231
|
+
return l.element_ids.length;
|
|
232
|
+
}
|
|
233
|
+
function update_layer_name(l, name) {
|
|
234
|
+
return {
|
|
235
|
+
...l,
|
|
236
|
+
name
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function update_layer_visibility(l, visible) {
|
|
240
|
+
return {
|
|
241
|
+
...l,
|
|
242
|
+
visible
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
function update_layer_locked(l, locked) {
|
|
246
|
+
return {
|
|
247
|
+
...l,
|
|
248
|
+
locked
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
function update_layer_opacity(l, opacity) {
|
|
252
|
+
return {
|
|
253
|
+
...l,
|
|
254
|
+
opacity: Math.max(0, Math.min(1, opacity))
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function add_element_id_to_layer(l, element_id) {
|
|
258
|
+
if (l.element_ids.includes(element_id)) {
|
|
259
|
+
return l;
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
...l,
|
|
263
|
+
element_ids: [...l.element_ids, element_id]
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function remove_element_id_from_layer(l, element_id) {
|
|
267
|
+
const index = l.element_ids.indexOf(element_id);
|
|
268
|
+
if (index === -1) {
|
|
269
|
+
return l;
|
|
270
|
+
}
|
|
271
|
+
const new_ids = [...l.element_ids];
|
|
272
|
+
new_ids.splice(index, 1);
|
|
273
|
+
return {
|
|
274
|
+
...l,
|
|
275
|
+
element_ids: new_ids
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function has_element_id_in_layer(l, element_id) {
|
|
279
|
+
return l.element_ids.includes(element_id);
|
|
280
|
+
}
|
|
281
|
+
function reorder_element_ids_in_layer(l, element_ids) {
|
|
282
|
+
const existing_set = new Set(l.element_ids);
|
|
283
|
+
const filtered_ids = element_ids.filter((id) => existing_set.has(id));
|
|
284
|
+
const new_ids_set = new Set(filtered_ids);
|
|
285
|
+
const remaining_ids = l.element_ids.filter((id) => !new_ids_set.has(id));
|
|
286
|
+
return {
|
|
287
|
+
...l,
|
|
288
|
+
element_ids: [...remaining_ids, ...filtered_ids]
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function equals_layer(a, b) {
|
|
292
|
+
if (a.id !== b.id || a.name !== b.name) {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
if (a.visible !== b.visible || a.locked !== b.locked) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
if (a.opacity !== b.opacity) {
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
if (a.element_ids.length !== b.element_ids.length) {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
for (let i = 0;i < a.element_ids.length; i = i + 1) {
|
|
305
|
+
if (a.element_ids[i] !== b.element_ids[i]) {
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// document/document.ts
|
|
313
|
+
function create_document(id, active_layer_id) {
|
|
314
|
+
return {
|
|
315
|
+
id,
|
|
316
|
+
elements: new Map,
|
|
317
|
+
layers: [],
|
|
318
|
+
active_layer_id,
|
|
319
|
+
assets: new Map,
|
|
320
|
+
bounds: [0, 0, 0, 0],
|
|
321
|
+
element_count: 0
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function clone_document(doc) {
|
|
325
|
+
const new_elements = new Map;
|
|
326
|
+
for (const [id, el] of doc.elements) {
|
|
327
|
+
new_elements.set(id, el);
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
id: doc.id,
|
|
331
|
+
elements: new_elements,
|
|
332
|
+
layers: doc.layers.map((l) => ({ ...l, element_ids: [...l.element_ids] })),
|
|
333
|
+
active_layer_id: doc.active_layer_id,
|
|
334
|
+
assets: new Map(doc.assets),
|
|
335
|
+
bounds: [doc.bounds[0], doc.bounds[1], doc.bounds[2], doc.bounds[3]],
|
|
336
|
+
element_count: doc.element_count
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
function id_of_document(doc) {
|
|
340
|
+
return doc.id;
|
|
341
|
+
}
|
|
342
|
+
function elements_map_of_document(doc) {
|
|
343
|
+
return doc.elements;
|
|
344
|
+
}
|
|
345
|
+
function layers_of_document(doc) {
|
|
346
|
+
return doc.layers;
|
|
347
|
+
}
|
|
348
|
+
function active_layer_id_of_document(doc) {
|
|
349
|
+
return doc.active_layer_id;
|
|
350
|
+
}
|
|
351
|
+
function assets_of_document(doc) {
|
|
352
|
+
return doc.assets;
|
|
353
|
+
}
|
|
354
|
+
function bounds_of_document(doc) {
|
|
355
|
+
return [doc.bounds[0], doc.bounds[1], doc.bounds[2], doc.bounds[3]];
|
|
356
|
+
}
|
|
357
|
+
function element_count_of_document(doc) {
|
|
358
|
+
return doc.element_count;
|
|
359
|
+
}
|
|
360
|
+
function get_element_by_id_document(doc, element_id) {
|
|
361
|
+
return doc.elements.get(element_id) || null;
|
|
362
|
+
}
|
|
363
|
+
function get_layer_by_id_document(doc, layer_id) {
|
|
364
|
+
for (let i = 0;i < doc.layers.length; i = i + 1) {
|
|
365
|
+
const layer = doc.layers[i];
|
|
366
|
+
if (layer && layer.id === layer_id) {
|
|
367
|
+
return layer;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
function add_element_document(doc, el) {
|
|
373
|
+
const layer = get_layer_by_id_document(doc, el.layer_id);
|
|
374
|
+
if (layer === null) {
|
|
375
|
+
return doc;
|
|
376
|
+
}
|
|
377
|
+
const new_elements = new Map(doc.elements);
|
|
378
|
+
new_elements.set(el.id, el);
|
|
379
|
+
const new_layers = doc.layers.map((l) => {
|
|
380
|
+
if (l.id === el.layer_id) {
|
|
381
|
+
return add_element_id_to_layer(l, el.id);
|
|
382
|
+
}
|
|
383
|
+
return l;
|
|
384
|
+
});
|
|
385
|
+
const new_bounds = calculate_bounds_document_internal(new_elements);
|
|
386
|
+
return {
|
|
387
|
+
id: doc.id,
|
|
388
|
+
elements: new_elements,
|
|
389
|
+
layers: new_layers,
|
|
390
|
+
active_layer_id: doc.active_layer_id,
|
|
391
|
+
assets: doc.assets,
|
|
392
|
+
bounds: new_bounds,
|
|
393
|
+
element_count: new_elements.size
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
function remove_element_document(doc, element_id) {
|
|
397
|
+
const el = doc.elements.get(element_id);
|
|
398
|
+
if (el === undefined) {
|
|
399
|
+
return doc;
|
|
400
|
+
}
|
|
401
|
+
const new_elements = new Map(doc.elements);
|
|
402
|
+
new_elements.delete(element_id);
|
|
403
|
+
const new_layers = doc.layers.map((l) => {
|
|
404
|
+
if (l.id === el.layer_id) {
|
|
405
|
+
return remove_element_id_from_layer(l, element_id);
|
|
406
|
+
}
|
|
407
|
+
return l;
|
|
408
|
+
});
|
|
409
|
+
const new_bounds = calculate_bounds_document_internal(new_elements);
|
|
410
|
+
return {
|
|
411
|
+
id: doc.id,
|
|
412
|
+
elements: new_elements,
|
|
413
|
+
layers: new_layers,
|
|
414
|
+
active_layer_id: doc.active_layer_id,
|
|
415
|
+
assets: doc.assets,
|
|
416
|
+
bounds: new_bounds,
|
|
417
|
+
element_count: new_elements.size
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function update_element_document(doc, element_id, new_element) {
|
|
421
|
+
if (!doc.elements.has(element_id)) {
|
|
422
|
+
return doc;
|
|
423
|
+
}
|
|
424
|
+
const old_element = doc.elements.get(element_id);
|
|
425
|
+
if (old_element === undefined) {
|
|
426
|
+
return doc;
|
|
427
|
+
}
|
|
428
|
+
const new_elements = new Map(doc.elements);
|
|
429
|
+
new_elements.set(element_id, new_element);
|
|
430
|
+
let new_layers = doc.layers;
|
|
431
|
+
if (old_element.layer_id !== new_element.layer_id) {
|
|
432
|
+
new_layers = doc.layers.map((l) => {
|
|
433
|
+
if (l.id === old_element.layer_id) {
|
|
434
|
+
return remove_element_id_from_layer(l, element_id);
|
|
435
|
+
}
|
|
436
|
+
if (l.id === new_element.layer_id) {
|
|
437
|
+
return add_element_id_to_layer(l, element_id);
|
|
438
|
+
}
|
|
439
|
+
return l;
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
const new_bounds = calculate_bounds_document_internal(new_elements);
|
|
443
|
+
return {
|
|
444
|
+
id: doc.id,
|
|
445
|
+
elements: new_elements,
|
|
446
|
+
layers: new_layers,
|
|
447
|
+
active_layer_id: doc.active_layer_id,
|
|
448
|
+
assets: doc.assets,
|
|
449
|
+
bounds: new_bounds,
|
|
450
|
+
element_count: new_elements.size
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
function add_layer_document(doc, layer) {
|
|
454
|
+
const existing = get_layer_by_id_document(doc, layer.id);
|
|
455
|
+
if (existing !== null) {
|
|
456
|
+
return doc;
|
|
457
|
+
}
|
|
458
|
+
return {
|
|
459
|
+
id: doc.id,
|
|
460
|
+
elements: doc.elements,
|
|
461
|
+
layers: [...doc.layers, layer],
|
|
462
|
+
active_layer_id: doc.active_layer_id,
|
|
463
|
+
assets: doc.assets,
|
|
464
|
+
bounds: doc.bounds,
|
|
465
|
+
element_count: doc.element_count
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
function remove_layer_document(doc, layer_id) {
|
|
469
|
+
const layer = get_layer_by_id_document(doc, layer_id);
|
|
470
|
+
if (layer === null) {
|
|
471
|
+
return doc;
|
|
472
|
+
}
|
|
473
|
+
const new_elements = new Map(doc.elements);
|
|
474
|
+
for (const element_id of layer.element_ids) {
|
|
475
|
+
new_elements.delete(element_id);
|
|
476
|
+
}
|
|
477
|
+
const new_layers = doc.layers.filter((l) => l.id !== layer_id);
|
|
478
|
+
let new_active_layer_id = doc.active_layer_id;
|
|
479
|
+
if (doc.active_layer_id === layer_id) {
|
|
480
|
+
if (new_layers.length > 0) {
|
|
481
|
+
const first_layer = new_layers[0];
|
|
482
|
+
if (first_layer !== undefined) {
|
|
483
|
+
new_active_layer_id = first_layer.id;
|
|
484
|
+
} else {
|
|
485
|
+
new_active_layer_id = "";
|
|
486
|
+
}
|
|
487
|
+
} else {
|
|
488
|
+
new_active_layer_id = "";
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
const new_bounds = calculate_bounds_document_internal(new_elements);
|
|
492
|
+
return {
|
|
493
|
+
id: doc.id,
|
|
494
|
+
elements: new_elements,
|
|
495
|
+
layers: new_layers,
|
|
496
|
+
active_layer_id: new_active_layer_id,
|
|
497
|
+
assets: doc.assets,
|
|
498
|
+
bounds: new_bounds,
|
|
499
|
+
element_count: new_elements.size
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
function reorder_layers_document(doc, layer_ids) {
|
|
503
|
+
const id_set = new Set(layer_ids);
|
|
504
|
+
const existing_ids = doc.layers.map((l) => l.id);
|
|
505
|
+
const missing_ids = existing_ids.filter((id) => !id_set.has(id));
|
|
506
|
+
const ordered_layers = [];
|
|
507
|
+
const layer_map = new Map(doc.layers.map((l) => [l.id, l]));
|
|
508
|
+
for (const id of layer_ids) {
|
|
509
|
+
const layer = layer_map.get(id);
|
|
510
|
+
if (layer) {
|
|
511
|
+
ordered_layers.push(layer);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
for (const id of missing_ids) {
|
|
515
|
+
const layer = layer_map.get(id);
|
|
516
|
+
if (layer) {
|
|
517
|
+
ordered_layers.push(layer);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return {
|
|
521
|
+
id: doc.id,
|
|
522
|
+
elements: doc.elements,
|
|
523
|
+
layers: ordered_layers,
|
|
524
|
+
active_layer_id: doc.active_layer_id,
|
|
525
|
+
assets: doc.assets,
|
|
526
|
+
bounds: doc.bounds,
|
|
527
|
+
element_count: doc.element_count
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
function set_active_layer_document(doc, layer_id) {
|
|
531
|
+
const layer = get_layer_by_id_document(doc, layer_id);
|
|
532
|
+
if (layer === null) {
|
|
533
|
+
return doc;
|
|
534
|
+
}
|
|
535
|
+
return {
|
|
536
|
+
id: doc.id,
|
|
537
|
+
elements: doc.elements,
|
|
538
|
+
layers: doc.layers,
|
|
539
|
+
active_layer_id: layer_id,
|
|
540
|
+
assets: doc.assets,
|
|
541
|
+
bounds: doc.bounds,
|
|
542
|
+
element_count: doc.element_count
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
function get_elements_in_layer_document(doc, layer_id) {
|
|
546
|
+
const layer = get_layer_by_id_document(doc, layer_id);
|
|
547
|
+
if (layer === null) {
|
|
548
|
+
return [];
|
|
549
|
+
}
|
|
550
|
+
const elements = [];
|
|
551
|
+
for (const element_id of layer.element_ids) {
|
|
552
|
+
const el = doc.elements.get(element_id);
|
|
553
|
+
if (el !== undefined) {
|
|
554
|
+
elements.push(el);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
return elements;
|
|
558
|
+
}
|
|
559
|
+
function get_all_elements_document(doc) {
|
|
560
|
+
return Array.from(doc.elements.values());
|
|
561
|
+
}
|
|
562
|
+
function calculate_bounds_document_internal(elements) {
|
|
563
|
+
if (elements.size === 0) {
|
|
564
|
+
return [0, 0, 0, 0];
|
|
565
|
+
}
|
|
566
|
+
let min_x = Infinity;
|
|
567
|
+
let min_y = Infinity;
|
|
568
|
+
let max_x = -Infinity;
|
|
569
|
+
let max_y = -Infinity;
|
|
570
|
+
for (const el of elements.values()) {
|
|
571
|
+
const bounds = el.bounds;
|
|
572
|
+
min_x = Math.min(min_x, bounds[0]);
|
|
573
|
+
min_y = Math.min(min_y, bounds[1]);
|
|
574
|
+
max_x = Math.max(max_x, bounds[0] + bounds[2]);
|
|
575
|
+
max_y = Math.max(max_y, bounds[1] + bounds[3]);
|
|
576
|
+
}
|
|
577
|
+
if (min_x === Infinity) {
|
|
578
|
+
return [0, 0, 0, 0];
|
|
579
|
+
}
|
|
580
|
+
return [min_x, min_y, max_x - min_x, max_y - min_y];
|
|
581
|
+
}
|
|
582
|
+
function recalculate_bounds_document(doc) {
|
|
583
|
+
const new_bounds = calculate_bounds_document_internal(doc.elements);
|
|
584
|
+
return {
|
|
585
|
+
id: doc.id,
|
|
586
|
+
elements: doc.elements,
|
|
587
|
+
layers: doc.layers,
|
|
588
|
+
active_layer_id: doc.active_layer_id,
|
|
589
|
+
assets: doc.assets,
|
|
590
|
+
bounds: new_bounds,
|
|
591
|
+
element_count: doc.element_count
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
function clear_document(doc) {
|
|
595
|
+
return {
|
|
596
|
+
id: doc.id,
|
|
597
|
+
elements: new Map,
|
|
598
|
+
layers: doc.layers.map((l) => ({ ...l, element_ids: [] })),
|
|
599
|
+
active_layer_id: doc.active_layer_id,
|
|
600
|
+
assets: doc.assets,
|
|
601
|
+
bounds: [0, 0, 0, 0],
|
|
602
|
+
element_count: 0
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
function add_image_asset_document(doc, asset) {
|
|
606
|
+
const assets = new Map(doc.assets);
|
|
607
|
+
assets.set(asset.id, { ...asset });
|
|
608
|
+
return {
|
|
609
|
+
...doc,
|
|
610
|
+
assets
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
function get_image_asset_document(doc, asset_id) {
|
|
614
|
+
return doc.assets.get(asset_id) ?? null;
|
|
615
|
+
}
|
|
616
|
+
function remove_image_asset_document(doc, asset_id) {
|
|
617
|
+
if (!doc.assets.has(asset_id))
|
|
618
|
+
return doc;
|
|
619
|
+
const assets = new Map(doc.assets);
|
|
620
|
+
assets.delete(asset_id);
|
|
621
|
+
return {
|
|
622
|
+
...doc,
|
|
623
|
+
assets
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// math/rectangle.ts
|
|
628
|
+
function create_rectangle(x = 0, y = 0, width = 0, height = 0) {
|
|
629
|
+
return [x, y, width, height];
|
|
630
|
+
}
|
|
631
|
+
function clone_rectangle(r) {
|
|
632
|
+
return [r[0], r[1], r[2], r[3]];
|
|
633
|
+
}
|
|
634
|
+
function from_center_rectangle(center, width, height, out) {
|
|
635
|
+
out[0] = center[0] - width / 2;
|
|
636
|
+
out[1] = center[1] - height / 2;
|
|
637
|
+
out[2] = width;
|
|
638
|
+
out[3] = height;
|
|
639
|
+
return out;
|
|
640
|
+
}
|
|
641
|
+
function top_of_rectangle(r) {
|
|
642
|
+
return r[1] + r[3];
|
|
643
|
+
}
|
|
644
|
+
function left_of_rectangle(r) {
|
|
645
|
+
return r[0];
|
|
646
|
+
}
|
|
647
|
+
function right_of_rectangle(r) {
|
|
648
|
+
return r[0] + r[2];
|
|
649
|
+
}
|
|
650
|
+
function bottom_of_rectangle(r) {
|
|
651
|
+
return r[1];
|
|
652
|
+
}
|
|
653
|
+
function center_of_rectangle(r, out) {
|
|
654
|
+
out[0] = r[0] + r[2] / 2;
|
|
655
|
+
out[1] = r[1] + r[3] / 2;
|
|
656
|
+
return out;
|
|
657
|
+
}
|
|
658
|
+
function width_of_rectangle(r) {
|
|
659
|
+
return r[2];
|
|
660
|
+
}
|
|
661
|
+
function height_of_rectangle(r) {
|
|
662
|
+
return r[3];
|
|
663
|
+
}
|
|
664
|
+
function area_of_rectangle(r) {
|
|
665
|
+
return r[2] * r[3];
|
|
666
|
+
}
|
|
667
|
+
function set_rectangle(x, y, width, height, out) {
|
|
668
|
+
out[0] = x;
|
|
669
|
+
out[1] = y;
|
|
670
|
+
out[2] = width;
|
|
671
|
+
out[3] = height;
|
|
672
|
+
return out;
|
|
673
|
+
}
|
|
674
|
+
function translate_rectangle(r, dx, dy, out) {
|
|
675
|
+
out[0] = r[0] + dx;
|
|
676
|
+
out[1] = r[1] + dy;
|
|
677
|
+
out[2] = r[2];
|
|
678
|
+
out[3] = r[3];
|
|
679
|
+
return out;
|
|
680
|
+
}
|
|
681
|
+
function scale_rectangle(r, scale, out) {
|
|
682
|
+
out[0] = r[0] * scale;
|
|
683
|
+
out[1] = r[1] * scale;
|
|
684
|
+
out[2] = r[2] * scale;
|
|
685
|
+
out[3] = r[3] * scale;
|
|
686
|
+
return out;
|
|
687
|
+
}
|
|
688
|
+
function contains_point_rectangle(r, x, y) {
|
|
689
|
+
return x >= r[0] && x <= r[0] + r[2] && y >= r[1] && y <= r[1] + r[3];
|
|
690
|
+
}
|
|
691
|
+
function contains_rectangle(r1, r2) {
|
|
692
|
+
return r2[0] >= r1[0] && r2[1] >= r1[1] && r2[0] + r2[2] <= r1[0] + r1[2] && r2[1] + r2[3] <= r1[1] + r1[3];
|
|
693
|
+
}
|
|
694
|
+
function intersects_rectangle(r1, r2) {
|
|
695
|
+
return r1[0] < r2[0] + r2[2] && r1[0] + r1[2] > r2[0] && r1[1] < r2[1] + r2[3] && r1[1] + r1[3] > r2[1];
|
|
696
|
+
}
|
|
697
|
+
function intersection_rectangle(r1, r2, out) {
|
|
698
|
+
const x1 = Math.max(r1[0], r2[0]);
|
|
699
|
+
const y1 = Math.max(r1[1], r2[1]);
|
|
700
|
+
const x2 = Math.min(r1[0] + r1[2], r2[0] + r2[2]);
|
|
701
|
+
const y2 = Math.min(r1[1] + r1[3], r2[1] + r2[3]);
|
|
702
|
+
if (x2 <= x1 || y2 <= y1) {
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
out[0] = x1;
|
|
706
|
+
out[1] = y1;
|
|
707
|
+
out[2] = x2 - x1;
|
|
708
|
+
out[3] = y2 - y1;
|
|
709
|
+
return out;
|
|
710
|
+
}
|
|
711
|
+
function union_rectangle(r1, r2, out) {
|
|
712
|
+
const x1 = Math.min(r1[0], r2[0]);
|
|
713
|
+
const y1 = Math.min(r1[1], r2[1]);
|
|
714
|
+
const x2 = Math.max(r1[0] + r1[2], r2[0] + r2[2]);
|
|
715
|
+
const y2 = Math.max(r1[1] + r1[3], r2[1] + r2[3]);
|
|
716
|
+
out[0] = x1;
|
|
717
|
+
out[1] = y1;
|
|
718
|
+
out[2] = x2 - x1;
|
|
719
|
+
out[3] = y2 - y1;
|
|
720
|
+
return out;
|
|
721
|
+
}
|
|
722
|
+
function expand_rectangle(r, amount, out) {
|
|
723
|
+
out[0] = r[0] - amount;
|
|
724
|
+
out[1] = r[1] - amount;
|
|
725
|
+
out[2] = r[2] + amount * 2;
|
|
726
|
+
out[3] = r[3] + amount * 2;
|
|
727
|
+
return out;
|
|
728
|
+
}
|
|
729
|
+
function is_empty_rectangle(r) {
|
|
730
|
+
return r[2] <= 0 || r[3] <= 0;
|
|
731
|
+
}
|
|
732
|
+
function equals_rectangle(r1, r2) {
|
|
733
|
+
return r1[0] === r2[0] && r1[1] === r2[1] && r1[2] === r2[2] && r1[3] === r2[3];
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// math/transform.ts
|
|
737
|
+
function create_transform(a = 1, b = 0, c = 0, d = 1, tx = 0, ty = 0) {
|
|
738
|
+
return [a, b, c, d, tx, ty];
|
|
739
|
+
}
|
|
740
|
+
function clone_transform(t) {
|
|
741
|
+
return [t[0], t[1], t[2], t[3], t[4], t[5]];
|
|
742
|
+
}
|
|
743
|
+
function set_transform(a, b, c, d, tx, ty, out) {
|
|
744
|
+
out[0] = a;
|
|
745
|
+
out[1] = b;
|
|
746
|
+
out[2] = c;
|
|
747
|
+
out[3] = d;
|
|
748
|
+
out[4] = tx;
|
|
749
|
+
out[5] = ty;
|
|
750
|
+
return out;
|
|
751
|
+
}
|
|
752
|
+
function copy_transform(t, out) {
|
|
753
|
+
out[0] = t[0];
|
|
754
|
+
out[1] = t[1];
|
|
755
|
+
out[2] = t[2];
|
|
756
|
+
out[3] = t[3];
|
|
757
|
+
out[4] = t[4];
|
|
758
|
+
out[5] = t[5];
|
|
759
|
+
return out;
|
|
760
|
+
}
|
|
761
|
+
function identity_transform() {
|
|
762
|
+
return [1, 0, 0, 1, 0, 0];
|
|
763
|
+
}
|
|
764
|
+
function set_identity_transform(out) {
|
|
765
|
+
out[0] = 1;
|
|
766
|
+
out[1] = 0;
|
|
767
|
+
out[2] = 0;
|
|
768
|
+
out[3] = 1;
|
|
769
|
+
out[4] = 0;
|
|
770
|
+
out[5] = 0;
|
|
771
|
+
return out;
|
|
772
|
+
}
|
|
773
|
+
function translate_transform(x, y) {
|
|
774
|
+
return [1, 0, 0, 1, x, y];
|
|
775
|
+
}
|
|
776
|
+
function scale_transform(sx, sy = sx) {
|
|
777
|
+
return [sx, 0, 0, sy, 0, 0];
|
|
778
|
+
}
|
|
779
|
+
function rotate_transform(angle) {
|
|
780
|
+
const cos = Math.cos(angle);
|
|
781
|
+
const sin = Math.sin(angle);
|
|
782
|
+
return [cos, sin, -sin, cos, 0, 0];
|
|
783
|
+
}
|
|
784
|
+
function from_trs_transform(translation, rotation, scale) {
|
|
785
|
+
const cos = Math.cos(rotation);
|
|
786
|
+
const sin = Math.sin(rotation);
|
|
787
|
+
return [
|
|
788
|
+
cos * scale[0],
|
|
789
|
+
sin * scale[0],
|
|
790
|
+
-sin * scale[1],
|
|
791
|
+
cos * scale[1],
|
|
792
|
+
translation[0],
|
|
793
|
+
translation[1]
|
|
794
|
+
];
|
|
795
|
+
}
|
|
796
|
+
function multiply_transform(t1, t2, out) {
|
|
797
|
+
out[0] = t1[0] * t2[0] + t1[2] * t2[1];
|
|
798
|
+
out[1] = t1[1] * t2[0] + t1[3] * t2[1];
|
|
799
|
+
out[2] = t1[0] * t2[2] + t1[2] * t2[3];
|
|
800
|
+
out[3] = t1[1] * t2[2] + t1[3] * t2[3];
|
|
801
|
+
out[4] = t1[0] * t2[4] + t1[2] * t2[5] + t1[4];
|
|
802
|
+
out[5] = t1[1] * t2[4] + t1[3] * t2[5] + t1[5];
|
|
803
|
+
return out;
|
|
804
|
+
}
|
|
805
|
+
function determinant_transform(t) {
|
|
806
|
+
return t[0] * t[3] - t[1] * t[2];
|
|
807
|
+
}
|
|
808
|
+
function invert_transform(t, out) {
|
|
809
|
+
const det = determinant_transform(t);
|
|
810
|
+
if (Math.abs(det) < 0.0000000001) {
|
|
811
|
+
return null;
|
|
812
|
+
}
|
|
813
|
+
const inverse_det = 1 / det;
|
|
814
|
+
out[0] = t[3] * inverse_det;
|
|
815
|
+
out[1] = -t[1] * inverse_det;
|
|
816
|
+
out[2] = -t[2] * inverse_det;
|
|
817
|
+
out[3] = t[0] * inverse_det;
|
|
818
|
+
out[4] = (t[2] * t[5] - t[3] * t[4]) * inverse_det;
|
|
819
|
+
out[5] = (t[1] * t[4] - t[0] * t[5]) * inverse_det;
|
|
820
|
+
return out;
|
|
821
|
+
}
|
|
822
|
+
function transform_point_transform(t, point, out) {
|
|
823
|
+
out[0] = t[0] * point[0] + t[2] * point[1] + t[4];
|
|
824
|
+
out[1] = t[1] * point[0] + t[3] * point[1] + t[5];
|
|
825
|
+
return out;
|
|
826
|
+
}
|
|
827
|
+
function transform_vector_transform(t, vector, out) {
|
|
828
|
+
out[0] = t[0] * vector[0] + t[2] * vector[1];
|
|
829
|
+
out[1] = t[1] * vector[0] + t[3] * vector[1];
|
|
830
|
+
return out;
|
|
831
|
+
}
|
|
832
|
+
function append_translation_transform(t, x, y, out) {
|
|
833
|
+
const translation = translate_transform(x, y);
|
|
834
|
+
return multiply_transform(t, translation, out);
|
|
835
|
+
}
|
|
836
|
+
function append_rotation_transform(t, angle, out) {
|
|
837
|
+
const rotation = rotate_transform(angle);
|
|
838
|
+
return multiply_transform(t, rotation, out);
|
|
839
|
+
}
|
|
840
|
+
function append_scale_transform(t, sx, sy = sx, out) {
|
|
841
|
+
const scale = scale_transform(sx, sy);
|
|
842
|
+
return multiply_transform(t, scale, out);
|
|
843
|
+
}
|
|
844
|
+
function prepend_translation_transform(x, y, t, out) {
|
|
845
|
+
const translation = translate_transform(x, y);
|
|
846
|
+
return multiply_transform(translation, t, out);
|
|
847
|
+
}
|
|
848
|
+
function prepend_rotation_transform(angle, t, out) {
|
|
849
|
+
const rotation = rotate_transform(angle);
|
|
850
|
+
return multiply_transform(rotation, t, out);
|
|
851
|
+
}
|
|
852
|
+
function prepend_scale_transform(sx, sy = sx, t, out) {
|
|
853
|
+
const scale = scale_transform(sx, sy);
|
|
854
|
+
return multiply_transform(scale, t, out);
|
|
855
|
+
}
|
|
856
|
+
function equals_transform(t1, t2, epsilon = 0.0001) {
|
|
857
|
+
return Math.abs(t1[0] - t2[0]) < epsilon && Math.abs(t1[1] - t2[1]) < epsilon && Math.abs(t1[2] - t2[2]) < epsilon && Math.abs(t1[3] - t2[3]) < epsilon && Math.abs(t1[4] - t2[4]) < epsilon && Math.abs(t1[5] - t2[5]) < epsilon;
|
|
858
|
+
}
|
|
859
|
+
function is_identity_transform(t, epsilon = 0.0001) {
|
|
860
|
+
const identity = identity_transform();
|
|
861
|
+
return equals_transform(t, identity, epsilon);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// renderer/path.ts
|
|
865
|
+
var path_move = 0;
|
|
866
|
+
var path_line = 1;
|
|
867
|
+
var path_cubic = 2;
|
|
868
|
+
var path_close = 3;
|
|
869
|
+
function create_path() {
|
|
870
|
+
return {
|
|
871
|
+
commands: [],
|
|
872
|
+
bounds: create_rectangle(0, 0, 0, 0)
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
function clone_path(p) {
|
|
876
|
+
return {
|
|
877
|
+
commands: [...p.commands],
|
|
878
|
+
bounds: [...p.bounds]
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
function move_to_path(p, x, y) {
|
|
882
|
+
const new_commands = [...p.commands, [path_move, x, y]];
|
|
883
|
+
return {
|
|
884
|
+
commands: new_commands,
|
|
885
|
+
bounds: calculate_bounds_of_path(new_commands)
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
function line_to_path(p, x, y) {
|
|
889
|
+
const new_commands = [...p.commands, [path_line, x, y]];
|
|
890
|
+
return {
|
|
891
|
+
commands: new_commands,
|
|
892
|
+
bounds: calculate_bounds_of_path(new_commands)
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
function cubic_to_path(p, c1x, c1y, c2x, c2y, x, y) {
|
|
896
|
+
const new_commands = [...p.commands, [path_cubic, c1x, c1y, c2x, c2y, x, y]];
|
|
897
|
+
return {
|
|
898
|
+
commands: new_commands,
|
|
899
|
+
bounds: calculate_bounds_of_path(new_commands)
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
function close_path(p) {
|
|
903
|
+
const new_commands = [...p.commands, [path_close]];
|
|
904
|
+
return {
|
|
905
|
+
commands: new_commands,
|
|
906
|
+
bounds: p.bounds
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
function bounds_of_path(p) {
|
|
910
|
+
return p.bounds;
|
|
911
|
+
}
|
|
912
|
+
function is_empty_path(p) {
|
|
913
|
+
return p.commands.length === 0;
|
|
914
|
+
}
|
|
915
|
+
function command_count_of_path(p) {
|
|
916
|
+
return p.commands.length;
|
|
917
|
+
}
|
|
918
|
+
function transform_path(p, t, out) {
|
|
919
|
+
const transformed_commands = [];
|
|
920
|
+
for (let i = 0;i < p.commands.length; i = i + 1) {
|
|
921
|
+
const cmd = p.commands[i];
|
|
922
|
+
if (cmd !== undefined) {
|
|
923
|
+
transformed_commands.push(transform_command(cmd, t));
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
const result = {
|
|
927
|
+
commands: transformed_commands,
|
|
928
|
+
bounds: calculate_bounds_of_path(transformed_commands)
|
|
929
|
+
};
|
|
930
|
+
Object.assign(out, result);
|
|
931
|
+
return out;
|
|
932
|
+
}
|
|
933
|
+
function hash_of_path(p) {
|
|
934
|
+
let hash = 0;
|
|
935
|
+
for (let i = 0;i < p.commands.length; i = i + 1) {
|
|
936
|
+
const cmd = p.commands[i];
|
|
937
|
+
if (cmd !== undefined) {
|
|
938
|
+
hash = hash * 31 + hash_command(cmd);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
return hash;
|
|
942
|
+
}
|
|
943
|
+
function from_rectangle_path(r) {
|
|
944
|
+
const commands = [
|
|
945
|
+
[path_move, r[0], r[1]],
|
|
946
|
+
[path_line, r[0] + r[2], r[1]],
|
|
947
|
+
[path_line, r[0] + r[2], r[1] + r[3]],
|
|
948
|
+
[path_line, r[0], r[1] + r[3]],
|
|
949
|
+
[path_close]
|
|
950
|
+
];
|
|
951
|
+
return {
|
|
952
|
+
commands,
|
|
953
|
+
bounds: [...r]
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
function from_circle_path(c, segments) {
|
|
957
|
+
const commands = [];
|
|
958
|
+
const cx = c[0];
|
|
959
|
+
const cy = c[1];
|
|
960
|
+
const r = c[2];
|
|
961
|
+
if (segments < 3) {
|
|
962
|
+
return create_path();
|
|
963
|
+
}
|
|
964
|
+
const kappa = 4 / 3 * Math.tan(Math.PI / (2 * segments));
|
|
965
|
+
for (let i = 0;i < segments; i = i + 1) {
|
|
966
|
+
const angle1 = 2 * Math.PI * i / segments;
|
|
967
|
+
const angle2 = 2 * Math.PI * (i + 1) / segments;
|
|
968
|
+
const x1 = cx + r * Math.cos(angle1);
|
|
969
|
+
const y1 = cy + r * Math.sin(angle1);
|
|
970
|
+
const x2 = cx + r * Math.cos(angle2);
|
|
971
|
+
const y2 = cy + r * Math.sin(angle2);
|
|
972
|
+
const cos1 = Math.cos(angle1);
|
|
973
|
+
const sin1 = Math.sin(angle1);
|
|
974
|
+
const cos2 = Math.cos(angle2);
|
|
975
|
+
const sin2 = Math.sin(angle2);
|
|
976
|
+
const c1x = x1 - r * kappa * sin1;
|
|
977
|
+
const c1y = y1 + r * kappa * cos1;
|
|
978
|
+
const c2x = x2 + r * kappa * sin2;
|
|
979
|
+
const c2y = y2 - r * kappa * cos2;
|
|
980
|
+
if (i === 0) {
|
|
981
|
+
commands.push([path_move, x1, y1]);
|
|
982
|
+
}
|
|
983
|
+
commands.push([path_cubic, c1x, c1y, c2x, c2y, x2, y2]);
|
|
984
|
+
}
|
|
985
|
+
commands.push([path_close]);
|
|
986
|
+
const bounds = create_rectangle(cx - r, cy - r, r * 2, r * 2);
|
|
987
|
+
return {
|
|
988
|
+
commands,
|
|
989
|
+
bounds
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
function from_polyline_path(points, closed) {
|
|
993
|
+
if (points.length === 0) {
|
|
994
|
+
return create_path();
|
|
995
|
+
}
|
|
996
|
+
const commands = [];
|
|
997
|
+
const first_point = points[0];
|
|
998
|
+
if (first_point !== undefined) {
|
|
999
|
+
commands.push([path_move, first_point[0], first_point[1]]);
|
|
1000
|
+
}
|
|
1001
|
+
for (let i = 1;i < points.length; i = i + 1) {
|
|
1002
|
+
const point = points[i];
|
|
1003
|
+
if (point !== undefined) {
|
|
1004
|
+
commands.push([path_line, point[0], point[1]]);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
if (closed && points.length > 2) {
|
|
1008
|
+
commands.push([path_close]);
|
|
1009
|
+
}
|
|
1010
|
+
return {
|
|
1011
|
+
commands,
|
|
1012
|
+
bounds: calculate_bounds_of_path(commands)
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
function calculate_bounds_of_path(commands) {
|
|
1016
|
+
if (commands.length === 0) {
|
|
1017
|
+
return create_rectangle(0, 0, 0, 0);
|
|
1018
|
+
}
|
|
1019
|
+
let min_x = Infinity;
|
|
1020
|
+
let min_y = Infinity;
|
|
1021
|
+
let max_x = -Infinity;
|
|
1022
|
+
let max_y = -Infinity;
|
|
1023
|
+
for (let i = 0;i < commands.length; i = i + 1) {
|
|
1024
|
+
const cmd = commands[i];
|
|
1025
|
+
if (cmd === undefined) {
|
|
1026
|
+
continue;
|
|
1027
|
+
}
|
|
1028
|
+
const type = cmd[0];
|
|
1029
|
+
if (type === path_move || type === path_line) {
|
|
1030
|
+
const cmd_move_line = cmd;
|
|
1031
|
+
const x = cmd_move_line[1];
|
|
1032
|
+
const y = cmd_move_line[2];
|
|
1033
|
+
min_x = Math.min(min_x, x);
|
|
1034
|
+
min_y = Math.min(min_y, y);
|
|
1035
|
+
max_x = Math.max(max_x, x);
|
|
1036
|
+
max_y = Math.max(max_y, y);
|
|
1037
|
+
} else if (type === path_cubic) {
|
|
1038
|
+
const cmd_cubic = cmd;
|
|
1039
|
+
const c1x = cmd_cubic[1];
|
|
1040
|
+
const c1y = cmd_cubic[2];
|
|
1041
|
+
const c2x = cmd_cubic[3];
|
|
1042
|
+
const c2y = cmd_cubic[4];
|
|
1043
|
+
const x = cmd_cubic[5];
|
|
1044
|
+
const y = cmd_cubic[6];
|
|
1045
|
+
min_x = Math.min(min_x, c1x, c2x, x);
|
|
1046
|
+
min_y = Math.min(min_y, c1y, c2y, y);
|
|
1047
|
+
max_x = Math.max(max_x, c1x, c2x, x);
|
|
1048
|
+
max_y = Math.max(max_y, c1y, c2y, y);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
if (min_x === Infinity) {
|
|
1052
|
+
return create_rectangle(0, 0, 0, 0);
|
|
1053
|
+
}
|
|
1054
|
+
return create_rectangle(min_x, min_y, max_x - min_x, max_y - min_y);
|
|
1055
|
+
}
|
|
1056
|
+
function transform_command(cmd, t) {
|
|
1057
|
+
const type = cmd[0];
|
|
1058
|
+
if (type === path_move || type === path_line) {
|
|
1059
|
+
const cmd_move_line = cmd;
|
|
1060
|
+
const p1 = [cmd_move_line[1], cmd_move_line[2]];
|
|
1061
|
+
const out1 = [0, 0];
|
|
1062
|
+
transform_point_transform(t, p1, out1);
|
|
1063
|
+
return [type, out1[0], out1[1]];
|
|
1064
|
+
} else if (type === path_cubic) {
|
|
1065
|
+
const cmd_cubic = cmd;
|
|
1066
|
+
const p1 = [cmd_cubic[1], cmd_cubic[2]];
|
|
1067
|
+
const p2 = [cmd_cubic[3], cmd_cubic[4]];
|
|
1068
|
+
const p3 = [cmd_cubic[5], cmd_cubic[6]];
|
|
1069
|
+
const out1 = [0, 0];
|
|
1070
|
+
const out2 = [0, 0];
|
|
1071
|
+
const out3 = [0, 0];
|
|
1072
|
+
transform_point_transform(t, p1, out1);
|
|
1073
|
+
transform_point_transform(t, p2, out2);
|
|
1074
|
+
transform_point_transform(t, p3, out3);
|
|
1075
|
+
return [path_cubic, out1[0], out1[1], out2[0], out2[1], out3[0], out3[1]];
|
|
1076
|
+
} else {
|
|
1077
|
+
return cmd;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
function hash_command(cmd) {
|
|
1081
|
+
const type = cmd[0];
|
|
1082
|
+
let hash = type * 31;
|
|
1083
|
+
if (type === path_move || type === path_line) {
|
|
1084
|
+
const cmd_move_line = cmd;
|
|
1085
|
+
hash = hash * 31 + Math.floor(cmd_move_line[1] * 1000);
|
|
1086
|
+
hash = hash * 31 + Math.floor(cmd_move_line[2] * 1000);
|
|
1087
|
+
} else if (type === path_cubic) {
|
|
1088
|
+
const cmd_cubic = cmd;
|
|
1089
|
+
hash = hash * 31 + Math.floor(cmd_cubic[1] * 1000);
|
|
1090
|
+
hash = hash * 31 + Math.floor(cmd_cubic[2] * 1000);
|
|
1091
|
+
hash = hash * 31 + Math.floor(cmd_cubic[3] * 1000);
|
|
1092
|
+
hash = hash * 31 + Math.floor(cmd_cubic[4] * 1000);
|
|
1093
|
+
hash = hash * 31 + Math.floor(cmd_cubic[5] * 1000);
|
|
1094
|
+
hash = hash * 31 + Math.floor(cmd_cubic[6] * 1000);
|
|
1095
|
+
}
|
|
1096
|
+
return hash;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// renderer/canvas/paths.ts
|
|
1100
|
+
function get_or_create_path2d_canvas(state, p) {
|
|
1101
|
+
const hash = hash_of_path(p);
|
|
1102
|
+
const cached = state.path_cache.get(hash);
|
|
1103
|
+
if (cached !== undefined && paths_equal(cached.path_commands, p.commands)) {
|
|
1104
|
+
state.cache_access_counter = state.cache_access_counter + 1;
|
|
1105
|
+
cached.last_used = state.cache_access_counter;
|
|
1106
|
+
return cached.path2d;
|
|
1107
|
+
}
|
|
1108
|
+
const path2d = build_path2d_canvas(p);
|
|
1109
|
+
if (state.path_cache.size >= state.cache_max_size) {
|
|
1110
|
+
evict_lru_canvas(state);
|
|
1111
|
+
}
|
|
1112
|
+
state.cache_access_counter = state.cache_access_counter + 1;
|
|
1113
|
+
state.path_cache.set(hash, {
|
|
1114
|
+
path2d,
|
|
1115
|
+
last_used: state.cache_access_counter,
|
|
1116
|
+
path_commands: p.commands
|
|
1117
|
+
});
|
|
1118
|
+
return path2d;
|
|
1119
|
+
}
|
|
1120
|
+
function paths_equal(a, b) {
|
|
1121
|
+
if (a.length !== b.length) {
|
|
1122
|
+
return false;
|
|
1123
|
+
}
|
|
1124
|
+
for (let i = 0;i < a.length; i = i + 1) {
|
|
1125
|
+
const cmd_a = a[i];
|
|
1126
|
+
const cmd_b = b[i];
|
|
1127
|
+
if (cmd_a === undefined || cmd_b === undefined) {
|
|
1128
|
+
return false;
|
|
1129
|
+
}
|
|
1130
|
+
if (cmd_a.length !== cmd_b.length) {
|
|
1131
|
+
return false;
|
|
1132
|
+
}
|
|
1133
|
+
for (let j = 0;j < cmd_a.length; j = j + 1) {
|
|
1134
|
+
if (cmd_a[j] !== cmd_b[j]) {
|
|
1135
|
+
return false;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
return true;
|
|
1140
|
+
}
|
|
1141
|
+
function build_path2d_canvas(p) {
|
|
1142
|
+
const path2d = new Path2D;
|
|
1143
|
+
for (let i = 0;i < p.commands.length; i = i + 1) {
|
|
1144
|
+
const cmd = p.commands[i];
|
|
1145
|
+
if (cmd === undefined) {
|
|
1146
|
+
continue;
|
|
1147
|
+
}
|
|
1148
|
+
const type = cmd[0];
|
|
1149
|
+
if (type === path_move) {
|
|
1150
|
+
path2d.moveTo(cmd[1], cmd[2]);
|
|
1151
|
+
} else if (type === path_line) {
|
|
1152
|
+
path2d.lineTo(cmd[1], cmd[2]);
|
|
1153
|
+
} else if (type === path_cubic) {
|
|
1154
|
+
path2d.bezierCurveTo(cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
|
|
1155
|
+
} else if (type === path_close) {
|
|
1156
|
+
path2d.closePath();
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
return path2d;
|
|
1160
|
+
}
|
|
1161
|
+
function evict_lru_canvas(state) {
|
|
1162
|
+
let oldest_key = null;
|
|
1163
|
+
let oldest_access = Infinity;
|
|
1164
|
+
for (const [key, entry] of state.path_cache) {
|
|
1165
|
+
if (entry.last_used < oldest_access) {
|
|
1166
|
+
oldest_access = entry.last_used;
|
|
1167
|
+
oldest_key = key;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
if (oldest_key !== null) {
|
|
1171
|
+
state.path_cache.delete(oldest_key);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
// math/color.ts
|
|
1176
|
+
function create_color(r = 0, g = 0, b = 0, a = 1) {
|
|
1177
|
+
return [
|
|
1178
|
+
Math.max(0, Math.min(1, r)),
|
|
1179
|
+
Math.max(0, Math.min(1, g)),
|
|
1180
|
+
Math.max(0, Math.min(1, b)),
|
|
1181
|
+
Math.max(0, Math.min(1, a))
|
|
1182
|
+
];
|
|
1183
|
+
}
|
|
1184
|
+
function clone_color(c) {
|
|
1185
|
+
return [c[0], c[1], c[2], c[3]];
|
|
1186
|
+
}
|
|
1187
|
+
function set_color(r, g, b, a = 1, out) {
|
|
1188
|
+
out[0] = Math.max(0, Math.min(1, r));
|
|
1189
|
+
out[1] = Math.max(0, Math.min(1, g));
|
|
1190
|
+
out[2] = Math.max(0, Math.min(1, b));
|
|
1191
|
+
out[3] = Math.max(0, Math.min(1, a));
|
|
1192
|
+
return out;
|
|
1193
|
+
}
|
|
1194
|
+
function copy_color(c, out) {
|
|
1195
|
+
out[0] = c[0];
|
|
1196
|
+
out[1] = c[1];
|
|
1197
|
+
out[2] = c[2];
|
|
1198
|
+
out[3] = c[3];
|
|
1199
|
+
return out;
|
|
1200
|
+
}
|
|
1201
|
+
function from_rgb_color(r, g, b, a = 255) {
|
|
1202
|
+
return create_color(r / 255, g / 255, b / 255, a / 255);
|
|
1203
|
+
}
|
|
1204
|
+
function from_hex_color(hex) {
|
|
1205
|
+
const cleaned = hex.replace("#", "");
|
|
1206
|
+
const r = parseInt(cleaned.substring(0, 2), 16) / 255;
|
|
1207
|
+
const g = parseInt(cleaned.substring(2, 4), 16) / 255;
|
|
1208
|
+
const b = parseInt(cleaned.substring(4, 6), 16) / 255;
|
|
1209
|
+
const a = cleaned.length === 8 ? parseInt(cleaned.substring(6, 8), 16) / 255 : 1;
|
|
1210
|
+
return create_color(r, g, b, a);
|
|
1211
|
+
}
|
|
1212
|
+
function to_rgba_string_color(c) {
|
|
1213
|
+
return `rgba(${Math.round(c[0] * 255)}, ${Math.round(c[1] * 255)}, ${Math.round(c[2] * 255)}, ${c[3]})`;
|
|
1214
|
+
}
|
|
1215
|
+
function to_hex_string_color(c) {
|
|
1216
|
+
const r = Math.round(c[0] * 255).toString(16).padStart(2, "0");
|
|
1217
|
+
const g = Math.round(c[1] * 255).toString(16).padStart(2, "0");
|
|
1218
|
+
const b = Math.round(c[2] * 255).toString(16).padStart(2, "0");
|
|
1219
|
+
const a = Math.round(c[3] * 255).toString(16).padStart(2, "0");
|
|
1220
|
+
return c[3] === 1 ? `#${r}${g}${b}` : `#${r}${g}${b}${a}`;
|
|
1221
|
+
}
|
|
1222
|
+
function lerp_color(c1, c2, t, out) {
|
|
1223
|
+
out[0] = c1[0] + (c2[0] - c1[0]) * t;
|
|
1224
|
+
out[1] = c1[1] + (c2[1] - c1[1]) * t;
|
|
1225
|
+
out[2] = c1[2] + (c2[2] - c1[2]) * t;
|
|
1226
|
+
out[3] = c1[3] + (c2[3] - c1[3]) * t;
|
|
1227
|
+
return out;
|
|
1228
|
+
}
|
|
1229
|
+
function equals_color(c1, c2, epsilon = 0.0001) {
|
|
1230
|
+
return Math.abs(c1[0] - c2[0]) < epsilon && Math.abs(c1[1] - c2[1]) < epsilon && Math.abs(c1[2] - c2[2]) < epsilon && Math.abs(c1[3] - c2[3]) < epsilon;
|
|
1231
|
+
}
|
|
1232
|
+
function white_color() {
|
|
1233
|
+
return [1, 1, 1, 1];
|
|
1234
|
+
}
|
|
1235
|
+
function black_color() {
|
|
1236
|
+
return [0, 0, 0, 1];
|
|
1237
|
+
}
|
|
1238
|
+
function transparent_color() {
|
|
1239
|
+
return [0, 0, 0, 0];
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
// renderer/style.ts
|
|
1243
|
+
var line_cap_butt = 0;
|
|
1244
|
+
var line_cap_round = 1;
|
|
1245
|
+
var line_cap_square = 2;
|
|
1246
|
+
var line_join_miter = 0;
|
|
1247
|
+
var line_join_round = 1;
|
|
1248
|
+
var line_join_bevel = 2;
|
|
1249
|
+
function create_draw_style() {
|
|
1250
|
+
return {
|
|
1251
|
+
fill: null,
|
|
1252
|
+
stroke: null,
|
|
1253
|
+
stroke_width: 1,
|
|
1254
|
+
line_cap: line_cap_round,
|
|
1255
|
+
line_join: line_join_round,
|
|
1256
|
+
miter_limit: 10,
|
|
1257
|
+
alpha: 1
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
function clone_draw_style(s) {
|
|
1261
|
+
return {
|
|
1262
|
+
fill: s.fill,
|
|
1263
|
+
stroke: s.stroke,
|
|
1264
|
+
stroke_width: s.stroke_width,
|
|
1265
|
+
line_cap: s.line_cap,
|
|
1266
|
+
line_join: s.line_join,
|
|
1267
|
+
miter_limit: s.miter_limit,
|
|
1268
|
+
alpha: s.alpha
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
function copy_draw_style(s, out) {
|
|
1272
|
+
const mutable_out = out;
|
|
1273
|
+
mutable_out.fill = s.fill;
|
|
1274
|
+
mutable_out.stroke = s.stroke;
|
|
1275
|
+
mutable_out.stroke_width = s.stroke_width;
|
|
1276
|
+
mutable_out.line_cap = s.line_cap;
|
|
1277
|
+
mutable_out.line_join = s.line_join;
|
|
1278
|
+
mutable_out.miter_limit = s.miter_limit;
|
|
1279
|
+
mutable_out.alpha = s.alpha;
|
|
1280
|
+
return out;
|
|
1281
|
+
}
|
|
1282
|
+
function set_draw_style(fill, stroke, stroke_width, line_cap, line_join, miter_limit, alpha, out) {
|
|
1283
|
+
const mutable_out = out;
|
|
1284
|
+
mutable_out.fill = fill;
|
|
1285
|
+
mutable_out.stroke = stroke;
|
|
1286
|
+
mutable_out.stroke_width = stroke_width;
|
|
1287
|
+
mutable_out.line_cap = line_cap;
|
|
1288
|
+
mutable_out.line_join = line_join;
|
|
1289
|
+
mutable_out.miter_limit = miter_limit;
|
|
1290
|
+
mutable_out.alpha = alpha;
|
|
1291
|
+
return out;
|
|
1292
|
+
}
|
|
1293
|
+
function with_fill_draw_style(s, fill, out) {
|
|
1294
|
+
const mutable_out = out;
|
|
1295
|
+
mutable_out.fill = fill;
|
|
1296
|
+
mutable_out.stroke = s.stroke;
|
|
1297
|
+
mutable_out.stroke_width = s.stroke_width;
|
|
1298
|
+
mutable_out.line_cap = s.line_cap;
|
|
1299
|
+
mutable_out.line_join = s.line_join;
|
|
1300
|
+
mutable_out.miter_limit = s.miter_limit;
|
|
1301
|
+
mutable_out.alpha = s.alpha;
|
|
1302
|
+
return out;
|
|
1303
|
+
}
|
|
1304
|
+
function with_stroke_draw_style(s, stroke, stroke_width, out) {
|
|
1305
|
+
const mutable_out = out;
|
|
1306
|
+
mutable_out.fill = s.fill;
|
|
1307
|
+
mutable_out.stroke = stroke;
|
|
1308
|
+
mutable_out.stroke_width = stroke_width;
|
|
1309
|
+
mutable_out.line_cap = s.line_cap;
|
|
1310
|
+
mutable_out.line_join = s.line_join;
|
|
1311
|
+
mutable_out.miter_limit = s.miter_limit;
|
|
1312
|
+
mutable_out.alpha = s.alpha;
|
|
1313
|
+
return out;
|
|
1314
|
+
}
|
|
1315
|
+
function with_alpha_draw_style(s, alpha, out) {
|
|
1316
|
+
const mutable_out = out;
|
|
1317
|
+
mutable_out.fill = s.fill;
|
|
1318
|
+
mutable_out.stroke = s.stroke;
|
|
1319
|
+
mutable_out.stroke_width = s.stroke_width;
|
|
1320
|
+
mutable_out.line_cap = s.line_cap;
|
|
1321
|
+
mutable_out.line_join = s.line_join;
|
|
1322
|
+
mutable_out.miter_limit = s.miter_limit;
|
|
1323
|
+
mutable_out.alpha = alpha;
|
|
1324
|
+
return out;
|
|
1325
|
+
}
|
|
1326
|
+
function with_line_cap_draw_style(s, line_cap, out) {
|
|
1327
|
+
const mutable_out = out;
|
|
1328
|
+
mutable_out.fill = s.fill;
|
|
1329
|
+
mutable_out.stroke = s.stroke;
|
|
1330
|
+
mutable_out.stroke_width = s.stroke_width;
|
|
1331
|
+
mutable_out.line_cap = line_cap;
|
|
1332
|
+
mutable_out.line_join = s.line_join;
|
|
1333
|
+
mutable_out.miter_limit = s.miter_limit;
|
|
1334
|
+
mutable_out.alpha = s.alpha;
|
|
1335
|
+
return out;
|
|
1336
|
+
}
|
|
1337
|
+
function with_line_join_draw_style(s, line_join, miter_limit, out) {
|
|
1338
|
+
const mutable_out = out;
|
|
1339
|
+
mutable_out.fill = s.fill;
|
|
1340
|
+
mutable_out.stroke = s.stroke;
|
|
1341
|
+
mutable_out.stroke_width = s.stroke_width;
|
|
1342
|
+
mutable_out.line_cap = s.line_cap;
|
|
1343
|
+
mutable_out.line_join = line_join;
|
|
1344
|
+
mutable_out.miter_limit = miter_limit;
|
|
1345
|
+
mutable_out.alpha = s.alpha;
|
|
1346
|
+
return out;
|
|
1347
|
+
}
|
|
1348
|
+
function equals_draw_style(a, b) {
|
|
1349
|
+
const fill_equal = a.fill === null && b.fill === null || a.fill !== null && b.fill !== null && equals_color(a.fill, b.fill);
|
|
1350
|
+
const stroke_equal = a.stroke === null && b.stroke === null || a.stroke !== null && b.stroke !== null && equals_color(a.stroke, b.stroke);
|
|
1351
|
+
return fill_equal && stroke_equal && a.stroke_width === b.stroke_width && a.line_cap === b.line_cap && a.line_join === b.line_join && a.miter_limit === b.miter_limit && a.alpha === b.alpha;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
// renderer/canvas/draw.ts
|
|
1355
|
+
function draw_path_canvas(state, p, s) {
|
|
1356
|
+
const path2d = get_or_create_path2d_canvas(state, p);
|
|
1357
|
+
apply_style_canvas(state.ctx, s);
|
|
1358
|
+
if (s.fill !== null) {
|
|
1359
|
+
state.ctx.fill(path2d);
|
|
1360
|
+
}
|
|
1361
|
+
if (s.stroke !== null && s.stroke_width > 0) {
|
|
1362
|
+
state.ctx.stroke(path2d);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
function draw_rectangle_canvas(state, r, s) {
|
|
1366
|
+
apply_style_canvas(state.ctx, s);
|
|
1367
|
+
if (s.fill !== null) {
|
|
1368
|
+
state.ctx.fillRect(r[0], r[1], r[2], r[3]);
|
|
1369
|
+
}
|
|
1370
|
+
if (s.stroke !== null && s.stroke_width > 0) {
|
|
1371
|
+
state.ctx.strokeRect(r[0], r[1], r[2], r[3]);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
function draw_circle_canvas(state, c, s) {
|
|
1375
|
+
state.ctx.beginPath();
|
|
1376
|
+
state.ctx.arc(c[0], c[1], c[2], 0, Math.PI * 2);
|
|
1377
|
+
apply_style_canvas(state.ctx, s);
|
|
1378
|
+
if (s.fill !== null) {
|
|
1379
|
+
state.ctx.fill();
|
|
1380
|
+
}
|
|
1381
|
+
if (s.stroke !== null && s.stroke_width > 0) {
|
|
1382
|
+
state.ctx.stroke();
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
function draw_line_canvas(state, a, b, s) {
|
|
1386
|
+
state.ctx.beginPath();
|
|
1387
|
+
state.ctx.moveTo(a[0], a[1]);
|
|
1388
|
+
state.ctx.lineTo(b[0], b[1]);
|
|
1389
|
+
apply_style_canvas(state.ctx, s);
|
|
1390
|
+
state.ctx.stroke();
|
|
1391
|
+
}
|
|
1392
|
+
function draw_polyline_canvas(state, points, s) {
|
|
1393
|
+
if (points.length < 2) {
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
state.ctx.beginPath();
|
|
1397
|
+
const first = points[0];
|
|
1398
|
+
if (first !== undefined) {
|
|
1399
|
+
state.ctx.moveTo(first[0], first[1]);
|
|
1400
|
+
}
|
|
1401
|
+
for (let i = 1;i < points.length; i = i + 1) {
|
|
1402
|
+
const point = points[i];
|
|
1403
|
+
if (point !== undefined) {
|
|
1404
|
+
state.ctx.lineTo(point[0], point[1]);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
apply_style_canvas(state.ctx, s);
|
|
1408
|
+
state.ctx.stroke();
|
|
1409
|
+
}
|
|
1410
|
+
function draw_image_canvas(state, image, r, opacity, _version) {
|
|
1411
|
+
const old_alpha = state.ctx.globalAlpha;
|
|
1412
|
+
state.ctx.globalAlpha = opacity;
|
|
1413
|
+
state.ctx.drawImage(image, r[0], r[1], r[2], r[3]);
|
|
1414
|
+
state.ctx.globalAlpha = old_alpha;
|
|
1415
|
+
}
|
|
1416
|
+
function apply_style_canvas(ctx, s) {
|
|
1417
|
+
ctx.globalAlpha = s.alpha;
|
|
1418
|
+
if (s.fill !== null) {
|
|
1419
|
+
ctx.fillStyle = to_rgba_string_color(s.fill);
|
|
1420
|
+
}
|
|
1421
|
+
if (s.stroke !== null) {
|
|
1422
|
+
ctx.strokeStyle = to_rgba_string_color(s.stroke);
|
|
1423
|
+
}
|
|
1424
|
+
ctx.lineWidth = s.stroke_width;
|
|
1425
|
+
ctx.lineCap = line_cap_to_string_canvas(s.line_cap);
|
|
1426
|
+
ctx.lineJoin = line_join_to_string_canvas(s.line_join);
|
|
1427
|
+
ctx.miterLimit = s.miter_limit;
|
|
1428
|
+
}
|
|
1429
|
+
function line_cap_to_string_canvas(cap) {
|
|
1430
|
+
if (cap === line_cap_round) {
|
|
1431
|
+
return "round";
|
|
1432
|
+
} else if (cap === line_cap_square) {
|
|
1433
|
+
return "square";
|
|
1434
|
+
} else {
|
|
1435
|
+
return "butt";
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
function line_join_to_string_canvas(join) {
|
|
1439
|
+
if (join === line_join_round) {
|
|
1440
|
+
return "round";
|
|
1441
|
+
} else if (join === line_join_bevel) {
|
|
1442
|
+
return "bevel";
|
|
1443
|
+
} else {
|
|
1444
|
+
return "miter";
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
// renderer/canvas/lifecycle.ts
|
|
1449
|
+
function begin_frame_canvas(state) {
|
|
1450
|
+
state.ctx.setTransform(state.current_pixel_ratio, 0, 0, state.current_pixel_ratio, 0, 0);
|
|
1451
|
+
}
|
|
1452
|
+
function end_frame_canvas(_state) {}
|
|
1453
|
+
function set_transform_canvas(state, t) {
|
|
1454
|
+
const pr = state.current_pixel_ratio;
|
|
1455
|
+
state.ctx.setTransform(t[0] * pr, t[1] * pr, t[2] * pr, t[3] * pr, t[4] * pr, t[5] * pr);
|
|
1456
|
+
}
|
|
1457
|
+
function reset_transform_canvas(state) {
|
|
1458
|
+
state.ctx.setTransform(state.current_pixel_ratio, 0, 0, state.current_pixel_ratio, 0, 0);
|
|
1459
|
+
}
|
|
1460
|
+
function save_canvas(state) {
|
|
1461
|
+
state.ctx.save();
|
|
1462
|
+
}
|
|
1463
|
+
function restore_canvas(state) {
|
|
1464
|
+
state.ctx.restore();
|
|
1465
|
+
}
|
|
1466
|
+
function clip_rectangle_canvas(state, r) {
|
|
1467
|
+
state.ctx.save();
|
|
1468
|
+
state.ctx.beginPath();
|
|
1469
|
+
state.ctx.rect(r[0], r[1], r[2], r[3]);
|
|
1470
|
+
state.ctx.clip();
|
|
1471
|
+
}
|
|
1472
|
+
function clear_canvas(state, c) {
|
|
1473
|
+
state.ctx.save();
|
|
1474
|
+
state.ctx.resetTransform();
|
|
1475
|
+
state.ctx.fillStyle = to_rgba_string_color(c);
|
|
1476
|
+
state.ctx.fillRect(0, 0, state.current_width * state.current_pixel_ratio, state.current_height * state.current_pixel_ratio);
|
|
1477
|
+
state.ctx.restore();
|
|
1478
|
+
}
|
|
1479
|
+
function resize_canvas(state, canvas, width, height, pixel_ratio) {
|
|
1480
|
+
state.current_width = width;
|
|
1481
|
+
state.current_height = height;
|
|
1482
|
+
state.current_pixel_ratio = pixel_ratio;
|
|
1483
|
+
canvas.width = width * pixel_ratio;
|
|
1484
|
+
canvas.height = height * pixel_ratio;
|
|
1485
|
+
canvas.style.width = width + "px";
|
|
1486
|
+
canvas.style.height = height + "px";
|
|
1487
|
+
state.ctx.setTransform(pixel_ratio, 0, 0, pixel_ratio, 0, 0);
|
|
1488
|
+
}
|
|
1489
|
+
function dispose_canvas(state) {
|
|
1490
|
+
state.path_cache.clear();
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// renderer/canvas/index.ts
|
|
1494
|
+
function create_renderer_canvas(canvas, config) {
|
|
1495
|
+
const ctx = canvas.getContext("2d");
|
|
1496
|
+
if (ctx === null) {
|
|
1497
|
+
return null;
|
|
1498
|
+
}
|
|
1499
|
+
const state = {
|
|
1500
|
+
ctx,
|
|
1501
|
+
path_cache: new Map,
|
|
1502
|
+
cache_access_counter: 0,
|
|
1503
|
+
cache_max_size: config.path_cache_size,
|
|
1504
|
+
current_width: canvas.width,
|
|
1505
|
+
current_height: canvas.height,
|
|
1506
|
+
current_pixel_ratio: 1
|
|
1507
|
+
};
|
|
1508
|
+
return {
|
|
1509
|
+
kind: renderer_kind_canvas,
|
|
1510
|
+
get width() {
|
|
1511
|
+
return state.current_width;
|
|
1512
|
+
},
|
|
1513
|
+
get height() {
|
|
1514
|
+
return state.current_height;
|
|
1515
|
+
},
|
|
1516
|
+
get pixel_ratio() {
|
|
1517
|
+
return state.current_pixel_ratio;
|
|
1518
|
+
},
|
|
1519
|
+
begin_frame: () => begin_frame_canvas(state),
|
|
1520
|
+
end_frame: () => end_frame_canvas(state),
|
|
1521
|
+
set_transform: (t) => set_transform_canvas(state, t),
|
|
1522
|
+
reset_transform: () => reset_transform_canvas(state),
|
|
1523
|
+
save: () => save_canvas(state),
|
|
1524
|
+
restore: () => restore_canvas(state),
|
|
1525
|
+
draw_path: (p, s) => draw_path_canvas(state, p, s),
|
|
1526
|
+
draw_rectangle: (r, s) => draw_rectangle_canvas(state, r, s),
|
|
1527
|
+
draw_circle: (c, s) => draw_circle_canvas(state, c, s),
|
|
1528
|
+
draw_line: (a, b, s) => draw_line_canvas(state, a, b, s),
|
|
1529
|
+
draw_polyline: (points, s) => draw_polyline_canvas(state, points, s),
|
|
1530
|
+
draw_image: (image, r, opacity, version) => draw_image_canvas(state, image, r, opacity, version),
|
|
1531
|
+
clip_rectangle: (r) => clip_rectangle_canvas(state, r),
|
|
1532
|
+
clear: (c) => clear_canvas(state, c),
|
|
1533
|
+
resize: (width, height, pixel_ratio) => resize_canvas(state, canvas, width, height, pixel_ratio),
|
|
1534
|
+
dispose: () => dispose_canvas(state)
|
|
1535
|
+
};
|
|
1536
|
+
}
|
|
1537
|
+
// renderer/webgl/lifecycle.ts
|
|
1538
|
+
function begin_frame_webgl(state) {
|
|
1539
|
+
state.vertex_count = 0;
|
|
1540
|
+
state.index_count = 0;
|
|
1541
|
+
state.scissor_enabled = false;
|
|
1542
|
+
state.gl.disable(state.gl.SCISSOR_TEST);
|
|
1543
|
+
}
|
|
1544
|
+
function end_frame_webgl(state) {
|
|
1545
|
+
if (state.index_count > 0) {
|
|
1546
|
+
flush_webgl(state);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
function flush_webgl(state) {
|
|
1550
|
+
if (state.index_count === 0) {
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
const gl = state.gl;
|
|
1554
|
+
gl.useProgram(state.program);
|
|
1555
|
+
gl.uniformMatrix3fv(state.program_locations.transform, false, state.current_transform);
|
|
1556
|
+
gl.uniform2f(state.program_locations.resolution, state.width, state.height);
|
|
1557
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, state.position_buffer);
|
|
1558
|
+
gl.bufferData(gl.ARRAY_BUFFER, state.vertices.subarray(0, state.vertex_count * 2), gl.DYNAMIC_DRAW);
|
|
1559
|
+
gl.enableVertexAttribArray(state.program_locations.position);
|
|
1560
|
+
gl.vertexAttribPointer(state.program_locations.position, 2, gl.FLOAT, false, 0, 0);
|
|
1561
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, state.color_buffer);
|
|
1562
|
+
gl.bufferData(gl.ARRAY_BUFFER, state.colors.subarray(0, state.vertex_count * 4), gl.DYNAMIC_DRAW);
|
|
1563
|
+
gl.enableVertexAttribArray(state.program_locations.color);
|
|
1564
|
+
gl.vertexAttribPointer(state.program_locations.color, 4, gl.FLOAT, false, 0, 0);
|
|
1565
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, state.index_buffer);
|
|
1566
|
+
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, state.indices.subarray(0, state.index_count), gl.DYNAMIC_DRAW);
|
|
1567
|
+
gl.drawElements(gl.TRIANGLES, state.index_count, gl.UNSIGNED_SHORT, 0);
|
|
1568
|
+
state.vertex_count = 0;
|
|
1569
|
+
state.index_count = 0;
|
|
1570
|
+
}
|
|
1571
|
+
function clip_rectangle_webgl(state, r) {
|
|
1572
|
+
if (state.index_count > 0) {
|
|
1573
|
+
flush_webgl(state);
|
|
1574
|
+
}
|
|
1575
|
+
state.scissor_enabled = true;
|
|
1576
|
+
state.gl.enable(state.gl.SCISSOR_TEST);
|
|
1577
|
+
state.gl.scissor(Math.floor(r[0] * state.pixel_ratio), Math.floor((state.height - r[1] - r[3]) * state.pixel_ratio), Math.ceil(r[2] * state.pixel_ratio), Math.ceil(r[3] * state.pixel_ratio));
|
|
1578
|
+
}
|
|
1579
|
+
function clear_webgl(state, c) {
|
|
1580
|
+
if (state.index_count > 0) {
|
|
1581
|
+
flush_webgl(state);
|
|
1582
|
+
}
|
|
1583
|
+
state.gl.clearColor(c[0], c[1], c[2], c[3]);
|
|
1584
|
+
state.gl.clear(state.gl.COLOR_BUFFER_BIT);
|
|
1585
|
+
}
|
|
1586
|
+
function resize_webgl(state, width, height, pixel_ratio) {
|
|
1587
|
+
state.width = width;
|
|
1588
|
+
state.height = height;
|
|
1589
|
+
state.pixel_ratio = pixel_ratio;
|
|
1590
|
+
state.canvas.width = width * pixel_ratio;
|
|
1591
|
+
state.canvas.height = height * pixel_ratio;
|
|
1592
|
+
state.canvas.style.width = width + "px";
|
|
1593
|
+
state.canvas.style.height = height + "px";
|
|
1594
|
+
state.gl.viewport(0, 0, state.canvas.width, state.canvas.height);
|
|
1595
|
+
}
|
|
1596
|
+
function dispose_webgl(state) {
|
|
1597
|
+
state.fill_cache.clear();
|
|
1598
|
+
state.stroke_cache.clear();
|
|
1599
|
+
for (const texture of state.texture_cache.values()) {
|
|
1600
|
+
state.gl.deleteTexture(texture.texture);
|
|
1601
|
+
}
|
|
1602
|
+
state.texture_cache.clear();
|
|
1603
|
+
state.gl.deleteBuffer(state.position_buffer);
|
|
1604
|
+
state.gl.deleteBuffer(state.color_buffer);
|
|
1605
|
+
state.gl.deleteBuffer(state.texcoord_buffer);
|
|
1606
|
+
state.gl.deleteBuffer(state.index_buffer);
|
|
1607
|
+
state.gl.deleteProgram(state.program);
|
|
1608
|
+
state.gl.deleteProgram(state.texture_program);
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
// renderer/webgl/batches.ts
|
|
1612
|
+
function draw_cached_triangles_webgl(state, cached, color, alpha) {
|
|
1613
|
+
if (cached.index_count === 0) {
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1616
|
+
const added_vertices = new Int32Array(cached.vertex_count);
|
|
1617
|
+
for (let i = 0;i < cached.vertex_count; i = i + 1) {
|
|
1618
|
+
added_vertices[i] = -1;
|
|
1619
|
+
}
|
|
1620
|
+
for (let tri = 0;tri < cached.index_count; tri = tri + 3) {
|
|
1621
|
+
const i0 = cached.indices[tri];
|
|
1622
|
+
const i1 = cached.indices[tri + 1];
|
|
1623
|
+
const i2 = cached.indices[tri + 2];
|
|
1624
|
+
if (i0 === undefined || i1 === undefined || i2 === undefined) {
|
|
1625
|
+
continue;
|
|
1626
|
+
}
|
|
1627
|
+
if (state.vertex_count + 3 > state.max_vertices || state.index_count + 3 > state.max_indices) {
|
|
1628
|
+
flush_webgl(state);
|
|
1629
|
+
for (let i = 0;i < cached.vertex_count; i = i + 1) {
|
|
1630
|
+
added_vertices[i] = -1;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
write_triangle_webgl(state, cached.vertices, added_vertices, [i0, i1, i2], color, alpha);
|
|
1634
|
+
const v0 = added_vertices[i0];
|
|
1635
|
+
const v1 = added_vertices[i1];
|
|
1636
|
+
const v2 = added_vertices[i2];
|
|
1637
|
+
if (v0 !== -1 && v0 !== undefined && v1 !== -1 && v1 !== undefined && v2 !== -1 && v2 !== undefined) {
|
|
1638
|
+
state.indices[state.index_count] = v0;
|
|
1639
|
+
state.indices[state.index_count + 1] = v1;
|
|
1640
|
+
state.indices[state.index_count + 2] = v2;
|
|
1641
|
+
state.index_count = state.index_count + 3;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
function write_triangle_webgl(state, vertices, added_vertices, indices, color, alpha) {
|
|
1646
|
+
for (let i = 0;i < indices.length; i = i + 1) {
|
|
1647
|
+
const idx = indices[i];
|
|
1648
|
+
if (idx === undefined || added_vertices[idx] !== -1) {
|
|
1649
|
+
continue;
|
|
1650
|
+
}
|
|
1651
|
+
const vx = vertices[idx * 2];
|
|
1652
|
+
const vy = vertices[idx * 2 + 1];
|
|
1653
|
+
if (vx !== undefined && vy !== undefined) {
|
|
1654
|
+
state.vertices[state.vertex_count * 2] = vx;
|
|
1655
|
+
state.vertices[state.vertex_count * 2 + 1] = vy;
|
|
1656
|
+
state.colors[state.vertex_count * 4] = color[0];
|
|
1657
|
+
state.colors[state.vertex_count * 4 + 1] = color[1];
|
|
1658
|
+
state.colors[state.vertex_count * 4 + 2] = color[2];
|
|
1659
|
+
state.colors[state.vertex_count * 4 + 3] = color[3] * alpha;
|
|
1660
|
+
added_vertices[idx] = state.vertex_count;
|
|
1661
|
+
state.vertex_count = state.vertex_count + 1;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// renderer/webgl/triangles.ts
|
|
1667
|
+
function triangulate_polygon_earcut(vertices) {
|
|
1668
|
+
if (vertices.length < 6) {
|
|
1669
|
+
return [];
|
|
1670
|
+
}
|
|
1671
|
+
const n = vertices.length / 2;
|
|
1672
|
+
if (n === 3) {
|
|
1673
|
+
return [0, 1, 2];
|
|
1674
|
+
}
|
|
1675
|
+
let head = null;
|
|
1676
|
+
let last = null;
|
|
1677
|
+
for (let i = 0;i < n; i = i + 1) {
|
|
1678
|
+
const x = vertices[i * 2];
|
|
1679
|
+
const y = vertices[i * 2 + 1];
|
|
1680
|
+
if (x !== undefined && y !== undefined) {
|
|
1681
|
+
const node = { i, x, y, prev: last, next: null };
|
|
1682
|
+
if (last !== null) {
|
|
1683
|
+
last.next = node;
|
|
1684
|
+
} else {
|
|
1685
|
+
head = node;
|
|
1686
|
+
}
|
|
1687
|
+
last = node;
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
if (head !== null && last !== null) {
|
|
1691
|
+
head.prev = last;
|
|
1692
|
+
last.next = head;
|
|
1693
|
+
}
|
|
1694
|
+
const indices = [];
|
|
1695
|
+
let ear = head;
|
|
1696
|
+
let count = n * 2;
|
|
1697
|
+
while (count > 0 && ear !== null && ear.next !== ear.prev) {
|
|
1698
|
+
count = count - 1;
|
|
1699
|
+
const prev = ear.prev;
|
|
1700
|
+
const next = ear.next;
|
|
1701
|
+
if (prev !== null && next !== null && is_ear(prev, ear, next)) {
|
|
1702
|
+
indices.push(prev.i, ear.i, next.i);
|
|
1703
|
+
if (prev.next === next) {
|
|
1704
|
+
break;
|
|
1705
|
+
}
|
|
1706
|
+
prev.next = next;
|
|
1707
|
+
next.prev = prev;
|
|
1708
|
+
ear = next;
|
|
1709
|
+
} else {
|
|
1710
|
+
ear = next;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
return indices;
|
|
1714
|
+
}
|
|
1715
|
+
function is_ear(a, b, c) {
|
|
1716
|
+
const area = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
|
1717
|
+
if (area >= 0) {
|
|
1718
|
+
return false;
|
|
1719
|
+
}
|
|
1720
|
+
let p = c.next;
|
|
1721
|
+
while (p !== null && p !== a) {
|
|
1722
|
+
if (point_in_triangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y)) {
|
|
1723
|
+
return false;
|
|
1724
|
+
}
|
|
1725
|
+
p = p.next;
|
|
1726
|
+
}
|
|
1727
|
+
return true;
|
|
1728
|
+
}
|
|
1729
|
+
function point_in_triangle(ax, ay, bx, by, cx, cy, px, py) {
|
|
1730
|
+
const denom = (by - cy) * (ax - cx) + (cx - bx) * (ay - cy);
|
|
1731
|
+
if (denom === 0) {
|
|
1732
|
+
return false;
|
|
1733
|
+
}
|
|
1734
|
+
const a = ((by - cy) * (px - cx) + (cx - bx) * (py - cy)) / denom;
|
|
1735
|
+
const b = ((cy - ay) * (px - cx) + (ax - cx) * (py - cy)) / denom;
|
|
1736
|
+
const c = 1 - a - b;
|
|
1737
|
+
return a >= 0 && b >= 0 && c >= 0;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
// renderer/webgl/fills.ts
|
|
1741
|
+
function cache_path_fill_webgl(p) {
|
|
1742
|
+
const fill_vertices = [];
|
|
1743
|
+
const fill_colors = [];
|
|
1744
|
+
const fill_indices = [];
|
|
1745
|
+
let current_x = 0;
|
|
1746
|
+
let current_y = 0;
|
|
1747
|
+
let start_x = 0;
|
|
1748
|
+
let start_y = 0;
|
|
1749
|
+
for (let i = 0;i < p.commands.length; i = i + 1) {
|
|
1750
|
+
const cmd = p.commands[i];
|
|
1751
|
+
if (cmd === undefined) {
|
|
1752
|
+
continue;
|
|
1753
|
+
}
|
|
1754
|
+
const type = cmd[0];
|
|
1755
|
+
if (type === path_move) {
|
|
1756
|
+
current_x = cmd[1];
|
|
1757
|
+
current_y = cmd[2];
|
|
1758
|
+
start_x = current_x;
|
|
1759
|
+
start_y = current_y;
|
|
1760
|
+
} else if (type === path_line) {
|
|
1761
|
+
fill_vertices.push(current_x, current_y);
|
|
1762
|
+
current_x = cmd[1];
|
|
1763
|
+
current_y = cmd[2];
|
|
1764
|
+
} else if (type === path_cubic) {
|
|
1765
|
+
const steps = 8;
|
|
1766
|
+
let prev_x = current_x;
|
|
1767
|
+
let prev_y = current_y;
|
|
1768
|
+
for (let step = 1;step <= steps; step = step + 1) {
|
|
1769
|
+
const t = step / steps;
|
|
1770
|
+
const inv_t = 1 - t;
|
|
1771
|
+
const t2 = t * t;
|
|
1772
|
+
const inv_t2 = inv_t * inv_t;
|
|
1773
|
+
const t3 = t2 * t;
|
|
1774
|
+
const inv_t3 = inv_t2 * inv_t;
|
|
1775
|
+
const x = inv_t3 * current_x + 3 * inv_t2 * t * cmd[1] + 3 * inv_t * t2 * cmd[3] + t3 * cmd[5];
|
|
1776
|
+
const y = inv_t3 * current_y + 3 * inv_t2 * t * cmd[2] + 3 * inv_t * t2 * cmd[4] + t3 * cmd[6];
|
|
1777
|
+
fill_vertices.push(prev_x, prev_y);
|
|
1778
|
+
prev_x = x;
|
|
1779
|
+
prev_y = y;
|
|
1780
|
+
}
|
|
1781
|
+
current_x = cmd[5];
|
|
1782
|
+
current_y = cmd[6];
|
|
1783
|
+
} else if (type === path_close) {
|
|
1784
|
+
if (current_x !== start_x || current_y !== start_y) {
|
|
1785
|
+
fill_vertices.push(current_x, current_y);
|
|
1786
|
+
}
|
|
1787
|
+
fill_vertices.push(start_x, start_y);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
if (fill_vertices.length > 0 && (current_x !== start_x || current_y !== start_y)) {
|
|
1791
|
+
fill_vertices.push(start_x, start_y);
|
|
1792
|
+
}
|
|
1793
|
+
const fill_tris = triangulate_polygon_earcut(fill_vertices);
|
|
1794
|
+
for (let i = 0;i < fill_tris.length; i = i + 1) {
|
|
1795
|
+
const tri_index = fill_tris[i];
|
|
1796
|
+
if (tri_index !== undefined) {
|
|
1797
|
+
fill_indices.push(tri_index);
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
for (let i = 0;i < fill_vertices.length / 2; i = i + 1) {
|
|
1801
|
+
fill_colors.push(1, 1, 1, 1);
|
|
1802
|
+
}
|
|
1803
|
+
return {
|
|
1804
|
+
vertices: new Float32Array(fill_vertices),
|
|
1805
|
+
colors: new Float32Array(fill_colors),
|
|
1806
|
+
indices: new Uint16Array(fill_indices),
|
|
1807
|
+
vertex_count: fill_vertices.length / 2,
|
|
1808
|
+
index_count: fill_indices.length,
|
|
1809
|
+
path_commands: p.commands
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
function draw_cached_path_fill_webgl(state, cached, color, alpha) {
|
|
1813
|
+
draw_cached_triangles_webgl(state, cached, color, alpha);
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
// renderer/webgl/strokes.ts
|
|
1817
|
+
function cache_path_stroke_webgl(p, stroke_width) {
|
|
1818
|
+
const stroke_vertices = [];
|
|
1819
|
+
const stroke_colors = [];
|
|
1820
|
+
const stroke_indices = [];
|
|
1821
|
+
let current_x = 0;
|
|
1822
|
+
let current_y = 0;
|
|
1823
|
+
let start_x = 0;
|
|
1824
|
+
let start_y = 0;
|
|
1825
|
+
let vertex_offset = 0;
|
|
1826
|
+
for (let i = 0;i < p.commands.length; i = i + 1) {
|
|
1827
|
+
const cmd = p.commands[i];
|
|
1828
|
+
if (cmd === undefined) {
|
|
1829
|
+
continue;
|
|
1830
|
+
}
|
|
1831
|
+
const type = cmd[0];
|
|
1832
|
+
if (type === path_move) {
|
|
1833
|
+
current_x = cmd[1];
|
|
1834
|
+
current_y = cmd[2];
|
|
1835
|
+
start_x = current_x;
|
|
1836
|
+
start_y = current_y;
|
|
1837
|
+
} else if (type === path_line) {
|
|
1838
|
+
add_segment_webgl(stroke_vertices, stroke_indices, vertex_offset, current_x, current_y, cmd[1], cmd[2], stroke_width / 2);
|
|
1839
|
+
vertex_offset = vertex_offset + 4;
|
|
1840
|
+
current_x = cmd[1];
|
|
1841
|
+
current_y = cmd[2];
|
|
1842
|
+
} else if (type === path_cubic) {
|
|
1843
|
+
const steps = 8;
|
|
1844
|
+
let prev_x = current_x;
|
|
1845
|
+
let prev_y = current_y;
|
|
1846
|
+
for (let step = 1;step <= steps; step = step + 1) {
|
|
1847
|
+
const t = step / steps;
|
|
1848
|
+
const inv_t = 1 - t;
|
|
1849
|
+
const t2 = t * t;
|
|
1850
|
+
const inv_t2 = inv_t * inv_t;
|
|
1851
|
+
const t3 = t2 * t;
|
|
1852
|
+
const inv_t3 = inv_t2 * inv_t;
|
|
1853
|
+
const x = inv_t3 * current_x + 3 * inv_t2 * t * cmd[1] + 3 * inv_t * t2 * cmd[3] + t3 * cmd[5];
|
|
1854
|
+
const y = inv_t3 * current_y + 3 * inv_t2 * t * cmd[2] + 3 * inv_t * t2 * cmd[4] + t3 * cmd[6];
|
|
1855
|
+
add_segment_webgl(stroke_vertices, stroke_indices, vertex_offset, prev_x, prev_y, x, y, stroke_width / 2);
|
|
1856
|
+
vertex_offset = vertex_offset + 4;
|
|
1857
|
+
prev_x = x;
|
|
1858
|
+
prev_y = y;
|
|
1859
|
+
}
|
|
1860
|
+
current_x = cmd[5];
|
|
1861
|
+
current_y = cmd[6];
|
|
1862
|
+
} else if (type === path_close) {
|
|
1863
|
+
if (current_x !== start_x || current_y !== start_y) {
|
|
1864
|
+
add_segment_webgl(stroke_vertices, stroke_indices, vertex_offset, current_x, current_y, start_x, start_y, stroke_width / 2);
|
|
1865
|
+
vertex_offset = vertex_offset + 4;
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
for (let i = 0;i < stroke_vertices.length / 2; i = i + 1) {
|
|
1870
|
+
stroke_colors.push(1, 1, 1, 1);
|
|
1871
|
+
}
|
|
1872
|
+
return {
|
|
1873
|
+
vertices: new Float32Array(stroke_vertices),
|
|
1874
|
+
colors: new Float32Array(stroke_colors),
|
|
1875
|
+
indices: new Uint16Array(stroke_indices),
|
|
1876
|
+
vertex_count: stroke_vertices.length / 2,
|
|
1877
|
+
index_count: stroke_indices.length,
|
|
1878
|
+
stroke_width,
|
|
1879
|
+
path_commands: p.commands
|
|
1880
|
+
};
|
|
1881
|
+
}
|
|
1882
|
+
function draw_cached_path_stroke_webgl(state, cached, color, alpha) {
|
|
1883
|
+
draw_cached_triangles_webgl(state, cached, color, alpha);
|
|
1884
|
+
}
|
|
1885
|
+
function add_segment_webgl(vertices, indices, base_index, x1, y1, x2, y2, half_width) {
|
|
1886
|
+
add_line_vertices(vertices, x1, y1, x2, y2, half_width);
|
|
1887
|
+
indices.push(base_index, base_index + 1, base_index + 2, base_index + 1, base_index + 3, base_index + 2);
|
|
1888
|
+
}
|
|
1889
|
+
function add_line_vertices(vertices, x1, y1, x2, y2, half_width) {
|
|
1890
|
+
const dx = x2 - x1;
|
|
1891
|
+
const dy = y2 - y1;
|
|
1892
|
+
const len = Math.sqrt(dx * dx + dy * dy);
|
|
1893
|
+
if (len === 0) {
|
|
1894
|
+
vertices.push(x1 - half_width, y1 - half_width, x1 + half_width, y1 - half_width, x1 - half_width, y1 + half_width, x1 + half_width, y1 + half_width);
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
const nx = -dy / len * half_width;
|
|
1898
|
+
const ny = dx / len * half_width;
|
|
1899
|
+
vertices.push(x1 + nx, y1 + ny, x2 + nx, y2 + ny, x1 - nx, y1 - ny, x2 - nx, y2 - ny);
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
// renderer/webgl/paths.ts
|
|
1903
|
+
function draw_path_webgl(state, p, s) {
|
|
1904
|
+
if (s.fill !== null) {
|
|
1905
|
+
const hash = hash_of_path(p);
|
|
1906
|
+
let fill_cached = state.fill_cache.get(hash);
|
|
1907
|
+
if (fill_cached === undefined || !paths_equal2(fill_cached.path_commands, p.commands)) {
|
|
1908
|
+
fill_cached = cache_path_fill_webgl(p);
|
|
1909
|
+
update_cache_webgl(state.fill_cache, state.cache_max_size, hash, fill_cached);
|
|
1910
|
+
}
|
|
1911
|
+
draw_cached_path_fill_webgl(state, fill_cached, s.fill, s.alpha);
|
|
1912
|
+
}
|
|
1913
|
+
if (s.stroke !== null && s.stroke_width > 0) {
|
|
1914
|
+
const hash = hash_of_path(p) * 31 + s.stroke_width;
|
|
1915
|
+
let stroke_cached = state.stroke_cache.get(hash);
|
|
1916
|
+
if (stroke_cached === undefined || stroke_cached.stroke_width !== s.stroke_width || !paths_equal2(stroke_cached.path_commands, p.commands)) {
|
|
1917
|
+
stroke_cached = cache_path_stroke_webgl(p, s.stroke_width);
|
|
1918
|
+
update_cache_webgl(state.stroke_cache, state.cache_max_size, hash, stroke_cached);
|
|
1919
|
+
}
|
|
1920
|
+
draw_cached_path_stroke_webgl(state, stroke_cached, s.stroke, s.alpha);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
function update_cache_webgl(cache, max_size, key, value) {
|
|
1924
|
+
if (cache.size >= max_size) {
|
|
1925
|
+
const first_key = cache.keys().next().value;
|
|
1926
|
+
if (first_key !== undefined) {
|
|
1927
|
+
cache.delete(first_key);
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
cache.set(key, value);
|
|
1931
|
+
}
|
|
1932
|
+
function paths_equal2(a, b) {
|
|
1933
|
+
if (a.length !== b.length) {
|
|
1934
|
+
return false;
|
|
1935
|
+
}
|
|
1936
|
+
for (let i = 0;i < a.length; i = i + 1) {
|
|
1937
|
+
const cmd_a = a[i];
|
|
1938
|
+
const cmd_b = b[i];
|
|
1939
|
+
if (cmd_a === undefined || cmd_b === undefined) {
|
|
1940
|
+
return false;
|
|
1941
|
+
}
|
|
1942
|
+
if (cmd_a.length !== cmd_b.length) {
|
|
1943
|
+
return false;
|
|
1944
|
+
}
|
|
1945
|
+
for (let j = 0;j < cmd_a.length; j = j + 1) {
|
|
1946
|
+
if (cmd_a[j] !== cmd_b[j]) {
|
|
1947
|
+
return false;
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
return true;
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
// renderer/webgl/shaders.ts
|
|
1955
|
+
var vertex_shader_source = `
|
|
1956
|
+
attribute vec2 a_position;
|
|
1957
|
+
attribute vec4 a_color;
|
|
1958
|
+
uniform mat3 u_transform;
|
|
1959
|
+
uniform vec2 u_resolution;
|
|
1960
|
+
varying vec4 v_color;
|
|
1961
|
+
|
|
1962
|
+
void main() {
|
|
1963
|
+
vec2 position = (u_transform * vec3(a_position, 1.0)).xy;
|
|
1964
|
+
vec2 clip_space = ((position / u_resolution) * 2.0) - 1.0;
|
|
1965
|
+
gl_Position = vec4(clip_space * vec2(1.0, -1.0), 0.0, 1.0);
|
|
1966
|
+
v_color = a_color;
|
|
1967
|
+
}
|
|
1968
|
+
`;
|
|
1969
|
+
var fragment_shader_source = `
|
|
1970
|
+
precision mediump float;
|
|
1971
|
+
varying vec4 v_color;
|
|
1972
|
+
|
|
1973
|
+
void main() {
|
|
1974
|
+
gl_FragColor = v_color;
|
|
1975
|
+
}
|
|
1976
|
+
`;
|
|
1977
|
+
var texture_vertex_shader_source = `
|
|
1978
|
+
attribute vec2 a_position;
|
|
1979
|
+
attribute vec2 a_texcoord;
|
|
1980
|
+
uniform mat3 u_transform;
|
|
1981
|
+
uniform vec2 u_resolution;
|
|
1982
|
+
varying vec2 v_texcoord;
|
|
1983
|
+
|
|
1984
|
+
void main() {
|
|
1985
|
+
vec2 position = (u_transform * vec3(a_position, 1.0)).xy;
|
|
1986
|
+
vec2 clip_space = ((position / u_resolution) * 2.0) - 1.0;
|
|
1987
|
+
gl_Position = vec4(clip_space * vec2(1.0, -1.0), 0.0, 1.0);
|
|
1988
|
+
v_texcoord = a_texcoord;
|
|
1989
|
+
}
|
|
1990
|
+
`;
|
|
1991
|
+
var texture_fragment_shader_source = `
|
|
1992
|
+
precision mediump float;
|
|
1993
|
+
uniform sampler2D u_image;
|
|
1994
|
+
uniform float u_alpha;
|
|
1995
|
+
varying vec2 v_texcoord;
|
|
1996
|
+
|
|
1997
|
+
void main() {
|
|
1998
|
+
gl_FragColor = texture2D(u_image, v_texcoord);
|
|
1999
|
+
gl_FragColor.a *= u_alpha;
|
|
2000
|
+
}
|
|
2001
|
+
`;
|
|
2002
|
+
function create_color_program_webgl(gl) {
|
|
2003
|
+
const program = create_shader_program(gl, vertex_shader_source, fragment_shader_source);
|
|
2004
|
+
if (program === null) {
|
|
2005
|
+
return null;
|
|
2006
|
+
}
|
|
2007
|
+
const position = gl.getAttribLocation(program, "a_position");
|
|
2008
|
+
const color = gl.getAttribLocation(program, "a_color");
|
|
2009
|
+
const transform = gl.getUniformLocation(program, "u_transform");
|
|
2010
|
+
const resolution = gl.getUniformLocation(program, "u_resolution");
|
|
2011
|
+
if (position < 0 || color < 0 || transform === null || resolution === null) {
|
|
2012
|
+
gl.deleteProgram(program);
|
|
2013
|
+
return null;
|
|
2014
|
+
}
|
|
2015
|
+
return {
|
|
2016
|
+
program,
|
|
2017
|
+
locations: { position, color, transform, resolution }
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
function create_texture_program_webgl(gl) {
|
|
2021
|
+
const program = create_shader_program(gl, texture_vertex_shader_source, texture_fragment_shader_source);
|
|
2022
|
+
if (program === null) {
|
|
2023
|
+
return null;
|
|
2024
|
+
}
|
|
2025
|
+
const position = gl.getAttribLocation(program, "a_position");
|
|
2026
|
+
const texcoord = gl.getAttribLocation(program, "a_texcoord");
|
|
2027
|
+
const transform = gl.getUniformLocation(program, "u_transform");
|
|
2028
|
+
const resolution = gl.getUniformLocation(program, "u_resolution");
|
|
2029
|
+
const alpha = gl.getUniformLocation(program, "u_alpha");
|
|
2030
|
+
const sampler = gl.getUniformLocation(program, "u_image");
|
|
2031
|
+
if (position < 0 || texcoord < 0 || transform === null || resolution === null || alpha === null || sampler === null) {
|
|
2032
|
+
gl.deleteProgram(program);
|
|
2033
|
+
return null;
|
|
2034
|
+
}
|
|
2035
|
+
return {
|
|
2036
|
+
program,
|
|
2037
|
+
locations: { position, texcoord, transform, resolution, alpha, sampler }
|
|
2038
|
+
};
|
|
2039
|
+
}
|
|
2040
|
+
function create_shader_program(gl, vertex_source, fragment_source) {
|
|
2041
|
+
const vertex_shader = compile_shader(gl, vertex_source, gl.VERTEX_SHADER);
|
|
2042
|
+
const fragment_shader = compile_shader(gl, fragment_source, gl.FRAGMENT_SHADER);
|
|
2043
|
+
if (vertex_shader === null || fragment_shader === null) {
|
|
2044
|
+
return null;
|
|
2045
|
+
}
|
|
2046
|
+
const program = gl.createProgram();
|
|
2047
|
+
if (program === null) {
|
|
2048
|
+
return null;
|
|
2049
|
+
}
|
|
2050
|
+
gl.attachShader(program, vertex_shader);
|
|
2051
|
+
gl.attachShader(program, fragment_shader);
|
|
2052
|
+
gl.linkProgram(program);
|
|
2053
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
2054
|
+
gl.deleteProgram(program);
|
|
2055
|
+
return null;
|
|
2056
|
+
}
|
|
2057
|
+
return program;
|
|
2058
|
+
}
|
|
2059
|
+
function compile_shader(gl, source, type) {
|
|
2060
|
+
const shader = gl.createShader(type);
|
|
2061
|
+
if (shader === null) {
|
|
2062
|
+
return null;
|
|
2063
|
+
}
|
|
2064
|
+
gl.shaderSource(shader, source);
|
|
2065
|
+
gl.compileShader(shader);
|
|
2066
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
2067
|
+
gl.deleteShader(shader);
|
|
2068
|
+
return null;
|
|
2069
|
+
}
|
|
2070
|
+
return shader;
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
// renderer/webgl/shapes.ts
|
|
2074
|
+
function draw_rectangle_webgl(state, r, s) {
|
|
2075
|
+
const x = r[0];
|
|
2076
|
+
const y = r[1];
|
|
2077
|
+
const w = r[2];
|
|
2078
|
+
const h = r[3];
|
|
2079
|
+
if (s.fill !== null) {
|
|
2080
|
+
add_quad_webgl(state, x, y, x + w, y, x + w, y + h, x, y + h, s.fill, s.alpha);
|
|
2081
|
+
}
|
|
2082
|
+
if (s.stroke !== null && s.stroke_width > 0) {
|
|
2083
|
+
const sw = s.stroke_width;
|
|
2084
|
+
add_quad_webgl(state, x - sw, y - sw, x + w + sw, y - sw, x + w + sw, y, x - sw, y, s.stroke, s.alpha);
|
|
2085
|
+
add_quad_webgl(state, x + w, y, x + w + sw, y, x + w + sw, y + h, x + w, y + h, s.stroke, s.alpha);
|
|
2086
|
+
add_quad_webgl(state, x - sw, y + h, x + w + sw, y + h, x + w + sw, y + h + sw, x - sw, y + h + sw, s.stroke, s.alpha);
|
|
2087
|
+
add_quad_webgl(state, x - sw, y, x, y, x, y + h, x - sw, y + h, s.stroke, s.alpha);
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
function draw_circle_webgl(state, c, s) {
|
|
2091
|
+
const cx = c[0];
|
|
2092
|
+
const cy = c[1];
|
|
2093
|
+
const r = c[2];
|
|
2094
|
+
const segments = 32;
|
|
2095
|
+
if (s.fill !== null) {
|
|
2096
|
+
const base_vertex = state.vertex_count;
|
|
2097
|
+
write_vertex_webgl(state, cx, cy, s.fill, s.alpha);
|
|
2098
|
+
for (let i = 0;i <= segments; i = i + 1) {
|
|
2099
|
+
const angle = i / segments * Math.PI * 2;
|
|
2100
|
+
write_vertex_webgl(state, cx + Math.cos(angle) * r, cy + Math.sin(angle) * r, s.fill, s.alpha);
|
|
2101
|
+
}
|
|
2102
|
+
for (let i = 0;i < segments; i = i + 1) {
|
|
2103
|
+
state.indices[state.index_count] = base_vertex;
|
|
2104
|
+
state.indices[state.index_count + 1] = base_vertex + i + 1;
|
|
2105
|
+
state.indices[state.index_count + 2] = base_vertex + i + 2;
|
|
2106
|
+
state.index_count = state.index_count + 3;
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
if (s.stroke !== null && s.stroke_width > 0 && r > 0) {
|
|
2110
|
+
const outer_r = r + s.stroke_width;
|
|
2111
|
+
const base_vertex = state.vertex_count;
|
|
2112
|
+
for (let i = 0;i <= segments; i = i + 1) {
|
|
2113
|
+
const angle = i / segments * Math.PI * 2;
|
|
2114
|
+
const cos = Math.cos(angle);
|
|
2115
|
+
const sin = Math.sin(angle);
|
|
2116
|
+
write_vertex_webgl(state, cx + cos * r, cy + sin * r, s.stroke, s.alpha);
|
|
2117
|
+
write_vertex_webgl(state, cx + cos * outer_r, cy + sin * outer_r, s.stroke, s.alpha);
|
|
2118
|
+
}
|
|
2119
|
+
for (let i = 0;i < segments; i = i + 1) {
|
|
2120
|
+
const base = base_vertex + i * 2;
|
|
2121
|
+
state.indices[state.index_count] = base;
|
|
2122
|
+
state.indices[state.index_count + 1] = base + 1;
|
|
2123
|
+
state.indices[state.index_count + 2] = base + 2;
|
|
2124
|
+
state.indices[state.index_count + 3] = base + 1;
|
|
2125
|
+
state.indices[state.index_count + 4] = base + 3;
|
|
2126
|
+
state.indices[state.index_count + 5] = base + 2;
|
|
2127
|
+
state.index_count = state.index_count + 6;
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
function draw_line_webgl(state, a, b, s) {
|
|
2132
|
+
if (s.stroke === null || s.stroke_width <= 0) {
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
const dx = b[0] - a[0];
|
|
2136
|
+
const dy = b[1] - a[1];
|
|
2137
|
+
const len = Math.sqrt(dx * dx + dy * dy);
|
|
2138
|
+
if (len === 0) {
|
|
2139
|
+
return;
|
|
2140
|
+
}
|
|
2141
|
+
const width = s.stroke_width / 2;
|
|
2142
|
+
const nx = -dy / len * width;
|
|
2143
|
+
const ny = dx / len * width;
|
|
2144
|
+
add_quad_webgl(state, a[0] + nx, a[1] + ny, b[0] + nx, b[1] + ny, b[0] - nx, b[1] - ny, a[0] - nx, a[1] - ny, s.stroke, s.alpha);
|
|
2145
|
+
}
|
|
2146
|
+
function draw_polyline_webgl(state, points, s) {
|
|
2147
|
+
if (points.length < 2 || s.stroke === null || s.stroke_width <= 0) {
|
|
2148
|
+
return;
|
|
2149
|
+
}
|
|
2150
|
+
for (let i = 0;i < points.length - 1; i = i + 1) {
|
|
2151
|
+
const p1 = points[i];
|
|
2152
|
+
const p2 = points[i + 1];
|
|
2153
|
+
if (p1 !== undefined && p2 !== undefined) {
|
|
2154
|
+
draw_line_webgl(state, p1, p2, s);
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
function add_quad_webgl(state, x1, y1, x2, y2, x3, y3, x4, y4, color, alpha) {
|
|
2159
|
+
if (state.vertex_count + 4 > state.max_vertices || state.index_count + 6 > state.max_indices) {
|
|
2160
|
+
flush_webgl(state);
|
|
2161
|
+
}
|
|
2162
|
+
const base = state.vertex_count;
|
|
2163
|
+
write_vertex_webgl(state, x1, y1, color, alpha);
|
|
2164
|
+
write_vertex_webgl(state, x2, y2, color, alpha);
|
|
2165
|
+
write_vertex_webgl(state, x3, y3, color, alpha);
|
|
2166
|
+
write_vertex_webgl(state, x4, y4, color, alpha);
|
|
2167
|
+
state.indices[state.index_count] = base;
|
|
2168
|
+
state.indices[state.index_count + 1] = base + 1;
|
|
2169
|
+
state.indices[state.index_count + 2] = base + 2;
|
|
2170
|
+
state.indices[state.index_count + 3] = base + 1;
|
|
2171
|
+
state.indices[state.index_count + 4] = base + 3;
|
|
2172
|
+
state.indices[state.index_count + 5] = base + 2;
|
|
2173
|
+
state.index_count = state.index_count + 6;
|
|
2174
|
+
}
|
|
2175
|
+
function write_vertex_webgl(state, x, y, color, alpha) {
|
|
2176
|
+
state.vertices[state.vertex_count * 2] = x;
|
|
2177
|
+
state.vertices[state.vertex_count * 2 + 1] = y;
|
|
2178
|
+
state.colors[state.vertex_count * 4] = color[0];
|
|
2179
|
+
state.colors[state.vertex_count * 4 + 1] = color[1];
|
|
2180
|
+
state.colors[state.vertex_count * 4 + 2] = color[2];
|
|
2181
|
+
state.colors[state.vertex_count * 4 + 3] = color[3] * alpha;
|
|
2182
|
+
state.vertex_count = state.vertex_count + 1;
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
// renderer/webgl/textures.ts
|
|
2186
|
+
function draw_image_webgl(state, image, r, opacity, version) {
|
|
2187
|
+
const texture = sync_texture_webgl(state, image, version);
|
|
2188
|
+
if (texture === null) {
|
|
2189
|
+
return;
|
|
2190
|
+
}
|
|
2191
|
+
if (state.index_count > 0) {
|
|
2192
|
+
flush_webgl(state);
|
|
2193
|
+
}
|
|
2194
|
+
const gl = state.gl;
|
|
2195
|
+
gl.useProgram(state.texture_program);
|
|
2196
|
+
gl.uniformMatrix3fv(state.texture_locations.transform, false, state.current_transform);
|
|
2197
|
+
gl.uniform2f(state.texture_locations.resolution, state.width, state.height);
|
|
2198
|
+
gl.uniform1f(state.texture_locations.alpha, opacity);
|
|
2199
|
+
const x = r[0];
|
|
2200
|
+
const y = r[1];
|
|
2201
|
+
const w = r[2];
|
|
2202
|
+
const h = r[3];
|
|
2203
|
+
const positions = new Float32Array([x, y, x + w, y, x, y + h, x, y + h, x + w, y, x + w, y + h]);
|
|
2204
|
+
const texcoords = new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1]);
|
|
2205
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, state.position_buffer);
|
|
2206
|
+
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.DYNAMIC_DRAW);
|
|
2207
|
+
gl.enableVertexAttribArray(state.texture_locations.position);
|
|
2208
|
+
gl.vertexAttribPointer(state.texture_locations.position, 2, gl.FLOAT, false, 0, 0);
|
|
2209
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, state.texcoord_buffer);
|
|
2210
|
+
gl.bufferData(gl.ARRAY_BUFFER, texcoords, gl.DYNAMIC_DRAW);
|
|
2211
|
+
gl.enableVertexAttribArray(state.texture_locations.texcoord);
|
|
2212
|
+
gl.vertexAttribPointer(state.texture_locations.texcoord, 2, gl.FLOAT, false, 0, 0);
|
|
2213
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
2214
|
+
gl.bindTexture(gl.TEXTURE_2D, texture.texture);
|
|
2215
|
+
gl.uniform1i(state.texture_locations.sampler, 0);
|
|
2216
|
+
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
2217
|
+
}
|
|
2218
|
+
function sync_texture_webgl(state, image, version) {
|
|
2219
|
+
if (!is_image_ready_webgl(image)) {
|
|
2220
|
+
return null;
|
|
2221
|
+
}
|
|
2222
|
+
const gl = state.gl;
|
|
2223
|
+
const width = image instanceof HTMLImageElement ? image.naturalWidth : image.width;
|
|
2224
|
+
const height = image instanceof HTMLImageElement ? image.naturalHeight : image.height;
|
|
2225
|
+
const refresh_canvas_each_draw = image instanceof HTMLCanvasElement && version === undefined;
|
|
2226
|
+
const source_key = image_source_key_webgl(image);
|
|
2227
|
+
let texture = state.texture_cache.get(image);
|
|
2228
|
+
if (texture === undefined) {
|
|
2229
|
+
const created = gl.createTexture();
|
|
2230
|
+
if (created === null) {
|
|
2231
|
+
return null;
|
|
2232
|
+
}
|
|
2233
|
+
texture = {
|
|
2234
|
+
texture: created,
|
|
2235
|
+
width,
|
|
2236
|
+
height,
|
|
2237
|
+
version: version ?? null,
|
|
2238
|
+
source_key
|
|
2239
|
+
};
|
|
2240
|
+
state.texture_cache.set(image, texture);
|
|
2241
|
+
gl.bindTexture(gl.TEXTURE_2D, created);
|
|
2242
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
2243
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
2244
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
2245
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
2246
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
|
2247
|
+
return texture;
|
|
2248
|
+
}
|
|
2249
|
+
const size_changed = texture.width !== width || texture.height !== height;
|
|
2250
|
+
const version_changed = version !== undefined && texture.version !== version;
|
|
2251
|
+
const source_changed = texture.source_key !== source_key;
|
|
2252
|
+
if (size_changed || refresh_canvas_each_draw || version_changed || source_changed) {
|
|
2253
|
+
gl.bindTexture(gl.TEXTURE_2D, texture.texture);
|
|
2254
|
+
if (size_changed) {
|
|
2255
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
|
2256
|
+
} else {
|
|
2257
|
+
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
|
2258
|
+
}
|
|
2259
|
+
texture.width = width;
|
|
2260
|
+
texture.height = height;
|
|
2261
|
+
texture.source_key = source_key;
|
|
2262
|
+
}
|
|
2263
|
+
if (version !== undefined) {
|
|
2264
|
+
texture.version = version;
|
|
2265
|
+
}
|
|
2266
|
+
return texture;
|
|
2267
|
+
}
|
|
2268
|
+
function is_image_ready_webgl(image) {
|
|
2269
|
+
if (image instanceof HTMLImageElement) {
|
|
2270
|
+
return image.complete && image.naturalWidth > 0 && image.naturalHeight > 0;
|
|
2271
|
+
}
|
|
2272
|
+
return image.width > 0 && image.height > 0;
|
|
2273
|
+
}
|
|
2274
|
+
function image_source_key_webgl(image) {
|
|
2275
|
+
if (image instanceof HTMLImageElement) {
|
|
2276
|
+
return image.currentSrc || image.src || null;
|
|
2277
|
+
}
|
|
2278
|
+
if (typeof ImageBitmap !== "undefined" && image instanceof ImageBitmap) {
|
|
2279
|
+
return `bitmap:${image.width}x${image.height}`;
|
|
2280
|
+
}
|
|
2281
|
+
return null;
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
// renderer/webgl/transforms.ts
|
|
2285
|
+
function set_transform_webgl(state, t) {
|
|
2286
|
+
state.current_transform[0] = t[0];
|
|
2287
|
+
state.current_transform[1] = t[2];
|
|
2288
|
+
state.current_transform[2] = t[4];
|
|
2289
|
+
state.current_transform[3] = t[1];
|
|
2290
|
+
state.current_transform[4] = t[3];
|
|
2291
|
+
state.current_transform[5] = t[5];
|
|
2292
|
+
state.current_transform[6] = 0;
|
|
2293
|
+
state.current_transform[7] = 0;
|
|
2294
|
+
state.current_transform[8] = 1;
|
|
2295
|
+
}
|
|
2296
|
+
function reset_transform_webgl(state) {
|
|
2297
|
+
state.current_transform[0] = 1;
|
|
2298
|
+
state.current_transform[1] = 0;
|
|
2299
|
+
state.current_transform[2] = 0;
|
|
2300
|
+
state.current_transform[3] = 0;
|
|
2301
|
+
state.current_transform[4] = 1;
|
|
2302
|
+
state.current_transform[5] = 0;
|
|
2303
|
+
state.current_transform[6] = 0;
|
|
2304
|
+
state.current_transform[7] = 0;
|
|
2305
|
+
state.current_transform[8] = 1;
|
|
2306
|
+
}
|
|
2307
|
+
function save_webgl(state) {
|
|
2308
|
+
const copy = new Float32Array(9);
|
|
2309
|
+
copy.set(state.current_transform);
|
|
2310
|
+
state.transform_stack.push(copy);
|
|
2311
|
+
}
|
|
2312
|
+
function restore_webgl(state) {
|
|
2313
|
+
const previous = state.transform_stack.pop();
|
|
2314
|
+
if (previous !== undefined) {
|
|
2315
|
+
state.current_transform.set(previous);
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
// renderer/webgl/index.ts
|
|
2320
|
+
function create_renderer_webgl(canvas, config) {
|
|
2321
|
+
const gl = canvas.getContext("webgl", { alpha: true, antialias: true });
|
|
2322
|
+
if (gl === null) {
|
|
2323
|
+
return null;
|
|
2324
|
+
}
|
|
2325
|
+
const color_program = create_color_program_webgl(gl);
|
|
2326
|
+
const texture_program = create_texture_program_webgl(gl);
|
|
2327
|
+
if (color_program === null || texture_program === null) {
|
|
2328
|
+
if (color_program !== null) {
|
|
2329
|
+
gl.deleteProgram(color_program.program);
|
|
2330
|
+
}
|
|
2331
|
+
if (texture_program !== null) {
|
|
2332
|
+
gl.deleteProgram(texture_program.program);
|
|
2333
|
+
}
|
|
2334
|
+
return null;
|
|
2335
|
+
}
|
|
2336
|
+
const max_vertices = 65536;
|
|
2337
|
+
const max_indices = max_vertices * 3;
|
|
2338
|
+
const state = {
|
|
2339
|
+
gl,
|
|
2340
|
+
canvas,
|
|
2341
|
+
program: color_program.program,
|
|
2342
|
+
texture_program: texture_program.program,
|
|
2343
|
+
program_locations: color_program.locations,
|
|
2344
|
+
texture_locations: texture_program.locations,
|
|
2345
|
+
position_buffer: gl.createBuffer(),
|
|
2346
|
+
color_buffer: gl.createBuffer(),
|
|
2347
|
+
texcoord_buffer: gl.createBuffer(),
|
|
2348
|
+
index_buffer: gl.createBuffer(),
|
|
2349
|
+
vertices: new Float32Array(max_vertices * 2),
|
|
2350
|
+
colors: new Float32Array(max_vertices * 4),
|
|
2351
|
+
indices: new Uint16Array(max_indices),
|
|
2352
|
+
vertex_count: 0,
|
|
2353
|
+
index_count: 0,
|
|
2354
|
+
max_vertices,
|
|
2355
|
+
max_indices,
|
|
2356
|
+
current_transform: new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1]),
|
|
2357
|
+
transform_stack: [],
|
|
2358
|
+
fill_cache: new Map,
|
|
2359
|
+
stroke_cache: new Map,
|
|
2360
|
+
texture_cache: new Map,
|
|
2361
|
+
cache_max_size: config.path_cache_size,
|
|
2362
|
+
width: canvas.width,
|
|
2363
|
+
height: canvas.height,
|
|
2364
|
+
pixel_ratio: 1,
|
|
2365
|
+
scissor_enabled: false
|
|
2366
|
+
};
|
|
2367
|
+
gl.enable(gl.BLEND);
|
|
2368
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
2369
|
+
return {
|
|
2370
|
+
kind: renderer_kind_webgl,
|
|
2371
|
+
get width() {
|
|
2372
|
+
return state.width;
|
|
2373
|
+
},
|
|
2374
|
+
get height() {
|
|
2375
|
+
return state.height;
|
|
2376
|
+
},
|
|
2377
|
+
get pixel_ratio() {
|
|
2378
|
+
return state.pixel_ratio;
|
|
2379
|
+
},
|
|
2380
|
+
begin_frame: () => begin_frame_webgl(state),
|
|
2381
|
+
end_frame: () => end_frame_webgl(state),
|
|
2382
|
+
set_transform: (t) => set_transform_webgl(state, t),
|
|
2383
|
+
reset_transform: () => reset_transform_webgl(state),
|
|
2384
|
+
save: () => save_webgl(state),
|
|
2385
|
+
restore: () => restore_webgl(state),
|
|
2386
|
+
draw_path: (p, s) => draw_path_webgl(state, p, s),
|
|
2387
|
+
draw_rectangle: (r, s) => draw_rectangle_webgl(state, r, s),
|
|
2388
|
+
draw_circle: (c, s) => draw_circle_webgl(state, c, s),
|
|
2389
|
+
draw_line: (a, b, s) => draw_line_webgl(state, a, b, s),
|
|
2390
|
+
draw_polyline: (points, s) => draw_polyline_webgl(state, points, s),
|
|
2391
|
+
draw_image: (image, r, opacity, version) => draw_image_webgl(state, image, r, opacity, version),
|
|
2392
|
+
clip_rectangle: (r) => clip_rectangle_webgl(state, r),
|
|
2393
|
+
clear: (c) => clear_webgl(state, c),
|
|
2394
|
+
resize: (width, height, pixel_ratio) => resize_webgl(state, width, height, pixel_ratio),
|
|
2395
|
+
dispose: () => dispose_webgl(state)
|
|
2396
|
+
};
|
|
2397
|
+
}
|
|
2398
|
+
// renderer/renderer.ts
|
|
2399
|
+
var renderer_kind_canvas = 0;
|
|
2400
|
+
var renderer_kind_webgl = 1;
|
|
2401
|
+
function create_renderer_config() {
|
|
2402
|
+
return {
|
|
2403
|
+
prefer_webgl: false,
|
|
2404
|
+
path_cache_size: 1024
|
|
2405
|
+
};
|
|
2406
|
+
}
|
|
2407
|
+
function create_renderer(canvas, config) {
|
|
2408
|
+
if (config.prefer_webgl) {
|
|
2409
|
+
const webgl_renderer = create_renderer_webgl(canvas, config);
|
|
2410
|
+
if (webgl_renderer !== null) {
|
|
2411
|
+
return webgl_renderer;
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
return create_renderer_canvas(canvas, config);
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
// engine/shared.ts
|
|
2418
|
+
var default_background = [1, 1, 1, 1];
|
|
2419
|
+
var default_element_fill = [0.95, 0.97, 1, 1];
|
|
2420
|
+
var default_element_stroke = [0.18, 0.22, 0.31, 1];
|
|
2421
|
+
var min_zoom_engine = 0.0001;
|
|
2422
|
+
var max_zoom_engine = 1e6;
|
|
2423
|
+
var scratch_camera = [0, 0, 1, 0];
|
|
2424
|
+
var scratch_frustum = [0, 0, 0, 0, 0, 0];
|
|
2425
|
+
var scratch_transform = [1, 0, 0, 1, 0, 0];
|
|
2426
|
+
var scratch_world = [0, 0];
|
|
2427
|
+
var scratch_screen = [0, 0];
|
|
2428
|
+
|
|
2429
|
+
// camera/projection.ts
|
|
2430
|
+
function world_to_screen_camera(c, v, world_pos, out) {
|
|
2431
|
+
const dx = world_pos[0] - c[0];
|
|
2432
|
+
const dy = world_pos[1] - c[1];
|
|
2433
|
+
const cos_r = Math.cos(c[3]);
|
|
2434
|
+
const sin_r = Math.sin(c[3]);
|
|
2435
|
+
const rotated_x = dx * cos_r - dy * sin_r;
|
|
2436
|
+
const rotated_y = dx * sin_r + dy * cos_r;
|
|
2437
|
+
const screen_x = rotated_x * c[2] + v[0] / 2;
|
|
2438
|
+
const screen_y = rotated_y * c[2] + v[1] / 2;
|
|
2439
|
+
out[0] = screen_x;
|
|
2440
|
+
out[1] = screen_y;
|
|
2441
|
+
return out;
|
|
2442
|
+
}
|
|
2443
|
+
function screen_to_world_camera(c, v, screen_pos, out) {
|
|
2444
|
+
if (c[2] <= 0) {
|
|
2445
|
+
out[0] = c[0];
|
|
2446
|
+
out[1] = c[1];
|
|
2447
|
+
return out;
|
|
2448
|
+
}
|
|
2449
|
+
const dx = screen_pos[0] - v[0] / 2;
|
|
2450
|
+
const dy = screen_pos[1] - v[1] / 2;
|
|
2451
|
+
const cos_r = Math.cos(-c[3]);
|
|
2452
|
+
const sin_r = Math.sin(-c[3]);
|
|
2453
|
+
const scaled_x = dx / c[2];
|
|
2454
|
+
const scaled_y = dy / c[2];
|
|
2455
|
+
const rotated_x = scaled_x * cos_r - scaled_y * sin_r;
|
|
2456
|
+
const rotated_y = scaled_x * sin_r + scaled_y * cos_r;
|
|
2457
|
+
out[0] = rotated_x + c[0];
|
|
2458
|
+
out[1] = rotated_y + c[1];
|
|
2459
|
+
return out;
|
|
2460
|
+
}
|
|
2461
|
+
function view_transform_camera(c, out) {
|
|
2462
|
+
const cos_r = Math.cos(c[3]);
|
|
2463
|
+
const sin_r = Math.sin(c[3]);
|
|
2464
|
+
out[0] = cos_r * c[2];
|
|
2465
|
+
out[1] = sin_r * c[2];
|
|
2466
|
+
out[2] = -sin_r * c[2];
|
|
2467
|
+
out[3] = cos_r * c[2];
|
|
2468
|
+
out[4] = -c[0] * cos_r * c[2] + c[1] * sin_r * c[2];
|
|
2469
|
+
out[5] = -c[0] * sin_r * c[2] - c[1] * cos_r * c[2];
|
|
2470
|
+
return out;
|
|
2471
|
+
}
|
|
2472
|
+
function inverse_view_transform_camera(c, out) {
|
|
2473
|
+
const cos_r = Math.cos(c[3]);
|
|
2474
|
+
const sin_r = Math.sin(c[3]);
|
|
2475
|
+
const safe_zoom = c[2] <= 0 ? 0.0001 : c[2];
|
|
2476
|
+
const inverse_zoom = 1 / safe_zoom;
|
|
2477
|
+
out[0] = cos_r * inverse_zoom;
|
|
2478
|
+
out[1] = -sin_r * inverse_zoom;
|
|
2479
|
+
out[2] = sin_r * inverse_zoom;
|
|
2480
|
+
out[3] = cos_r * inverse_zoom;
|
|
2481
|
+
out[4] = c[0];
|
|
2482
|
+
out[5] = c[1];
|
|
2483
|
+
return out;
|
|
2484
|
+
}
|
|
2485
|
+
function camera_frustum(c, v, out) {
|
|
2486
|
+
if (c[2] <= 0) {
|
|
2487
|
+
out[0] = c[0];
|
|
2488
|
+
out[1] = c[0];
|
|
2489
|
+
out[2] = c[1];
|
|
2490
|
+
out[3] = c[1];
|
|
2491
|
+
out[4] = 0;
|
|
2492
|
+
out[5] = Number.MAX_VALUE;
|
|
2493
|
+
return out;
|
|
2494
|
+
}
|
|
2495
|
+
const half_width = v[0] / (2 * c[2]);
|
|
2496
|
+
const half_height = v[1] / (2 * c[2]);
|
|
2497
|
+
if (c[3] === 0) {
|
|
2498
|
+
out[0] = c[0] - half_width;
|
|
2499
|
+
out[1] = c[0] + half_width;
|
|
2500
|
+
out[2] = c[1] + half_height;
|
|
2501
|
+
out[3] = c[1] - half_height;
|
|
2502
|
+
out[4] = 0;
|
|
2503
|
+
out[5] = Number.MAX_VALUE;
|
|
2504
|
+
return out;
|
|
2505
|
+
}
|
|
2506
|
+
const cos_r = Math.abs(Math.cos(c[3]));
|
|
2507
|
+
const sin_r = Math.abs(Math.sin(c[3]));
|
|
2508
|
+
const x_extent = half_width * cos_r + half_height * sin_r;
|
|
2509
|
+
const y_extent = half_width * sin_r + half_height * cos_r;
|
|
2510
|
+
out[0] = c[0] - x_extent;
|
|
2511
|
+
out[1] = c[0] + x_extent;
|
|
2512
|
+
out[2] = c[1] + y_extent;
|
|
2513
|
+
out[3] = c[1] - y_extent;
|
|
2514
|
+
out[4] = 0;
|
|
2515
|
+
out[5] = Number.MAX_VALUE;
|
|
2516
|
+
return out;
|
|
2517
|
+
}
|
|
2518
|
+
function fit_camera_to_bounds(bounds, v, margin = 0.1, out) {
|
|
2519
|
+
const bounds_width = bounds[2];
|
|
2520
|
+
const bounds_height = bounds[3];
|
|
2521
|
+
if (bounds_width <= 0 || bounds_height <= 0) {
|
|
2522
|
+
out[0] = bounds[0];
|
|
2523
|
+
out[1] = bounds[1];
|
|
2524
|
+
out[2] = 1;
|
|
2525
|
+
out[3] = 0;
|
|
2526
|
+
return out;
|
|
2527
|
+
}
|
|
2528
|
+
const viewport_width = v[0] * (1 - margin * 2);
|
|
2529
|
+
const viewport_height = v[1] * (1 - margin * 2);
|
|
2530
|
+
if (viewport_width <= 0 || viewport_height <= 0) {
|
|
2531
|
+
out[0] = bounds[0] + bounds_width / 2;
|
|
2532
|
+
out[1] = bounds[1] + bounds_height / 2;
|
|
2533
|
+
out[2] = 1;
|
|
2534
|
+
out[3] = 0;
|
|
2535
|
+
return out;
|
|
2536
|
+
}
|
|
2537
|
+
const zoom_x = viewport_width / bounds_width;
|
|
2538
|
+
const zoom_y = viewport_height / bounds_height;
|
|
2539
|
+
const zoom = Math.min(zoom_x, zoom_y);
|
|
2540
|
+
const center_x = bounds[0] + bounds_width / 2;
|
|
2541
|
+
const center_y = bounds[1] + bounds_height / 2;
|
|
2542
|
+
out[0] = center_x;
|
|
2543
|
+
out[1] = center_y;
|
|
2544
|
+
out[2] = zoom;
|
|
2545
|
+
out[3] = 0;
|
|
2546
|
+
return out;
|
|
2547
|
+
}
|
|
2548
|
+
function contains_point_camera_frustum(c, v, world_x, world_y) {
|
|
2549
|
+
const dx = world_x - c[0];
|
|
2550
|
+
const dy = world_y - c[1];
|
|
2551
|
+
const cos_r = Math.cos(-c[3]);
|
|
2552
|
+
const sin_r = Math.sin(-c[3]);
|
|
2553
|
+
const rotated_x = dx * cos_r - dy * sin_r;
|
|
2554
|
+
const rotated_y = dx * sin_r + dy * cos_r;
|
|
2555
|
+
const scaled_x = rotated_x * c[2];
|
|
2556
|
+
const scaled_y = rotated_y * c[2];
|
|
2557
|
+
const half_width = v[0] / 2;
|
|
2558
|
+
const half_height = v[1] / 2;
|
|
2559
|
+
return scaled_x >= -half_width && scaled_x <= half_width && scaled_y >= -half_height && scaled_y <= half_height;
|
|
2560
|
+
}
|
|
2561
|
+
function width_of_camera_world(c, v) {
|
|
2562
|
+
const safe_zoom = c[2] <= 0 ? 0.0001 : c[2];
|
|
2563
|
+
return v[0] / safe_zoom;
|
|
2564
|
+
}
|
|
2565
|
+
function height_of_camera_world(c, v) {
|
|
2566
|
+
const safe_zoom = c[2] <= 0 ? 0.0001 : c[2];
|
|
2567
|
+
return v[1] / safe_zoom;
|
|
2568
|
+
}
|
|
2569
|
+
function pixel_size_of_camera(c) {
|
|
2570
|
+
const safe_zoom = c[2] <= 0 ? 0.0001 : c[2];
|
|
2571
|
+
return 1 / safe_zoom;
|
|
2572
|
+
}
|
|
2573
|
+
function pixel_ratio_of_viewport(v) {
|
|
2574
|
+
return v[2];
|
|
2575
|
+
}
|
|
2576
|
+
function device_width_of_viewport(v) {
|
|
2577
|
+
return v[0] * v[2];
|
|
2578
|
+
}
|
|
2579
|
+
function device_height_of_viewport(v) {
|
|
2580
|
+
return v[1] * v[2];
|
|
2581
|
+
}
|
|
2582
|
+
function world_to_device_camera(c, v, world_pos, out) {
|
|
2583
|
+
world_to_screen_camera(c, v, world_pos, out);
|
|
2584
|
+
out[0] = out[0] * v[2];
|
|
2585
|
+
out[1] = out[1] * v[2];
|
|
2586
|
+
return out;
|
|
2587
|
+
}
|
|
2588
|
+
function device_to_world_camera(c, v, device_pos, out) {
|
|
2589
|
+
const screen_pos = [device_pos[0] / v[2], device_pos[1] / v[2]];
|
|
2590
|
+
return screen_to_world_camera(c, v, screen_pos, out);
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
// engine/camera.ts
|
|
2594
|
+
function set_camera_engine(state, x, y, zoom, rotation = 0) {
|
|
2595
|
+
set_camera(sanitize_number_engine(x, state.camera[0]), sanitize_number_engine(y, state.camera[1]), sanitize_zoom_engine(zoom, state.camera[2]), sanitize_number_engine(rotation, state.camera[3]), state.camera);
|
|
2596
|
+
return state;
|
|
2597
|
+
}
|
|
2598
|
+
function pan_engine(state, dx, dy) {
|
|
2599
|
+
pan_camera(state.camera, dx, dy, scratch_camera);
|
|
2600
|
+
copy_camera_into_engine(state, scratch_camera);
|
|
2601
|
+
return state;
|
|
2602
|
+
}
|
|
2603
|
+
function zoom_engine(state, factor) {
|
|
2604
|
+
zoom_camera(state.camera, factor, scratch_camera);
|
|
2605
|
+
copy_camera_into_engine(state, scratch_camera);
|
|
2606
|
+
return state;
|
|
2607
|
+
}
|
|
2608
|
+
function zoom_at_engine(state, factor, screen_x, screen_y) {
|
|
2609
|
+
scratch_screen[0] = screen_x;
|
|
2610
|
+
scratch_screen[1] = screen_y;
|
|
2611
|
+
screen_to_world_camera(state.camera, state.viewport, scratch_screen, scratch_world);
|
|
2612
|
+
zoom_at_camera(state.camera, factor, {
|
|
2613
|
+
screen_x,
|
|
2614
|
+
screen_y,
|
|
2615
|
+
world_x: scratch_world[0],
|
|
2616
|
+
world_y: scratch_world[1]
|
|
2617
|
+
}, scratch_camera);
|
|
2618
|
+
copy_camera_into_engine(state, scratch_camera);
|
|
2619
|
+
return state;
|
|
2620
|
+
}
|
|
2621
|
+
function resize_engine(state, width, height, pixel_ratio = Math.max(1, globalThis.devicePixelRatio || 1)) {
|
|
2622
|
+
const safe_width = Math.max(1, width);
|
|
2623
|
+
const safe_height = Math.max(1, height);
|
|
2624
|
+
const safe_pixel_ratio = Math.max(1, pixel_ratio);
|
|
2625
|
+
state.viewport[0] = safe_width;
|
|
2626
|
+
state.viewport[1] = safe_height;
|
|
2627
|
+
state.viewport[2] = safe_pixel_ratio;
|
|
2628
|
+
state.renderer.resize(safe_width, safe_height, safe_pixel_ratio);
|
|
2629
|
+
return state;
|
|
2630
|
+
}
|
|
2631
|
+
function world_to_screen_engine(state, world, out) {
|
|
2632
|
+
return world_to_screen_camera(state.camera, state.viewport, world, out);
|
|
2633
|
+
}
|
|
2634
|
+
function screen_to_world_engine(state, screen, out) {
|
|
2635
|
+
return screen_to_world_camera(state.camera, state.viewport, screen, out);
|
|
2636
|
+
}
|
|
2637
|
+
function visible_bounds_world_engine(state) {
|
|
2638
|
+
camera_frustum(state.camera, state.viewport, scratch_frustum);
|
|
2639
|
+
return [
|
|
2640
|
+
scratch_frustum[0],
|
|
2641
|
+
scratch_frustum[3],
|
|
2642
|
+
scratch_frustum[1] - scratch_frustum[0],
|
|
2643
|
+
scratch_frustum[2] - scratch_frustum[3]
|
|
2644
|
+
];
|
|
2645
|
+
}
|
|
2646
|
+
function camera_to_screen_transform_engine(c, v, out) {
|
|
2647
|
+
const cos_r = Math.cos(c[3]);
|
|
2648
|
+
const sin_r = Math.sin(c[3]);
|
|
2649
|
+
const zoom = c[2];
|
|
2650
|
+
out[0] = cos_r * zoom;
|
|
2651
|
+
out[1] = sin_r * zoom;
|
|
2652
|
+
out[2] = -sin_r * zoom;
|
|
2653
|
+
out[3] = cos_r * zoom;
|
|
2654
|
+
out[4] = v[0] / 2 - c[0] * cos_r * zoom + c[1] * sin_r * zoom;
|
|
2655
|
+
out[5] = v[1] / 2 - c[0] * sin_r * zoom - c[1] * cos_r * zoom;
|
|
2656
|
+
return out;
|
|
2657
|
+
}
|
|
2658
|
+
function sanitize_number_engine(value, fallback) {
|
|
2659
|
+
if (Number.isFinite(value)) {
|
|
2660
|
+
return value;
|
|
2661
|
+
}
|
|
2662
|
+
return Number.isFinite(fallback) ? fallback : 0;
|
|
2663
|
+
}
|
|
2664
|
+
function sanitize_zoom_engine(value, fallback) {
|
|
2665
|
+
const safe_value = Number.isFinite(value) ? value : fallback;
|
|
2666
|
+
if (!Number.isFinite(safe_value) || safe_value <= 0) {
|
|
2667
|
+
return 1;
|
|
2668
|
+
}
|
|
2669
|
+
return Math.min(max_zoom_engine, Math.max(min_zoom_engine, safe_value));
|
|
2670
|
+
}
|
|
2671
|
+
function copy_camera_into_engine(state, value) {
|
|
2672
|
+
state.camera[0] = sanitize_number_engine(value[0], state.camera[0]);
|
|
2673
|
+
state.camera[1] = sanitize_number_engine(value[1], state.camera[1]);
|
|
2674
|
+
state.camera[2] = sanitize_zoom_engine(value[2], state.camera[2]);
|
|
2675
|
+
state.camera[3] = sanitize_number_engine(value[3], state.camera[3]);
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2678
|
+
// engine/create.ts
|
|
2679
|
+
function create_engine(canvas, options = {}) {
|
|
2680
|
+
const defaults = create_renderer_config();
|
|
2681
|
+
const renderer_config = {
|
|
2682
|
+
prefer_webgl: options.prefer_webgl ?? defaults.prefer_webgl,
|
|
2683
|
+
path_cache_size: options.path_cache_size ?? defaults.path_cache_size
|
|
2684
|
+
};
|
|
2685
|
+
const render_backend = create_renderer(canvas, renderer_config);
|
|
2686
|
+
if (render_backend === null) {
|
|
2687
|
+
return null;
|
|
2688
|
+
}
|
|
2689
|
+
const initial_width = Math.max(1, canvas.clientWidth || canvas.width || 1);
|
|
2690
|
+
const initial_height = Math.max(1, canvas.clientHeight || canvas.height || 1);
|
|
2691
|
+
const initial_pixel_ratio = Math.max(1, globalThis.devicePixelRatio || 1);
|
|
2692
|
+
const initial_viewport = [initial_width, initial_height, initial_pixel_ratio];
|
|
2693
|
+
render_backend.resize(initial_width, initial_height, initial_pixel_ratio);
|
|
2694
|
+
let doc = options.document;
|
|
2695
|
+
if (doc === undefined) {
|
|
2696
|
+
const created = create_document("doc_0", "layer_0");
|
|
2697
|
+
doc = add_layer_document(created, create_layer("layer_0", "Layer 1"));
|
|
2698
|
+
}
|
|
2699
|
+
const initial_camera = create_camera(0, 0, 1, 0);
|
|
2700
|
+
if (options.camera !== undefined) {
|
|
2701
|
+
initial_camera[0] = sanitize_number_engine(options.camera[0], 0);
|
|
2702
|
+
initial_camera[1] = sanitize_number_engine(options.camera[1], 0);
|
|
2703
|
+
initial_camera[2] = sanitize_zoom_engine(options.camera[2], 1);
|
|
2704
|
+
initial_camera[3] = sanitize_number_engine(options.camera[3], 0);
|
|
2705
|
+
}
|
|
2706
|
+
return {
|
|
2707
|
+
renderer: render_backend,
|
|
2708
|
+
document: doc,
|
|
2709
|
+
camera: initial_camera,
|
|
2710
|
+
viewport: [initial_viewport[0], initial_viewport[1], initial_viewport[2]],
|
|
2711
|
+
background: options.background ? [options.background[0], options.background[1], options.background[2], options.background[3]] : [
|
|
2712
|
+
default_background[0],
|
|
2713
|
+
default_background[1],
|
|
2714
|
+
default_background[2],
|
|
2715
|
+
default_background[3]
|
|
2716
|
+
],
|
|
2717
|
+
background_image: options.background_image ?? null
|
|
2718
|
+
};
|
|
2719
|
+
}
|
|
2720
|
+
function dispose_engine(state) {
|
|
2721
|
+
state.renderer.dispose();
|
|
2722
|
+
}
|
|
2723
|
+
function set_document_engine(state, doc) {
|
|
2724
|
+
state.document = doc;
|
|
2725
|
+
return state;
|
|
2726
|
+
}
|
|
2727
|
+
function set_background_engine(state, value) {
|
|
2728
|
+
state.background[0] = value[0];
|
|
2729
|
+
state.background[1] = value[1];
|
|
2730
|
+
state.background[2] = value[2];
|
|
2731
|
+
state.background[3] = value[3];
|
|
2732
|
+
return state;
|
|
2733
|
+
}
|
|
2734
|
+
function set_background_image_engine(state, value) {
|
|
2735
|
+
state.background_image = value;
|
|
2736
|
+
return state;
|
|
2737
|
+
}
|
|
2738
|
+
// document/element.ts
|
|
2739
|
+
var element_type_stroke = 0;
|
|
2740
|
+
var element_type_shape = 1;
|
|
2741
|
+
var element_type_image = 2;
|
|
2742
|
+
var element_type_text = 3;
|
|
2743
|
+
var shape_type_rectangle = 0;
|
|
2744
|
+
var shape_type_ellipse = 1;
|
|
2745
|
+
var shape_type_line = 2;
|
|
2746
|
+
var shape_type_arrow = 3;
|
|
2747
|
+
var shape_type_frame = 4;
|
|
2748
|
+
var text_align_left = 0;
|
|
2749
|
+
var text_align_center = 1;
|
|
2750
|
+
var text_align_right = 2;
|
|
2751
|
+
function create_stroke_element(id, bounds, z_index, layer_id, points, pressure, color, width, group_id = null) {
|
|
2752
|
+
return {
|
|
2753
|
+
type: element_type_stroke,
|
|
2754
|
+
id,
|
|
2755
|
+
bounds: [bounds[0], bounds[1], bounds[2], bounds[3]],
|
|
2756
|
+
z_index,
|
|
2757
|
+
layer_id,
|
|
2758
|
+
group_id,
|
|
2759
|
+
points: points.map((p) => [p[0], p[1]]),
|
|
2760
|
+
pressure: pressure === null ? null : [...pressure],
|
|
2761
|
+
color: [color[0], color[1], color[2], color[3]],
|
|
2762
|
+
width,
|
|
2763
|
+
simplified_points: null,
|
|
2764
|
+
spline: null
|
|
2765
|
+
};
|
|
2766
|
+
}
|
|
2767
|
+
function create_shape_element(id, bounds, z_index, layer_id, shape_type, fill_color, stroke_color, stroke_width, start_point, end_point, group_id = null) {
|
|
2768
|
+
return {
|
|
2769
|
+
type: element_type_shape,
|
|
2770
|
+
id,
|
|
2771
|
+
bounds: [bounds[0], bounds[1], bounds[2], bounds[3]],
|
|
2772
|
+
z_index,
|
|
2773
|
+
layer_id,
|
|
2774
|
+
group_id,
|
|
2775
|
+
shape_type,
|
|
2776
|
+
fill_color: fill_color === null ? null : [fill_color[0], fill_color[1], fill_color[2], fill_color[3]],
|
|
2777
|
+
stroke_color: stroke_color === null ? null : [stroke_color[0], stroke_color[1], stroke_color[2], stroke_color[3]],
|
|
2778
|
+
stroke_width,
|
|
2779
|
+
start_point: start_point === null ? null : [start_point[0], start_point[1]],
|
|
2780
|
+
end_point: end_point === null ? null : [end_point[0], end_point[1]]
|
|
2781
|
+
};
|
|
2782
|
+
}
|
|
2783
|
+
function create_image_element(id, bounds, z_index, layer_id, src, original_width, original_height, opacity, group_id = null, asset_id = null) {
|
|
2784
|
+
return {
|
|
2785
|
+
type: element_type_image,
|
|
2786
|
+
id,
|
|
2787
|
+
bounds: [bounds[0], bounds[1], bounds[2], bounds[3]],
|
|
2788
|
+
z_index,
|
|
2789
|
+
layer_id,
|
|
2790
|
+
group_id,
|
|
2791
|
+
asset_id,
|
|
2792
|
+
src,
|
|
2793
|
+
original_width,
|
|
2794
|
+
original_height,
|
|
2795
|
+
opacity,
|
|
2796
|
+
loaded: false,
|
|
2797
|
+
load_error: false,
|
|
2798
|
+
bitmap: null
|
|
2799
|
+
};
|
|
2800
|
+
}
|
|
2801
|
+
function create_text_element(id, bounds, z_index, layer_id, content, font_family, font_size, color, align, group_id = null) {
|
|
2802
|
+
return {
|
|
2803
|
+
type: element_type_text,
|
|
2804
|
+
id,
|
|
2805
|
+
bounds: [bounds[0], bounds[1], bounds[2], bounds[3]],
|
|
2806
|
+
z_index,
|
|
2807
|
+
layer_id,
|
|
2808
|
+
group_id,
|
|
2809
|
+
content,
|
|
2810
|
+
font_family,
|
|
2811
|
+
font_size,
|
|
2812
|
+
color: [color[0], color[1], color[2], color[3]],
|
|
2813
|
+
align
|
|
2814
|
+
};
|
|
2815
|
+
}
|
|
2816
|
+
function clone_element(el) {
|
|
2817
|
+
if (el.type === element_type_stroke) {
|
|
2818
|
+
const stroke = el;
|
|
2819
|
+
return create_stroke_element(stroke.id, stroke.bounds, stroke.z_index, stroke.layer_id, stroke.points, stroke.pressure, stroke.color, stroke.width, stroke.group_id);
|
|
2820
|
+
} else if (el.type === element_type_shape) {
|
|
2821
|
+
const shape = el;
|
|
2822
|
+
return create_shape_element(shape.id, shape.bounds, shape.z_index, shape.layer_id, shape.shape_type, shape.fill_color, shape.stroke_color, shape.stroke_width, shape.start_point, shape.end_point, shape.group_id);
|
|
2823
|
+
} else if (el.type === element_type_image) {
|
|
2824
|
+
const image = el;
|
|
2825
|
+
const cloned = create_image_element(image.id, image.bounds, image.z_index, image.layer_id, image.src, image.original_width, image.original_height, image.opacity, image.group_id, image.asset_id);
|
|
2826
|
+
cloned.loaded = image.loaded;
|
|
2827
|
+
cloned.load_error = image.load_error;
|
|
2828
|
+
cloned.bitmap = image.bitmap;
|
|
2829
|
+
return cloned;
|
|
2830
|
+
} else {
|
|
2831
|
+
const text = el;
|
|
2832
|
+
return create_text_element(text.id, text.bounds, text.z_index, text.layer_id, text.content, text.font_family, text.font_size, text.color, text.align, text.group_id);
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
function id_of_element(el) {
|
|
2836
|
+
return el.id;
|
|
2837
|
+
}
|
|
2838
|
+
function bounds_of_element(el) {
|
|
2839
|
+
return [el.bounds[0], el.bounds[1], el.bounds[2], el.bounds[3]];
|
|
2840
|
+
}
|
|
2841
|
+
function z_index_of_element(el) {
|
|
2842
|
+
return el.z_index;
|
|
2843
|
+
}
|
|
2844
|
+
function layer_id_of_element(el) {
|
|
2845
|
+
return el.layer_id;
|
|
2846
|
+
}
|
|
2847
|
+
function type_of_element(el) {
|
|
2848
|
+
return el.type;
|
|
2849
|
+
}
|
|
2850
|
+
function update_element_bounds(el, bounds) {
|
|
2851
|
+
const cloned = clone_element(el);
|
|
2852
|
+
const mutable = cloned;
|
|
2853
|
+
mutable.bounds = [bounds[0], bounds[1], bounds[2], bounds[3]];
|
|
2854
|
+
return cloned;
|
|
2855
|
+
}
|
|
2856
|
+
function update_element_z_index(el, z_index) {
|
|
2857
|
+
const cloned = clone_element(el);
|
|
2858
|
+
const mutable = cloned;
|
|
2859
|
+
mutable.z_index = z_index;
|
|
2860
|
+
return cloned;
|
|
2861
|
+
}
|
|
2862
|
+
function update_element_layer_id(el, layer_id) {
|
|
2863
|
+
const cloned = clone_element(el);
|
|
2864
|
+
const mutable = cloned;
|
|
2865
|
+
mutable.layer_id = layer_id;
|
|
2866
|
+
return cloned;
|
|
2867
|
+
}
|
|
2868
|
+
function update_stroke_points(el, points) {
|
|
2869
|
+
return {
|
|
2870
|
+
...el,
|
|
2871
|
+
points: points.map((p) => [p[0], p[1]]),
|
|
2872
|
+
simplified_points: null,
|
|
2873
|
+
spline: null
|
|
2874
|
+
};
|
|
2875
|
+
}
|
|
2876
|
+
function update_stroke_simplified_points(el, simplified) {
|
|
2877
|
+
return {
|
|
2878
|
+
...el,
|
|
2879
|
+
simplified_points: simplified === null ? null : simplified.map((p) => [p[0], p[1]])
|
|
2880
|
+
};
|
|
2881
|
+
}
|
|
2882
|
+
function update_stroke_spline(el, spline_data) {
|
|
2883
|
+
return {
|
|
2884
|
+
...el,
|
|
2885
|
+
spline: spline_data
|
|
2886
|
+
};
|
|
2887
|
+
}
|
|
2888
|
+
function update_image_loaded(el, loaded, bitmap, load_error = false) {
|
|
2889
|
+
return {
|
|
2890
|
+
...el,
|
|
2891
|
+
loaded,
|
|
2892
|
+
load_error,
|
|
2893
|
+
bitmap
|
|
2894
|
+
};
|
|
2895
|
+
}
|
|
2896
|
+
function update_image_asset_id(el, asset_id) {
|
|
2897
|
+
return {
|
|
2898
|
+
...el,
|
|
2899
|
+
asset_id
|
|
2900
|
+
};
|
|
2901
|
+
}
|
|
2902
|
+
function update_text_content(el, content) {
|
|
2903
|
+
return {
|
|
2904
|
+
...el,
|
|
2905
|
+
content
|
|
2906
|
+
};
|
|
2907
|
+
}
|
|
2908
|
+
function equals_element_identity(a, b) {
|
|
2909
|
+
if (a.type !== b.type || a.id !== b.id) {
|
|
2910
|
+
return false;
|
|
2911
|
+
}
|
|
2912
|
+
if (a.z_index !== b.z_index || a.layer_id !== b.layer_id) {
|
|
2913
|
+
return false;
|
|
2914
|
+
}
|
|
2915
|
+
if (a.group_id !== b.group_id) {
|
|
2916
|
+
return false;
|
|
2917
|
+
}
|
|
2918
|
+
if (a.bounds[0] !== b.bounds[0] || a.bounds[1] !== b.bounds[1] || a.bounds[2] !== b.bounds[2] || a.bounds[3] !== b.bounds[3]) {
|
|
2919
|
+
return false;
|
|
2920
|
+
}
|
|
2921
|
+
return true;
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2924
|
+
// math/quadtree.ts
|
|
2925
|
+
function create_quadtree_point(x, y, data = null) {
|
|
2926
|
+
return [x, y, data];
|
|
2927
|
+
}
|
|
2928
|
+
function x_of_quadtree_point(point) {
|
|
2929
|
+
return point[0];
|
|
2930
|
+
}
|
|
2931
|
+
function y_of_quadtree_point(point) {
|
|
2932
|
+
return point[1];
|
|
2933
|
+
}
|
|
2934
|
+
function data_of_quadtree_point(point) {
|
|
2935
|
+
return point[2];
|
|
2936
|
+
}
|
|
2937
|
+
function create_quadtree_node(boundary, capacity = 4, depth = 0) {
|
|
2938
|
+
return {
|
|
2939
|
+
boundary: [boundary[0], boundary[1], boundary[2], boundary[3]],
|
|
2940
|
+
points: [],
|
|
2941
|
+
children: null,
|
|
2942
|
+
capacity: Math.max(1, capacity),
|
|
2943
|
+
depth: Math.max(0, depth)
|
|
2944
|
+
};
|
|
2945
|
+
}
|
|
2946
|
+
function create_quadtree(boundary, capacity = 4, max_depth = 8) {
|
|
2947
|
+
const normalized_capacity = Math.max(1, capacity);
|
|
2948
|
+
const normalized_max_depth = Math.max(1, max_depth);
|
|
2949
|
+
return {
|
|
2950
|
+
root: create_quadtree_node(boundary, normalized_capacity, 0),
|
|
2951
|
+
capacity: normalized_capacity,
|
|
2952
|
+
max_depth: normalized_max_depth
|
|
2953
|
+
};
|
|
2954
|
+
}
|
|
2955
|
+
function is_leaf_quadtree_node(node) {
|
|
2956
|
+
return node.children === null;
|
|
2957
|
+
}
|
|
2958
|
+
function point_count_quadtree_node(node) {
|
|
2959
|
+
if (is_leaf_quadtree_node(node)) {
|
|
2960
|
+
return node.points.length;
|
|
2961
|
+
}
|
|
2962
|
+
let total = 0;
|
|
2963
|
+
if (node.children !== null) {
|
|
2964
|
+
for (let i = 0;i < 4; i = i + 1) {
|
|
2965
|
+
const child = node.children[i];
|
|
2966
|
+
if (child) {
|
|
2967
|
+
total = total + point_count_quadtree_node(child);
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
return total;
|
|
2972
|
+
}
|
|
2973
|
+
function subdivide_quadtree_node(node, max_depth) {
|
|
2974
|
+
if (node.children !== null) {
|
|
2975
|
+
return node;
|
|
2976
|
+
}
|
|
2977
|
+
const boundary = node.boundary;
|
|
2978
|
+
const half_width = boundary[2] / 2;
|
|
2979
|
+
const half_height = boundary[3] / 2;
|
|
2980
|
+
const x = boundary[0];
|
|
2981
|
+
const y = boundary[1];
|
|
2982
|
+
const next_depth = node.depth + 1;
|
|
2983
|
+
const new_children = [
|
|
2984
|
+
create_quadtree_node(create_rectangle(x, y, half_width, half_height), node.capacity, next_depth),
|
|
2985
|
+
create_quadtree_node(create_rectangle(x + half_width, y, half_width, half_height), node.capacity, next_depth),
|
|
2986
|
+
create_quadtree_node(create_rectangle(x, y + half_height, half_width, half_height), node.capacity, next_depth),
|
|
2987
|
+
create_quadtree_node(create_rectangle(x + half_width, y + half_height, half_width, half_height), node.capacity, next_depth)
|
|
2988
|
+
];
|
|
2989
|
+
let updated_children = [...new_children];
|
|
2990
|
+
for (let i = 0;i < node.points.length; i = i + 1) {
|
|
2991
|
+
const point = node.points[i];
|
|
2992
|
+
if (point) {
|
|
2993
|
+
for (let j = 0;j < 4; j = j + 1) {
|
|
2994
|
+
const child = updated_children[j];
|
|
2995
|
+
if (child) {
|
|
2996
|
+
const child_result = insert_quadtree_node(child, point, max_depth);
|
|
2997
|
+
if (child_result !== null) {
|
|
2998
|
+
updated_children[j] = child_result;
|
|
2999
|
+
break;
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
return {
|
|
3006
|
+
boundary: node.boundary,
|
|
3007
|
+
points: [],
|
|
3008
|
+
children: updated_children,
|
|
3009
|
+
capacity: node.capacity,
|
|
3010
|
+
depth: node.depth
|
|
3011
|
+
};
|
|
3012
|
+
}
|
|
3013
|
+
function insert_quadtree_node(node, point, max_depth) {
|
|
3014
|
+
if (!contains_point_rectangle(node.boundary, point[0], point[1])) {
|
|
3015
|
+
return null;
|
|
3016
|
+
}
|
|
3017
|
+
if (is_leaf_quadtree_node(node)) {
|
|
3018
|
+
if (node.points.length < node.capacity || node.depth >= max_depth) {
|
|
3019
|
+
const new_points = [...node.points, [point[0], point[1], point[2]]];
|
|
3020
|
+
return {
|
|
3021
|
+
boundary: node.boundary,
|
|
3022
|
+
points: new_points,
|
|
3023
|
+
children: null,
|
|
3024
|
+
capacity: node.capacity,
|
|
3025
|
+
depth: node.depth
|
|
3026
|
+
};
|
|
3027
|
+
}
|
|
3028
|
+
const subdivided = subdivide_quadtree_node(node, max_depth);
|
|
3029
|
+
return insert_quadtree_node(subdivided, point, max_depth);
|
|
3030
|
+
}
|
|
3031
|
+
if (node.children !== null && node.children.length === 4) {
|
|
3032
|
+
const new_children = [...node.children];
|
|
3033
|
+
for (let i = 0;i < 4; i = i + 1) {
|
|
3034
|
+
const child = node.children[i];
|
|
3035
|
+
if (child) {
|
|
3036
|
+
const child_result = insert_quadtree_node(child, point, max_depth);
|
|
3037
|
+
if (child_result !== null) {
|
|
3038
|
+
new_children[i] = child_result;
|
|
3039
|
+
return {
|
|
3040
|
+
boundary: node.boundary,
|
|
3041
|
+
points: node.points,
|
|
3042
|
+
children: new_children,
|
|
3043
|
+
capacity: node.capacity,
|
|
3044
|
+
depth: node.depth
|
|
3045
|
+
};
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
return null;
|
|
3051
|
+
}
|
|
3052
|
+
function insert_quadtree(tree, point) {
|
|
3053
|
+
const new_root = insert_quadtree_node(tree.root, point, tree.max_depth);
|
|
3054
|
+
if (new_root !== null) {
|
|
3055
|
+
return {
|
|
3056
|
+
root: new_root,
|
|
3057
|
+
capacity: tree.capacity,
|
|
3058
|
+
max_depth: tree.max_depth
|
|
3059
|
+
};
|
|
3060
|
+
}
|
|
3061
|
+
return tree;
|
|
3062
|
+
}
|
|
3063
|
+
function query_range_quadtree_node(node, range, out_results) {
|
|
3064
|
+
if (!intersects_rectangle(node.boundary, range)) {
|
|
3065
|
+
return out_results;
|
|
3066
|
+
}
|
|
3067
|
+
if (is_leaf_quadtree_node(node)) {
|
|
3068
|
+
for (let i = 0;i < node.points.length; i = i + 1) {
|
|
3069
|
+
const point = node.points[i];
|
|
3070
|
+
if (point && contains_point_rectangle(range, point[0], point[1])) {
|
|
3071
|
+
out_results.push([point[0], point[1], point[2]]);
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
return out_results;
|
|
3075
|
+
}
|
|
3076
|
+
if (node.children !== null && node.children.length === 4) {
|
|
3077
|
+
for (let i = 0;i < 4; i = i + 1) {
|
|
3078
|
+
const child = node.children[i];
|
|
3079
|
+
if (child) {
|
|
3080
|
+
query_range_quadtree_node(child, range, out_results);
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
return out_results;
|
|
3085
|
+
}
|
|
3086
|
+
function query_range_quadtree(tree, range, out_results) {
|
|
3087
|
+
return query_range_quadtree_node(tree.root, range, out_results);
|
|
3088
|
+
}
|
|
3089
|
+
function query_radius_quadtree_node(node, center_x, center_y, radius, out_results) {
|
|
3090
|
+
const radius_squared = radius * radius;
|
|
3091
|
+
const query_bounds = create_rectangle(center_x - radius, center_y - radius, radius * 2, radius * 2);
|
|
3092
|
+
const candidates = [];
|
|
3093
|
+
query_range_quadtree_node(node, query_bounds, candidates);
|
|
3094
|
+
for (let i = 0;i < candidates.length; i = i + 1) {
|
|
3095
|
+
const point = candidates[i];
|
|
3096
|
+
if (point) {
|
|
3097
|
+
const dx = point[0] - center_x;
|
|
3098
|
+
const dy = point[1] - center_y;
|
|
3099
|
+
const distance_squared = dx * dx + dy * dy;
|
|
3100
|
+
if (distance_squared <= radius_squared) {
|
|
3101
|
+
out_results.push([point[0], point[1], point[2]]);
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
return out_results;
|
|
3106
|
+
}
|
|
3107
|
+
function query_radius_quadtree(tree, center_x, center_y, radius, out_results) {
|
|
3108
|
+
return query_radius_quadtree_node(tree.root, center_x, center_y, radius, out_results);
|
|
3109
|
+
}
|
|
3110
|
+
function find_nearest_quadtree(tree, x, y, max_distance = Number.POSITIVE_INFINITY) {
|
|
3111
|
+
return find_nearest_quadtree_node(tree.root, x, y, max_distance);
|
|
3112
|
+
}
|
|
3113
|
+
function find_nearest_quadtree_node(node, x, y, max_distance = Number.POSITIVE_INFINITY) {
|
|
3114
|
+
let best_point = null;
|
|
3115
|
+
let best_distance_squared = max_distance * max_distance;
|
|
3116
|
+
function search_node_recursive(search_node) {
|
|
3117
|
+
if (is_leaf_quadtree_node(search_node)) {
|
|
3118
|
+
for (let i = 0;i < search_node.points.length; i = i + 1) {
|
|
3119
|
+
const point = search_node.points[i];
|
|
3120
|
+
if (point) {
|
|
3121
|
+
const dx = point[0] - x;
|
|
3122
|
+
const dy = point[1] - y;
|
|
3123
|
+
const distance_squared = dx * dx + dy * dy;
|
|
3124
|
+
if (distance_squared < best_distance_squared) {
|
|
3125
|
+
best_distance_squared = distance_squared;
|
|
3126
|
+
best_point = [point[0], point[1], point[2]];
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
} else if (search_node.children !== null && search_node.children.length === 4) {
|
|
3131
|
+
const child_distances = [];
|
|
3132
|
+
for (let i = 0;i < 4; i = i + 1) {
|
|
3133
|
+
const child = search_node.children[i];
|
|
3134
|
+
if (child) {
|
|
3135
|
+
const boundary = child.boundary;
|
|
3136
|
+
const closest_x = Math.max(boundary[0], Math.min(x, boundary[0] + boundary[2]));
|
|
3137
|
+
const closest_y = Math.max(boundary[1], Math.min(y, boundary[1] + boundary[3]));
|
|
3138
|
+
const dx = x - closest_x;
|
|
3139
|
+
const dy = y - closest_y;
|
|
3140
|
+
const distance = dx * dx + dy * dy;
|
|
3141
|
+
if (distance < best_distance_squared) {
|
|
3142
|
+
child_distances.push({ node: child, distance });
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
child_distances.sort((a, b) => a.distance - b.distance);
|
|
3147
|
+
for (let i = 0;i < child_distances.length; i = i + 1) {
|
|
3148
|
+
const child_info = child_distances[i];
|
|
3149
|
+
if (child_info && child_info.distance < best_distance_squared) {
|
|
3150
|
+
search_node_recursive(child_info.node);
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
search_node_recursive(node);
|
|
3156
|
+
return best_point;
|
|
3157
|
+
}
|
|
3158
|
+
function remove_quadtree_node(node, point) {
|
|
3159
|
+
if (!contains_point_rectangle(node.boundary, point[0], point[1])) {
|
|
3160
|
+
return node;
|
|
3161
|
+
}
|
|
3162
|
+
if (is_leaf_quadtree_node(node)) {
|
|
3163
|
+
const new_points = [];
|
|
3164
|
+
let removed = false;
|
|
3165
|
+
for (let i = 0;i < node.points.length; i = i + 1) {
|
|
3166
|
+
const existing = node.points[i];
|
|
3167
|
+
if (existing && existing[0] === point[0] && existing[1] === point[1] && !removed) {
|
|
3168
|
+
removed = true;
|
|
3169
|
+
} else if (existing) {
|
|
3170
|
+
new_points.push([existing[0], existing[1], existing[2]]);
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
return {
|
|
3174
|
+
boundary: node.boundary,
|
|
3175
|
+
points: new_points,
|
|
3176
|
+
children: null,
|
|
3177
|
+
capacity: node.capacity,
|
|
3178
|
+
depth: node.depth
|
|
3179
|
+
};
|
|
3180
|
+
}
|
|
3181
|
+
if (node.children !== null && node.children.length === 4) {
|
|
3182
|
+
const new_children = [...node.children];
|
|
3183
|
+
let changed = false;
|
|
3184
|
+
for (let i = 0;i < 4; i = i + 1) {
|
|
3185
|
+
const child = node.children[i];
|
|
3186
|
+
if (child) {
|
|
3187
|
+
const new_child = remove_quadtree_node(child, point);
|
|
3188
|
+
if (new_child !== child) {
|
|
3189
|
+
new_children[i] = new_child;
|
|
3190
|
+
changed = true;
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
if (changed) {
|
|
3195
|
+
let total_points = 0;
|
|
3196
|
+
for (let i = 0;i < 4; i = i + 1) {
|
|
3197
|
+
const child = new_children[i];
|
|
3198
|
+
if (child) {
|
|
3199
|
+
total_points = total_points + point_count_quadtree_node(child);
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
if (total_points <= node.capacity) {
|
|
3203
|
+
const all_points = [];
|
|
3204
|
+
collect_all_points_quadtree_node({ ...node, children: new_children }, all_points);
|
|
3205
|
+
return {
|
|
3206
|
+
boundary: node.boundary,
|
|
3207
|
+
points: all_points,
|
|
3208
|
+
children: null,
|
|
3209
|
+
capacity: node.capacity,
|
|
3210
|
+
depth: node.depth
|
|
3211
|
+
};
|
|
3212
|
+
}
|
|
3213
|
+
return {
|
|
3214
|
+
boundary: node.boundary,
|
|
3215
|
+
points: node.points,
|
|
3216
|
+
children: new_children,
|
|
3217
|
+
capacity: node.capacity,
|
|
3218
|
+
depth: node.depth
|
|
3219
|
+
};
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
return node;
|
|
3223
|
+
}
|
|
3224
|
+
function remove_quadtree(tree, point) {
|
|
3225
|
+
const new_root = remove_quadtree_node(tree.root, point);
|
|
3226
|
+
return {
|
|
3227
|
+
root: new_root,
|
|
3228
|
+
capacity: tree.capacity,
|
|
3229
|
+
max_depth: tree.max_depth
|
|
3230
|
+
};
|
|
3231
|
+
}
|
|
3232
|
+
function collect_all_points_quadtree_node(node, out_points) {
|
|
3233
|
+
if (is_leaf_quadtree_node(node)) {
|
|
3234
|
+
for (let i = 0;i < node.points.length; i = i + 1) {
|
|
3235
|
+
const point = node.points[i];
|
|
3236
|
+
if (point) {
|
|
3237
|
+
out_points.push([point[0], point[1], point[2]]);
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
} else if (node.children !== null && node.children.length === 4) {
|
|
3241
|
+
for (let i = 0;i < 4; i = i + 1) {
|
|
3242
|
+
const child = node.children[i];
|
|
3243
|
+
if (child) {
|
|
3244
|
+
collect_all_points_quadtree_node(child, out_points);
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
return out_points;
|
|
3249
|
+
}
|
|
3250
|
+
function clear_quadtree(tree) {
|
|
3251
|
+
return {
|
|
3252
|
+
root: create_quadtree_node(tree.root.boundary, tree.capacity, 0),
|
|
3253
|
+
capacity: tree.capacity,
|
|
3254
|
+
max_depth: tree.max_depth
|
|
3255
|
+
};
|
|
3256
|
+
}
|
|
3257
|
+
function point_count_quadtree(tree) {
|
|
3258
|
+
return point_count_quadtree_node(tree.root);
|
|
3259
|
+
}
|
|
3260
|
+
function depth_of_quadtree_node(node) {
|
|
3261
|
+
if (is_leaf_quadtree_node(node)) {
|
|
3262
|
+
return node.depth;
|
|
3263
|
+
}
|
|
3264
|
+
let max_depth = node.depth;
|
|
3265
|
+
if (node.children !== null && node.children.length === 4) {
|
|
3266
|
+
for (let i = 0;i < 4; i = i + 1) {
|
|
3267
|
+
const child = node.children[i];
|
|
3268
|
+
if (child) {
|
|
3269
|
+
const child_depth = depth_of_quadtree_node(child);
|
|
3270
|
+
if (child_depth > max_depth) {
|
|
3271
|
+
max_depth = child_depth;
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
return max_depth;
|
|
3277
|
+
}
|
|
3278
|
+
function depth_of_quadtree(tree) {
|
|
3279
|
+
return depth_of_quadtree_node(tree.root);
|
|
3280
|
+
}
|
|
3281
|
+
function is_empty_quadtree(tree) {
|
|
3282
|
+
return point_count_quadtree(tree) === 0;
|
|
3283
|
+
}
|
|
3284
|
+
function bounds_of_quadtree(tree, out_bounds) {
|
|
3285
|
+
out_bounds[0] = tree.root.boundary[0];
|
|
3286
|
+
out_bounds[1] = tree.root.boundary[1];
|
|
3287
|
+
out_bounds[2] = tree.root.boundary[2];
|
|
3288
|
+
out_bounds[3] = tree.root.boundary[3];
|
|
3289
|
+
return out_bounds;
|
|
3290
|
+
}
|
|
3291
|
+
function find_k_nearest_quadtree(tree, x, y, k, max_distance, out_results) {
|
|
3292
|
+
if (point_count_quadtree(tree) <= k * 2) {
|
|
3293
|
+
const all_points = [];
|
|
3294
|
+
collect_all_points_quadtree_node(tree.root, all_points);
|
|
3295
|
+
const distances2 = [];
|
|
3296
|
+
for (let i = 0;i < all_points.length; i = i + 1) {
|
|
3297
|
+
const point = all_points[i];
|
|
3298
|
+
if (point) {
|
|
3299
|
+
const dx = point[0] - x;
|
|
3300
|
+
const dy = point[1] - y;
|
|
3301
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
3302
|
+
if (distance <= max_distance) {
|
|
3303
|
+
distances2.push({ point, distance });
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
distances2.sort((a, b) => a.distance - b.distance);
|
|
3308
|
+
const limit2 = Math.min(k, distances2.length);
|
|
3309
|
+
for (let i = 0;i < limit2; i = i + 1) {
|
|
3310
|
+
const item = distances2[i];
|
|
3311
|
+
if (item && item.point) {
|
|
3312
|
+
out_results.push([item.point[0], item.point[1], item.point[2]]);
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
return out_results;
|
|
3316
|
+
}
|
|
3317
|
+
let current_radius = 100;
|
|
3318
|
+
const bounds = [0, 0, 0, 0];
|
|
3319
|
+
bounds_of_quadtree(tree, bounds);
|
|
3320
|
+
const max_possible_radius = Math.sqrt(bounds[2] * bounds[2] + bounds[3] * bounds[3]);
|
|
3321
|
+
while (current_radius <= max_possible_radius) {
|
|
3322
|
+
const candidates = [];
|
|
3323
|
+
query_radius_quadtree(tree, x, y, Math.min(current_radius, max_distance), candidates);
|
|
3324
|
+
if (candidates.length >= k) {
|
|
3325
|
+
const distances2 = [];
|
|
3326
|
+
for (let i = 0;i < candidates.length; i = i + 1) {
|
|
3327
|
+
const point = candidates[i];
|
|
3328
|
+
if (point) {
|
|
3329
|
+
const dx = point[0] - x;
|
|
3330
|
+
const dy = point[1] - y;
|
|
3331
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
3332
|
+
if (distance <= max_distance) {
|
|
3333
|
+
distances2.push({ point, distance });
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3336
|
+
}
|
|
3337
|
+
distances2.sort((a, b) => a.distance - b.distance);
|
|
3338
|
+
const limit2 = Math.min(k, distances2.length);
|
|
3339
|
+
for (let i = 0;i < limit2; i = i + 1) {
|
|
3340
|
+
const item = distances2[i];
|
|
3341
|
+
if (item && item.point) {
|
|
3342
|
+
out_results.push([item.point[0], item.point[1], item.point[2]]);
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
return out_results;
|
|
3346
|
+
}
|
|
3347
|
+
current_radius = current_radius * 2;
|
|
3348
|
+
}
|
|
3349
|
+
const final_candidates = [];
|
|
3350
|
+
query_radius_quadtree(tree, x, y, max_distance, final_candidates);
|
|
3351
|
+
const distances = [];
|
|
3352
|
+
for (let i = 0;i < final_candidates.length; i = i + 1) {
|
|
3353
|
+
const point = final_candidates[i];
|
|
3354
|
+
if (point) {
|
|
3355
|
+
const dx = point[0] - x;
|
|
3356
|
+
const dy = point[1] - y;
|
|
3357
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
3358
|
+
distances.push({ point, distance });
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
distances.sort((a, b) => a.distance - b.distance);
|
|
3362
|
+
const limit = Math.min(k, distances.length);
|
|
3363
|
+
for (let i = 0;i < limit; i = i + 1) {
|
|
3364
|
+
const item = distances[i];
|
|
3365
|
+
if (item && item.point) {
|
|
3366
|
+
out_results.push([item.point[0], item.point[1], item.point[2]]);
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
return out_results;
|
|
3370
|
+
}
|
|
3371
|
+
function detect_collisions_quadtree(tree, point, radius, out_collisions) {
|
|
3372
|
+
return query_radius_quadtree(tree, point[0], point[1], radius, out_collisions);
|
|
3373
|
+
}
|
|
3374
|
+
function detect_collisions_rectangle_quadtree(tree, rect, out_collisions) {
|
|
3375
|
+
return query_range_quadtree(tree, rect, out_collisions);
|
|
3376
|
+
}
|
|
3377
|
+
|
|
3378
|
+
// document/query.ts
|
|
3379
|
+
function elements_in_bounds_document(doc, bounds) {
|
|
3380
|
+
const results = [];
|
|
3381
|
+
for (const el of doc.elements.values()) {
|
|
3382
|
+
const el_bounds = el.bounds;
|
|
3383
|
+
if (el_bounds[0] < bounds[0] + bounds[2] && el_bounds[0] + el_bounds[2] > bounds[0] && el_bounds[1] < bounds[1] + bounds[3] && el_bounds[1] + el_bounds[3] > bounds[1]) {
|
|
3384
|
+
results.push(el);
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
return results;
|
|
3388
|
+
}
|
|
3389
|
+
|
|
3390
|
+
// engine/elements.ts
|
|
3391
|
+
var loaded_images_engine = new Map;
|
|
3392
|
+
var text_bitmap_cache_engine = new Map;
|
|
3393
|
+
var text_bitmap_cache_limit_engine = 128;
|
|
3394
|
+
function render_element_engine(drawer, el, asset_src = null) {
|
|
3395
|
+
if (el.type === element_type_stroke) {
|
|
3396
|
+
render_stroke_element_engine(drawer, el);
|
|
3397
|
+
return;
|
|
3398
|
+
}
|
|
3399
|
+
if (el.type === element_type_shape) {
|
|
3400
|
+
render_shape_element_engine(drawer, el);
|
|
3401
|
+
return;
|
|
3402
|
+
}
|
|
3403
|
+
if (el.type === element_type_image) {
|
|
3404
|
+
render_image_element_engine(drawer, el, asset_src);
|
|
3405
|
+
return;
|
|
3406
|
+
}
|
|
3407
|
+
if (el.type === element_type_text) {
|
|
3408
|
+
render_text_element_engine(drawer, el);
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
function render_stroke_element_engine(drawer, el) {
|
|
3412
|
+
const style = {
|
|
3413
|
+
fill: null,
|
|
3414
|
+
stroke: el.color,
|
|
3415
|
+
stroke_width: el.width,
|
|
3416
|
+
line_cap: 1,
|
|
3417
|
+
line_join: 1,
|
|
3418
|
+
miter_limit: 10,
|
|
3419
|
+
alpha: el.color[3]
|
|
3420
|
+
};
|
|
3421
|
+
const points = el.simplified_points && el.simplified_points.length > 1 ? el.simplified_points : el.points;
|
|
3422
|
+
if (points.length === 1) {
|
|
3423
|
+
const p = points[0];
|
|
3424
|
+
if (p !== undefined) {
|
|
3425
|
+
drawer.draw_circle([p[0], p[1], Math.max(0.5, el.width * 0.5)], style);
|
|
3426
|
+
}
|
|
3427
|
+
return;
|
|
3428
|
+
}
|
|
3429
|
+
if (points.length > 1) {
|
|
3430
|
+
drawer.draw_polyline(points, style);
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
function render_shape_element_engine(drawer, el) {
|
|
3434
|
+
const style = {
|
|
3435
|
+
fill: el.fill_color,
|
|
3436
|
+
stroke: el.stroke_color,
|
|
3437
|
+
stroke_width: el.stroke_width,
|
|
3438
|
+
line_cap: 1,
|
|
3439
|
+
line_join: 1,
|
|
3440
|
+
miter_limit: 10,
|
|
3441
|
+
alpha: 1
|
|
3442
|
+
};
|
|
3443
|
+
if (el.shape_type === shape_type_rectangle) {
|
|
3444
|
+
drawer.draw_rectangle(el.bounds, style);
|
|
3445
|
+
return;
|
|
3446
|
+
}
|
|
3447
|
+
if (el.shape_type === shape_type_frame) {
|
|
3448
|
+
const header_height = Math.max(20, Math.min(36, el.bounds[3] * 0.18));
|
|
3449
|
+
const header_style = {
|
|
3450
|
+
fill: style.stroke,
|
|
3451
|
+
stroke: style.stroke,
|
|
3452
|
+
stroke_width: Math.max(1, style.stroke_width),
|
|
3453
|
+
line_cap: style.line_cap,
|
|
3454
|
+
line_join: style.line_join,
|
|
3455
|
+
miter_limit: style.miter_limit,
|
|
3456
|
+
alpha: 0.12
|
|
3457
|
+
};
|
|
3458
|
+
const outline_style = {
|
|
3459
|
+
...style,
|
|
3460
|
+
fill: null,
|
|
3461
|
+
alpha: 1
|
|
3462
|
+
};
|
|
3463
|
+
drawer.draw_rectangle(el.bounds, outline_style);
|
|
3464
|
+
drawer.draw_rectangle([el.bounds[0], el.bounds[1], el.bounds[2], header_height], header_style);
|
|
3465
|
+
return;
|
|
3466
|
+
}
|
|
3467
|
+
if (el.shape_type === shape_type_ellipse) {
|
|
3468
|
+
const cx = el.bounds[0] + el.bounds[2] / 2;
|
|
3469
|
+
const cy = el.bounds[1] + el.bounds[3] / 2;
|
|
3470
|
+
const rx = Math.max(0.5, Math.abs(el.bounds[2]) / 2);
|
|
3471
|
+
const ry = Math.max(0.5, Math.abs(el.bounds[3]) / 2);
|
|
3472
|
+
const segments = 40;
|
|
3473
|
+
const points = [];
|
|
3474
|
+
for (let i = 0;i <= segments; i = i + 1) {
|
|
3475
|
+
const t = i / segments * Math.PI * 2;
|
|
3476
|
+
points.push([cx + Math.cos(t) * rx, cy + Math.sin(t) * ry]);
|
|
3477
|
+
}
|
|
3478
|
+
drawer.draw_polyline(points, style);
|
|
3479
|
+
return;
|
|
3480
|
+
}
|
|
3481
|
+
if (el.shape_type === shape_type_line || el.shape_type === shape_type_arrow) {
|
|
3482
|
+
const from = el.start_point ?? [el.bounds[0], el.bounds[1]];
|
|
3483
|
+
const to = el.end_point ?? [el.bounds[0] + el.bounds[2], el.bounds[1] + el.bounds[3]];
|
|
3484
|
+
drawer.draw_line(from, to, style);
|
|
3485
|
+
if (el.shape_type === shape_type_arrow) {
|
|
3486
|
+
draw_arrowhead_engine(drawer, from, to, style);
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
function render_image_element_engine(drawer, el, asset_src) {
|
|
3491
|
+
const image = resolve_image_source_engine(el, asset_src);
|
|
3492
|
+
const opacity = Math.max(0, Math.min(1, el.opacity));
|
|
3493
|
+
if (image.status === "loaded" && image.source !== null) {
|
|
3494
|
+
drawer.draw_image(image.source, fit_image_bounds_engine(el.bounds, image.source), opacity);
|
|
3495
|
+
return;
|
|
3496
|
+
}
|
|
3497
|
+
const style = {
|
|
3498
|
+
fill: default_element_fill,
|
|
3499
|
+
stroke: default_element_stroke,
|
|
3500
|
+
stroke_width: 1,
|
|
3501
|
+
line_cap: 1,
|
|
3502
|
+
line_join: 1,
|
|
3503
|
+
miter_limit: 10,
|
|
3504
|
+
alpha: opacity
|
|
3505
|
+
};
|
|
3506
|
+
drawer.draw_rectangle(el.bounds, style);
|
|
3507
|
+
if (image.status === "error") {
|
|
3508
|
+
const mark_style = {
|
|
3509
|
+
fill: null,
|
|
3510
|
+
stroke: default_element_stroke,
|
|
3511
|
+
stroke_width: 1.5,
|
|
3512
|
+
line_cap: 1,
|
|
3513
|
+
line_join: 1,
|
|
3514
|
+
miter_limit: 10,
|
|
3515
|
+
alpha: opacity
|
|
3516
|
+
};
|
|
3517
|
+
const inset = Math.max(8, Math.min(el.bounds[2], el.bounds[3]) * 0.12);
|
|
3518
|
+
const x0 = el.bounds[0] + inset;
|
|
3519
|
+
const y0 = el.bounds[1] + inset;
|
|
3520
|
+
const x1 = el.bounds[0] + el.bounds[2] - inset;
|
|
3521
|
+
const y1 = el.bounds[1] + el.bounds[3] - inset;
|
|
3522
|
+
drawer.draw_line([x0, y0], [x1, y1], mark_style);
|
|
3523
|
+
drawer.draw_line([x1, y0], [x0, y1], mark_style);
|
|
3524
|
+
}
|
|
3525
|
+
}
|
|
3526
|
+
function render_text_element_engine(drawer, el) {
|
|
3527
|
+
const key = [
|
|
3528
|
+
el.content,
|
|
3529
|
+
el.font_family,
|
|
3530
|
+
el.font_size,
|
|
3531
|
+
el.color.join(","),
|
|
3532
|
+
el.align,
|
|
3533
|
+
Math.max(1, Math.round(el.bounds[2])),
|
|
3534
|
+
Math.max(1, Math.round(el.bounds[3])),
|
|
3535
|
+
Math.max(1, Math.round(drawer.pixel_ratio))
|
|
3536
|
+
].join("|");
|
|
3537
|
+
let bitmap = text_bitmap_cache_engine.get(key);
|
|
3538
|
+
if (bitmap === undefined) {
|
|
3539
|
+
bitmap = rasterize_text_engine(el, Math.max(1, Math.round(drawer.pixel_ratio)));
|
|
3540
|
+
text_bitmap_cache_engine.set(key, bitmap);
|
|
3541
|
+
trim_text_bitmap_cache_engine();
|
|
3542
|
+
}
|
|
3543
|
+
drawer.draw_image(bitmap, el.bounds, 1);
|
|
3544
|
+
}
|
|
3545
|
+
function resolve_image_source_engine(el, asset_src) {
|
|
3546
|
+
if (el.bitmap !== null) {
|
|
3547
|
+
return { status: "loaded", source: el.bitmap };
|
|
3548
|
+
}
|
|
3549
|
+
const src = (asset_src ?? el.src).trim();
|
|
3550
|
+
if (src.length === 0 || typeof Image === "undefined") {
|
|
3551
|
+
return { status: "empty", source: null };
|
|
3552
|
+
}
|
|
3553
|
+
let cached = loaded_images_engine.get(src);
|
|
3554
|
+
if (cached === undefined) {
|
|
3555
|
+
const image = new Image;
|
|
3556
|
+
cached = { image, status: "loading" };
|
|
3557
|
+
loaded_images_engine.set(src, cached);
|
|
3558
|
+
image.onload = () => {
|
|
3559
|
+
const current = loaded_images_engine.get(src);
|
|
3560
|
+
if (current !== undefined)
|
|
3561
|
+
current.status = "loaded";
|
|
3562
|
+
};
|
|
3563
|
+
image.onerror = () => {
|
|
3564
|
+
const current = loaded_images_engine.get(src);
|
|
3565
|
+
if (current !== undefined)
|
|
3566
|
+
current.status = "error";
|
|
3567
|
+
};
|
|
3568
|
+
image.src = src;
|
|
3569
|
+
return { status: "loading", source: null };
|
|
3570
|
+
}
|
|
3571
|
+
if (cached.status === "loaded") {
|
|
3572
|
+
return { status: "loaded", source: cached.image };
|
|
3573
|
+
}
|
|
3574
|
+
return { status: cached.status, source: null };
|
|
3575
|
+
}
|
|
3576
|
+
function fit_image_bounds_engine(bounds, source) {
|
|
3577
|
+
const source_w = source instanceof HTMLImageElement ? source.naturalWidth : source.width;
|
|
3578
|
+
const source_h = source instanceof HTMLImageElement ? source.naturalHeight : source.height;
|
|
3579
|
+
const x = bounds[0];
|
|
3580
|
+
const y = bounds[1];
|
|
3581
|
+
const w = Math.max(1, bounds[2]);
|
|
3582
|
+
const h = Math.max(1, bounds[3]);
|
|
3583
|
+
if (source_w <= 0 || source_h <= 0) {
|
|
3584
|
+
return [x, y, w, h];
|
|
3585
|
+
}
|
|
3586
|
+
const scale = Math.min(w / source_w, h / source_h);
|
|
3587
|
+
const draw_w = source_w * scale;
|
|
3588
|
+
const draw_h = source_h * scale;
|
|
3589
|
+
const draw_x = x + (w - draw_w) * 0.5;
|
|
3590
|
+
const draw_y = y + (h - draw_h) * 0.5;
|
|
3591
|
+
return [draw_x, draw_y, draw_w, draw_h];
|
|
3592
|
+
}
|
|
3593
|
+
function draw_arrowhead_engine(drawer, from, to, style) {
|
|
3594
|
+
const dx = to[0] - from[0];
|
|
3595
|
+
const dy = to[1] - from[1];
|
|
3596
|
+
const len = Math.hypot(dx, dy);
|
|
3597
|
+
if (len < 0.0001)
|
|
3598
|
+
return;
|
|
3599
|
+
const ux = dx / len;
|
|
3600
|
+
const uy = dy / len;
|
|
3601
|
+
const size = Math.max(8, style.stroke_width * 5);
|
|
3602
|
+
const wing_angle = Math.PI / 7;
|
|
3603
|
+
const cos = Math.cos(wing_angle);
|
|
3604
|
+
const sin = Math.sin(wing_angle);
|
|
3605
|
+
const lx = to[0] - (ux * cos - uy * sin) * size;
|
|
3606
|
+
const ly = to[1] - (uy * cos + ux * sin) * size;
|
|
3607
|
+
const rx = to[0] - (ux * cos + uy * sin) * size;
|
|
3608
|
+
const ry = to[1] - (uy * cos - ux * sin) * size;
|
|
3609
|
+
drawer.draw_line(to, [lx, ly], style);
|
|
3610
|
+
drawer.draw_line(to, [rx, ry], style);
|
|
3611
|
+
}
|
|
3612
|
+
function rasterize_text_engine(el, scale) {
|
|
3613
|
+
const canvas = document.createElement("canvas");
|
|
3614
|
+
const width = Math.max(1, Math.ceil(el.bounds[2] * scale));
|
|
3615
|
+
const height = Math.max(1, Math.ceil(el.bounds[3] * scale));
|
|
3616
|
+
canvas.width = width;
|
|
3617
|
+
canvas.height = height;
|
|
3618
|
+
const ctx = canvas.getContext("2d");
|
|
3619
|
+
if (ctx === null)
|
|
3620
|
+
return canvas;
|
|
3621
|
+
const font_size = Math.max(1, el.font_size);
|
|
3622
|
+
const line_height = font_size * 1.3;
|
|
3623
|
+
const padding = Math.max(4, Math.round(font_size * 0.35));
|
|
3624
|
+
const max_text_width = Math.max(1, el.bounds[2] - padding * 2);
|
|
3625
|
+
ctx.scale(scale, scale);
|
|
3626
|
+
ctx.font = `${font_size}px ${el.font_family}`;
|
|
3627
|
+
ctx.textBaseline = "top";
|
|
3628
|
+
ctx.fillStyle = to_rgba_string_color(el.color);
|
|
3629
|
+
const lines = wrap_text_lines_engine(ctx, el.content, max_text_width);
|
|
3630
|
+
const base_x = el.bounds[0];
|
|
3631
|
+
const base_y = el.bounds[1];
|
|
3632
|
+
const text_top = base_y + padding;
|
|
3633
|
+
const text_bottom = base_y + el.bounds[3] - padding;
|
|
3634
|
+
let y = text_top;
|
|
3635
|
+
for (let i = 0;i < lines.length; i = i + 1) {
|
|
3636
|
+
const line = lines[i];
|
|
3637
|
+
if (line === undefined)
|
|
3638
|
+
continue;
|
|
3639
|
+
if (y + line_height > text_bottom + 0.5)
|
|
3640
|
+
break;
|
|
3641
|
+
const line_width = ctx.measureText(line).width;
|
|
3642
|
+
let x = base_x + padding;
|
|
3643
|
+
if (el.align === text_align_center) {
|
|
3644
|
+
x = base_x + el.bounds[2] * 0.5 - line_width * 0.5;
|
|
3645
|
+
} else if (el.align === text_align_right) {
|
|
3646
|
+
x = base_x + el.bounds[2] - padding - line_width;
|
|
3647
|
+
}
|
|
3648
|
+
ctx.fillText(line, x - base_x, y - base_y);
|
|
3649
|
+
y = y + line_height;
|
|
3650
|
+
}
|
|
3651
|
+
return canvas;
|
|
3652
|
+
}
|
|
3653
|
+
function wrap_text_lines_engine(ctx, content, max_width) {
|
|
3654
|
+
const normalized = content.length > 0 ? content : "";
|
|
3655
|
+
const paragraphs = normalized.split(/\r?\n/);
|
|
3656
|
+
const lines = [];
|
|
3657
|
+
for (let i = 0;i < paragraphs.length; i = i + 1) {
|
|
3658
|
+
const paragraph = paragraphs[i];
|
|
3659
|
+
if (paragraph === undefined)
|
|
3660
|
+
continue;
|
|
3661
|
+
if (paragraph.length === 0) {
|
|
3662
|
+
lines.push("");
|
|
3663
|
+
continue;
|
|
3664
|
+
}
|
|
3665
|
+
const words = paragraph.split(/(\s+)/).filter((word) => word.length > 0);
|
|
3666
|
+
let current = "";
|
|
3667
|
+
for (let j = 0;j < words.length; j = j + 1) {
|
|
3668
|
+
const word = words[j];
|
|
3669
|
+
if (word === undefined)
|
|
3670
|
+
continue;
|
|
3671
|
+
const candidate = current.length === 0 ? word : `${current}${word}`;
|
|
3672
|
+
if (ctx.measureText(candidate).width <= max_width || current.length === 0) {
|
|
3673
|
+
current = candidate;
|
|
3674
|
+
} else {
|
|
3675
|
+
lines.push(current.trimEnd());
|
|
3676
|
+
current = word.trimStart();
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
lines.push(current.length > 0 ? current : "");
|
|
3680
|
+
}
|
|
3681
|
+
return lines;
|
|
3682
|
+
}
|
|
3683
|
+
function trim_text_bitmap_cache_engine() {
|
|
3684
|
+
if (text_bitmap_cache_engine.size <= text_bitmap_cache_limit_engine) {
|
|
3685
|
+
return;
|
|
3686
|
+
}
|
|
3687
|
+
const remove_count = Math.ceil(text_bitmap_cache_engine.size / 3);
|
|
3688
|
+
let removed = 0;
|
|
3689
|
+
for (const key of text_bitmap_cache_engine.keys()) {
|
|
3690
|
+
text_bitmap_cache_engine.delete(key);
|
|
3691
|
+
removed = removed + 1;
|
|
3692
|
+
if (removed >= remove_count)
|
|
3693
|
+
return;
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
|
|
3697
|
+
// engine/render.ts
|
|
3698
|
+
function render_engine(state) {
|
|
3699
|
+
render_engine_with_overlay(state, null);
|
|
3700
|
+
}
|
|
3701
|
+
function render_engine_with_overlay(state, overlay) {
|
|
3702
|
+
const view_bounds = visible_bounds_world_engine(state);
|
|
3703
|
+
const visible_elements = elements_in_bounds_document(state.document, view_bounds);
|
|
3704
|
+
const layer_info = build_layer_render_info_map_engine(state.document);
|
|
3705
|
+
const renderable_elements = [];
|
|
3706
|
+
for (let i = 0;i < visible_elements.length; i = i + 1) {
|
|
3707
|
+
const el = visible_elements[i];
|
|
3708
|
+
if (el === undefined) {
|
|
3709
|
+
continue;
|
|
3710
|
+
}
|
|
3711
|
+
const info = layer_info.get(el.layer_id);
|
|
3712
|
+
if (info === undefined || info.visible) {
|
|
3713
|
+
renderable_elements.push(el);
|
|
3714
|
+
}
|
|
3715
|
+
}
|
|
3716
|
+
renderable_elements.sort((a, b) => {
|
|
3717
|
+
const a_order = layer_info.get(a.layer_id)?.order ?? Number.MAX_SAFE_INTEGER;
|
|
3718
|
+
const b_order = layer_info.get(b.layer_id)?.order ?? Number.MAX_SAFE_INTEGER;
|
|
3719
|
+
if (a_order !== b_order) {
|
|
3720
|
+
return a_order - b_order;
|
|
3721
|
+
}
|
|
3722
|
+
return a.z_index - b.z_index;
|
|
3723
|
+
});
|
|
3724
|
+
state.renderer.begin_frame();
|
|
3725
|
+
state.renderer.clear(state.background);
|
|
3726
|
+
camera_to_screen_transform_engine(state.camera, state.viewport, scratch_transform);
|
|
3727
|
+
state.renderer.set_transform(scratch_transform);
|
|
3728
|
+
if (state.background_image !== null) {
|
|
3729
|
+
state.renderer.draw_image(state.background_image, [0, 0, state.background_image.width, state.background_image.height], 1);
|
|
3730
|
+
}
|
|
3731
|
+
for (let i = 0;i < renderable_elements.length; i = i + 1) {
|
|
3732
|
+
const el = renderable_elements[i];
|
|
3733
|
+
if (el !== undefined) {
|
|
3734
|
+
const asset_src = el.type === element_type_image && el.asset_id !== null ? state.document.assets.get(el.asset_id)?.src ?? null : null;
|
|
3735
|
+
render_element_engine(state.renderer, el, asset_src);
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3738
|
+
if (overlay !== null) {
|
|
3739
|
+
overlay(state.renderer);
|
|
3740
|
+
}
|
|
3741
|
+
state.renderer.end_frame();
|
|
3742
|
+
}
|
|
3743
|
+
function build_layer_render_info_map_engine(doc) {
|
|
3744
|
+
const map = new Map;
|
|
3745
|
+
for (let i = 0;i < doc.layers.length; i = i + 1) {
|
|
3746
|
+
const layer = doc.layers[i];
|
|
3747
|
+
if (layer !== undefined) {
|
|
3748
|
+
map.set(layer.id, { order: i, visible: layer.visible });
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
return map;
|
|
3752
|
+
}
|