@lvl99dotcom/paintcanvas 0.0.0-stage → 0.2.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 +94 -0
- package/dist/lib/i18n/index.js +182 -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 +188 -0
- package/dist/locales/en-US.js +188 -0
- package/dist/locales/fr.js +188 -0
- package/dist/locales/ja.js +188 -0
- package/dist/locales/ko.js +188 -0
- package/dist/locales/zh-Hans.js +188 -0
- package/dist/locales/zh-Hant.js +188 -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 +463 -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 +150 -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 +141 -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 +543 -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 +186 -0
- package/locales/en-US.json +186 -0
- package/locales/fr.json +186 -0
- package/locales/ja.json +186 -0
- package/locales/ko.json +186 -0
- package/locales/zh-Hans.json +186 -0
- package/locales/zh-Hant.json +186 -0
- package/package.json +97 -3
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { AssetAuthor, BrushConfig } from "../types";
|
|
2
|
+
/** The author of the presets that come with PaintCanvas, and their heading unless the implementer names it */
|
|
3
|
+
export declare const BUILT_IN_AUTHOR = "PaintCanvas";
|
|
4
|
+
/** The heading of the user's own presets */
|
|
5
|
+
export declare const MY_PRESETS = "My presets";
|
|
6
|
+
/** The heading of presets without an author, and the author name of presets made by a user with no name or handle */
|
|
7
|
+
export declare const UNKNOWN_AUTHOR = "Unknown";
|
|
8
|
+
/** Names used when grouping presets, instead of PaintCanvas's own */
|
|
9
|
+
export interface AuthorNames {
|
|
10
|
+
/** Heading of the presets that come with the app (default `BUILT_IN_AUTHOR`) */
|
|
11
|
+
builtIn?: string;
|
|
12
|
+
/** Author name of presets made by a user without a handle (default `UNKNOWN_AUTHOR`) */
|
|
13
|
+
unknown?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Presets listed under one heading */
|
|
16
|
+
export interface PresetGroup {
|
|
17
|
+
label: string;
|
|
18
|
+
presets: BrushConfig[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* An author's name.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* authorName({ name: "Ada", handle: "ada.example" }); // "Ada"
|
|
25
|
+
* authorName(undefined); // "Unknown"
|
|
26
|
+
*/
|
|
27
|
+
export declare const authorName: (author: AssetAuthor | undefined) => string;
|
|
28
|
+
/**
|
|
29
|
+
* An author's handle, if known.
|
|
30
|
+
*/
|
|
31
|
+
export declare const authorHandle: (author: AssetAuthor | undefined) => string | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Whether a preset's author is the user: their handle matches, or for a user without a handle, the author has the
|
|
34
|
+
* unknown author's name (`unknownName`) and no handle.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* isOwnAuthor({ name: "Ada", handle: "ada.example" }, "ada.example"); // true
|
|
38
|
+
* isOwnAuthor({ name: "Unknown" }, ""); // true
|
|
39
|
+
*/
|
|
40
|
+
export declare const isOwnAuthor: (author: AssetAuthor | undefined, userHandle: string, unknownName?: string) => boolean;
|
|
41
|
+
/**
|
|
42
|
+
* The heading a preset is listed under: the built-in heading (the app's name) for the presets that come with the app,
|
|
43
|
+
* "My presets" for the user's own (see `isOwnAuthor`), otherwise the author's name.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* authorGroup(preset, false, "ada.example"); // "My presets"
|
|
47
|
+
*/
|
|
48
|
+
export declare const authorGroup: (preset: BrushConfig, builtIn: boolean, userHandle: string, names?: AuthorNames) => string;
|
|
49
|
+
/**
|
|
50
|
+
* Group presets by author, in their order within each group: the user's own first, then the presets that come with
|
|
51
|
+
* the app (under the built-in heading), then other authors alphabetically.
|
|
52
|
+
*
|
|
53
|
+
* @param isBuiltIn - whether a preset comes with the app
|
|
54
|
+
*/
|
|
55
|
+
export declare const groupPresetsByAuthor: (presets: BrushConfig[], isBuiltIn: (id: string) => boolean, userHandle: string, names?: AuthorNames) => PresetGroup[];
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// src/lib/brushes/authors/index.ts
|
|
2
|
+
/** The author of the presets that come with PaintCanvas, and their heading unless the implementer names it */
|
|
3
|
+
var BUILT_IN_AUTHOR = "PaintCanvas";
|
|
4
|
+
/** The heading of the user's own presets */
|
|
5
|
+
var MY_PRESETS = "My presets";
|
|
6
|
+
/** The heading of presets without an author, and the author name of presets made by a user with no name or handle */
|
|
7
|
+
var UNKNOWN_AUTHOR = "Unknown";
|
|
8
|
+
/**
|
|
9
|
+
* An author's name.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* authorName({ name: "Ada", handle: "ada.example" }); // "Ada"
|
|
13
|
+
* authorName(undefined); // "Unknown"
|
|
14
|
+
*/
|
|
15
|
+
var authorName = (author) => (typeof author === "string" ? author : author?.name) || "Unknown";
|
|
16
|
+
/**
|
|
17
|
+
* An author's handle, if known.
|
|
18
|
+
*/
|
|
19
|
+
var authorHandle = (author) => typeof author === "object" ? author.handle : void 0;
|
|
20
|
+
/**
|
|
21
|
+
* Whether a preset's author is the user: their handle matches, or for a user without a handle, the author has the
|
|
22
|
+
* unknown author's name (`unknownName`) and no handle.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* isOwnAuthor({ name: "Ada", handle: "ada.example" }, "ada.example"); // true
|
|
26
|
+
* isOwnAuthor({ name: "Unknown" }, ""); // true
|
|
27
|
+
*/
|
|
28
|
+
var isOwnAuthor = (author, userHandle, unknownName = UNKNOWN_AUTHOR) => userHandle ? authorHandle(author) === userHandle : typeof author === "object" && !author.handle && author.name === unknownName;
|
|
29
|
+
/**
|
|
30
|
+
* The heading a preset is listed under: the built-in heading (the app's name) for the presets that come with the app,
|
|
31
|
+
* "My presets" for the user's own (see `isOwnAuthor`), otherwise the author's name.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* authorGroup(preset, false, "ada.example"); // "My presets"
|
|
35
|
+
*/
|
|
36
|
+
var authorGroup = (preset, builtIn, userHandle, names = {}) => {
|
|
37
|
+
if (builtIn) return names.builtIn ?? "PaintCanvas";
|
|
38
|
+
if (isOwnAuthor(preset.author, userHandle, names.unknown)) return MY_PRESETS;
|
|
39
|
+
return authorName(preset.author);
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Group presets by author, in their order within each group: the user's own first, then the presets that come with
|
|
43
|
+
* the app (under the built-in heading), then other authors alphabetically.
|
|
44
|
+
*
|
|
45
|
+
* @param isBuiltIn - whether a preset comes with the app
|
|
46
|
+
*/
|
|
47
|
+
var groupPresetsByAuthor = (presets, isBuiltIn, userHandle, names = {}) => {
|
|
48
|
+
const builtInLabel = names.builtIn ?? "PaintCanvas";
|
|
49
|
+
const groups = /* @__PURE__ */ new Map();
|
|
50
|
+
for (const preset of presets) {
|
|
51
|
+
const label = authorGroup(preset, isBuiltIn(preset.id), userHandle, names);
|
|
52
|
+
groups.set(label, [...groups.get(label) ?? [], preset]);
|
|
53
|
+
}
|
|
54
|
+
const rank = (label) => label === "My presets" ? 0 : label === builtInLabel ? 1 : 2;
|
|
55
|
+
return [...groups].map(([label, list]) => ({
|
|
56
|
+
label,
|
|
57
|
+
presets: list
|
|
58
|
+
})).sort((a, b) => rank(a.label) - rank(b.label) || a.label.localeCompare(b.label));
|
|
59
|
+
};
|
|
60
|
+
export { BUILT_IN_AUTHOR, MY_PRESETS, UNKNOWN_AUTHOR, authorGroup, authorHandle, authorName, groupPresetsByAuthor, isOwnAuthor };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { BrushConfig } from "../types";
|
|
2
|
+
/** The brush settings the configuration window can vary with pressure, tilt or randomness */
|
|
3
|
+
export type VaryingParam = "width" | "height" | "alpha" | "rotation" | "offsetX" | "offsetY";
|
|
4
|
+
/** What a setting can vary with */
|
|
5
|
+
export type VariationSource = "pressure" | "tilt" | "randomise";
|
|
6
|
+
/** All variation sources, in the order they are shown */
|
|
7
|
+
export declare const VARIATION_SOURCES: readonly VariationSource[];
|
|
8
|
+
/** A setting as the configuration window shows it, in its unit (pixels, percent or degrees) */
|
|
9
|
+
export interface ParamRange {
|
|
10
|
+
/** The setting's value when it doesn't vary */
|
|
11
|
+
value: number;
|
|
12
|
+
/** Lowest value when it varies */
|
|
13
|
+
min: number;
|
|
14
|
+
/** Highest value when it varies */
|
|
15
|
+
max: number;
|
|
16
|
+
/** Which sources it varies with */
|
|
17
|
+
sources: Record<VariationSource, boolean>;
|
|
18
|
+
/** Whether it varies with any source */
|
|
19
|
+
varies: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** Each setting's unit range in the window */
|
|
22
|
+
export declare const PARAM_LIMITS: Record<VaryingParam, {
|
|
23
|
+
min: number;
|
|
24
|
+
max: number;
|
|
25
|
+
}>;
|
|
26
|
+
/**
|
|
27
|
+
* Read a setting as the configuration window shows it. When it varies, its range comes from the first source turned
|
|
28
|
+
* on: scale settings (width, height, opacity) go from `value × min factor` to the value; rotation goes from the angle
|
|
29
|
+
* to the angle plus up to 360° × the factors; offsets scatter up to the factors × the brush width.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* readRange({ id: "a", width: 40, widthConfig: { usePressure: true, minPressure: 0.25 } }, "width");
|
|
33
|
+
* // { value: 40, min: 10, max: 40, sources: { pressure: true, tilt: false, randomise: false }, varies: true }
|
|
34
|
+
*/
|
|
35
|
+
export declare const readRange: (config: BrushConfig, param: VaryingParam) => ParamRange;
|
|
36
|
+
/**
|
|
37
|
+
* Write a setting's range (in the window's unit) to every source turned on, as config changes. For a setting that
|
|
38
|
+
* doesn't vary, only `value` is used.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* writeRange(config, "width", { min: 10, max: 40 }); // { width: 40, widthConfig: { usePressure: true, minPressure: 0.25, maxPressure: 1 } }
|
|
42
|
+
*/
|
|
43
|
+
export declare const writeRange: (config: BrushConfig, param: VaryingParam, range: {
|
|
44
|
+
value?: number;
|
|
45
|
+
min?: number;
|
|
46
|
+
max?: number;
|
|
47
|
+
}) => Partial<BrushConfig>;
|
|
48
|
+
/**
|
|
49
|
+
* Turn a source on or off for a setting, as config changes. A source turned on takes the setting's current range, or
|
|
50
|
+
* when it is the first, a starting range (half to full size, the angle itself, or no scatter).
|
|
51
|
+
*/
|
|
52
|
+
export declare const toggleSource: (config: BrushConfig, param: VaryingParam, source: VariationSource, on: boolean) => Partial<BrushConfig>;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// src/lib/brushes/configRanges/index.ts
|
|
2
|
+
/** All variation sources, in the order they are shown */
|
|
3
|
+
var VARIATION_SOURCES = [
|
|
4
|
+
"pressure",
|
|
5
|
+
"tilt",
|
|
6
|
+
"randomise"
|
|
7
|
+
];
|
|
8
|
+
/** Each setting's unit range in the window */
|
|
9
|
+
var PARAM_LIMITS = {
|
|
10
|
+
width: {
|
|
11
|
+
min: 1,
|
|
12
|
+
max: 256
|
|
13
|
+
},
|
|
14
|
+
height: {
|
|
15
|
+
min: 1,
|
|
16
|
+
max: 256
|
|
17
|
+
},
|
|
18
|
+
alpha: {
|
|
19
|
+
min: 0,
|
|
20
|
+
max: 100
|
|
21
|
+
},
|
|
22
|
+
rotation: {
|
|
23
|
+
min: 0,
|
|
24
|
+
max: 360
|
|
25
|
+
},
|
|
26
|
+
offsetX: {
|
|
27
|
+
min: 0,
|
|
28
|
+
max: 256
|
|
29
|
+
},
|
|
30
|
+
offsetY: {
|
|
31
|
+
min: 0,
|
|
32
|
+
max: 256
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
/** How settings are varied (see `BrushParamConfig`) */
|
|
36
|
+
var KINDS = {
|
|
37
|
+
width: "scale",
|
|
38
|
+
height: "scale",
|
|
39
|
+
alpha: "scale",
|
|
40
|
+
rotation: "angle",
|
|
41
|
+
offsetX: "offset",
|
|
42
|
+
offsetY: "offset"
|
|
43
|
+
};
|
|
44
|
+
/** The config keys of each source's switch and range */
|
|
45
|
+
var SOURCE_KEYS = {
|
|
46
|
+
pressure: [
|
|
47
|
+
"usePressure",
|
|
48
|
+
"minPressure",
|
|
49
|
+
"maxPressure"
|
|
50
|
+
],
|
|
51
|
+
tilt: [
|
|
52
|
+
"useTilt",
|
|
53
|
+
"minTilt",
|
|
54
|
+
"maxTilt"
|
|
55
|
+
],
|
|
56
|
+
randomise: [
|
|
57
|
+
"useRandomise",
|
|
58
|
+
"minRandomise",
|
|
59
|
+
"maxRandomise"
|
|
60
|
+
]
|
|
61
|
+
};
|
|
62
|
+
/** Each source's range when not set (see `BrushParamConfig`) */
|
|
63
|
+
var SOURCE_DEFAULTS = {
|
|
64
|
+
pressure: [0, 1],
|
|
65
|
+
tilt: [0, 1],
|
|
66
|
+
randomise: [1, 1]
|
|
67
|
+
};
|
|
68
|
+
var configKey = (param) => `${param}Config`;
|
|
69
|
+
var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
|
|
70
|
+
/** A setting's value as stored (alpha as 0–1) */
|
|
71
|
+
var storedValue = (config, param) => {
|
|
72
|
+
if (param === "alpha") return config.alpha ?? 1;
|
|
73
|
+
if (param === "width") return config.width ?? 1;
|
|
74
|
+
if (param === "height") return config.height ?? config.width ?? 1;
|
|
75
|
+
return config[param] ?? 0;
|
|
76
|
+
};
|
|
77
|
+
/** A stored value in the window's unit */
|
|
78
|
+
var toUnit = (param, value) => param === "alpha" ? value * 100 : value;
|
|
79
|
+
/** A value in the window's unit as stored */
|
|
80
|
+
var fromUnit = (param, value) => param === "alpha" ? value / 100 : value;
|
|
81
|
+
/**
|
|
82
|
+
* Read a setting as the configuration window shows it. When it varies, its range comes from the first source turned
|
|
83
|
+
* on: scale settings (width, height, opacity) go from `value × min factor` to the value; rotation goes from the angle
|
|
84
|
+
* to the angle plus up to 360° × the factors; offsets scatter up to the factors × the brush width.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* readRange({ id: "a", width: 40, widthConfig: { usePressure: true, minPressure: 0.25 } }, "width");
|
|
88
|
+
* // { value: 40, min: 10, max: 40, sources: { pressure: true, tilt: false, randomise: false }, varies: true }
|
|
89
|
+
*/
|
|
90
|
+
var readRange = (config, param) => {
|
|
91
|
+
const variation = config[configKey(param)] ?? {};
|
|
92
|
+
const sources = Object.fromEntries(VARIATION_SOURCES.map((source) => [source, Boolean(variation[SOURCE_KEYS[source][0]])]));
|
|
93
|
+
const first = VARIATION_SOURCES.find((source) => sources[source]);
|
|
94
|
+
const value = toUnit(param, storedValue(config, param));
|
|
95
|
+
if (!first) return {
|
|
96
|
+
value,
|
|
97
|
+
min: value,
|
|
98
|
+
max: value,
|
|
99
|
+
sources,
|
|
100
|
+
varies: false
|
|
101
|
+
};
|
|
102
|
+
const [, minKey, maxKey] = SOURCE_KEYS[first];
|
|
103
|
+
const [minDefault, maxDefault] = SOURCE_DEFAULTS[first];
|
|
104
|
+
const low = variation[minKey] ?? minDefault;
|
|
105
|
+
const high = variation[maxKey] ?? maxDefault;
|
|
106
|
+
const width = config.width ?? 1;
|
|
107
|
+
switch (KINDS[param]) {
|
|
108
|
+
case "scale": return {
|
|
109
|
+
value,
|
|
110
|
+
min: value * low,
|
|
111
|
+
max: value * high,
|
|
112
|
+
sources,
|
|
113
|
+
varies: true
|
|
114
|
+
};
|
|
115
|
+
case "angle": return {
|
|
116
|
+
value,
|
|
117
|
+
min: value + low * 360,
|
|
118
|
+
max: value + high * 360,
|
|
119
|
+
sources,
|
|
120
|
+
varies: true
|
|
121
|
+
};
|
|
122
|
+
default: return {
|
|
123
|
+
value,
|
|
124
|
+
min: low * width,
|
|
125
|
+
max: high * width,
|
|
126
|
+
sources,
|
|
127
|
+
varies: true
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Write a setting's range (in the window's unit) to every source turned on, as config changes. For a setting that
|
|
133
|
+
* doesn't vary, only `value` is used.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* writeRange(config, "width", { min: 10, max: 40 }); // { width: 40, widthConfig: { usePressure: true, minPressure: 0.25, maxPressure: 1 } }
|
|
137
|
+
*/
|
|
138
|
+
var writeRange = (config, param, range) => {
|
|
139
|
+
const current = readRange(config, param);
|
|
140
|
+
const limits = PARAM_LIMITS[param];
|
|
141
|
+
if (!current.varies) {
|
|
142
|
+
const value = clamp(range.value ?? current.value, limits.min, limits.max);
|
|
143
|
+
return KINDS[param] === "offset" ? {} : { [param]: fromUnit(param, value) };
|
|
144
|
+
}
|
|
145
|
+
let min = clamp(range.min ?? current.min, limits.min, limits.max);
|
|
146
|
+
let max = clamp(range.max ?? current.max, limits.min, limits.max);
|
|
147
|
+
if (min > max) [min, max] = [max, min];
|
|
148
|
+
const width = config.width ?? 1;
|
|
149
|
+
let changes;
|
|
150
|
+
let factors;
|
|
151
|
+
switch (KINDS[param]) {
|
|
152
|
+
case "scale":
|
|
153
|
+
changes = { [param]: fromUnit(param, Math.max(max, limits.min || 1e-6)) };
|
|
154
|
+
factors = [max > 0 ? min / max : 0, 1];
|
|
155
|
+
break;
|
|
156
|
+
case "angle":
|
|
157
|
+
changes = { rotation: min };
|
|
158
|
+
factors = [0, (max - min) / 360];
|
|
159
|
+
break;
|
|
160
|
+
default:
|
|
161
|
+
changes = {};
|
|
162
|
+
factors = [clamp(min / width, 0, 1), clamp(max / width, 0, 1)];
|
|
163
|
+
}
|
|
164
|
+
const variation = { ...config[configKey(param)] };
|
|
165
|
+
for (const source of VARIATION_SOURCES) {
|
|
166
|
+
if (!current.sources[source]) continue;
|
|
167
|
+
const [, minKey, maxKey] = SOURCE_KEYS[source];
|
|
168
|
+
variation[minKey] = factors[0];
|
|
169
|
+
variation[maxKey] = factors[1];
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
...changes,
|
|
173
|
+
[configKey(param)]: variation
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* Turn a source on or off for a setting, as config changes. A source turned on takes the setting's current range, or
|
|
178
|
+
* when it is the first, a starting range (half to full size, the angle itself, or no scatter).
|
|
179
|
+
*/
|
|
180
|
+
var toggleSource = (config, param, source, on) => {
|
|
181
|
+
const key = configKey(param);
|
|
182
|
+
const [useKey, minKey, maxKey] = SOURCE_KEYS[source];
|
|
183
|
+
const current = readRange(config, param);
|
|
184
|
+
const variation = {
|
|
185
|
+
...config[key],
|
|
186
|
+
[useKey]: on
|
|
187
|
+
};
|
|
188
|
+
if (on && !current.varies) {
|
|
189
|
+
const starting = {
|
|
190
|
+
scale: [.5, 1],
|
|
191
|
+
angle: [0, 0],
|
|
192
|
+
offset: [0, .5]
|
|
193
|
+
};
|
|
194
|
+
[variation[minKey], variation[maxKey]] = starting[KINDS[param]];
|
|
195
|
+
return { [key]: variation };
|
|
196
|
+
}
|
|
197
|
+
if (on) return writeRange({
|
|
198
|
+
...config,
|
|
199
|
+
[key]: variation
|
|
200
|
+
}, param, {
|
|
201
|
+
min: current.min,
|
|
202
|
+
max: current.max
|
|
203
|
+
});
|
|
204
|
+
return { [key]: variation };
|
|
205
|
+
};
|
|
206
|
+
export { PARAM_LIMITS, VARIATION_SOURCES, readRange, toggleSource, writeRange };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import round_default from "./presets/round/index.js";
|
|
2
|
+
import square_default from "./presets/square/index.js";
|
|
3
|
+
import drawing_default from "./presets/drawing/index.js";
|
|
4
|
+
import painting_default from "./presets/painting/index.js";
|
|
5
|
+
import sketching from "./presets/sketching/index.js";
|
|
6
|
+
// src/lib/brushes/index.ts
|
|
7
|
+
/** All presets, in the order they are listed: round, square and soft round, pencils and pens, paints and textures, then sketching presets */
|
|
8
|
+
var allPresets = {
|
|
9
|
+
...round_default,
|
|
10
|
+
...square_default,
|
|
11
|
+
...drawing_default,
|
|
12
|
+
...painting_default,
|
|
13
|
+
...sketching
|
|
14
|
+
};
|
|
15
|
+
/** Brush presets */
|
|
16
|
+
var brushPresets = structuredClone(allPresets);
|
|
17
|
+
/** Eraser presets (a separate copy of the brush presets) */
|
|
18
|
+
var eraserPresets = structuredClone(allPresets);
|
|
19
|
+
export { brushPresets, eraserPresets };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { BrushConfig, BrushParamConfig, BrushStrokePoint } from "../types";
|
|
2
|
+
import type { RandomFn } from "../../utils/random";
|
|
3
|
+
/** A stamp's final size, rotation, offset and alpha after variation */
|
|
4
|
+
export interface StampParams {
|
|
5
|
+
/** Width in pixels */
|
|
6
|
+
width: number;
|
|
7
|
+
/** Height in pixels */
|
|
8
|
+
height: number;
|
|
9
|
+
/** Rotation in degrees */
|
|
10
|
+
rotation: number;
|
|
11
|
+
/** Horizontal offset from the stroke point, in pixels */
|
|
12
|
+
offsetX: number;
|
|
13
|
+
/** Vertical offset from the stroke point, in pixels */
|
|
14
|
+
offsetY: number;
|
|
15
|
+
/** Alpha (0–1) */
|
|
16
|
+
alpha: number;
|
|
17
|
+
}
|
|
18
|
+
/** What the input device reports for a point */
|
|
19
|
+
export interface PointInput {
|
|
20
|
+
/** Pressure (0–1), only meaningful for pens */
|
|
21
|
+
pressure?: number;
|
|
22
|
+
/** Tilt in degrees along X and Y (-90–90), only meaningful for pens */
|
|
23
|
+
tiltX?: number;
|
|
24
|
+
tiltY?: number;
|
|
25
|
+
/** Whether the point came from a pen (mouse and touch report no real pressure or tilt) */
|
|
26
|
+
isPen: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Linear interpolation between two values.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* lerp(0, 10, 0.25); // 2.5
|
|
33
|
+
*/
|
|
34
|
+
export declare const lerp: (from: number, to: number, amount: number) => number;
|
|
35
|
+
/**
|
|
36
|
+
* How far the pen leans: 0 when upright, 1 when flat.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* tiltAmount(0, 0); // 0
|
|
40
|
+
* tiltAmount(90, 0); // 1
|
|
41
|
+
*/
|
|
42
|
+
export declare const tiltAmount: (tiltX?: number, tiltY?: number) => number;
|
|
43
|
+
/**
|
|
44
|
+
* The direction the pen leans, in degrees (0 = towards +X, 90 = towards +Y).
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* tiltDirection(0, 45); // 90
|
|
48
|
+
*/
|
|
49
|
+
export declare const tiltDirection: (tiltX?: number, tiltY?: number) => number;
|
|
50
|
+
/**
|
|
51
|
+
* Combine a parameter's randomise, pressure and tilt factors into one factor (they multiply). Returns 1 when nothing is enabled.
|
|
52
|
+
* Pressure and tilt only apply to pens; for other devices they count as full (`maxPressure`/`maxTilt`).
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* variationFactor({ useRandomise: true, minRandomise: 0.5, maxRandomise: 1 }, { isPen: false }, () => 0); // 0.5
|
|
56
|
+
*/
|
|
57
|
+
export declare const variationFactor: (config: BrushParamConfig | undefined, input: PointInput, random?: RandomFn) => number;
|
|
58
|
+
/**
|
|
59
|
+
* Work out one stamp's size, rotation, offset and alpha from the preset, the user's chosen size and the input at that point.
|
|
60
|
+
*
|
|
61
|
+
* - Size and alpha are scaled by their factor.
|
|
62
|
+
* - Rotation adds `factor × 360°`; with `useTilt`, it follows the pen's lean direction instead.
|
|
63
|
+
* - Offsets scatter up to `factor × size` in a random direction.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* stampParams(preset, { width: 20, height: 20 }, { isPen: true, pressure: 0.5 });
|
|
67
|
+
*/
|
|
68
|
+
export declare const stampParams: (preset: BrushConfig, size: {
|
|
69
|
+
width: number;
|
|
70
|
+
height: number;
|
|
71
|
+
}, input: PointInput, random?: RandomFn) => StampParams;
|
|
72
|
+
/** An area on whole pixels, given by its centre and size */
|
|
73
|
+
export interface PixelArea {
|
|
74
|
+
/** Centre X, in pixels */
|
|
75
|
+
x: number;
|
|
76
|
+
/** Centre Y, in pixels */
|
|
77
|
+
y: number;
|
|
78
|
+
/** Width in whole pixels */
|
|
79
|
+
width: number;
|
|
80
|
+
/** Height in whole pixels */
|
|
81
|
+
height: number;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Move an area centred on a point onto whole pixels. The size is rounded to whole pixels and the top-left corner is
|
|
85
|
+
* rounded down, so the pixel under the point is always covered.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* snapToPixelGrid(10.7, 4.2, 1, 1); // { x: 10.5, y: 4.5, width: 1, height: 1 }: the pixel at (10, 4)
|
|
89
|
+
* snapToPixelGrid(10.7, 4.2, 2, 2); // { x: 10, y: 4, width: 2, height: 2 }: pixels 9–10 and 3–4
|
|
90
|
+
*/
|
|
91
|
+
export declare const snapToPixelGrid: (x: number, y: number, width: number, height: number) => PixelArea;
|
|
92
|
+
/**
|
|
93
|
+
* Move a stamp onto whole pixels (see {@link snapToPixelGrid}). Its offset is included in the returned point, so the
|
|
94
|
+
* returned params have no offset.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* snapStamp(10.7, 4.2, { width: 1, height: 1, rotation: 0, offsetX: 2, offsetY: 0, alpha: 1 });
|
|
98
|
+
* // { x: 12.5, y: 4.5, params: { width: 1, height: 1, rotation: 0, offsetX: 0, offsetY: 0, alpha: 1 } }
|
|
99
|
+
*/
|
|
100
|
+
export declare const snapStamp: (x: number, y: number, params: StampParams) => {
|
|
101
|
+
x: number;
|
|
102
|
+
y: number;
|
|
103
|
+
params: StampParams;
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Describe a stroke point's input for parameter variation.
|
|
107
|
+
*/
|
|
108
|
+
export declare const pointInput: (point: BrushStrokePoint, isPen: boolean) => PointInput;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// src/lib/brushes/params/index.ts
|
|
2
|
+
/**
|
|
3
|
+
* Linear interpolation between two values.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* lerp(0, 10, 0.25); // 2.5
|
|
7
|
+
*/
|
|
8
|
+
var lerp = (from, to, amount) => from + (to - from) * amount;
|
|
9
|
+
/**
|
|
10
|
+
* Clamp a value between 0 and 1.
|
|
11
|
+
*/
|
|
12
|
+
var clamp01 = (value) => Math.max(0, Math.min(1, value));
|
|
13
|
+
/**
|
|
14
|
+
* How far the pen leans: 0 when upright, 1 when flat.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* tiltAmount(0, 0); // 0
|
|
18
|
+
* tiltAmount(90, 0); // 1
|
|
19
|
+
*/
|
|
20
|
+
var tiltAmount = (tiltX = 0, tiltY = 0) => clamp01(Math.hypot(tiltX, tiltY) / 90);
|
|
21
|
+
/**
|
|
22
|
+
* The direction the pen leans, in degrees (0 = towards +X, 90 = towards +Y).
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* tiltDirection(0, 45); // 90
|
|
26
|
+
*/
|
|
27
|
+
var tiltDirection = (tiltX = 0, tiltY = 0) => Math.atan2(tiltY, tiltX) * 180 / Math.PI;
|
|
28
|
+
/**
|
|
29
|
+
* Combine a parameter's randomise, pressure and tilt factors into one factor (they multiply). Returns 1 when nothing is enabled.
|
|
30
|
+
* Pressure and tilt only apply to pens; for other devices they count as full (`maxPressure`/`maxTilt`).
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* variationFactor({ useRandomise: true, minRandomise: 0.5, maxRandomise: 1 }, { isPen: false }, () => 0); // 0.5
|
|
34
|
+
*/
|
|
35
|
+
var variationFactor = (config, input, random = Math.random) => {
|
|
36
|
+
if (!config) return 1;
|
|
37
|
+
let factor = 1;
|
|
38
|
+
if (config.useRandomise) factor *= lerp(config.minRandomise ?? 1, config.maxRandomise ?? 1, random());
|
|
39
|
+
if (config.usePressure) {
|
|
40
|
+
const pressure = input.isPen ? clamp01(input.pressure ?? 1) : 1;
|
|
41
|
+
factor *= lerp(config.minPressure ?? 0, config.maxPressure ?? 1, pressure);
|
|
42
|
+
}
|
|
43
|
+
if (config.useTilt) {
|
|
44
|
+
const tilt = input.isPen ? tiltAmount(input.tiltX, input.tiltY) : 1;
|
|
45
|
+
factor *= lerp(config.minTilt ?? 0, config.maxTilt ?? 1, tilt);
|
|
46
|
+
}
|
|
47
|
+
return factor;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Work out one stamp's size, rotation, offset and alpha from the preset, the user's chosen size and the input at that point.
|
|
51
|
+
*
|
|
52
|
+
* - Size and alpha are scaled by their factor.
|
|
53
|
+
* - Rotation adds `factor × 360°`; with `useTilt`, it follows the pen's lean direction instead.
|
|
54
|
+
* - Offsets scatter up to `factor × size` in a random direction.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* stampParams(preset, { width: 20, height: 20 }, { isPen: true, pressure: 0.5 });
|
|
58
|
+
*/
|
|
59
|
+
var stampParams = (preset, size, input, random = Math.random) => {
|
|
60
|
+
const width = Math.max(0, size.width * variationFactor(preset.widthConfig, input, random));
|
|
61
|
+
const height = preset.constrainProportions ? width * size.height / size.width : Math.max(0, size.height * variationFactor(preset.heightConfig, input, random));
|
|
62
|
+
let rotation = preset.rotation ?? 0;
|
|
63
|
+
const rotationConfig = preset.rotationConfig;
|
|
64
|
+
if (rotationConfig?.useTilt && input.isPen && tiltAmount(input.tiltX, input.tiltY) > 0) rotation += tiltDirection(input.tiltX, input.tiltY);
|
|
65
|
+
else if (rotationConfig?.useRandomise || rotationConfig?.usePressure) rotation += variationFactor({
|
|
66
|
+
...rotationConfig,
|
|
67
|
+
useTilt: false
|
|
68
|
+
}, input, random) * 360;
|
|
69
|
+
/** Base offset plus a scatter of up to `factor × extent` in a random direction */
|
|
70
|
+
const scatter = (base, config, extent) => {
|
|
71
|
+
if (!config?.useRandomise && !config?.usePressure && !config?.useTilt) return base ?? 0;
|
|
72
|
+
const direction = random() < .5 ? -1 : 1;
|
|
73
|
+
return (base ?? 0) + direction * variationFactor(config, input, random) * extent;
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
width,
|
|
77
|
+
height,
|
|
78
|
+
rotation,
|
|
79
|
+
offsetX: scatter(preset.offsetX, preset.offsetXConfig, width),
|
|
80
|
+
offsetY: scatter(preset.offsetY, preset.offsetYConfig, height),
|
|
81
|
+
alpha: clamp01((preset.alpha ?? 1) * variationFactor(preset.alphaConfig, input, random))
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Round a size to whole pixels: at least 1 pixel unless it is 0 or less (nothing is drawn).
|
|
86
|
+
*/
|
|
87
|
+
var wholePixels = (size) => size > 0 ? Math.max(1, Math.round(size)) : 0;
|
|
88
|
+
/**
|
|
89
|
+
* Move an area centred on a point onto whole pixels. The size is rounded to whole pixels and the top-left corner is
|
|
90
|
+
* rounded down, so the pixel under the point is always covered.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* snapToPixelGrid(10.7, 4.2, 1, 1); // { x: 10.5, y: 4.5, width: 1, height: 1 }: the pixel at (10, 4)
|
|
94
|
+
* snapToPixelGrid(10.7, 4.2, 2, 2); // { x: 10, y: 4, width: 2, height: 2 }: pixels 9–10 and 3–4
|
|
95
|
+
*/
|
|
96
|
+
var snapToPixelGrid = (x, y, width, height) => {
|
|
97
|
+
const snappedWidth = wholePixels(width);
|
|
98
|
+
const snappedHeight = wholePixels(height);
|
|
99
|
+
const left = Math.floor(x) - Math.floor(snappedWidth / 2);
|
|
100
|
+
const top = Math.floor(y) - Math.floor(snappedHeight / 2);
|
|
101
|
+
return {
|
|
102
|
+
x: left + snappedWidth / 2,
|
|
103
|
+
y: top + snappedHeight / 2,
|
|
104
|
+
width: snappedWidth,
|
|
105
|
+
height: snappedHeight
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Move a stamp onto whole pixels (see {@link snapToPixelGrid}). Its offset is included in the returned point, so the
|
|
110
|
+
* returned params have no offset.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* snapStamp(10.7, 4.2, { width: 1, height: 1, rotation: 0, offsetX: 2, offsetY: 0, alpha: 1 });
|
|
114
|
+
* // { x: 12.5, y: 4.5, params: { width: 1, height: 1, rotation: 0, offsetX: 0, offsetY: 0, alpha: 1 } }
|
|
115
|
+
*/
|
|
116
|
+
var snapStamp = (x, y, params) => {
|
|
117
|
+
const area = snapToPixelGrid(x + params.offsetX, y + params.offsetY, params.width, params.height);
|
|
118
|
+
return {
|
|
119
|
+
x: area.x,
|
|
120
|
+
y: area.y,
|
|
121
|
+
params: {
|
|
122
|
+
...params,
|
|
123
|
+
width: area.width,
|
|
124
|
+
height: area.height,
|
|
125
|
+
offsetX: 0,
|
|
126
|
+
offsetY: 0
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Describe a stroke point's input for parameter variation.
|
|
132
|
+
*/
|
|
133
|
+
var pointInput = (point, isPen) => ({
|
|
134
|
+
pressure: point.pressure,
|
|
135
|
+
tiltX: point.tiltX,
|
|
136
|
+
tiltY: point.tiltY,
|
|
137
|
+
isPen
|
|
138
|
+
});
|
|
139
|
+
export { lerp, pointInput, snapStamp, snapToPixelGrid, stampParams, tiltAmount, tiltDirection, variationFactor };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { BrushConfig } from "../types";
|
|
2
|
+
/** Messages shown when importing or exporting presets fails */
|
|
3
|
+
export declare const PRESET_FILE_ERRORS: {
|
|
4
|
+
readonly incompatible: "Brush preset JSON file not compatible";
|
|
5
|
+
readonly read: "Failed to read brush preset JSON file";
|
|
6
|
+
readonly export: "Error exporting brush preset JSON file";
|
|
7
|
+
readonly empty: "No brush presets to export";
|
|
8
|
+
};
|
|
9
|
+
/** Importing or exporting presets failed, with a message for the user */
|
|
10
|
+
export declare class PresetFileError extends Error {
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The file name presets are exported to on a day, starting with a prefix (see `PaintCanvasNames.filePrefix`).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* presetFileName(new Date(2026, 9, 24)); // "paintcanvas-brush-presets-2026-10-24.json"
|
|
17
|
+
* presetFileName(new Date(2026, 9, 24), "myapp"); // "myapp-brush-presets-2026-10-24.json"
|
|
18
|
+
*/
|
|
19
|
+
export declare const presetFileName: (date?: Date, prefix?: string) => string;
|
|
20
|
+
/**
|
|
21
|
+
* Write presets to a brush preset file (JSON) of a format (see `PaintCanvasNames.presetFileFormat`). Throws a
|
|
22
|
+
* `PresetFileError` if there are none, or one can't be exported (e.g. its shape isn't a PNG data URL).
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* exportPresetFile([myPreset]); // '{ "format": "paintcanvas-brush-presets", "version": 1, "presets": [...] }'
|
|
26
|
+
*/
|
|
27
|
+
export declare const exportPresetFile: (presets: BrushConfig[], format?: string) => string;
|
|
28
|
+
/**
|
|
29
|
+
* Read the presets in a brush preset file of one of the given formats (PaintCanvas's by default).
|
|
30
|
+
* Throws a `PresetFileError` if it isn't valid JSON or a compatible file.
|
|
31
|
+
*/
|
|
32
|
+
export declare const parsePresetFile: (text: string, formats?: readonly string[]) => BrushConfig[];
|