@lvl99dotcom/paintcanvas 0.0.0-stage → 0.3.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 +201 -0
- package/README.md +131 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/lib/brushes/BrushTipCache/index.d.ts +32 -0
- package/dist/lib/brushes/BrushTipCache/index.js +60 -0
- package/dist/lib/brushes/StrokeEngine/index.d.ts +171 -0
- package/dist/lib/brushes/StrokeEngine/index.js +324 -0
- package/dist/lib/brushes/StrokePath/index.d.ts +53 -0
- package/dist/lib/brushes/StrokePath/index.js +161 -0
- package/dist/lib/brushes/StrokeSampler/index.d.ts +55 -0
- package/dist/lib/brushes/StrokeSampler/index.js +75 -0
- package/dist/lib/brushes/authors/index.d.ts +55 -0
- package/dist/lib/brushes/authors/index.js +60 -0
- package/dist/lib/brushes/configRanges/index.d.ts +52 -0
- package/dist/lib/brushes/configRanges/index.js +206 -0
- package/dist/lib/brushes/index.d.ts +5 -0
- package/dist/lib/brushes/index.js +19 -0
- package/dist/lib/brushes/params/index.d.ts +108 -0
- package/dist/lib/brushes/params/index.js +139 -0
- package/dist/lib/brushes/presetFile/index.d.ts +32 -0
- package/dist/lib/brushes/presetFile/index.js +55 -0
- package/dist/lib/brushes/presets/drawing/brush-pen.js +3 -0
- package/dist/lib/brushes/presets/drawing/felt-pen.js +3 -0
- package/dist/lib/brushes/presets/drawing/index.d.ts +13 -0
- package/dist/lib/brushes/presets/drawing/index.js +88 -0
- package/dist/lib/brushes/presets/drawing/pencil-2b.js +3 -0
- package/dist/lib/brushes/presets/drawing/pencil-hb.js +3 -0
- package/dist/lib/brushes/presets/painting/grass.js +3 -0
- package/dist/lib/brushes/presets/painting/hair.js +3 -0
- package/dist/lib/brushes/presets/painting/index.d.ts +18 -0
- package/dist/lib/brushes/presets/painting/index.js +118 -0
- package/dist/lib/brushes/presets/painting/splatter.js +3 -0
- package/dist/lib/brushes/presets/painting/textured-paint.js +3 -0
- package/dist/lib/brushes/presets/painting/watery-paint.js +3 -0
- package/dist/lib/brushes/presets/round/index.d.ts +9 -0
- package/dist/lib/brushes/presets/round/index.js +44 -0
- package/dist/lib/brushes/presets/round/round.js +3 -0
- package/dist/lib/brushes/presets/round/soft-round.js +3 -0
- package/dist/lib/brushes/presets/sized/index.d.ts +20 -0
- package/dist/lib/brushes/presets/sized/index.js +30 -0
- package/dist/lib/brushes/presets/sketching/index.d.ts +14 -0
- package/dist/lib/brushes/presets/sketching/index.js +107 -0
- package/dist/lib/brushes/presets/square/index.d.ts +7 -0
- package/dist/lib/brushes/presets/square/index.js +22 -0
- package/dist/lib/brushes/presets/square/square.js +3 -0
- package/dist/lib/brushes/shapeImage/index.d.ts +40 -0
- package/dist/lib/brushes/shapeImage/index.js +61 -0
- package/dist/lib/brushes/types/index.d.ts +243 -0
- package/dist/lib/brushes/types/index.js +20 -0
- package/dist/lib/classes/BackgroundWork/index.d.ts +55 -0
- package/dist/lib/classes/BackgroundWork/index.js +97 -0
- package/dist/lib/classes/BrushSettings/index.d.ts +91 -0
- package/dist/lib/classes/BrushSettings/index.js +152 -0
- package/dist/lib/classes/CustomPresets/index.d.ts +53 -0
- package/dist/lib/classes/CustomPresets/index.js +71 -0
- package/dist/lib/classes/ExternalSheets/index.d.ts +64 -0
- package/dist/lib/classes/ExternalSheets/index.js +112 -0
- package/dist/lib/classes/GestureTracker/index.d.ts +62 -0
- package/dist/lib/classes/GestureTracker/index.js +122 -0
- package/dist/lib/classes/History/index.d.ts +83 -0
- package/dist/lib/classes/History/index.js +126 -0
- package/dist/lib/classes/HistoryGuard/index.d.ts +58 -0
- package/dist/lib/classes/HistoryGuard/index.js +97 -0
- package/dist/lib/classes/InputSettings/index.d.ts +74 -0
- package/dist/lib/classes/InputSettings/index.js +101 -0
- package/dist/lib/classes/KeyboardShortcuts/index.d.ts +48 -0
- package/dist/lib/classes/KeyboardShortcuts/index.js +137 -0
- package/dist/lib/classes/Layer/index.d.ts +86 -0
- package/dist/lib/classes/Layer/index.js +111 -0
- package/dist/lib/classes/LayerAutosave/index.d.ts +180 -0
- package/dist/lib/classes/LayerAutosave/index.js +346 -0
- package/dist/lib/classes/LayerStack/index.d.ts +177 -0
- package/dist/lib/classes/LayerStack/index.js +394 -0
- package/dist/lib/classes/PaintCanvas/index.d.ts +537 -0
- package/dist/lib/classes/PaintCanvas/index.js +965 -0
- package/dist/lib/classes/StrokeLog/index.d.ts +149 -0
- package/dist/lib/classes/StrokeLog/index.js +257 -0
- package/dist/lib/classes/Viewport/index.d.ts +108 -0
- package/dist/lib/classes/Viewport/index.js +194 -0
- package/dist/lib/document/index.d.ts +17 -0
- package/dist/lib/document/index.js +25 -0
- package/dist/lib/document/schema.d.ts +132 -0
- package/dist/lib/document/schema.js +110 -0
- package/dist/lib/i18n/index.d.ts +131 -0
- package/dist/lib/i18n/index.js +277 -0
- package/dist/lib/image/index.d.ts +90 -0
- package/dist/lib/image/index.js +147 -0
- package/dist/lib/messages/PatchSender/index.d.ts +75 -0
- package/dist/lib/messages/PatchSender/index.js +125 -0
- package/dist/lib/messages/index.d.ts +26 -0
- package/dist/lib/messages/index.js +59 -0
- package/dist/lib/messages/schema.d.ts +1775 -0
- package/dist/lib/messages/schema.js +192 -0
- package/dist/lib/presets/index.d.ts +32 -0
- package/dist/lib/presets/index.js +35 -0
- package/dist/lib/presets/schema.d.ts +753 -0
- package/dist/lib/presets/schema.js +181 -0
- package/dist/lib/rendering/BrushTip/index.d.ts +63 -0
- package/dist/lib/rendering/BrushTip/index.js +112 -0
- package/dist/lib/rendering/checkerboard/index.d.ts +22 -0
- package/dist/lib/rendering/checkerboard/index.js +31 -0
- package/dist/lib/rendering/compositing/index.d.ts +64 -0
- package/dist/lib/rendering/compositing/index.js +124 -0
- package/dist/lib/rendering/cursor/index.d.ts +14 -0
- package/dist/lib/rendering/cursor/index.js +39 -0
- package/dist/lib/rendering/cursors/index.d.ts +18 -0
- package/dist/lib/rendering/cursors/index.js +23 -0
- package/dist/lib/rendering/maskOutline/index.d.ts +22 -0
- package/dist/lib/rendering/maskOutline/index.js +78 -0
- package/dist/lib/rendering/outline/index.d.ts +36 -0
- package/dist/lib/rendering/outline/index.js +120 -0
- package/dist/lib/rendering/sample/index.d.ts +55 -0
- package/dist/lib/rendering/sample/index.js +83 -0
- package/dist/lib/rendering/screenshot/index.d.ts +67 -0
- package/dist/lib/rendering/screenshot/index.js +96 -0
- package/dist/lib/rendering/transformBox/index.d.ts +10 -0
- package/dist/lib/rendering/transformBox/index.js +57 -0
- package/dist/lib/selection/Lasso/index.d.ts +70 -0
- package/dist/lib/selection/Lasso/index.js +156 -0
- package/dist/lib/selection/Selection/index.d.ts +86 -0
- package/dist/lib/selection/Selection/index.js +228 -0
- package/dist/lib/selection/geometry/index.d.ts +37 -0
- package/dist/lib/selection/geometry/index.js +57 -0
- package/dist/lib/storage/index.d.ts +63 -0
- package/dist/lib/storage/index.js +114 -0
- package/dist/lib/transform/Transformer/index.d.ts +124 -0
- package/dist/lib/transform/Transformer/index.js +286 -0
- package/dist/lib/transform/geometry/index.d.ts +95 -0
- package/dist/lib/transform/geometry/index.js +258 -0
- package/dist/lib/utils/array/index.d.ts +10 -0
- package/dist/lib/utils/array/index.js +21 -0
- package/dist/lib/utils/gestures/index.d.ts +35 -0
- package/dist/lib/utils/gestures/index.js +81 -0
- package/dist/lib/utils/navigation/index.d.ts +51 -0
- package/dist/lib/utils/navigation/index.js +59 -0
- package/dist/lib/utils/random/index.d.ts +18 -0
- package/dist/lib/utils/random/index.js +27 -0
- package/dist/lib/utils/shortcuts/index.d.ts +74 -0
- package/dist/lib/utils/shortcuts/index.js +189 -0
- package/dist/lib/validation/validators.generated.d.ts +27 -0
- package/dist/lib/validation/validators.generated.js +19826 -0
- package/dist/locales/en-GB.js +181 -0
- package/dist/locales/en-US.js +181 -0
- package/dist/locales/fr.js +181 -0
- package/dist/locales/ja.js +181 -0
- package/dist/locales/ko.js +181 -0
- package/dist/locales/zh-Hans.js +181 -0
- package/dist/locales/zh-Hant.js +181 -0
- package/dist/package.js +3 -0
- package/dist/testing/app.d.ts +7 -0
- package/dist/testing/app.js +16 -0
- package/dist/testing/elements.d.ts +42 -0
- package/dist/testing/elements.js +58 -0
- package/dist/testing/fakeCanvas.d.ts +21 -0
- package/dist/testing/fakeCanvas.js +26 -0
- package/dist/testing/layerStore.d.ts +26 -0
- package/dist/testing/layerStore.js +43 -0
- package/dist/testing/locks.d.ts +15 -0
- package/dist/testing/locks.js +41 -0
- package/dist/testing/recordingCanvas.d.ts +14 -0
- package/dist/testing/recordingCanvas.js +28 -0
- package/dist/testing/recordingContext.d.ts +10 -0
- package/dist/testing/recordingContext.js +19 -0
- package/dist/ui/ChangeController/index.d.ts +40 -0
- package/dist/ui/ChangeController/index.js +62 -0
- package/dist/ui/base/index.d.ts +69 -0
- package/dist/ui/base/index.js +94 -0
- package/dist/ui/elements/PaintCanvas/index.d.ts +249 -0
- package/dist/ui/elements/PaintCanvas/index.js +584 -0
- package/dist/ui/elements/PaintCanvas/style.js +3 -0
- package/dist/ui/elements/PcBrushConfig/index.d.ts +130 -0
- package/dist/ui/elements/PcBrushConfig/index.js +465 -0
- package/dist/ui/elements/PcBrushConfig/style.js +3 -0
- package/dist/ui/elements/PcBrushMenu/index.d.ts +121 -0
- package/dist/ui/elements/PcBrushMenu/index.js +362 -0
- package/dist/ui/elements/PcBrushMenu/style.js +3 -0
- package/dist/ui/elements/PcBrushTest/index.d.ts +71 -0
- package/dist/ui/elements/PcBrushTest/index.js +136 -0
- package/dist/ui/elements/PcBrushTest/style.js +3 -0
- package/dist/ui/elements/PcButton/index.d.ts +57 -0
- package/dist/ui/elements/PcButton/index.js +124 -0
- package/dist/ui/elements/PcButtonGroup/index.d.ts +44 -0
- package/dist/ui/elements/PcButtonGroup/index.js +93 -0
- package/dist/ui/elements/PcCanvas/index.d.ts +356 -0
- package/dist/ui/elements/PcCanvas/index.js +1004 -0
- package/dist/ui/elements/PcCanvas/style.js +3 -0
- package/dist/ui/elements/PcCanvasMap/index.d.ts +96 -0
- package/dist/ui/elements/PcCanvasMap/index.js +187 -0
- package/dist/ui/elements/PcCanvasMap/style.js +3 -0
- package/dist/ui/elements/PcContextMenu/index.d.ts +72 -0
- package/dist/ui/elements/PcContextMenu/index.js +148 -0
- package/dist/ui/elements/PcDialog/index.d.ts +63 -0
- package/dist/ui/elements/PcDialog/index.js +114 -0
- package/dist/ui/elements/PcIcon/index.d.ts +36 -0
- package/dist/ui/elements/PcIcon/index.js +43 -0
- package/dist/ui/elements/PcIconToggle/index.d.ts +46 -0
- package/dist/ui/elements/PcIconToggle/index.js +87 -0
- package/dist/ui/elements/PcInfoTip/index.d.ts +40 -0
- package/dist/ui/elements/PcInfoTip/index.js +95 -0
- package/dist/ui/elements/PcInputSettings/index.d.ts +69 -0
- package/dist/ui/elements/PcInputSettings/index.js +154 -0
- package/dist/ui/elements/PcLayerItem/index.d.ts +221 -0
- package/dist/ui/elements/PcLayerItem/index.js +561 -0
- package/dist/ui/elements/PcLayerItem/style.js +3 -0
- package/dist/ui/elements/PcLayersMenu/index.d.ts +108 -0
- package/dist/ui/elements/PcLayersMenu/index.js +208 -0
- package/dist/ui/elements/PcLayersMenu/style.js +3 -0
- package/dist/ui/elements/PcMatch/index.d.ts +31 -0
- package/dist/ui/elements/PcMatch/index.js +58 -0
- package/dist/ui/elements/PcPanel/index.d.ts +43 -0
- package/dist/ui/elements/PcPanel/index.js +51 -0
- package/dist/ui/elements/PcPopover/index.d.ts +48 -0
- package/dist/ui/elements/PcPopover/index.js +86 -0
- package/dist/ui/elements/PcSlider/index.d.ts +57 -0
- package/dist/ui/elements/PcSlider/index.js +88 -0
- package/dist/ui/elements/PcStats/index.d.ts +54 -0
- package/dist/ui/elements/PcStats/index.js +144 -0
- package/dist/ui/elements/PcThemePicker/index.d.ts +56 -0
- package/dist/ui/elements/PcThemePicker/index.js +134 -0
- package/dist/ui/elements/PcToast/index.d.ts +58 -0
- package/dist/ui/elements/PcToast/index.js +110 -0
- package/dist/ui/elements/PcToggleCheckbox/index.d.ts +37 -0
- package/dist/ui/elements/PcToggleCheckbox/index.js +105 -0
- package/dist/ui/elements/PcToolSettings/index.d.ts +128 -0
- package/dist/ui/elements/PcToolSettings/index.js +549 -0
- package/dist/ui/elements/PcToolSettings/style.js +3 -0
- package/dist/ui/elements/PcToolbar/index.d.ts +71 -0
- package/dist/ui/elements/PcToolbar/index.js +190 -0
- package/dist/ui/elements/PcToolbar/style.js +3 -0
- package/dist/ui/icons/images/brush.js +3 -0
- package/dist/ui/icons/images/circle-mask.js +3 -0
- package/dist/ui/icons/images/clipboard.js +3 -0
- package/dist/ui/icons/images/cursor.js +3 -0
- package/dist/ui/icons/images/eraser.js +3 -0
- package/dist/ui/icons/images/eyedropper.js +3 -0
- package/dist/ui/icons/images/flip-horizontal.js +3 -0
- package/dist/ui/icons/images/flip-vertical.js +3 -0
- package/dist/ui/icons/images/focus-pen.js +3 -0
- package/dist/ui/icons/images/freehand-mask.js +3 -0
- package/dist/ui/icons/images/hidden.js +3 -0
- package/dist/ui/icons/images/layers.js +3 -0
- package/dist/ui/icons/images/linked.js +3 -0
- package/dist/ui/icons/images/locked.js +3 -0
- package/dist/ui/icons/images/moon.js +3 -0
- package/dist/ui/icons/images/pan.js +3 -0
- package/dist/ui/icons/images/polygon-mask.js +3 -0
- package/dist/ui/icons/images/pressure.js +3 -0
- package/dist/ui/icons/images/randomise.js +3 -0
- package/dist/ui/icons/images/rectangle-mask.js +3 -0
- package/dist/ui/icons/images/redo.js +3 -0
- package/dist/ui/icons/images/rotate-90-left.js +3 -0
- package/dist/ui/icons/images/rotate-90-right.js +3 -0
- package/dist/ui/icons/images/smoothing-bezier-cubic.js +3 -0
- package/dist/ui/icons/images/smoothing-bezier-quad.js +3 -0
- package/dist/ui/icons/images/smoothing-linear.js +3 -0
- package/dist/ui/icons/images/star.js +3 -0
- package/dist/ui/icons/images/sun.js +3 -0
- package/dist/ui/icons/images/tilt.js +3 -0
- package/dist/ui/icons/images/undo.js +3 -0
- package/dist/ui/icons/images/unlinked.js +3 -0
- package/dist/ui/icons/images/unlocked.js +3 -0
- package/dist/ui/icons/images/visible.js +3 -0
- package/dist/ui/icons/images/zoom-in.js +3 -0
- package/dist/ui/icons/images/zoom-out.js +3 -0
- package/dist/ui/icons/images/zoom.js +3 -0
- package/dist/ui/icons/index.d.ts +42 -0
- package/dist/ui/icons/index.js +130 -0
- package/dist/ui/index.d.ts +21 -0
- package/dist/ui/index.js +22 -0
- package/dist/ui/layout/index.d.ts +88 -0
- package/dist/ui/layout/index.js +96 -0
- package/dist/ui/styles/index.d.ts +13 -0
- package/dist/ui/styles/index.js +195 -0
- package/dist/ui/tokens/index.d.ts +29 -0
- package/dist/ui/tokens/index.js +114 -0
- package/locales/en-GB.json +179 -0
- package/locales/en-US.json +179 -0
- package/locales/fr.json +179 -0
- package/locales/ja.json +179 -0
- package/locales/ko.json +179 -0
- package/locales/zh-Hans.json +179 -0
- package/locales/zh-Hant.json +179 -0
- package/package.json +97 -3
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
// src/lib/transform/geometry/index.ts
|
|
2
|
+
/** The 8 move & scale handles: corners and edge centres */
|
|
3
|
+
var SCALE_HANDLES = [
|
|
4
|
+
{
|
|
5
|
+
hx: -1,
|
|
6
|
+
hy: -1
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
hx: 0,
|
|
10
|
+
hy: -1
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
hx: 1,
|
|
14
|
+
hy: -1
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
hx: -1,
|
|
18
|
+
hy: 0
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
hx: 1,
|
|
22
|
+
hy: 0
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
hx: -1,
|
|
26
|
+
hy: 1
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
hx: 0,
|
|
30
|
+
hy: 1
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
hx: 1,
|
|
34
|
+
hy: 1
|
|
35
|
+
}
|
|
36
|
+
];
|
|
37
|
+
/** Distance (screen pixels) of the rotate handle beyond the top-centre handle */
|
|
38
|
+
var ROTATE_HANDLE_DISTANCE = 24;
|
|
39
|
+
/** Degrees per rotation step when Shift is held */
|
|
40
|
+
var ROTATION_STEP = 45;
|
|
41
|
+
var toRadians = (degrees) => degrees * Math.PI / 180;
|
|
42
|
+
/**
|
|
43
|
+
* Rotate a vector by an angle in degrees.
|
|
44
|
+
*/
|
|
45
|
+
var rotate = ({ x, y }, degrees) => {
|
|
46
|
+
const cos = Math.cos(toRadians(degrees));
|
|
47
|
+
const sin = Math.sin(toRadians(degrees));
|
|
48
|
+
return {
|
|
49
|
+
x: x * cos - y * sin,
|
|
50
|
+
y: x * sin + y * cos
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* The box that exactly covers a rectangle, unrotated.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* boxFromRect({ x: 0, y: 0, width: 10, height: 20 }); // { cx: 5, cy: 10, width: 10, height: 20, rotation: 0 }
|
|
58
|
+
*/
|
|
59
|
+
var boxFromRect = ({ x, y, width, height }) => ({
|
|
60
|
+
cx: x + width / 2,
|
|
61
|
+
cy: y + height / 2,
|
|
62
|
+
width,
|
|
63
|
+
height,
|
|
64
|
+
rotation: 0
|
|
65
|
+
});
|
|
66
|
+
/**
|
|
67
|
+
* Where a handle is on the canvas.
|
|
68
|
+
*/
|
|
69
|
+
var handlePosition = (box, { hx, hy }) => {
|
|
70
|
+
const offset = rotate({
|
|
71
|
+
x: hx * box.width / 2,
|
|
72
|
+
y: hy * box.height / 2
|
|
73
|
+
}, box.rotation);
|
|
74
|
+
return {
|
|
75
|
+
x: box.cx + offset.x,
|
|
76
|
+
y: box.cy + offset.y
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* The box's four corners in drawing order (top-left, top-right, bottom-right, bottom-left), for drawing its outline.
|
|
81
|
+
*/
|
|
82
|
+
var boxCorners = (box) => [
|
|
83
|
+
{
|
|
84
|
+
hx: -1,
|
|
85
|
+
hy: -1
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
hx: 1,
|
|
89
|
+
hy: -1
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
hx: 1,
|
|
93
|
+
hy: 1
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
hx: -1,
|
|
97
|
+
hy: 1
|
|
98
|
+
}
|
|
99
|
+
].map((h) => handlePosition(box, h));
|
|
100
|
+
/**
|
|
101
|
+
* Where the rotate handle is, in screen units: `distance` beyond the top-centre handle, away from the box's centre, so it
|
|
102
|
+
* turns with the box. Returns the handle and the top-centre handle (`anchor`) it is joined to by a line.
|
|
103
|
+
*
|
|
104
|
+
* @param toScreen - converts canvas points to screen points
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* rotateHandle({ cx: 50, cy: 50, width: 40, height: 20, rotation: 0 }); // { anchor: { x: 50, y: 40 }, handle: { x: 50, y: 16 } }
|
|
108
|
+
*/
|
|
109
|
+
var rotateHandle = (box, toScreen = (p) => p, distance = 24) => {
|
|
110
|
+
const anchor = toScreen(handlePosition(box, {
|
|
111
|
+
hx: 0,
|
|
112
|
+
hy: -1
|
|
113
|
+
}));
|
|
114
|
+
const centre = toScreen({
|
|
115
|
+
x: box.cx,
|
|
116
|
+
y: box.cy
|
|
117
|
+
});
|
|
118
|
+
let dx = anchor.x - centre.x;
|
|
119
|
+
let dy = anchor.y - centre.y;
|
|
120
|
+
let length = Math.hypot(dx, dy);
|
|
121
|
+
if (length === 0) {
|
|
122
|
+
({x: dx, y: dy} = rotate({
|
|
123
|
+
x: 0,
|
|
124
|
+
y: -1
|
|
125
|
+
}, box.rotation));
|
|
126
|
+
length = 1;
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
anchor,
|
|
130
|
+
handle: {
|
|
131
|
+
x: anchor.x + dx / length * distance,
|
|
132
|
+
y: anchor.y + dy / length * distance
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Find the handle under a point, if any. Points and radius are in the same units (usually screen pixels).
|
|
138
|
+
*
|
|
139
|
+
* @param toScreen - converts canvas points to the point's units
|
|
140
|
+
*/
|
|
141
|
+
var handleAt = (box, handles, point, radius, toScreen = (p) => p) => handles.find((handle) => {
|
|
142
|
+
const position = toScreen(handlePosition(box, handle));
|
|
143
|
+
return Math.hypot(position.x - point.x, position.y - point.y) <= radius;
|
|
144
|
+
});
|
|
145
|
+
/**
|
|
146
|
+
* Check whether a canvas point is inside the (possibly rotated) box.
|
|
147
|
+
*/
|
|
148
|
+
var isInsideBox = (box, point) => {
|
|
149
|
+
const local = rotate({
|
|
150
|
+
x: point.x - box.cx,
|
|
151
|
+
y: point.y - box.cy
|
|
152
|
+
}, -box.rotation);
|
|
153
|
+
return Math.abs(local.x) <= Math.abs(box.width) / 2 && Math.abs(local.y) <= Math.abs(box.height) / 2;
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Move the box by a distance. With `constrain` (Shift), it only moves along the axis it has moved furthest on.
|
|
157
|
+
*
|
|
158
|
+
* @param start - the box when the drag started
|
|
159
|
+
*/
|
|
160
|
+
var moveBox = (start, dx, dy, constrain) => {
|
|
161
|
+
if (constrain) {
|
|
162
|
+
if (Math.abs(dx) >= Math.abs(dy)) dy = 0;
|
|
163
|
+
else dx = 0;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
...start,
|
|
167
|
+
cx: start.cx + dx,
|
|
168
|
+
cy: start.cy + dy
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Scale the box by dragging a handle to a point, keeping the opposite side (or corner) fixed. Works on rotated boxes.
|
|
173
|
+
* With `proportional` (Shift), the box keeps the original's proportions; edge handles then scale the other axis around its centre.
|
|
174
|
+
*
|
|
175
|
+
* @param start - the box when the drag started
|
|
176
|
+
* @param aspect - original width ÷ height, kept when proportional
|
|
177
|
+
*/
|
|
178
|
+
var scaleBox = (start, handle, point, proportional, aspect) => {
|
|
179
|
+
const { hx, hy } = handle;
|
|
180
|
+
const anchor = handlePosition(start, {
|
|
181
|
+
hx: -hx || 0,
|
|
182
|
+
hy: -hy || 0
|
|
183
|
+
});
|
|
184
|
+
const local = rotate({
|
|
185
|
+
x: point.x - anchor.x,
|
|
186
|
+
y: point.y - anchor.y
|
|
187
|
+
}, -start.rotation);
|
|
188
|
+
let width = hx !== 0 ? hx * local.x : start.width;
|
|
189
|
+
let height = hy !== 0 ? hy * local.y : start.height;
|
|
190
|
+
if (proportional) {
|
|
191
|
+
const sign = (value) => value < 0 ? -1 : 1;
|
|
192
|
+
if (hx !== 0 && hy !== 0) {
|
|
193
|
+
if (Math.abs(width) / aspect >= Math.abs(height)) height = sign(height) * Math.abs(width) / aspect;
|
|
194
|
+
else width = sign(width) * Math.abs(height) * aspect;
|
|
195
|
+
} else if (hx !== 0) height = sign(start.height) * Math.abs(width) / aspect;
|
|
196
|
+
else width = sign(start.width) * Math.abs(height) * aspect;
|
|
197
|
+
}
|
|
198
|
+
const centreOffset = rotate({
|
|
199
|
+
x: hx !== 0 ? hx * width / 2 : 0,
|
|
200
|
+
y: hy !== 0 ? hy * height / 2 : 0
|
|
201
|
+
}, start.rotation);
|
|
202
|
+
return {
|
|
203
|
+
...start,
|
|
204
|
+
cx: anchor.x + centreOffset.x,
|
|
205
|
+
cy: anchor.y + centreOffset.y,
|
|
206
|
+
width,
|
|
207
|
+
height
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* Rotate the box around its centre by how far the pointer has turned around it since the drag started.
|
|
212
|
+
* With `snap` (Shift), the rotation is a multiple of 45°.
|
|
213
|
+
*
|
|
214
|
+
* @param start - the box when the drag started
|
|
215
|
+
* @param from - pointer position when the drag started
|
|
216
|
+
*/
|
|
217
|
+
var rotateBox = (start, from, to, snap) => {
|
|
218
|
+
const angle = (p) => Math.atan2(p.y - start.cy, p.x - start.cx) * 180 / Math.PI;
|
|
219
|
+
let rotation = start.rotation + angle(to) - angle(from);
|
|
220
|
+
if (snap) rotation = Math.round(rotation / 45) * 45;
|
|
221
|
+
rotation = ((rotation + 180) % 360 + 360) % 360 - 180;
|
|
222
|
+
return {
|
|
223
|
+
...start,
|
|
224
|
+
rotation
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
/**
|
|
228
|
+
* CSS resize cursor for a handle, taking the box's rotation into account.
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* resizeCursor({ hx: 1, hy: 0 }, 0); // "ew-resize"
|
|
232
|
+
*/
|
|
233
|
+
var resizeCursor = ({ hx, hy }, rotation) => {
|
|
234
|
+
const direction = ((Math.atan2(hy, hx) * 180 / Math.PI + rotation) % 180 + 180) % 180;
|
|
235
|
+
return [
|
|
236
|
+
"ew-resize",
|
|
237
|
+
"nwse-resize",
|
|
238
|
+
"ns-resize",
|
|
239
|
+
"nesw-resize"
|
|
240
|
+
][Math.round(direction / 45) % 4];
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* Smallest unrotated rectangle containing the box (e.g. the area the transformed pixels now cover).
|
|
244
|
+
*/
|
|
245
|
+
var boxBounds = (box) => {
|
|
246
|
+
const corners = boxCorners(box);
|
|
247
|
+
const xs = corners.map((p) => p.x);
|
|
248
|
+
const ys = corners.map((p) => p.y);
|
|
249
|
+
const x = Math.min(...xs);
|
|
250
|
+
const y = Math.min(...ys);
|
|
251
|
+
return {
|
|
252
|
+
x,
|
|
253
|
+
y,
|
|
254
|
+
width: Math.max(...xs) - x,
|
|
255
|
+
height: Math.max(...ys) - y
|
|
256
|
+
};
|
|
257
|
+
};
|
|
258
|
+
export { ROTATE_HANDLE_DISTANCE, ROTATION_STEP, SCALE_HANDLES, boxBounds, boxCorners, boxFromRect, handleAt, handlePosition, isInsideBox, moveBox, resizeCursor, rotateBox, rotateHandle, scaleBox };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return a copy of a list with one item moved from one index to another. Items in between shift to fill the gap.
|
|
3
|
+
*
|
|
4
|
+
* Indexes are clamped to the list bounds.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* moveItem(["a", "b", "c"], 0, 2); // ["b", "c", "a"]
|
|
8
|
+
* moveItem(["a", "b", "c"], 2, 0); // ["c", "a", "b"]
|
|
9
|
+
*/
|
|
10
|
+
export declare const moveItem: <T>(items: readonly T[], from: number, to: number) => T[];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// src/lib/utils/array/index.ts
|
|
2
|
+
/**
|
|
3
|
+
* Return a copy of a list with one item moved from one index to another. Items in between shift to fill the gap.
|
|
4
|
+
*
|
|
5
|
+
* Indexes are clamped to the list bounds.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* moveItem(["a", "b", "c"], 0, 2); // ["b", "c", "a"]
|
|
9
|
+
* moveItem(["a", "b", "c"], 2, 0); // ["c", "a", "b"]
|
|
10
|
+
*/
|
|
11
|
+
var moveItem = (items, from, to) => {
|
|
12
|
+
const result = [...items];
|
|
13
|
+
if (items.length === 0) return result;
|
|
14
|
+
const last = items.length - 1;
|
|
15
|
+
const fromIndex = Math.max(0, Math.min(from, last));
|
|
16
|
+
const toIndex = Math.max(0, Math.min(to, last));
|
|
17
|
+
const [item] = result.splice(fromIndex, 1);
|
|
18
|
+
result.splice(toIndex, 0, item);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
export { moveItem };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Touch gestures that can undo, redo, copy or paste */
|
|
2
|
+
export declare const TOUCH_GESTURES: readonly ["tap2", "tap3", "swipeLeft3", "swipeRight3", "swipeUp3", "swipeDown3", "swipeOut3", "swipeIn3"];
|
|
3
|
+
export type TouchGesture = (typeof TOUCH_GESTURES)[number];
|
|
4
|
+
/** What each gesture is called in "My settings" */
|
|
5
|
+
export declare const TOUCH_GESTURE_NAMES: Record<TouchGesture, string>;
|
|
6
|
+
/** Actions a touch gesture can do */
|
|
7
|
+
export declare const GESTURE_ACTIONS: readonly ["undo", "redo", "copy", "paste"];
|
|
8
|
+
export type GestureAction = (typeof GESTURE_ACTIONS)[number];
|
|
9
|
+
/** Which gesture does each action, or "off" */
|
|
10
|
+
export type GestureSettings = Record<GestureAction, TouchGesture | "off">;
|
|
11
|
+
/** The gestures each action starts with (copy and paste as iPadOS does them) */
|
|
12
|
+
export declare const DEFAULT_GESTURES: GestureSettings;
|
|
13
|
+
/**
|
|
14
|
+
* Keep only valid gestures from stored (untrusted) settings, using the defaults for anything missing or invalid. A
|
|
15
|
+
* gesture used by an earlier action is turned off for later ones, so one gesture never does two things.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* sanitiseGestures({ undo: "tap3", redo: "tap3" }); // { undo: "tap3", redo: "off", copy: "swipeIn3", paste: "swipeOut3" }
|
|
19
|
+
*/
|
|
20
|
+
export declare const sanitiseGestures: (input: unknown) => GestureSettings;
|
|
21
|
+
/**
|
|
22
|
+
* Give an action a gesture. If another action had it, that action takes this action's old gesture instead (the two
|
|
23
|
+
* swap).
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* assignGesture(DEFAULT_GESTURES, "undo", "tap3"); // undo: "tap3", redo: "tap2"
|
|
27
|
+
*/
|
|
28
|
+
export declare const assignGesture: (gestures: GestureSettings, action: GestureAction, gesture: TouchGesture | "off") => GestureSettings;
|
|
29
|
+
/**
|
|
30
|
+
* The action a gesture does, if any.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* gestureAction(DEFAULT_GESTURES, "tap2"); // "undo"
|
|
34
|
+
*/
|
|
35
|
+
export declare const gestureAction: (gestures: GestureSettings, gesture: TouchGesture) => GestureAction | undefined;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// src/lib/utils/gestures/index.ts
|
|
2
|
+
/** Touch gestures that can undo, redo, copy or paste */
|
|
3
|
+
var TOUCH_GESTURES = [
|
|
4
|
+
"tap2",
|
|
5
|
+
"tap3",
|
|
6
|
+
"swipeLeft3",
|
|
7
|
+
"swipeRight3",
|
|
8
|
+
"swipeUp3",
|
|
9
|
+
"swipeDown3",
|
|
10
|
+
"swipeOut3",
|
|
11
|
+
"swipeIn3"
|
|
12
|
+
];
|
|
13
|
+
/** What each gesture is called in "My settings" */
|
|
14
|
+
var TOUCH_GESTURE_NAMES = {
|
|
15
|
+
tap2: "Tap with two fingers",
|
|
16
|
+
tap3: "Tap with three fingers",
|
|
17
|
+
swipeLeft3: "Swipe left with three fingers",
|
|
18
|
+
swipeRight3: "Swipe right with three fingers",
|
|
19
|
+
swipeUp3: "Swipe up with three fingers",
|
|
20
|
+
swipeDown3: "Swipe down with three fingers",
|
|
21
|
+
swipeOut3: "Swipe out with three fingers",
|
|
22
|
+
swipeIn3: "Swipe in with three fingers"
|
|
23
|
+
};
|
|
24
|
+
/** Actions a touch gesture can do */
|
|
25
|
+
var GESTURE_ACTIONS = [
|
|
26
|
+
"undo",
|
|
27
|
+
"redo",
|
|
28
|
+
"copy",
|
|
29
|
+
"paste"
|
|
30
|
+
];
|
|
31
|
+
/** The gestures each action starts with (copy and paste as iPadOS does them) */
|
|
32
|
+
var DEFAULT_GESTURES = {
|
|
33
|
+
undo: "tap2",
|
|
34
|
+
redo: "tap3",
|
|
35
|
+
copy: "swipeIn3",
|
|
36
|
+
paste: "swipeOut3"
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Keep only valid gestures from stored (untrusted) settings, using the defaults for anything missing or invalid. A
|
|
40
|
+
* gesture used by an earlier action is turned off for later ones, so one gesture never does two things.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* sanitiseGestures({ undo: "tap3", redo: "tap3" }); // { undo: "tap3", redo: "off", copy: "swipeIn3", paste: "swipeOut3" }
|
|
44
|
+
*/
|
|
45
|
+
var sanitiseGestures = (input) => {
|
|
46
|
+
const value = typeof input === "object" && input !== null ? input : {};
|
|
47
|
+
const used = /* @__PURE__ */ new Set();
|
|
48
|
+
const result = { ...DEFAULT_GESTURES };
|
|
49
|
+
for (const action of GESTURE_ACTIONS) {
|
|
50
|
+
const chosen = value[action];
|
|
51
|
+
const gesture = chosen === "off" || TOUCH_GESTURES.includes(chosen) ? chosen : DEFAULT_GESTURES[action];
|
|
52
|
+
result[action] = gesture !== "off" && used.has(gesture) ? "off" : gesture;
|
|
53
|
+
if (result[action] !== "off") used.add(result[action]);
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Give an action a gesture. If another action had it, that action takes this action's old gesture instead (the two
|
|
59
|
+
* swap).
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* assignGesture(DEFAULT_GESTURES, "undo", "tap3"); // undo: "tap3", redo: "tap2"
|
|
63
|
+
*/
|
|
64
|
+
var assignGesture = (gestures, action, gesture) => {
|
|
65
|
+
const result = {
|
|
66
|
+
...gestures,
|
|
67
|
+
[action]: gesture
|
|
68
|
+
};
|
|
69
|
+
if (gesture === "off") return result;
|
|
70
|
+
const other = GESTURE_ACTIONS.find((each) => each !== action && gestures[each] === gesture);
|
|
71
|
+
if (other) result[other] = gestures[action];
|
|
72
|
+
return result;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* The action a gesture does, if any.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* gestureAction(DEFAULT_GESTURES, "tap2"); // "undo"
|
|
79
|
+
*/
|
|
80
|
+
var gestureAction = (gestures, gesture) => GESTURE_ACTIONS.find((action) => gestures[action] === gesture);
|
|
81
|
+
export { DEFAULT_GESTURES, GESTURE_ACTIONS, TOUCH_GESTURES, TOUCH_GESTURE_NAMES, assignGesture, gestureAction, sanitiseGestures };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Point } from "../../classes/Viewport";
|
|
2
|
+
import type { InputSettingsData } from "../../classes/InputSettings";
|
|
3
|
+
/** Pixels per line when a wheel reports its movement in lines */
|
|
4
|
+
export declare const WHEEL_LINE_PIXELS = 16;
|
|
5
|
+
/** Pixels per page when a wheel reports its movement in pages */
|
|
6
|
+
export declare const WHEEL_PAGE_PIXELS = 800;
|
|
7
|
+
/** Largest wheel movement (pixels) counted for one zoom step, so one mouse wheel notch can't zoom too far */
|
|
8
|
+
export declare const MAX_WHEEL_ZOOM_DELTA = 50;
|
|
9
|
+
/** How strongly wheel movement zooms: each pixel changes the zoom by about 1% */
|
|
10
|
+
export declare const WHEEL_ZOOM_SPEED = 0.01;
|
|
11
|
+
/** What a wheel or trackpad movement does to the view */
|
|
12
|
+
export type WheelAction = {
|
|
13
|
+
kind: "pan";
|
|
14
|
+
dx: number;
|
|
15
|
+
dy: number;
|
|
16
|
+
} | {
|
|
17
|
+
kind: "zoom";
|
|
18
|
+
factor: number;
|
|
19
|
+
} | null;
|
|
20
|
+
/** The navigation settings a wheel movement depends on */
|
|
21
|
+
export type NavigationSettings = Pick<InputSettingsData, "scrollPan" | "pinchZoom" | "modifierScrollZoom">;
|
|
22
|
+
/**
|
|
23
|
+
* Convert a wheel movement to pixels.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* wheelPixels(3, WheelEvent.DOM_DELTA_LINE); // 48
|
|
27
|
+
*/
|
|
28
|
+
export declare const wheelPixels: (delta: number, deltaMode: number) => number;
|
|
29
|
+
/**
|
|
30
|
+
* Decide what a wheel movement does:
|
|
31
|
+
*
|
|
32
|
+
* - With Ctrl held, or a trackpad pinch (which browsers report as Ctrl + wheel): zoom, if pinch zoom is on (or, where
|
|
33
|
+
* Ctrl is the zoom key, if modifier zoom is on).
|
|
34
|
+
* - With Cmd held (Apple devices): zoom, if modifier zoom is on.
|
|
35
|
+
* - Otherwise: pan, if scroll panning is on.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* wheelAction({ deltaX: 0, deltaY: 10, deltaMode: 0, ctrlKey: false, metaKey: false }, settings, "Cmd");
|
|
39
|
+
* // { kind: "pan", dx: -0, dy: -10 }
|
|
40
|
+
*/
|
|
41
|
+
export declare const wheelAction: (event: Pick<WheelEvent, "deltaX" | "deltaY" | "deltaMode" | "ctrlKey" | "metaKey">, settings: NavigationSettings, modifier: "Cmd" | "Ctrl") => WheelAction;
|
|
42
|
+
/**
|
|
43
|
+
* The midpoint of two touches and the distance between them, for two-finger pan and pinch.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* touchPair({ x: 0, y: 0 }, { x: 6, y: 8 }); // { mid: { x: 3, y: 4 }, distance: 10 }
|
|
47
|
+
*/
|
|
48
|
+
export declare const touchPair: (a: Point, b: Point) => {
|
|
49
|
+
mid: Point;
|
|
50
|
+
distance: number;
|
|
51
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/lib/utils/navigation/index.ts
|
|
2
|
+
/** Pixels per line when a wheel reports its movement in lines */
|
|
3
|
+
var WHEEL_LINE_PIXELS = 16;
|
|
4
|
+
/** Pixels per page when a wheel reports its movement in pages */
|
|
5
|
+
var WHEEL_PAGE_PIXELS = 800;
|
|
6
|
+
/** Largest wheel movement (pixels) counted for one zoom step, so one mouse wheel notch can't zoom too far */
|
|
7
|
+
var MAX_WHEEL_ZOOM_DELTA = 50;
|
|
8
|
+
/** How strongly wheel movement zooms: each pixel changes the zoom by about 1% */
|
|
9
|
+
var WHEEL_ZOOM_SPEED = .01;
|
|
10
|
+
/**
|
|
11
|
+
* Convert a wheel movement to pixels.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* wheelPixels(3, WheelEvent.DOM_DELTA_LINE); // 48
|
|
15
|
+
*/
|
|
16
|
+
var wheelPixels = (delta, deltaMode) => delta * (deltaMode === 1 ? 16 : deltaMode === 2 ? 800 : 1);
|
|
17
|
+
/**
|
|
18
|
+
* Decide what a wheel movement does:
|
|
19
|
+
*
|
|
20
|
+
* - With Ctrl held, or a trackpad pinch (which browsers report as Ctrl + wheel): zoom, if pinch zoom is on (or, where
|
|
21
|
+
* Ctrl is the zoom key, if modifier zoom is on).
|
|
22
|
+
* - With Cmd held (Apple devices): zoom, if modifier zoom is on.
|
|
23
|
+
* - Otherwise: pan, if scroll panning is on.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* wheelAction({ deltaX: 0, deltaY: 10, deltaMode: 0, ctrlKey: false, metaKey: false }, settings, "Cmd");
|
|
27
|
+
* // { kind: "pan", dx: -0, dy: -10 }
|
|
28
|
+
*/
|
|
29
|
+
var wheelAction = (event, settings, modifier) => {
|
|
30
|
+
const dx = wheelPixels(event.deltaX, event.deltaMode);
|
|
31
|
+
const dy = wheelPixels(event.deltaY, event.deltaMode);
|
|
32
|
+
const zooms = event.ctrlKey ? settings.pinchZoom || modifier === "Ctrl" && settings.modifierScrollZoom : event.metaKey && modifier === "Cmd" && settings.modifierScrollZoom;
|
|
33
|
+
if (event.ctrlKey || event.metaKey) {
|
|
34
|
+
if (!zooms) return null;
|
|
35
|
+
return {
|
|
36
|
+
kind: "zoom",
|
|
37
|
+
factor: Math.exp(-Math.max(-50, Math.min(50, dy)) * WHEEL_ZOOM_SPEED)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return settings.scrollPan ? {
|
|
41
|
+
kind: "pan",
|
|
42
|
+
dx: -dx,
|
|
43
|
+
dy: -dy
|
|
44
|
+
} : null;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* The midpoint of two touches and the distance between them, for two-finger pan and pinch.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* touchPair({ x: 0, y: 0 }, { x: 6, y: 8 }); // { mid: { x: 3, y: 4 }, distance: 10 }
|
|
51
|
+
*/
|
|
52
|
+
var touchPair = (a, b) => ({
|
|
53
|
+
mid: {
|
|
54
|
+
x: (a.x + b.x) / 2,
|
|
55
|
+
y: (a.y + b.y) / 2
|
|
56
|
+
},
|
|
57
|
+
distance: Math.hypot(b.x - a.x, b.y - a.y)
|
|
58
|
+
});
|
|
59
|
+
export { MAX_WHEEL_ZOOM_DELTA, WHEEL_LINE_PIXELS, WHEEL_PAGE_PIXELS, WHEEL_ZOOM_SPEED, touchPair, wheelAction, wheelPixels };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** A random number generator returning numbers from 0 (inclusive) to 1 (exclusive), like `Math.random` */
|
|
2
|
+
export type RandomFn = () => number;
|
|
3
|
+
/**
|
|
4
|
+
* A random whole number from 0 to 2³² − 1, e.g. a seed for `seededRandom`.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* randomSeed(); // 3141592653
|
|
8
|
+
*/
|
|
9
|
+
export declare const randomSeed: (random?: RandomFn) => number;
|
|
10
|
+
/**
|
|
11
|
+
* A random number generator that always gives the same numbers for the same seed (mulberry32), so random variation can
|
|
12
|
+
* be repeated, e.g. to draw a received stroke the same way.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const random = seededRandom(42);
|
|
16
|
+
* random(); // 0.6011037519201636, every time
|
|
17
|
+
*/
|
|
18
|
+
export declare const seededRandom: (seed: number) => RandomFn;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// src/lib/utils/random/index.ts
|
|
2
|
+
/**
|
|
3
|
+
* A random whole number from 0 to 2³² − 1, e.g. a seed for `seededRandom`.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* randomSeed(); // 3141592653
|
|
7
|
+
*/
|
|
8
|
+
var randomSeed = (random = Math.random) => Math.floor(random() * 4294967296);
|
|
9
|
+
/**
|
|
10
|
+
* A random number generator that always gives the same numbers for the same seed (mulberry32), so random variation can
|
|
11
|
+
* be repeated, e.g. to draw a received stroke the same way.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const random = seededRandom(42);
|
|
15
|
+
* random(); // 0.6011037519201636, every time
|
|
16
|
+
*/
|
|
17
|
+
var seededRandom = (seed) => {
|
|
18
|
+
let state = seed >>> 0;
|
|
19
|
+
return () => {
|
|
20
|
+
state = state + 1831565813 >>> 0;
|
|
21
|
+
let t = state;
|
|
22
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
23
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
24
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export { randomSeed, seededRandom };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Tool } from "../../classes/PaintCanvas";
|
|
2
|
+
/** Actions that keyboard shortcuts trigger */
|
|
3
|
+
export type ShortcutAction = {
|
|
4
|
+
type: "tool";
|
|
5
|
+
tool: Tool;
|
|
6
|
+
} | {
|
|
7
|
+
type: "brush-smaller";
|
|
8
|
+
} | {
|
|
9
|
+
type: "brush-bigger";
|
|
10
|
+
} | {
|
|
11
|
+
type: "toggle-interface";
|
|
12
|
+
} | {
|
|
13
|
+
type: "select-all";
|
|
14
|
+
} | {
|
|
15
|
+
type: "deselect";
|
|
16
|
+
} | {
|
|
17
|
+
type: "save";
|
|
18
|
+
} | {
|
|
19
|
+
type: "undo";
|
|
20
|
+
} | {
|
|
21
|
+
type: "redo";
|
|
22
|
+
} | {
|
|
23
|
+
type: "cut";
|
|
24
|
+
} | {
|
|
25
|
+
type: "delete";
|
|
26
|
+
} | {
|
|
27
|
+
type: "copy";
|
|
28
|
+
} | {
|
|
29
|
+
type: "paste";
|
|
30
|
+
} | {
|
|
31
|
+
type: "cancel";
|
|
32
|
+
} | {
|
|
33
|
+
type: "commit";
|
|
34
|
+
};
|
|
35
|
+
/** A key combination. `cmd` matches the Command key on macOS and Ctrl on Windows/Linux. */
|
|
36
|
+
export interface KeyCombo {
|
|
37
|
+
/** Lowercase `KeyboardEvent.key` value */
|
|
38
|
+
key: string;
|
|
39
|
+
cmd?: boolean;
|
|
40
|
+
shift?: boolean;
|
|
41
|
+
alt?: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface Shortcut extends KeyCombo {
|
|
44
|
+
action: ShortcutAction;
|
|
45
|
+
}
|
|
46
|
+
/** All keyboard shortcuts */
|
|
47
|
+
export declare const SHORTCUTS: readonly Shortcut[];
|
|
48
|
+
/** Key held to pan temporarily (handled separately because it acts while held, not on press) */
|
|
49
|
+
/**
|
|
50
|
+
* The symbol for the "cmd" key in shortcut labels: ⌘ on Apple devices, ^ (Control) elsewhere.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* cmdSymbol("MacIntel"); // "⌘"
|
|
54
|
+
* cmdSymbol("Win32"); // "^"
|
|
55
|
+
*/
|
|
56
|
+
export declare const cmdSymbol: (platform?: string) => string;
|
|
57
|
+
export declare const HOLD_TO_PAN_KEY = " ";
|
|
58
|
+
/** The parts of a keyboard event used to match shortcuts */
|
|
59
|
+
export type KeyEventLike = Pick<KeyboardEvent, "key" | "metaKey" | "ctrlKey" | "shiftKey" | "altKey">;
|
|
60
|
+
/**
|
|
61
|
+
* Find the action for a key press. Modifier keys must match exactly.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* matchShortcut({ key: "Z", metaKey: true, ctrlKey: false, shiftKey: true, altKey: false });
|
|
65
|
+
* // { type: "redo" }
|
|
66
|
+
*/
|
|
67
|
+
export declare const matchShortcut: (event: KeyEventLike, shortcuts?: readonly Shortcut[]) => ShortcutAction | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Check if a key press happened while typing in a text field, where shortcuts must not apply.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* isTypingTarget(document.querySelector("textarea")); // true
|
|
73
|
+
*/
|
|
74
|
+
export declare const isTypingTarget: (target: EventTarget | null | undefined) => boolean;
|