@pixerate/editor 0.109.0 → 0.110.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/dist/blending-DXl06NSx.d.ts +486 -0
- package/dist/blending-aOE6Y3UE.d.cts +486 -0
- package/dist/chunk-7VOHOPLW.cjs +1136 -0
- package/dist/chunk-7VOHOPLW.cjs.map +1 -0
- package/dist/{chunk-RE77QYUF.js → chunk-DMRSJBJF.js} +3 -3
- package/dist/chunk-FTBZPI77.cjs +1 -0
- package/dist/chunk-FTBZPI77.cjs.map +1 -0
- package/dist/{chunk-WMBIJCHA.js → chunk-HNS7UHCM.js} +5 -1
- package/dist/{chunk-WMBIJCHA.js.map → chunk-HNS7UHCM.js.map} +1 -1
- package/dist/{chunk-CFMWALBM.js → chunk-LQEEDHOP.js} +2 -2
- package/dist/{chunk-D37BX2ZA.cjs → chunk-MGL4U5WA.cjs} +199 -199
- package/dist/{chunk-D37BX2ZA.cjs.map → chunk-MGL4U5WA.cjs.map} +1 -1
- package/dist/{chunk-AZTZRLDI.cjs → chunk-POGBUYWE.cjs} +92 -92
- package/dist/{chunk-AZTZRLDI.cjs.map → chunk-POGBUYWE.cjs.map} +1 -1
- package/dist/chunk-VKK5VS2T.js +1 -0
- package/dist/{chunk-SZ6KMZNM.cjs → chunk-XIOMMGPD.cjs} +6 -2
- package/dist/chunk-XIOMMGPD.cjs.map +1 -0
- package/dist/{chunk-3PHA3UKI.js → chunk-Z4BC6M74.js} +523 -121
- package/dist/chunk-Z4BC6M74.js.map +1 -0
- package/dist/{export-BLPLaE6Z.d.ts → export-CO2ryu3I.d.ts} +3 -3
- package/dist/{export-DSUc4fQD.d.cts → export-DRiLzP_j.d.cts} +3 -3
- package/dist/image-editor/index.cjs +722 -696
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +9 -9
- package/dist/image-editor/index.d.ts +9 -9
- package/dist/image-editor/index.js +31 -5
- package/dist/image-editor/index.js.map +1 -1
- package/dist/paint/index.cjs +28 -4
- package/dist/paint/index.cjs.map +1 -1
- package/dist/paint/index.d.cts +3 -89
- package/dist/paint/index.d.ts +3 -89
- package/dist/paint/index.js +27 -3
- package/dist/paint-editor/index.cjs +1052 -286
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +301 -22
- package/dist/paint-editor/index.d.ts +301 -22
- package/dist/paint-editor/index.js +848 -82
- package/dist/paint-editor/index.js.map +1 -1
- package/dist/raster/index.cjs +5 -3
- package/dist/raster/index.cjs.map +1 -1
- package/dist/raster/index.d.cts +2 -2
- package/dist/raster/index.d.ts +2 -2
- package/dist/raster/index.js +4 -2
- package/dist/{render-C-fO_mes.d.cts → render-BEriFuqK.d.cts} +1 -1
- package/dist/{render-OLIMk_v0.d.ts → render-tjzKaik-.d.ts} +1 -1
- package/dist/{surface-CkKOHGPm.d.cts → surface-C8RRg_Bh.d.cts} +109 -1
- package/dist/{surface-CkKOHGPm.d.ts → surface-C8RRg_Bh.d.ts} +109 -1
- package/package.json +1 -1
- package/skills/pixerate-paint-editor/SKILL.md +49 -13
- package/dist/chunk-3PHA3UKI.js.map +0 -1
- package/dist/chunk-6BU5WU4R.cjs +0 -1
- package/dist/chunk-6BU5WU4R.cjs.map +0 -1
- package/dist/chunk-HUFQD4CB.cjs +0 -734
- package/dist/chunk-HUFQD4CB.cjs.map +0 -1
- package/dist/chunk-SZ6KMZNM.cjs.map +0 -1
- package/dist/chunk-XYBJKUAU.js +0 -1
- package/dist/mixer-5ziHU8OS.d.cts +0 -282
- package/dist/mixer-DNqiYXYQ.d.ts +0 -282
- /package/dist/{chunk-RE77QYUF.js.map → chunk-DMRSJBJF.js.map} +0 -0
- /package/dist/{chunk-CFMWALBM.js.map → chunk-LQEEDHOP.js.map} +0 -0
- /package/dist/{chunk-XYBJKUAU.js.map → chunk-VKK5VS2T.js.map} +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7; var _class8; var _class9; var _class10; var _class11;
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7; var _class8; var _class9; var _class10; var _class11; var _class12;
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
var
|
|
7
|
+
var _chunkMGL4U5WAcjs = require('../chunk-MGL4U5WA.cjs');
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
@@ -17,7 +17,9 @@ var _chunkD37BX2ZAcjs = require('../chunk-D37BX2ZA.cjs');
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
var _chunkPOGBUYWEcjs = require('../chunk-POGBUYWE.cjs');
|
|
21
23
|
require('../chunk-MUUXNRU4.cjs');
|
|
22
24
|
|
|
23
25
|
|
|
@@ -35,11 +37,18 @@ require('../chunk-MUUXNRU4.cjs');
|
|
|
35
37
|
|
|
36
38
|
|
|
37
39
|
|
|
38
|
-
var _chunkHUFQD4CBcjs = require('../chunk-HUFQD4CB.cjs');
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
var _chunk7VOHOPLWcjs = require('../chunk-7VOHOPLW.cjs');
|
|
51
|
+
|
|
43
52
|
|
|
44
53
|
|
|
45
54
|
|
|
@@ -53,7 +62,13 @@ var _chunkHUFQD4CBcjs = require('../chunk-HUFQD4CB.cjs');
|
|
|
53
62
|
|
|
54
63
|
|
|
55
64
|
|
|
56
|
-
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
var _chunkXIOMMGPDcjs = require('../chunk-XIOMMGPD.cjs');
|
|
57
72
|
|
|
58
73
|
|
|
59
74
|
|
|
@@ -412,7 +427,7 @@ function newPaintLayerId() {
|
|
|
412
427
|
return `pl${Date.now().toString(36)}${(nextId++).toString(36)}`;
|
|
413
428
|
}
|
|
414
429
|
function createPaintLayer(name, id = newPaintLayerId()) {
|
|
415
|
-
return { type: "pixel", id, name, visible: true, opacity: 1, blend: "normal", surface:
|
|
430
|
+
return { type: "pixel", id, name, visible: true, opacity: 1, blend: "normal", surface: _chunkXIOMMGPDcjs.emptySurface.call(void 0, 4), offset: { x: 0, y: 0 } };
|
|
416
431
|
}
|
|
417
432
|
var PaintDocumentError = class extends Error {
|
|
418
433
|
constructor(message) {
|
|
@@ -438,8 +453,11 @@ function createPaintDocument(options = {}) {
|
|
|
438
453
|
}
|
|
439
454
|
|
|
440
455
|
// src/paint-editor/brushes.ts
|
|
441
|
-
var PAINT_BRUSH_ENGINES = ["dab", "eraser", "mixer"];
|
|
442
|
-
var PAINT_BRUSH_CATEGORIES = ["pencil", "pastel", "charcoal", "airbrush", "marker", "ink", "eraser", "blender", "smudge", "mixer", "custom"];
|
|
456
|
+
var PAINT_BRUSH_ENGINES = ["dab", "eraser", "mixer", "blender", "smudge"];
|
|
457
|
+
var PAINT_BRUSH_CATEGORIES = ["pencil", "pastel", "charcoal", "airbrush", "marker", "ink", "paint", "eraser", "blender", "smudge", "mixer", "custom"];
|
|
458
|
+
var MULTI_COLOR_MODES = ["off", "tip", "dab"];
|
|
459
|
+
var DEFAULT_MULTI_COLOR = { mode: "off", colors: [], order: "sequence", length: 12, softness: 1 };
|
|
460
|
+
var DEFAULT_BEHAVIOR = { pickup: 0, mix: 0.5, dirty: false, mixing: "pigment", multicolor: DEFAULT_MULTI_COLOR };
|
|
443
461
|
var BRUSH_ROTATIONS = ["fixed", "stroke", "barrel"];
|
|
444
462
|
var DEFAULT_DYNAMICS = {
|
|
445
463
|
minSize: 0,
|
|
@@ -472,21 +490,62 @@ function normalizeDynamics(raw = {}, base = DEFAULT_DYNAMICS) {
|
|
|
472
490
|
};
|
|
473
491
|
}
|
|
474
492
|
var BRUSH_ID = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
493
|
+
var HEX2 = /^#[0-9a-f]{6}$/i;
|
|
494
|
+
function normalizeMultiColor(raw = {}, base = DEFAULT_MULTI_COLOR) {
|
|
495
|
+
const colors = Array.isArray(raw.colors) ? raw.colors.filter((c) => typeof c === "string" && HEX2.test(c.trim())).map((c) => c.trim().toLowerCase()).slice(0, 8) : base.colors;
|
|
496
|
+
return {
|
|
497
|
+
mode: MULTI_COLOR_MODES.includes(raw.mode) ? raw.mode : base.mode,
|
|
498
|
+
colors: [...colors],
|
|
499
|
+
order: raw.order === "random" || raw.order === "sequence" ? raw.order : base.order,
|
|
500
|
+
length: typeof raw.length === "number" && Number.isFinite(raw.length) ? Math.min(500, Math.max(1, Math.round(raw.length))) : base.length,
|
|
501
|
+
softness: unit2(raw.softness, base.softness)
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
function normalizeBehavior(raw = {}, base = DEFAULT_BEHAVIOR) {
|
|
505
|
+
return {
|
|
506
|
+
pickup: unit2(raw.pickup, base.pickup),
|
|
507
|
+
mix: unit2(raw.mix, base.mix),
|
|
508
|
+
dirty: typeof raw.dirty === "boolean" ? raw.dirty : base.dirty,
|
|
509
|
+
mixing: raw.mixing === "rgb" || raw.mixing === "pigment" ? raw.mixing : base.mixing,
|
|
510
|
+
multicolor: normalizeMultiColor(_nullishCoalesce(raw.multicolor, () => ( {})), base.multicolor)
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
function isWetCapable(brush) {
|
|
514
|
+
return brush.engine === "dab" || brush.engine === "mixer";
|
|
515
|
+
}
|
|
516
|
+
function usesPaintExchange(brush) {
|
|
517
|
+
return brush.engine === "dab" && (brush.behavior.pickup > 0 || brush.behavior.multicolor.mode !== "off");
|
|
518
|
+
}
|
|
475
519
|
function normalizePaintBrush(raw = {}, base) {
|
|
476
520
|
const b = _nullishCoalesce(base, () => ( BASE_BRUSH));
|
|
477
521
|
const id = typeof raw.id === "string" && BRUSH_ID.test(raw.id) ? raw.id : b.id;
|
|
522
|
+
const engine = PAINT_BRUSH_ENGINES.includes(raw.engine) ? raw.engine : b.engine;
|
|
523
|
+
const rb = _nullishCoalesce(raw.behavior, () => ( {}));
|
|
524
|
+
const mixerIn = { ...b.mixer, ..._nullishCoalesce(raw.mixer, () => ( {})) };
|
|
525
|
+
if (engine === "mixer") {
|
|
526
|
+
if (rb.pickup !== void 0) mixerIn.wet = rb.pickup;
|
|
527
|
+
if (rb.mix !== void 0) mixerIn.mix = rb.mix;
|
|
528
|
+
if (rb.dirty !== void 0) mixerIn.cleanEachStroke = !rb.dirty;
|
|
529
|
+
if (rb.mixing !== void 0) mixerIn.mixing = rb.mixing;
|
|
530
|
+
}
|
|
531
|
+
const mixer = _chunk7VOHOPLWcjs.normalizeMixer.call(void 0, mixerIn);
|
|
532
|
+
let behavior = normalizeBehavior(rb, b.behavior);
|
|
533
|
+
if (engine === "mixer") behavior = { ...behavior, pickup: mixer.wet, mix: mixer.mix, dirty: !mixer.cleanEachStroke, mixing: mixer.mixing };
|
|
478
534
|
return {
|
|
479
535
|
id,
|
|
480
536
|
name: typeof raw.name === "string" && raw.name.trim() ? raw.name.trim().slice(0, 80) : b.name,
|
|
481
537
|
category: PAINT_BRUSH_CATEGORIES.includes(raw.category) ? raw.category : b.category,
|
|
482
|
-
engine
|
|
483
|
-
tip:
|
|
538
|
+
engine,
|
|
539
|
+
tip: _chunk7VOHOPLWcjs.normalizeBrush.call(void 0, { ...b.tip, ..._nullishCoalesce(raw.tip, () => ( {})) }),
|
|
484
540
|
grain: unit2(raw.grain, b.grain),
|
|
485
541
|
pressureSmoothing: range(raw.pressureSmoothing, 0, 0.95, b.pressureSmoothing),
|
|
486
542
|
rotation: BRUSH_ROTATIONS.includes(raw.rotation) ? raw.rotation : b.rotation,
|
|
487
543
|
dynamics: normalizeDynamics(_nullishCoalesce(raw.dynamics, () => ( {})), b.dynamics),
|
|
488
|
-
mixer
|
|
544
|
+
mixer,
|
|
489
545
|
loadsColor: typeof raw.loadsColor === "boolean" ? raw.loadsColor : b.loadsColor,
|
|
546
|
+
behavior,
|
|
547
|
+
blender: _chunk7VOHOPLWcjs.normalizeBlender.call(void 0, _nullishCoalesce(raw.blender, () => ( {})), b.blender),
|
|
548
|
+
smudge: _chunk7VOHOPLWcjs.normalizeSmudge.call(void 0, _nullishCoalesce(raw.smudge, () => ( {})), b.smudge),
|
|
490
549
|
...raw.builtin ? { builtin: true } : {}
|
|
491
550
|
};
|
|
492
551
|
}
|
|
@@ -495,13 +554,16 @@ var BASE_BRUSH = {
|
|
|
495
554
|
name: "Round",
|
|
496
555
|
category: "custom",
|
|
497
556
|
engine: "dab",
|
|
498
|
-
tip: { ...
|
|
557
|
+
tip: { ..._chunk7VOHOPLWcjs.DEFAULT_BRUSH },
|
|
499
558
|
grain: 0,
|
|
500
559
|
pressureSmoothing: 0,
|
|
501
560
|
rotation: "fixed",
|
|
502
561
|
dynamics: { ...DEFAULT_DYNAMICS },
|
|
503
|
-
mixer: { ...
|
|
504
|
-
loadsColor: true
|
|
562
|
+
mixer: { ..._chunk7VOHOPLWcjs.DEFAULT_MIXER },
|
|
563
|
+
loadsColor: true,
|
|
564
|
+
behavior: { ...DEFAULT_BEHAVIOR, multicolor: { ...DEFAULT_MULTI_COLOR, colors: [] } },
|
|
565
|
+
blender: { ..._chunk7VOHOPLWcjs.DEFAULT_BLENDER },
|
|
566
|
+
smudge: { ..._chunk7VOHOPLWcjs.DEFAULT_SMUDGE }
|
|
505
567
|
};
|
|
506
568
|
var builtin = (init) => ({ ...normalizePaintBrush(init), builtin: true });
|
|
507
569
|
var BUILTIN_BRUSHES = [
|
|
@@ -615,19 +677,54 @@ var BUILTIN_BRUSHES = [
|
|
|
615
677
|
id: "blender",
|
|
616
678
|
name: "Blender",
|
|
617
679
|
category: "blender",
|
|
618
|
-
engine: "
|
|
680
|
+
engine: "blender",
|
|
619
681
|
loadsColor: false,
|
|
620
|
-
tip: { size: 34, hardness: 0.4, spacing: 0.
|
|
621
|
-
|
|
682
|
+
tip: { size: 34, hardness: 0.4, spacing: 0.1, opacity: 1, flow: 1, smoothing: 0.2, pressureSize: false, pressureOpacity: true },
|
|
683
|
+
blender: { strength: 0.5, distance: 0.35, mixing: "pigment" }
|
|
622
684
|
}),
|
|
623
685
|
builtin({
|
|
624
686
|
id: "smudge",
|
|
625
687
|
name: "Smudge",
|
|
626
688
|
category: "smudge",
|
|
627
|
-
engine: "
|
|
689
|
+
engine: "smudge",
|
|
628
690
|
loadsColor: false,
|
|
629
|
-
tip: { size: 26, hardness: 0.6, spacing: 0.06, opacity: 1, flow:
|
|
630
|
-
|
|
691
|
+
tip: { size: 26, hardness: 0.6, spacing: 0.06, opacity: 1, flow: 1, smoothing: 0.2, pressureSize: false, pressureOpacity: true },
|
|
692
|
+
smudge: { strength: 0.75, length: 0.85, mixing: "rgb" }
|
|
693
|
+
}),
|
|
694
|
+
builtin({
|
|
695
|
+
id: "wet-smudge",
|
|
696
|
+
name: "Pigment smudge",
|
|
697
|
+
category: "smudge",
|
|
698
|
+
engine: "smudge",
|
|
699
|
+
loadsColor: false,
|
|
700
|
+
tip: { size: 30, hardness: 0.5, spacing: 0.06, opacity: 1, flow: 1, smoothing: 0.2, pressureSize: true, pressureOpacity: true },
|
|
701
|
+
smudge: { strength: 0.7, length: 0.9, mixing: "pigment" },
|
|
702
|
+
dynamics: { minSize: 0.6 }
|
|
703
|
+
}),
|
|
704
|
+
builtin({
|
|
705
|
+
id: "dirty-round",
|
|
706
|
+
name: "Dirty round",
|
|
707
|
+
category: "paint",
|
|
708
|
+
tip: { size: 22, hardness: 0.65, spacing: 0.06, opacity: 1, flow: 0.7, smoothing: 0.2, pressureSize: true, pressureOpacity: false },
|
|
709
|
+
behavior: { pickup: 0.55, mix: 0.6, dirty: true, mixing: "pigment" },
|
|
710
|
+
dynamics: { minSize: 0.5 }
|
|
711
|
+
}),
|
|
712
|
+
builtin({
|
|
713
|
+
id: "multicolor-flat",
|
|
714
|
+
name: "Multi-colour flat",
|
|
715
|
+
category: "paint",
|
|
716
|
+
rotation: "stroke",
|
|
717
|
+
tip: { size: 30, hardness: 0.75, spacing: 0.05, angle: 90, roundness: 0.3, opacity: 1, flow: 0.85, smoothing: 0.2, pressureSize: true, pressureOpacity: false },
|
|
718
|
+
behavior: { pickup: 0.15, mix: 0.4, mixing: "pigment", multicolor: { mode: "tip", softness: 0.7 } },
|
|
719
|
+
dynamics: { minSize: 0.6 }
|
|
720
|
+
}),
|
|
721
|
+
builtin({
|
|
722
|
+
id: "palette-dabber",
|
|
723
|
+
name: "Palette dabber",
|
|
724
|
+
category: "paint",
|
|
725
|
+
tip: { size: 18, hardness: 0.7, spacing: 0.35, opacity: 1, flow: 0.9, smoothing: 0.1, pressureSize: true, pressureOpacity: false },
|
|
726
|
+
behavior: { multicolor: { mode: "dab", order: "sequence", length: 6, softness: 0.4 } },
|
|
727
|
+
dynamics: { minSize: 0.5, sizeJitter: 0.15, scatter: 0.3 }
|
|
631
728
|
}),
|
|
632
729
|
builtin({
|
|
633
730
|
id: "mixer-brush",
|
|
@@ -807,7 +904,9 @@ var PenDabWalker = (_class4 = class {
|
|
|
807
904
|
opacity: Math.min(1, Math.max(0, opacity)),
|
|
808
905
|
angle,
|
|
809
906
|
roundness: Math.min(1, Math.max(0.01, tip.roundness * (1 - dyn.tiltRoundness * s.lean * 0.8))),
|
|
810
|
-
pressure: s.pressure
|
|
907
|
+
pressure: s.pressure,
|
|
908
|
+
direction: this.dir,
|
|
909
|
+
index: i
|
|
811
910
|
};
|
|
812
911
|
}
|
|
813
912
|
push(p, out) {
|
|
@@ -877,7 +976,7 @@ var DryStroke = (_class5 = class {
|
|
|
877
976
|
}
|
|
878
977
|
|
|
879
978
|
|
|
880
|
-
__init12() {this.coverage = new (0,
|
|
979
|
+
__init12() {this.coverage = new (0, _chunk7VOHOPLWcjs.CoverageMap)()}
|
|
881
980
|
__init13() {this.dabCount = 0}
|
|
882
981
|
|
|
883
982
|
|
|
@@ -911,11 +1010,11 @@ var DryStroke = (_class5 = class {
|
|
|
911
1010
|
const hardness = this.brush.tip.hardness;
|
|
912
1011
|
const grain = this.options.grain;
|
|
913
1012
|
for (const d of dabs) {
|
|
914
|
-
const rect =
|
|
1013
|
+
const rect = _chunk7VOHOPLWcjs.dabRect.call(void 0, d, hardness);
|
|
915
1014
|
const n = rect.width * rect.height;
|
|
916
1015
|
if (this.buf.length < n) this.buf = new Float32Array(n);
|
|
917
1016
|
const vals = this.buf.subarray(0, n);
|
|
918
|
-
|
|
1017
|
+
_chunk7VOHOPLWcjs.rasterizeDab.call(void 0, d, hardness, rect, vals);
|
|
919
1018
|
if (this.depth > 0 && grain) {
|
|
920
1019
|
for (let yy = 0, k = 0; yy < rect.height; yy++) {
|
|
921
1020
|
const y = rect.y + yy + 0.5;
|
|
@@ -938,9 +1037,251 @@ function toStrokePoint(p) {
|
|
|
938
1037
|
return { x: p.x, y: p.y, pressure: pressureOf(p), ...typeof p.t === "number" ? { t: p.t } : {} };
|
|
939
1038
|
}
|
|
940
1039
|
|
|
1040
|
+
// src/paint-editor/dab-stroke.ts
|
|
1041
|
+
var GRADIENT_STEPS = 32;
|
|
1042
|
+
function multiColorPalette(mc, color, secondary) {
|
|
1043
|
+
if (mc.colors.length >= 2) {
|
|
1044
|
+
return mc.colors.map((h) => {
|
|
1045
|
+
const n = parseInt(h.slice(1), 16);
|
|
1046
|
+
return [(n >> 16 & 255) / 255, (n >> 8 & 255) / 255, (n & 255) / 255];
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
return [[...color], [...secondary]];
|
|
1050
|
+
}
|
|
1051
|
+
function soften(f, softness) {
|
|
1052
|
+
if (softness >= 1) return f;
|
|
1053
|
+
const w = Math.max(1e-3, softness);
|
|
1054
|
+
return Math.min(1, Math.max(0, (f - 0.5) / w + 0.5));
|
|
1055
|
+
}
|
|
1056
|
+
function mixRgb(a, b, t, mixing) {
|
|
1057
|
+
if (t <= 0) return [...a];
|
|
1058
|
+
if (t >= 1) return [...b];
|
|
1059
|
+
if (mixing === "pigment") {
|
|
1060
|
+
const o = [0, 0, 0];
|
|
1061
|
+
const q = (v) => Math.round(Math.min(1, Math.max(0, v)) * 255);
|
|
1062
|
+
_chunkXIOMMGPDcjs.mixPigment8.call(void 0, q(a[0]), q(a[1]), q(a[2]), q(b[0]), q(b[1]), q(b[2]), t, o);
|
|
1063
|
+
return [o[0], o[1], o[2]];
|
|
1064
|
+
}
|
|
1065
|
+
return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t];
|
|
1066
|
+
}
|
|
1067
|
+
function multiColorGradient(palette, softness, mixing) {
|
|
1068
|
+
const n = palette.length;
|
|
1069
|
+
const out = [];
|
|
1070
|
+
for (let j = 0; j < GRADIENT_STEPS; j++) {
|
|
1071
|
+
const f = j / (GRADIENT_STEPS - 1) * (n - 1);
|
|
1072
|
+
const i = Math.min(n - 2, Math.floor(f));
|
|
1073
|
+
out.push(mixRgb(palette[i], palette[i + 1], soften(f - i, softness), mixing));
|
|
1074
|
+
}
|
|
1075
|
+
return out;
|
|
1076
|
+
}
|
|
1077
|
+
function multiColorForDab(mc, palette, index, seed, mixing) {
|
|
1078
|
+
const n = palette.length;
|
|
1079
|
+
if (mc.order === "random") {
|
|
1080
|
+
const run = Math.floor(index / mc.length);
|
|
1081
|
+
return [...palette[Math.min(n - 1, Math.floor(rand01(seed, run, 11) * n))]];
|
|
1082
|
+
}
|
|
1083
|
+
const f = index / mc.length % n;
|
|
1084
|
+
const i = Math.floor(f);
|
|
1085
|
+
return mixRgb(palette[i], palette[(i + 1) % n], soften(f - i, mc.softness), mixing);
|
|
1086
|
+
}
|
|
1087
|
+
var PaintDabStroke = (_class6 = class {
|
|
1088
|
+
constructor(brush, options) {;_class6.prototype.__init17.call(this);_class6.prototype.__init18.call(this);_class6.prototype.__init19.call(this);_class6.prototype.__init20.call(this);_class6.prototype.__init21.call(this);_class6.prototype.__init22.call(this);_class6.prototype.__init23.call(this);_class6.prototype.__init24.call(this);
|
|
1089
|
+
this.brush = brush;
|
|
1090
|
+
this.options = options;
|
|
1091
|
+
this.smoother = new PenSmoother(brush.tip.smoothing, brush.pressureSmoothing);
|
|
1092
|
+
this.copies = _optionalChain([options, 'access', _4 => _4.copies, 'optionalAccess', _5 => _5.length]) ? options.copies : [IDENTITY_COPY];
|
|
1093
|
+
const exchanging = brush.engine === "mixer" || brush.engine === "dab";
|
|
1094
|
+
const rates = brush.engine === "mixer" ? _chunk7VOHOPLWcjs.mixerRates.call(void 0, brush.mixer) : _chunk7VOHOPLWcjs.exchangeRates.call(void 0, brush.behavior.pickup, brush.behavior.mix, 1, brush.behavior.mixing);
|
|
1095
|
+
this.mixing = brush.engine === "mixer" ? brush.mixer.mixing : brush.behavior.mixing;
|
|
1096
|
+
this.states = this.copies.map((c, i) => ({
|
|
1097
|
+
walker: new PenDabWalker(brush, (_nullishCoalesce(options.seed, () => ( 0))) + i * 7919 | 0, c.linear),
|
|
1098
|
+
exchange: exchanging ? new (0, _chunk7VOHOPLWcjs.PaintExchange)(rates, options.load) : null,
|
|
1099
|
+
smudge: brush.engine === "smudge" ? new (0, _chunk7VOHOPLWcjs.SmudgeTip)(brush.smudge, brush.tip.size * (1 + brush.dynamics.tiltSize * 1.5)) : null
|
|
1100
|
+
}));
|
|
1101
|
+
this.medium = new (0, _chunk7VOHOPLWcjs.TileMedium)(options.target, brush.engine === "dab" ? { blend: brush.tip.blend } : {});
|
|
1102
|
+
this.depth = options.grain && !options.grain.flat ? Math.min(1, Math.max(0, options.paperDepth * brush.grain)) : 0;
|
|
1103
|
+
this.coverage = brush.engine === "dab" ? new (0, _chunk7VOHOPLWcjs.CoverageMap)() : null;
|
|
1104
|
+
const mc = brush.behavior.multicolor;
|
|
1105
|
+
this.palette = exchanging && mc.mode !== "off" ? multiColorPalette(mc, options.color, options.secondary) : null;
|
|
1106
|
+
this.gradient = this.palette && mc.mode === "tip" ? multiColorGradient(this.palette, mc.softness, this.mixing) : null;
|
|
1107
|
+
this.scratch = brush.engine === "blender" ? _chunk7VOHOPLWcjs.createBlenderScratch.call(void 0, ) : null;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
|
|
1111
|
+
__init17() {this.dabCount = 0}
|
|
1112
|
+
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
__init18() {this.fieldLut = []}
|
|
1121
|
+
|
|
1122
|
+
|
|
1123
|
+
__init19() {this.tip = new Float32Array(0)}
|
|
1124
|
+
__init20() {this.cov = new Float32Array(0)}
|
|
1125
|
+
__init21() {this.field = new Float32Array(0)}
|
|
1126
|
+
__init22() {this.paint = [0, 0, 0]}
|
|
1127
|
+
__init23() {this.lastRaw = null}
|
|
1128
|
+
__init24() {this.lastFed = null}
|
|
1129
|
+
/** What copy 0's brush carries after the dabs so far (mixer and wet-capable dab brushes). */
|
|
1130
|
+
get load() {
|
|
1131
|
+
return _nullishCoalesce(_optionalChain([this, 'access', _6 => _6.states, 'access', _7 => _7[0], 'access', _8 => _8.exchange, 'optionalAccess', _9 => _9.load]), () => ( null));
|
|
1132
|
+
}
|
|
1133
|
+
push(points) {
|
|
1134
|
+
const rects = [];
|
|
1135
|
+
for (const raw of points) {
|
|
1136
|
+
this.lastRaw = raw;
|
|
1137
|
+
const p = this.smoother.push(raw);
|
|
1138
|
+
this.lastFed = p;
|
|
1139
|
+
this.copies.forEach((c, i) => {
|
|
1140
|
+
const dabs = [];
|
|
1141
|
+
this.states[i].walker.push(c.map(p), dabs);
|
|
1142
|
+
this.paintDabs(i, dabs, rects);
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
return rects;
|
|
1146
|
+
}
|
|
1147
|
+
/** Ends the stroke: the smoothed path catches up with the last raw sample; a tap paints one dab. */
|
|
1148
|
+
finish() {
|
|
1149
|
+
const rects = [];
|
|
1150
|
+
const raw = this.lastRaw;
|
|
1151
|
+
const fed = this.lastFed;
|
|
1152
|
+
this.copies.forEach((c, i) => {
|
|
1153
|
+
const dabs = [];
|
|
1154
|
+
if (raw && fed && (raw.x !== fed.x || raw.y !== fed.y)) this.states[i].walker.push(c.map({ ...raw, pressure: fed.pressure }), dabs);
|
|
1155
|
+
this.states[i].walker.finish(dabs);
|
|
1156
|
+
this.paintDabs(i, dabs, rects);
|
|
1157
|
+
});
|
|
1158
|
+
return rects;
|
|
1159
|
+
}
|
|
1160
|
+
paintDabs(copy, dabs, rects) {
|
|
1161
|
+
for (const d of dabs) {
|
|
1162
|
+
const rect = _chunk7VOHOPLWcjs.dabRect.call(void 0, d, this.brush.tip.hardness);
|
|
1163
|
+
if (rect.width <= 0 || rect.height <= 0) continue;
|
|
1164
|
+
this.dab(copy, d, rect);
|
|
1165
|
+
rects.push(rect);
|
|
1166
|
+
this.dabCount++;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
/** The tip's pure coverage (`tip`) and, with grain, what the paper catches of it (`cov`). */
|
|
1170
|
+
rasterize(d, rect) {
|
|
1171
|
+
const n = rect.width * rect.height;
|
|
1172
|
+
if (this.tip.length < n) {
|
|
1173
|
+
this.tip = new Float32Array(n);
|
|
1174
|
+
this.cov = new Float32Array(n);
|
|
1175
|
+
}
|
|
1176
|
+
const tip = this.tip.subarray(0, n);
|
|
1177
|
+
tip.fill(0);
|
|
1178
|
+
_chunk7VOHOPLWcjs.rasterizeDab.call(void 0, { ...d, alpha: 1 }, this.brush.tip.hardness, rect, tip);
|
|
1179
|
+
if (!(this.depth > 0 && this.options.grain)) return { tip, cov: tip };
|
|
1180
|
+
const cov = this.cov.subarray(0, n);
|
|
1181
|
+
const grain = this.options.grain;
|
|
1182
|
+
for (let yy = 0, k = 0; yy < rect.height; yy++) {
|
|
1183
|
+
const y = rect.y + yy + 0.5;
|
|
1184
|
+
for (let xx = 0; xx < rect.width; xx++, k++) cov[k] = tip[k] > 0 ? tip[k] * grainCatch(grain.at(rect.x + xx + 0.5, y), this.depth, d.pressure) : 0;
|
|
1185
|
+
}
|
|
1186
|
+
return { tip, cov };
|
|
1187
|
+
}
|
|
1188
|
+
dab(copy, d, rect) {
|
|
1189
|
+
const brush = this.brush;
|
|
1190
|
+
const { tip, cov } = this.rasterize(d, rect);
|
|
1191
|
+
const st = this.states[copy];
|
|
1192
|
+
if (brush.engine === "blender") {
|
|
1193
|
+
_chunk7VOHOPLWcjs.blenderDab.call(void 0, this.medium, rect, cov, d.opacity * d.alpha * brush.tip.opacity, d.radius, brush.blender, this.scratch);
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
if (brush.engine === "smudge") {
|
|
1197
|
+
st.smudge.dab(this.medium, { x: d.x, y: d.y, radius: d.radius, opacity: d.opacity * d.alpha * brush.tip.opacity }, rect, cov);
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
const ex = st.exchange;
|
|
1201
|
+
ex.pickUp(this.medium.sample(rect, tip));
|
|
1202
|
+
const mc = brush.behavior.multicolor;
|
|
1203
|
+
if (this.palette && mc.mode === "dab") {
|
|
1204
|
+
const c = multiColorForDab(mc, this.palette, d.index, _nullishCoalesce(this.options.seed, () => ( 0)), this.mixing);
|
|
1205
|
+
ex.setReservoirColor(c[0], c[1], c[2]);
|
|
1206
|
+
}
|
|
1207
|
+
let field = null;
|
|
1208
|
+
let total;
|
|
1209
|
+
if (this.gradient) {
|
|
1210
|
+
total = this.tipField(ex, d, rect);
|
|
1211
|
+
field = this.field;
|
|
1212
|
+
} else total = ex.paint(this.paint);
|
|
1213
|
+
if (total > 0) {
|
|
1214
|
+
if (this.coverage) this.depositWithCeiling(d, rect, cov, field, ex);
|
|
1215
|
+
else {
|
|
1216
|
+
const dep = Math.min(1, total) * d.alpha * brush.tip.opacity * d.opacity;
|
|
1217
|
+
if (dep > 0) this.medium.deposit(rect, cov, dep, this.paint, ex.paintPigment, field);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
ex.drain();
|
|
1221
|
+
}
|
|
1222
|
+
/** Fills `this.field` with the tip gradient (mixed with the carried paint) per pixel; returns the paint weight. */
|
|
1223
|
+
tipField(ex, d, rect) {
|
|
1224
|
+
const g = this.gradient;
|
|
1225
|
+
const lut = this.fieldLut;
|
|
1226
|
+
let total = 0;
|
|
1227
|
+
for (let j = 0; j < g.length; j++) {
|
|
1228
|
+
ex.setReservoirColor(g[j][0], g[j][1], g[j][2]);
|
|
1229
|
+
const out = _nullishCoalesce(lut[j], () => ( [0, 0, 0]));
|
|
1230
|
+
total = ex.paint(out);
|
|
1231
|
+
lut[j] = out;
|
|
1232
|
+
}
|
|
1233
|
+
const n = rect.width * rect.height;
|
|
1234
|
+
if (this.field.length < n * 3) this.field = new Float32Array(n * 3);
|
|
1235
|
+
const f = this.field;
|
|
1236
|
+
const nx = Math.sin(d.direction);
|
|
1237
|
+
const ny = Math.cos(d.direction);
|
|
1238
|
+
const r = Math.max(0.5, d.radius);
|
|
1239
|
+
const last = g.length - 1;
|
|
1240
|
+
for (let yy = 0, k = 0; yy < rect.height; yy++) {
|
|
1241
|
+
const py = rect.y + yy + 0.5 - d.y;
|
|
1242
|
+
for (let xx = 0; xx < rect.width; xx++, k++) {
|
|
1243
|
+
const u = ((rect.x + xx + 0.5 - d.x) * nx + py * ny) / r;
|
|
1244
|
+
const j = Math.min(last, Math.max(0, Math.round((u + 1) / 2 * last)));
|
|
1245
|
+
f[k * 3] = lut[j][0];
|
|
1246
|
+
f[k * 3 + 1] = lut[j][1];
|
|
1247
|
+
f[k * 3 + 2] = lut[j][2];
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
return total;
|
|
1251
|
+
}
|
|
1252
|
+
/** A dab brush's deposit: only what this dab adds to the stroke's coverage, under its opacity ceiling. */
|
|
1253
|
+
depositWithCeiling(d, rect, cov, field, ex) {
|
|
1254
|
+
const map = this.coverage;
|
|
1255
|
+
const n = rect.width * rect.height;
|
|
1256
|
+
const before = new Float32Array(n);
|
|
1257
|
+
const vals = new Float32Array(n);
|
|
1258
|
+
for (let yy = 0, k = 0; yy < rect.height; yy++) {
|
|
1259
|
+
for (let xx = 0; xx < rect.width; xx++, k++) {
|
|
1260
|
+
if (cov[k] <= 0) continue;
|
|
1261
|
+
before[k] = map.get(rect.x + xx, rect.y + yy);
|
|
1262
|
+
vals[k] = cov[k] * d.alpha;
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
map.accumulate(rect, vals, d.opacity);
|
|
1266
|
+
const o = this.brush.tip.opacity;
|
|
1267
|
+
for (let yy = 0, k = 0; yy < rect.height; yy++) {
|
|
1268
|
+
for (let xx = 0; xx < rect.width; xx++, k++) {
|
|
1269
|
+
if (cov[k] <= 0) {
|
|
1270
|
+
vals[k] = 0;
|
|
1271
|
+
continue;
|
|
1272
|
+
}
|
|
1273
|
+
const c0 = before[k] * o;
|
|
1274
|
+
const c1 = map.get(rect.x + xx, rect.y + yy) * o;
|
|
1275
|
+
vals[k] = c1 > c0 && c0 < 1 ? (c1 - c0) / (1 - c0) : 0;
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
this.medium.deposit(rect, vals, 1, this.paint, ex.paintPigment, field);
|
|
1279
|
+
}
|
|
1280
|
+
}, _class6);
|
|
1281
|
+
|
|
941
1282
|
// src/paint-editor/stroke.ts
|
|
942
1283
|
function defaultTile(surface) {
|
|
943
|
-
const data = new Uint8ClampedArray(
|
|
1284
|
+
const data = new Uint8ClampedArray(_chunkXIOMMGPDcjs.TILE * _chunkXIOMMGPDcjs.TILE * surface.channels);
|
|
944
1285
|
if (surface.defaultValue) data.fill(surface.defaultValue);
|
|
945
1286
|
return data;
|
|
946
1287
|
}
|
|
@@ -957,8 +1298,8 @@ function union(a, b) {
|
|
|
957
1298
|
const y = Math.min(a.y, b.y);
|
|
958
1299
|
return { x, y, width: Math.max(a.x + a.width, b.x + b.width) - x, height: Math.max(a.y + a.height, b.y + b.height) - y };
|
|
959
1300
|
}
|
|
960
|
-
var PaintStrokeSession = (
|
|
961
|
-
constructor(host, init) {;
|
|
1301
|
+
var PaintStrokeSession = (_class7 = class {
|
|
1302
|
+
constructor(host, init) {;_class7.prototype.__init25.call(this);_class7.prototype.__init26.call(this);_class7.prototype.__init27.call(this);_class7.prototype.__init28.call(this);_class7.prototype.__init29.call(this);_class7.prototype.__init30.call(this);_class7.prototype.__init31.call(this);_class7.prototype.__init32.call(this);_class7.prototype.__init33.call(this);
|
|
962
1303
|
this.host = host;
|
|
963
1304
|
this.init = init;
|
|
964
1305
|
this.layerId = init.layerId;
|
|
@@ -976,10 +1317,18 @@ var PaintStrokeSession = (_class6 = class {
|
|
|
976
1317
|
selection: null,
|
|
977
1318
|
offset: init.offset
|
|
978
1319
|
};
|
|
979
|
-
if (brush.engine === "mixer") {
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1320
|
+
if (brush.engine === "mixer" || brush.engine === "blender" || brush.engine === "smudge" || usesPaintExchange(brush)) {
|
|
1321
|
+
const rgb = (c) => [c[0] / 255, c[1] / 255, c[2] / 255];
|
|
1322
|
+
this.wet = new PaintDabStroke(brush, {
|
|
1323
|
+
target: this.tileTarget(),
|
|
1324
|
+
color: rgb(init.color),
|
|
1325
|
+
secondary: rgb(_nullishCoalesce(init.secondary, () => ( init.background))),
|
|
1326
|
+
load: init.mixerLoad,
|
|
1327
|
+
grain: init.grain,
|
|
1328
|
+
paperDepth: init.paperDepth,
|
|
1329
|
+
seed: init.seed,
|
|
1330
|
+
copies: this.copies
|
|
1331
|
+
});
|
|
983
1332
|
} else {
|
|
984
1333
|
this.dry = new DryStroke(brush, { grain: init.grain, paperDepth: init.paperDepth, seed: init.seed, copies: this.copies });
|
|
985
1334
|
}
|
|
@@ -988,30 +1337,29 @@ var PaintStrokeSession = (_class6 = class {
|
|
|
988
1337
|
|
|
989
1338
|
|
|
990
1339
|
/** The raw input so far (what a replay feeds back in). */
|
|
991
|
-
|
|
1340
|
+
__init25() {this.points = []}
|
|
992
1341
|
|
|
993
1342
|
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
__init22() {this.mixerSmoother = null}
|
|
1343
|
+
__init26() {this.touched = /* @__PURE__ */ new Set()}
|
|
1344
|
+
__init27() {this.fresh = /* @__PURE__ */ new Set()}
|
|
1345
|
+
__init28() {this.dry = null}
|
|
1346
|
+
__init29() {this.wet = null}
|
|
999
1347
|
|
|
1000
1348
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1349
|
+
__init30() {this.bounds = null}
|
|
1350
|
+
__init31() {this.lastRaw = null}
|
|
1351
|
+
__init32() {this.closed = false}
|
|
1352
|
+
__init33() {this.result = null}
|
|
1353
|
+
tileTarget() {
|
|
1006
1354
|
return {
|
|
1007
1355
|
offset: this.init.offset,
|
|
1008
1356
|
lockTransparency: this.init.lockTransparency,
|
|
1009
1357
|
selection: null,
|
|
1010
|
-
readTile: (tx, ty) => _nullishCoalesce(_optionalChain([this, 'access',
|
|
1358
|
+
readTile: (tx, ty) => _nullishCoalesce(_optionalChain([this, 'access', _10 => _10.live, 'access', _11 => _11.get, 'call', _12 => _12(_chunkXIOMMGPDcjs.tileKey.call(void 0, tx, ty)), 'optionalAccess', _13 => _13.data]), () => ( null)),
|
|
1011
1359
|
writeTile: (tx, ty) => {
|
|
1012
|
-
const key =
|
|
1360
|
+
const key = _chunkXIOMMGPDcjs.tileKey.call(void 0, tx, ty);
|
|
1013
1361
|
if (!this.touched.has(key)) {
|
|
1014
|
-
const src = _nullishCoalesce(_optionalChain([this, 'access',
|
|
1362
|
+
const src = _nullishCoalesce(_optionalChain([this, 'access', _14 => _14.base, 'access', _15 => _15.tiles, 'access', _16 => _16.get, 'call', _17 => _17(key), 'optionalAccess', _18 => _18.data]), () => ( defaultTile(this.base)));
|
|
1015
1363
|
this.live.set(key, { data: new Uint8ClampedArray(src), uniform: null });
|
|
1016
1364
|
this.touched.add(key);
|
|
1017
1365
|
}
|
|
@@ -1030,15 +1378,7 @@ var PaintStrokeSession = (_class6 = class {
|
|
|
1030
1378
|
if (filter) points = points.map(filter);
|
|
1031
1379
|
for (const p of points) this.points.push(p);
|
|
1032
1380
|
this.lastRaw = points[points.length - 1];
|
|
1033
|
-
|
|
1034
|
-
else {
|
|
1035
|
-
const rects = [];
|
|
1036
|
-
for (const raw of points) {
|
|
1037
|
-
const p = toStrokePoint(this.mixerSmoother.push(raw));
|
|
1038
|
-
this.copies.forEach((c, i) => rects.push(...this.mixers[i].push([toStrokePoint(c.map(p))])));
|
|
1039
|
-
}
|
|
1040
|
-
this.update(rects);
|
|
1041
|
-
}
|
|
1381
|
+
this.update(this.dry ? this.dry.push(points) : this.wet.push(points));
|
|
1042
1382
|
}
|
|
1043
1383
|
update(rects) {
|
|
1044
1384
|
if (!rects.length) return;
|
|
@@ -1047,57 +1387,47 @@ var PaintStrokeSession = (_class6 = class {
|
|
|
1047
1387
|
if (!this.dry) {
|
|
1048
1388
|
for (const key of this.fresh) this.live.set(key, { data: this.live.get(key).data, uniform: null });
|
|
1049
1389
|
this.fresh.clear();
|
|
1050
|
-
this.host.update(this.layerId, new (0,
|
|
1390
|
+
this.host.update(this.layerId, new (0, _chunkXIOMMGPDcjs.TiledSurface)(ch, new Map(this.live), this.base.defaultValue), rects);
|
|
1051
1391
|
return;
|
|
1052
1392
|
}
|
|
1053
1393
|
const off = this.init.offset;
|
|
1054
1394
|
const regions = /* @__PURE__ */ new Map();
|
|
1055
1395
|
for (const r of rects) {
|
|
1056
|
-
for (const t of
|
|
1057
|
-
const key =
|
|
1396
|
+
for (const t of _chunkXIOMMGPDcjs.tilesInRect.call(void 0, { x: r.x - off.x, y: r.y - off.y, width: r.width, height: r.height })) {
|
|
1397
|
+
const key = _chunkXIOMMGPDcjs.tileKey.call(void 0, t.tx, t.ty);
|
|
1058
1398
|
const cur = regions.get(key);
|
|
1059
1399
|
regions.set(key, { tx: t.tx, ty: t.ty, r: cur ? union(cur.r, r) : r });
|
|
1060
1400
|
}
|
|
1061
1401
|
}
|
|
1062
1402
|
for (const [key, { tx, ty, r }] of regions) {
|
|
1063
|
-
const src = _nullishCoalesce(_optionalChain([this, 'access',
|
|
1064
|
-
const prev = this.touched.has(key) ? _optionalChain([this, 'access',
|
|
1403
|
+
const src = _nullishCoalesce(_optionalChain([this, 'access', _19 => _19.base, 'access', _20 => _20.tiles, 'access', _21 => _21.get, 'call', _22 => _22(key), 'optionalAccess', _23 => _23.data]), () => ( defaultTile(this.base)));
|
|
1404
|
+
const prev = this.touched.has(key) ? _optionalChain([this, 'access', _24 => _24.live, 'access', _25 => _25.get, 'call', _26 => _26(key), 'optionalAccess', _27 => _27.data]) : void 0;
|
|
1065
1405
|
this.touched.add(key);
|
|
1066
|
-
const data =
|
|
1406
|
+
const data = _chunk7VOHOPLWcjs.applyStrokeToTile.call(void 0, this.dry.coverage, src, ch, tx, ty, this.apply, prev ? { into: prev, region: r } : {});
|
|
1067
1407
|
this.live.set(key, { data, uniform: null });
|
|
1068
1408
|
}
|
|
1069
|
-
this.host.update(this.layerId, new (0,
|
|
1409
|
+
this.host.update(this.layerId, new (0, _chunkXIOMMGPDcjs.TiledSurface)(ch, new Map(this.live), this.base.defaultValue), rects);
|
|
1070
1410
|
}
|
|
1071
1411
|
/** Ends the stroke (one undo step) and returns what changed. */
|
|
1072
1412
|
end() {
|
|
1073
1413
|
if (this.closed) throw new Error("This stroke has already ended.");
|
|
1074
|
-
|
|
1075
|
-
else {
|
|
1076
|
-
const rects = [];
|
|
1077
|
-
const last = this.lastRaw ? toStrokePoint(this.lastRaw) : void 0;
|
|
1078
|
-
this.copies.forEach((c, i) => rects.push(...this.mixers[i].finish(last ? toStrokePoint(c.map(last)) : void 0)));
|
|
1079
|
-
this.update(rects);
|
|
1080
|
-
}
|
|
1414
|
+
this.update(this.dry ? this.dry.finish() : this.wet.finish());
|
|
1081
1415
|
this.closed = true;
|
|
1082
1416
|
const ch = this.base.channels;
|
|
1083
1417
|
const tiles = new Map(this.base.tiles);
|
|
1084
1418
|
let changed = false;
|
|
1085
1419
|
for (const key of this.touched) {
|
|
1086
|
-
const tile =
|
|
1420
|
+
const tile = _chunkXIOMMGPDcjs.finishTile.call(void 0, this.live.get(key).data, ch, this.base.defaultValue);
|
|
1087
1421
|
const prev = this.base.tiles.get(key);
|
|
1088
1422
|
if (tile) tiles.set(key, tile);
|
|
1089
1423
|
else tiles.delete(key);
|
|
1090
1424
|
if (!sameTile(prev, tile)) changed = true;
|
|
1091
1425
|
}
|
|
1092
|
-
const surface = changed ? new (0,
|
|
1093
|
-
const dabs = this.dry ? this.dry.dabCount : this.
|
|
1426
|
+
const surface = changed ? new (0, _chunkXIOMMGPDcjs.TiledSurface)(ch, tiles, this.base.defaultValue) : null;
|
|
1427
|
+
const dabs = this.dry ? this.dry.dabCount : this.wet.dabCount;
|
|
1094
1428
|
this.result = { layerId: this.layerId, bounds: changed ? this.bounds : null, dabs, strokeId: null };
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
let load = this.mixers[0].load;
|
|
1098
|
-
if (brush.mixer.cleanEachStroke) load = _chunkHUFQD4CBcjs.cleanMixer.call(void 0, load);
|
|
1099
|
-
this.result.mixerLoad = load;
|
|
1100
|
-
}
|
|
1429
|
+
const carried = _optionalChain([this, 'access', _28 => _28.wet, 'optionalAccess', _29 => _29.load]);
|
|
1430
|
+
if (carried) this.result.mixerLoad = this.init.brush.behavior.dirty ? carried : _chunk7VOHOPLWcjs.cleanMixer.call(void 0, carried);
|
|
1101
1431
|
this.host.commit(this, surface, false);
|
|
1102
1432
|
return this.result;
|
|
1103
1433
|
}
|
|
@@ -1107,12 +1437,17 @@ var PaintStrokeSession = (_class6 = class {
|
|
|
1107
1437
|
this.closed = true;
|
|
1108
1438
|
this.host.commit(this, null, true);
|
|
1109
1439
|
}
|
|
1110
|
-
},
|
|
1440
|
+
}, _class7);
|
|
1111
1441
|
function mixerLoadForStroke(brush, current, color) {
|
|
1112
1442
|
if (!brush.loadsColor) return { reservoir: null, amount: 0, pickup: brush.mixer.cleanEachStroke ? null : current.pickup };
|
|
1113
|
-
if (brush.mixer.loadEachStroke || !current.reservoir) return
|
|
1443
|
+
if (brush.mixer.loadEachStroke || !current.reservoir) return _chunk7VOHOPLWcjs.loadMixer.call(void 0, current, color);
|
|
1114
1444
|
return current;
|
|
1115
1445
|
}
|
|
1446
|
+
function brushLoadForStroke(brush, current, color) {
|
|
1447
|
+
if (brush.engine === "mixer") return mixerLoadForStroke(brush, current, color);
|
|
1448
|
+
if (usesPaintExchange(brush)) return { reservoir: [color[0], color[1], color[2]], amount: 1, pickup: brush.behavior.dirty ? current.pickup : null };
|
|
1449
|
+
return _chunk7VOHOPLWcjs.EMPTY_MIXER_LOAD;
|
|
1450
|
+
}
|
|
1116
1451
|
|
|
1117
1452
|
// src/paint-editor/strokes.ts
|
|
1118
1453
|
var IDENTITY_AFFINE = [1, 0, 0, 1, 0, 0];
|
|
@@ -1158,6 +1493,17 @@ function readPoint(raw) {
|
|
|
1158
1493
|
function readAffine(raw) {
|
|
1159
1494
|
return Array.isArray(raw) && raw.length === 6 && raw.every(finite) ? raw : null;
|
|
1160
1495
|
}
|
|
1496
|
+
function readRgb(raw) {
|
|
1497
|
+
return Array.isArray(raw) && raw.length === 3 && raw.every(finite) ? raw.map((v) => Math.min(1, Math.max(0, v))) : null;
|
|
1498
|
+
}
|
|
1499
|
+
function readLoad(raw) {
|
|
1500
|
+
if (!raw || typeof raw !== "object") return null;
|
|
1501
|
+
const r = raw;
|
|
1502
|
+
const reservoir = readRgb(r.reservoir);
|
|
1503
|
+
const pickup = readRgb(r.pickup);
|
|
1504
|
+
if (!reservoir && !pickup) return null;
|
|
1505
|
+
return { reservoir, amount: reservoir && finite(r.amount) ? Math.min(1, Math.max(0, r.amount)) : 0, pickup };
|
|
1506
|
+
}
|
|
1161
1507
|
function normalizeBrushStroke(raw) {
|
|
1162
1508
|
if (!raw || typeof raw !== "object") return null;
|
|
1163
1509
|
const r = raw;
|
|
@@ -1174,6 +1520,8 @@ function normalizeBrushStroke(raw) {
|
|
|
1174
1520
|
};
|
|
1175
1521
|
const copies = Array.isArray(r.copies) ? r.copies.map(readAffine).filter((m) => m !== null) : [];
|
|
1176
1522
|
if (copies.length) out.copies = copies;
|
|
1523
|
+
const load = readLoad(r.load);
|
|
1524
|
+
if (load) out.load = load;
|
|
1177
1525
|
return out;
|
|
1178
1526
|
}
|
|
1179
1527
|
function strokeBounds(points) {
|
|
@@ -1210,7 +1558,7 @@ function varyStroke(stroke, m, v) {
|
|
|
1210
1558
|
});
|
|
1211
1559
|
const brush = s !== 1 ? normalizePaintBrush({ ...stroke.brush, tip: { ...stroke.brush.tip, size: stroke.brush.tip.size * s } }) : stroke.brush;
|
|
1212
1560
|
const out = { ...stroke, brush, points };
|
|
1213
|
-
if (_optionalChain([stroke, 'access',
|
|
1561
|
+
if (_optionalChain([stroke, 'access', _30 => _30.copies, 'optionalAccess', _31 => _31.length])) {
|
|
1214
1562
|
const inv = invertAffine(m);
|
|
1215
1563
|
out.copies = stroke.copies.map((c) => composeAffine(m, composeAffine(c, inv)));
|
|
1216
1564
|
}
|
|
@@ -1277,7 +1625,7 @@ var PROFILE = {
|
|
|
1277
1625
|
}
|
|
1278
1626
|
};
|
|
1279
1627
|
function createPaintCompositor() {
|
|
1280
|
-
return new (0,
|
|
1628
|
+
return new (0, _chunkPOGBUYWEcjs.RasterCompositor)(PROFILE);
|
|
1281
1629
|
}
|
|
1282
1630
|
function downsampleRgba(data, width, height, tw, th) {
|
|
1283
1631
|
tw = Math.max(1, Math.round(tw));
|
|
@@ -1325,7 +1673,7 @@ function downsampleRgba(data, width, height, tw, th) {
|
|
|
1325
1673
|
// src/paint-editor/serialize.ts
|
|
1326
1674
|
var PAINT_FORMAT = "pixerate-paint";
|
|
1327
1675
|
var PAINT_SCHEMA_VERSION = 1;
|
|
1328
|
-
var KNOWN = /* @__PURE__ */ new Set(["format", "version", "width", "height", "dpi", "paper", "layers", "assets", "activeLayerId", "strokes", "guides", "swatches"]);
|
|
1676
|
+
var KNOWN = /* @__PURE__ */ new Set(["format", "version", "width", "height", "dpi", "paper", "layers", "assets", "activeLayerId", "strokes", "guides", "swatches", "mixingPad"]);
|
|
1329
1677
|
function documentExtensions(raw) {
|
|
1330
1678
|
const out = {};
|
|
1331
1679
|
for (const [k, v] of Object.entries(raw)) if (!KNOWN.has(k)) out[k] = v;
|
|
@@ -1345,14 +1693,15 @@ function header(state, options) {
|
|
|
1345
1693
|
if (options.strokes !== false && state.strokes.length) out.strokes = JSON.parse(JSON.stringify(state.strokes));
|
|
1346
1694
|
if (options.guides !== void 0) out.guides = JSON.parse(JSON.stringify(options.guides));
|
|
1347
1695
|
if (options.swatches !== void 0) out.swatches = JSON.parse(JSON.stringify(options.swatches));
|
|
1696
|
+
if (options.mixingPad !== void 0) out.mixingPad = JSON.parse(JSON.stringify(options.mixingPad));
|
|
1348
1697
|
return out;
|
|
1349
1698
|
}
|
|
1350
1699
|
function serializePaintDocument(state, options = {}) {
|
|
1351
|
-
const { layers, assets } =
|
|
1700
|
+
const { layers, assets } = _chunkMGL4U5WAcjs.serializeLayers.call(void 0, state.layers);
|
|
1352
1701
|
return { ...header(state, options), layers, assets };
|
|
1353
1702
|
}
|
|
1354
1703
|
async function serializePaintDocumentExternal(state, store, options = {}) {
|
|
1355
|
-
const { layers, assets } = await
|
|
1704
|
+
const { layers, assets } = await _chunkMGL4U5WAcjs.serializeLayersExternal.call(void 0, state.layers, store);
|
|
1356
1705
|
return { ...header(state, options), layers, assets };
|
|
1357
1706
|
}
|
|
1358
1707
|
function paintLayersOnly(layers, problems) {
|
|
@@ -1360,7 +1709,7 @@ function paintLayersOnly(layers, problems) {
|
|
|
1360
1709
|
for (const l of layers) {
|
|
1361
1710
|
if (l.type === "pixel") {
|
|
1362
1711
|
const { filters, source, ...rest } = l;
|
|
1363
|
-
if (_optionalChain([filters, 'optionalAccess',
|
|
1712
|
+
if (_optionalChain([filters, 'optionalAccess', _32 => _32.length])) problems.push(`Layer "${l.name}": its filters are an image editor feature and were dropped.`);
|
|
1364
1713
|
if (source) problems.push(`Layer "${l.name}" refers to an image editor source image; it loads empty.`);
|
|
1365
1714
|
out.push(rest);
|
|
1366
1715
|
} else if (l.type === "group") out.push({ ...l, children: paintLayersOnly(l.children, problems) });
|
|
@@ -1389,16 +1738,16 @@ function deserializePaintDocument(raw) {
|
|
|
1389
1738
|
const canvas = normalizeCanvas({ width: r.width, height: r.height, dpi: _nullishCoalesce(r.dpi, () => ( void 0)) });
|
|
1390
1739
|
const paper = normalizePaper(_nullishCoalesce(r.paper, () => ( {})));
|
|
1391
1740
|
const assets = r.assets && typeof r.assets === "object" ? r.assets : {};
|
|
1392
|
-
const layers = paintLayersOnly(
|
|
1741
|
+
const layers = paintLayersOnly(_chunkMGL4U5WAcjs.deserializeLayers.call(void 0, r.layers, { assets, problems }), problems);
|
|
1393
1742
|
const wanted = typeof r.activeLayerId === "string" ? r.activeLayerId : null;
|
|
1394
|
-
const activeLayerId = wanted &&
|
|
1395
|
-
return { state: { canvas, paper, layers, activeLayerId, strokes: readStrokes(r.strokes, problems) }, extensions: documentExtensions(r), guides: r.guides, swatches: r.swatches, problems };
|
|
1743
|
+
const activeLayerId = wanted && _chunkPOGBUYWEcjs.findLayer.call(void 0, layers, wanted) ? wanted : _nullishCoalesce(_optionalChain([_chunkPOGBUYWEcjs.flattenLayers.call(void 0, layers), 'access', _33 => _33.filter, 'call', _34 => _34((l) => l.type === "pixel"), 'access', _35 => _35.pop, 'call', _36 => _36(), 'optionalAccess', _37 => _37.id]), () => ( null));
|
|
1744
|
+
return { state: { canvas, paper, layers, activeLayerId, strokes: readStrokes(r.strokes, problems) }, extensions: documentExtensions(r), guides: r.guides, swatches: r.swatches, mixingPad: r.mixingPad, problems };
|
|
1396
1745
|
}
|
|
1397
1746
|
async function deserializePaintDocumentAsync(raw, store) {
|
|
1398
1747
|
if (!raw || typeof raw !== "object") throw new Error("Not a paint document: expected a JSON object.");
|
|
1399
1748
|
const r = raw;
|
|
1400
1749
|
const problems = [];
|
|
1401
|
-
const assets = await
|
|
1750
|
+
const assets = await _chunkMGL4U5WAcjs.resolveExternalAssets.call(void 0, _nullishCoalesce(r.assets, () => ( {})), store, problems);
|
|
1402
1751
|
const loaded = deserializePaintDocument({ ...r, assets });
|
|
1403
1752
|
return { ...loaded, problems: [...problems, ...loaded.problems] };
|
|
1404
1753
|
}
|
|
@@ -1530,7 +1879,7 @@ async function decodeImageBlob(blob) {
|
|
|
1530
1879
|
if (!ctx) throw new Error("No 2D context to decode with.");
|
|
1531
1880
|
ctx.drawImage(bitmap, 0, 0);
|
|
1532
1881
|
const data = new Uint8ClampedArray(ctx.getImageData(0, 0, bitmap.width, bitmap.height).data);
|
|
1533
|
-
_optionalChain([bitmap, 'access',
|
|
1882
|
+
_optionalChain([bitmap, 'access', _38 => _38.close, 'optionalCall', _39 => _39()]);
|
|
1534
1883
|
return { width: bitmap.width, height: bitmap.height, data };
|
|
1535
1884
|
}
|
|
1536
1885
|
function downloadBytes(bytes, fileName, mimeType) {
|
|
@@ -1577,7 +1926,7 @@ function paintTestStroke(surface, brush, paper, color, width, height) {
|
|
|
1577
1926
|
colorHex: color,
|
|
1578
1927
|
affineCopies: [],
|
|
1579
1928
|
copies: [IDENTITY_COPY],
|
|
1580
|
-
mixerLoad:
|
|
1929
|
+
mixerLoad: brushLoadForStroke(brush, _chunk7VOHOPLWcjs.EMPTY_MIXER_LOAD, [r / 255, g / 255, b / 255])
|
|
1581
1930
|
}
|
|
1582
1931
|
);
|
|
1583
1932
|
session.push(thumbnailStroke(width, height));
|
|
@@ -1590,13 +1939,13 @@ function renderBrushThumbnail(brush, options) {
|
|
|
1590
1939
|
const maxSize = height * 0.42;
|
|
1591
1940
|
const b = brush.tip.size > maxSize ? normalizePaintBrush({ tip: { ...brush.tip, size: maxSize } }, brush) : brush;
|
|
1592
1941
|
const rgba = [...hexToRgb8(options.color), 255];
|
|
1593
|
-
let surface =
|
|
1942
|
+
let surface = _chunkXIOMMGPDcjs.emptySurface.call(void 0, 4);
|
|
1594
1943
|
if (b.engine === "eraser") {
|
|
1595
|
-
surface =
|
|
1596
|
-
} else if (b.engine === "mixer" && !b.loadsColor) {
|
|
1944
|
+
surface = _chunkXIOMMGPDcjs.fillRect.call(void 0, surface, { x: 0, y: 0, width, height }, rgba);
|
|
1945
|
+
} else if (b.engine === "blender" || b.engine === "smudge" || b.engine === "mixer" && !b.loadsColor || b.behavior.pickup > 0) {
|
|
1597
1946
|
const half = Math.ceil(height / 2);
|
|
1598
|
-
surface =
|
|
1599
|
-
surface =
|
|
1947
|
+
surface = _chunkXIOMMGPDcjs.fillRect.call(void 0, surface, { x: 0, y: 0, width, height: half }, rgba);
|
|
1948
|
+
surface = _chunkXIOMMGPDcjs.fillRect.call(void 0, surface, { x: 0, y: half, width, height: height - half }, [232, 196, 64, 255]);
|
|
1600
1949
|
}
|
|
1601
1950
|
surface = paintTestStroke(surface, b, options.paper, options.color, width, height);
|
|
1602
1951
|
const layer = { type: "pixel", id: "thumbnail", name: "thumbnail", visible: true, opacity: 1, blend: "normal", surface, offset: { x: 0, y: 0 } };
|
|
@@ -1728,13 +2077,13 @@ function perspectiveDirections(g, p) {
|
|
|
1728
2077
|
return out;
|
|
1729
2078
|
}
|
|
1730
2079
|
var SNAP_DECIDE_DISTANCE = 6;
|
|
1731
|
-
var PerspectiveSnapper = (
|
|
1732
|
-
constructor(guide) {;
|
|
2080
|
+
var PerspectiveSnapper = (_class8 = class {
|
|
2081
|
+
constructor(guide) {;_class8.prototype.__init34.call(this);_class8.prototype.__init35.call(this);
|
|
1733
2082
|
this.guide = guide;
|
|
1734
2083
|
}
|
|
1735
2084
|
|
|
1736
|
-
|
|
1737
|
-
|
|
2085
|
+
__init34() {this.start = null}
|
|
2086
|
+
__init35() {this.dir = null}
|
|
1738
2087
|
/** The line the stroke follows, once chosen. */
|
|
1739
2088
|
get line() {
|
|
1740
2089
|
return this.start && this.dir ? { from: this.start, direction: this.dir } : null;
|
|
@@ -1767,7 +2116,7 @@ var PerspectiveSnapper = (_class7 = class {
|
|
|
1767
2116
|
const t = (p.x - s.x) * d.x + (p.y - s.y) * d.y;
|
|
1768
2117
|
return { ...p, x: s.x + d.x * t, y: s.y + d.y * t };
|
|
1769
2118
|
}
|
|
1770
|
-
},
|
|
2119
|
+
}, _class8);
|
|
1771
2120
|
function perspectiveGuideLines(g, size, count = 12) {
|
|
1772
2121
|
const out = [];
|
|
1773
2122
|
const far = Math.hypot(size.width, size.height) * 4;
|
|
@@ -1786,9 +2135,9 @@ function perspectiveGuideLines(g, size, count = 12) {
|
|
|
1786
2135
|
}
|
|
1787
2136
|
|
|
1788
2137
|
// src/paint-editor/color.ts
|
|
1789
|
-
var
|
|
2138
|
+
var HEX3 = /^#?([0-9a-f]{6})$/i;
|
|
1790
2139
|
function parseHexColor(value, what = "color") {
|
|
1791
|
-
const m = typeof value === "string" ?
|
|
2140
|
+
const m = typeof value === "string" ? HEX3.exec(value.trim()) : null;
|
|
1792
2141
|
if (!m) throw new Error(`${what} is ${JSON.stringify(value)}; use #rrggbb, e.g. "#2a4b8d".`);
|
|
1793
2142
|
const n = parseInt(m[1], 16);
|
|
1794
2143
|
return [n >> 16 & 255, n >> 8 & 255, n & 255];
|
|
@@ -1916,17 +2265,25 @@ var PAD_KNIFE = normalizePaintBrush({
|
|
|
1916
2265
|
id: "pad-knife",
|
|
1917
2266
|
name: "Palette knife",
|
|
1918
2267
|
category: "smudge",
|
|
1919
|
-
engine: "
|
|
2268
|
+
engine: "smudge",
|
|
1920
2269
|
loadsColor: false,
|
|
1921
|
-
tip: { size: 34, hardness: 0.9, spacing: 0.08, roundness: 0.35, angle: 90, opacity: 1, flow:
|
|
1922
|
-
|
|
2270
|
+
tip: { size: 34, hardness: 0.9, spacing: 0.08, roundness: 0.35, angle: 90, opacity: 1, flow: 1, pressureSize: false, pressureOpacity: false },
|
|
2271
|
+
// A knife drags streaks of paint and folds them into each other, mixing them as pigment.
|
|
2272
|
+
smudge: { strength: 0.6, length: 0.8, mixing: "pigment" }
|
|
2273
|
+
});
|
|
2274
|
+
var PAD_BLOB = normalizePaintBrush({
|
|
2275
|
+
id: "pad-blob",
|
|
2276
|
+
name: "Pad blob",
|
|
2277
|
+
category: "custom",
|
|
2278
|
+
engine: "dab",
|
|
2279
|
+
tip: { size: 20, hardness: 1, spacing: 0.1, opacity: 1, flow: 1, pressureSize: false, pressureOpacity: false }
|
|
1923
2280
|
});
|
|
1924
|
-
var MixingPad = (
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
2281
|
+
var MixingPad = (_class9 = class {constructor() { _class9.prototype.__init36.call(this);_class9.prototype.__init37.call(this);_class9.prototype.__init38.call(this);_class9.prototype.__init39.call(this); }
|
|
2282
|
+
__init36() {this.width = MIXING_PAD_WIDTH}
|
|
2283
|
+
__init37() {this.height = MIXING_PAD_HEIGHT}
|
|
2284
|
+
__init38() {this.surface = _chunkXIOMMGPDcjs.emptySurface.call(void 0, 4)}
|
|
1928
2285
|
/** Increments on every change (for redrawing). */
|
|
1929
|
-
|
|
2286
|
+
__init39() {this.version = 0}
|
|
1930
2287
|
/** Paints (`paint`, with `color`) or smears (`knife`) through `points` in pad pixels. */
|
|
1931
2288
|
stroke(points, options) {
|
|
1932
2289
|
if (!points.length) throw new Error("points is empty; give at least one {x, y} in pad pixels.");
|
|
@@ -1937,7 +2294,11 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init29.cal
|
|
|
1937
2294
|
}
|
|
1938
2295
|
let brush = options.tool === "knife" ? PAD_KNIFE : PAD_PAINT;
|
|
1939
2296
|
if (options.size !== void 0) brush = normalizePaintBrush({ tip: { ...brush.tip, size: options.size } }, brush);
|
|
1940
|
-
|
|
2297
|
+
return this.run(brush, points, options.color);
|
|
2298
|
+
}
|
|
2299
|
+
/** Paints `points` with `brush` in `color` on the pad. */
|
|
2300
|
+
run(brush, points, color) {
|
|
2301
|
+
const [r, g, b] = hexToRgb8(color);
|
|
1941
2302
|
let result = null;
|
|
1942
2303
|
const session = new PaintStrokeSession(
|
|
1943
2304
|
{ update: () => {
|
|
@@ -1953,10 +2314,10 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init29.cal
|
|
|
1953
2314
|
grain: null,
|
|
1954
2315
|
paperDepth: 0,
|
|
1955
2316
|
seed: 1,
|
|
1956
|
-
colorHex:
|
|
2317
|
+
colorHex: color,
|
|
1957
2318
|
affineCopies: [],
|
|
1958
2319
|
copies: [IDENTITY_COPY],
|
|
1959
|
-
mixerLoad:
|
|
2320
|
+
mixerLoad: brushLoadForStroke(brush, _chunk7VOHOPLWcjs.EMPTY_MIXER_LOAD, [r / 255, g / 255, b / 255])
|
|
1960
2321
|
}
|
|
1961
2322
|
);
|
|
1962
2323
|
session.push(points);
|
|
@@ -1975,7 +2336,7 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init29.cal
|
|
|
1975
2336
|
const y0 = Math.max(0, Math.floor(y) - r);
|
|
1976
2337
|
const x1 = Math.min(this.width, Math.floor(x) + r + 1);
|
|
1977
2338
|
const y1 = Math.min(this.height, Math.floor(y) + r + 1);
|
|
1978
|
-
const px =
|
|
2339
|
+
const px = _chunkXIOMMGPDcjs.readRegion.call(void 0, this.surface, { x: x0, y: y0, width: x1 - x0, height: y1 - y0 });
|
|
1979
2340
|
let R = 0;
|
|
1980
2341
|
let G = 0;
|
|
1981
2342
|
let B = 0;
|
|
@@ -1992,12 +2353,88 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init29.cal
|
|
|
1992
2353
|
return `#${hex(R)}${hex(G)}${hex(B)}`;
|
|
1993
2354
|
}
|
|
1994
2355
|
clear() {
|
|
1995
|
-
this.surface =
|
|
2356
|
+
this.surface = _chunkXIOMMGPDcjs.emptySurface.call(void 0, 4);
|
|
2357
|
+
this.version++;
|
|
2358
|
+
}
|
|
2359
|
+
/**
|
|
2360
|
+
* Mixes paints the way a painter does on a palette: blobs whose areas are in proportion to
|
|
2361
|
+
* their parts, side by side around (x, y), spiral knife passes that fold them together, and then the centre worked until it is fully mixed: a soft-edged core
|
|
2362
|
+
* of the Kubelka-Munk mix of the parts (`mixPigments`), with partly mixed streaks around it.
|
|
2363
|
+
* Returns that colour (picked back from the centre) and the pile's bounds.
|
|
2364
|
+
*
|
|
2365
|
+
* The core is laid, not smeared, because smearing alone drifts: the knife samples in RGB and a
|
|
2366
|
+
* mixed colour's scattering is re-estimated from its luminance, so repeated small mixes favour
|
|
2367
|
+
* the lighter paint (see GOTCHAS).
|
|
2368
|
+
*/
|
|
2369
|
+
mix(paints, options = {}) {
|
|
2370
|
+
if (!paints.length || paints.length > 8) throw new Error(`Give 1 to 8 paints to mix; got ${paints.length}.`);
|
|
2371
|
+
const total = paints.reduce((n, p) => n + p.parts, 0);
|
|
2372
|
+
paints.forEach((p, i) => {
|
|
2373
|
+
if (!(Number.isFinite(p.parts) && p.parts >= 0)) throw new Error(`paints[${i}].parts is ${p.parts}; use a number \u2265 0.`);
|
|
2374
|
+
});
|
|
2375
|
+
if (!(total > 0)) throw new Error("Every paint has 0 parts; give at least one paint parts above 0.");
|
|
2376
|
+
const R = _nullishCoalesce(options.radius, () => ( 40));
|
|
2377
|
+
if (!(R >= 12 && R <= 100)) throw new Error(`radius ${R} is out of range; use 12..100 pad px.`);
|
|
2378
|
+
const passes = _nullishCoalesce(options.passes, () => ( 3));
|
|
2379
|
+
if (!(Number.isInteger(passes) && passes >= 1 && passes <= 20)) throw new Error(`passes ${passes} is out of range; use a whole number 1..20.`);
|
|
2380
|
+
const cx = _nullishCoalesce(options.x, () => ( this.width / 2));
|
|
2381
|
+
const cy = _nullishCoalesce(options.y, () => ( this.height / 2));
|
|
2382
|
+
if (!(cx - R >= 0 && cy - R >= 0 && cx + R <= this.width && cy + R <= this.height)) {
|
|
2383
|
+
throw new Error(`A pile of radius ${R} at (${cx}, ${cy}) does not fit on the ${this.width}\xD7${this.height} pad; move it at least ${R} px from every edge or use a smaller radius.`);
|
|
2384
|
+
}
|
|
2385
|
+
const bounds = { x: Math.floor(cx - R), y: Math.floor(cy - R), width: Math.ceil(2 * R), height: Math.ceil(2 * R) };
|
|
2386
|
+
if (options.clear !== false) this.surface = _chunkXIOMMGPDcjs.writeRegion.call(void 0, this.surface, bounds, new Uint8ClampedArray(bounds.width * bounds.height * 4));
|
|
2387
|
+
const used = paints.filter((p) => p.parts > 0);
|
|
2388
|
+
const area = Math.PI * (0.7 * R) ** 2;
|
|
2389
|
+
used.forEach((p, i) => {
|
|
2390
|
+
const r = Math.sqrt(area * (p.parts / total) / Math.PI);
|
|
2391
|
+
const a = i / used.length * Math.PI * 2;
|
|
2392
|
+
const off = used.length === 1 ? 0 : 0.45 * R;
|
|
2393
|
+
const x = Math.min(cx + R - r, Math.max(cx - R + r, cx + Math.cos(a) * off));
|
|
2394
|
+
const y = Math.min(cy + R - r, Math.max(cy - R + r, cy + Math.sin(a) * off));
|
|
2395
|
+
const blob = normalizePaintBrush({ tip: { ...PAD_BLOB.tip, size: Math.max(2, 2 * r) } }, PAD_BLOB);
|
|
2396
|
+
this.run(blob, [{ x, y }], p.color.toLowerCase());
|
|
2397
|
+
});
|
|
2398
|
+
const knife = normalizePaintBrush({ tip: { ...PAD_KNIFE.tip, size: Math.max(12, R * 0.5) } }, PAD_KNIFE);
|
|
2399
|
+
for (let k = 0; k < passes; k++) {
|
|
2400
|
+
const stir = [];
|
|
2401
|
+
const turns = 1.6;
|
|
2402
|
+
for (let t = 0; t <= 48; t++) {
|
|
2403
|
+
const a = t / 48 * Math.PI * 2 * turns + k * 1.3;
|
|
2404
|
+
const rr = R * (0.85 - 0.6 * (t / 48));
|
|
2405
|
+
stir.push({ x: cx + Math.cos(a) * rr, y: cy + Math.sin(a) * rr });
|
|
2406
|
+
}
|
|
2407
|
+
this.run(knife, stir, "#000000");
|
|
2408
|
+
}
|
|
2409
|
+
const mixed = _chunkXIOMMGPDcjs.mixPigments.call(void 0,
|
|
2410
|
+
paints.map((p) => hexToRgb8(p.color).map((v) => v / 255)),
|
|
2411
|
+
paints.map((p) => p.parts)
|
|
2412
|
+
);
|
|
2413
|
+
const core = normalizePaintBrush({ tip: { ...PAD_BLOB.tip, size: R * 1.1, hardness: 0.55 } }, PAD_BLOB);
|
|
2414
|
+
const hex = "#" + mixed.map((v) => Math.round(v * 255).toString(16).padStart(2, "0")).join("");
|
|
2415
|
+
this.run(core, [{ x: cx, y: cy }], hex);
|
|
2416
|
+
return { color: this.pick(cx, cy, 2), bounds };
|
|
2417
|
+
}
|
|
2418
|
+
/** The pad's pixels as a PNG data URL (with alpha), for saving. */
|
|
2419
|
+
toPng() {
|
|
2420
|
+
return _chunkPOGBUYWEcjs.pngDataUrl.call(void 0, this.width, this.height, _chunkXIOMMGPDcjs.readRegion.call(void 0, this.surface, { x: 0, y: 0, width: this.width, height: this.height }));
|
|
2421
|
+
}
|
|
2422
|
+
/** Replaces the pad with a PNG (data URL or base64) of the pad's size, as `toPng` writes. */
|
|
2423
|
+
fromPng(png) {
|
|
2424
|
+
const bytes = png.startsWith("data:") ? _optionalChain([_chunkPOGBUYWEcjs.parseDataUrl.call(void 0, png), 'optionalAccess', _40 => _40.bytes]) : _chunkPOGBUYWEcjs.base64ToBytes.call(void 0, png);
|
|
2425
|
+
if (!bytes || !_chunkPOGBUYWEcjs.isPng.call(void 0, bytes)) throw new Error("The pad image is not a PNG data URL or base64 PNG.");
|
|
2426
|
+
const img = _chunkPOGBUYWEcjs.decodePng.call(void 0, bytes);
|
|
2427
|
+
if (img.width !== this.width || img.height !== this.height) throw new Error(`The pad image is ${img.width}\xD7${img.height}; a mixing pad is ${this.width}\xD7${this.height}.`);
|
|
2428
|
+
this.surface = _chunkXIOMMGPDcjs.surfaceFromPixels.call(void 0, { width: img.width, height: img.height, data: img.data }, 4);
|
|
1996
2429
|
this.version++;
|
|
1997
2430
|
}
|
|
2431
|
+
/** Whether nothing is on the pad. */
|
|
2432
|
+
get empty() {
|
|
2433
|
+
return this.surface.tiles.size === 0;
|
|
2434
|
+
}
|
|
1998
2435
|
/** The pad over white, `width × height` RGBA. */
|
|
1999
2436
|
pixels() {
|
|
2000
|
-
const px =
|
|
2437
|
+
const px = _chunkXIOMMGPDcjs.readRegion.call(void 0, this.surface, { x: 0, y: 0, width: this.width, height: this.height });
|
|
2001
2438
|
for (let i = 0; i < px.length; i += 4) {
|
|
2002
2439
|
const a = px[i + 3] / 255;
|
|
2003
2440
|
px[i] = Math.round(px[i] * a + 255 * (1 - a));
|
|
@@ -2007,7 +2444,7 @@ var MixingPad = (_class8 = class {constructor() { _class8.prototype.__init29.cal
|
|
|
2007
2444
|
}
|
|
2008
2445
|
return px;
|
|
2009
2446
|
}
|
|
2010
|
-
},
|
|
2447
|
+
}, _class9);
|
|
2011
2448
|
|
|
2012
2449
|
// src/paint-editor/controller.ts
|
|
2013
2450
|
var PaintEditorError = class extends Error {
|
|
@@ -2016,9 +2453,9 @@ var PaintEditorError = class extends Error {
|
|
|
2016
2453
|
this.name = "PaintEditorError";
|
|
2017
2454
|
}
|
|
2018
2455
|
};
|
|
2019
|
-
var
|
|
2456
|
+
var HEX4 = /^#[0-9a-f]{6}$/i;
|
|
2020
2457
|
function parseColor(value, what = "color") {
|
|
2021
|
-
if (typeof value !== "string" || !
|
|
2458
|
+
if (typeof value !== "string" || !HEX4.test(value.trim())) throw new PaintEditorError(`${what} is ${JSON.stringify(value)}; use #rrggbb, e.g. "#2a4b8d".`);
|
|
2022
2459
|
return value.trim().toLowerCase();
|
|
2023
2460
|
}
|
|
2024
2461
|
function rgbaOf(hex) {
|
|
@@ -2035,40 +2472,44 @@ function unionRect(a, b) {
|
|
|
2035
2472
|
const y = Math.min(a.y, b.y);
|
|
2036
2473
|
return { x, y, width: Math.max(a.x + a.width, b.x + b.width) - x, height: Math.max(a.y + a.height, b.y + b.height) - y };
|
|
2037
2474
|
}
|
|
2038
|
-
var isPixelLayer = (l) => _optionalChain([l, 'optionalAccess',
|
|
2039
|
-
var PaintEditorController = (
|
|
2475
|
+
var isPixelLayer = (l) => _optionalChain([l, 'optionalAccess', _41 => _41.type]) === "pixel";
|
|
2476
|
+
var PaintEditorController = (_class10 = class {
|
|
2040
2477
|
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2478
|
+
__init40() {this.extensions = {}}
|
|
2479
|
+
__init41() {this.undoStack = []}
|
|
2480
|
+
__init42() {this.redoStack = []}
|
|
2044
2481
|
|
|
2045
2482
|
|
|
2046
2483
|
|
|
2484
|
+
/** The library list in the state (the same array until the user brushes change, so thumbnails are not redrawn). */
|
|
2485
|
+
__init43() {this.library = null}
|
|
2047
2486
|
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2487
|
+
__init44() {this.secondaryColor = "#ffffff"}
|
|
2488
|
+
__init45() {this.mixerLoad = _chunk7VOHOPLWcjs.EMPTY_MIXER_LOAD}
|
|
2489
|
+
__init46() {this.view = { ...DEFAULT_VIEW, pan: { ...DEFAULT_VIEW.pan } }}
|
|
2051
2490
|
|
|
2052
2491
|
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2492
|
+
__init47() {this.listeners = /* @__PURE__ */ new Set()}
|
|
2493
|
+
__init48() {this.version = 0}
|
|
2494
|
+
__init49() {this.cached = null}
|
|
2495
|
+
__init50() {this.session = null}
|
|
2496
|
+
__init51() {this.live = null}
|
|
2497
|
+
__init52() {this.liveLayers = null}
|
|
2498
|
+
__init53() {this.dirty = "all"}
|
|
2499
|
+
__init54() {this.seedCounter = 1}
|
|
2500
|
+
__init55() {this.strokeCounter = 1}
|
|
2062
2501
|
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2502
|
+
__init56() {this.swatches = DEFAULT_SWATCHES.map((s) => ({ ...s }))}
|
|
2503
|
+
__init57() {this.pad = new MixingPad()}
|
|
2504
|
+
__init58() {this.savedPads = []}
|
|
2505
|
+
__init59() {this.nextPadId = 1}
|
|
2506
|
+
__init60() {this.tracingVersion = 1}
|
|
2507
|
+
__init61() {this.tracingCache = null}
|
|
2508
|
+
__init62() {this.imagePixels = /* @__PURE__ */ new Map()}
|
|
2509
|
+
__init63() {this.nextReferenceId = 1}
|
|
2510
|
+
__init64() {this.viewCompositor = createPaintCompositor()}
|
|
2511
|
+
__init65() {this.exportCompositor = createPaintCompositor()}
|
|
2512
|
+
constructor(options = {}) {;_class10.prototype.__init40.call(this);_class10.prototype.__init41.call(this);_class10.prototype.__init42.call(this);_class10.prototype.__init43.call(this);_class10.prototype.__init44.call(this);_class10.prototype.__init45.call(this);_class10.prototype.__init46.call(this);_class10.prototype.__init47.call(this);_class10.prototype.__init48.call(this);_class10.prototype.__init49.call(this);_class10.prototype.__init50.call(this);_class10.prototype.__init51.call(this);_class10.prototype.__init52.call(this);_class10.prototype.__init53.call(this);_class10.prototype.__init54.call(this);_class10.prototype.__init55.call(this);_class10.prototype.__init56.call(this);_class10.prototype.__init57.call(this);_class10.prototype.__init58.call(this);_class10.prototype.__init59.call(this);_class10.prototype.__init60.call(this);_class10.prototype.__init61.call(this);_class10.prototype.__init62.call(this);_class10.prototype.__init63.call(this);_class10.prototype.__init64.call(this);_class10.prototype.__init65.call(this);
|
|
2072
2513
|
this.historyLimit = Math.max(1, _nullishCoalesce(options.historyLimit, () => ( 200)));
|
|
2073
2514
|
this.doc = createPaintDocument({ width: options.width, height: options.height, dpi: options.dpi, paper: this.resolvePaper(options.paper) });
|
|
2074
2515
|
this.userBrushes = (_nullishCoalesce(options.brushes, () => ( []))).map((b) => normalizePaintBrush({ ...b, builtin: false }));
|
|
@@ -2099,7 +2540,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2099
2540
|
this.cached = {
|
|
2100
2541
|
document: this.doc,
|
|
2101
2542
|
brush: this.brush,
|
|
2102
|
-
brushes: [...BUILTIN_BRUSHES, ...this.userBrushes],
|
|
2543
|
+
brushes: this.library ??= [...BUILTIN_BRUSHES, ...this.userBrushes],
|
|
2103
2544
|
color: this.color,
|
|
2104
2545
|
secondaryColor: this.secondaryColor,
|
|
2105
2546
|
mixerLoad: this.mixerLoad,
|
|
@@ -2107,11 +2548,12 @@ var PaintEditorController = (_class9 = class {
|
|
|
2107
2548
|
hand: this.hand,
|
|
2108
2549
|
canUndo: this.undoStack.length > 0 && !this.session,
|
|
2109
2550
|
canRedo: this.redoStack.length > 0 && !this.session,
|
|
2110
|
-
undoLabel: _nullishCoalesce(_optionalChain([last, 'optionalAccess',
|
|
2111
|
-
redoLabel: _nullishCoalesce(_optionalChain([next, 'optionalAccess',
|
|
2551
|
+
undoLabel: _nullishCoalesce(_optionalChain([last, 'optionalAccess', _42 => _42.label]), () => ( null)),
|
|
2552
|
+
redoLabel: _nullishCoalesce(_optionalChain([next, 'optionalAccess', _43 => _43.label]), () => ( null)),
|
|
2112
2553
|
guides: this.guides,
|
|
2113
2554
|
swatches: this.swatches,
|
|
2114
2555
|
mixingPadVersion: this.pad.version,
|
|
2556
|
+
savedMixingPads: this.savedPads.map(({ id, name }) => ({ id, name })),
|
|
2115
2557
|
stroking: Boolean(this.session),
|
|
2116
2558
|
version: this.version
|
|
2117
2559
|
};
|
|
@@ -2216,7 +2658,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2216
2658
|
}
|
|
2217
2659
|
// --- Layers --------------------------------------------------------------------
|
|
2218
2660
|
getLayer(id) {
|
|
2219
|
-
return _optionalChain([
|
|
2661
|
+
return _optionalChain([_chunkPOGBUYWEcjs.findLayer.call(void 0, this.doc.layers, id), 'optionalAccess', _44 => _44.layer]);
|
|
2220
2662
|
}
|
|
2221
2663
|
/** The active pixel layer (or `undefined`). */
|
|
2222
2664
|
getActiveLayer() {
|
|
@@ -2235,12 +2677,12 @@ var PaintEditorController = (_class9 = class {
|
|
|
2235
2677
|
walk(this.doc.layers, null, 0);
|
|
2236
2678
|
return out;
|
|
2237
2679
|
}
|
|
2238
|
-
describeLayer(l, parentId = _nullishCoalesce(_optionalChain([
|
|
2680
|
+
describeLayer(l, parentId = _nullishCoalesce(_optionalChain([_chunkPOGBUYWEcjs.findLayer.call(void 0, this.doc.layers, l.id), 'optionalAccess', _45 => _45.parent, 'optionalAccess', _46 => _46.id]), () => ( null)), depth = _nullishCoalesce(_optionalChain([_chunkPOGBUYWEcjs.findLayer.call(void 0, this.doc.layers, l.id), 'optionalAccess', _47 => _47.path, 'access', _48 => _48.length]), () => ( 0))) {
|
|
2239
2681
|
let bounds = null;
|
|
2240
2682
|
if (l.type === "pixel") {
|
|
2241
|
-
const b =
|
|
2683
|
+
const b = _chunkXIOMMGPDcjs.contentBounds.call(void 0, l.surface);
|
|
2242
2684
|
bounds = b ? { ...b, x: b.x + l.offset.x, y: b.y + l.offset.y } : null;
|
|
2243
|
-
} else for (const c of
|
|
2685
|
+
} else for (const c of _chunkPOGBUYWEcjs.flattenLayers.call(void 0, l.children)) if (c.type === "pixel") bounds = unionRect(bounds, this.describeLayer(c, l.id, depth + 1).bounds);
|
|
2244
2686
|
return {
|
|
2245
2687
|
id: l.id,
|
|
2246
2688
|
name: l.name,
|
|
@@ -2267,7 +2709,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2267
2709
|
if (!id) throw new PaintEditorError(`There is no active layer to ${what}; add one with add_layer.`);
|
|
2268
2710
|
const l = this.requireLayer(id);
|
|
2269
2711
|
if (!isPixelLayer(l)) throw new PaintEditorError(`Layer "${l.name}" is a group; ${what} a pixel layer inside it.`);
|
|
2270
|
-
if (
|
|
2712
|
+
if (_chunkPOGBUYWEcjs.isLayerLocked.call(void 0, l, "pixels")) throw new PaintEditorError(`Layer "${l.name}" has its pixels locked; unlock it (update_layer locks) before you ${what} it.`);
|
|
2271
2713
|
if (!l.visible) throw new PaintEditorError(`Layer "${l.name}" is hidden; show it (update_layer visible true) before you ${what} it.`);
|
|
2272
2714
|
return l;
|
|
2273
2715
|
}
|
|
@@ -2280,9 +2722,9 @@ var PaintEditorController = (_class9 = class {
|
|
|
2280
2722
|
/** Adds an empty pixel layer or group above `aboveId` (default: the active layer) and makes it active. */
|
|
2281
2723
|
addLayer(options = {}) {
|
|
2282
2724
|
this.requireIdle("adding a layer");
|
|
2283
|
-
const count =
|
|
2725
|
+
const count = _chunkPOGBUYWEcjs.flattenLayers.call(void 0, this.doc.layers).length;
|
|
2284
2726
|
const type = _nullishCoalesce(options.type, () => ( "pixel"));
|
|
2285
|
-
const name = _optionalChain([options, 'access',
|
|
2727
|
+
const name = _optionalChain([options, 'access', _49 => _49.name, 'optionalAccess', _50 => _50.trim, 'call', _51 => _51()]) || (type === "group" ? `Group ${count + 1}` : `Layer ${count + 1}`);
|
|
2286
2728
|
const layer = type === "group" ? { type: "group", id: newPaintLayerId(), name, visible: true, opacity: 1, blend: "pass-through", children: [] } : createPaintLayer(name);
|
|
2287
2729
|
let parentId = null;
|
|
2288
2730
|
let index;
|
|
@@ -2292,21 +2734,21 @@ var PaintEditorController = (_class9 = class {
|
|
|
2292
2734
|
if (g.type !== "group") throw new PaintEditorError(`Layer "${g.name}" is not a group; pass a group id as parentId.`);
|
|
2293
2735
|
parentId = g.id;
|
|
2294
2736
|
} else if (above) {
|
|
2295
|
-
const loc =
|
|
2737
|
+
const loc = _chunkPOGBUYWEcjs.findLayer.call(void 0, this.doc.layers, above);
|
|
2296
2738
|
if (!loc) throw new PaintEditorError(`Unknown layer "${above}". Call list_layers for the ids.`);
|
|
2297
|
-
parentId = _nullishCoalesce(_optionalChain([loc, 'access',
|
|
2739
|
+
parentId = _nullishCoalesce(_optionalChain([loc, 'access', _52 => _52.parent, 'optionalAccess', _53 => _53.id]), () => ( null));
|
|
2298
2740
|
index = loc.index + 1;
|
|
2299
2741
|
}
|
|
2300
|
-
const layers =
|
|
2742
|
+
const layers = _chunkPOGBUYWEcjs.insertLayer.call(void 0, this.doc.layers, layer, parentId, index);
|
|
2301
2743
|
this.commit(type === "group" ? "Add group" : "Add layer", { ...this.doc, layers, activeLayerId: layer.id });
|
|
2302
2744
|
return this.describeLayer(layer);
|
|
2303
2745
|
}
|
|
2304
2746
|
updateLayer(id, update) {
|
|
2305
2747
|
this.requireIdle("changing a layer");
|
|
2306
2748
|
const l = this.requireLayer(id);
|
|
2307
|
-
if (
|
|
2749
|
+
if (_chunkPOGBUYWEcjs.isLayerLocked.call(void 0, l, "all") && Object.keys(update).some((k) => k !== "locks")) throw new PaintEditorError(`Layer "${l.name}" is fully locked; unlock it first (locks {all: false}).`);
|
|
2308
2750
|
if (update.opacity !== void 0 && !(update.opacity >= 0 && update.opacity <= 1)) throw new PaintEditorError(`opacity ${update.opacity} is out of range; use 0..1.`);
|
|
2309
|
-
if (update.blend !== void 0 && !
|
|
2751
|
+
if (update.blend !== void 0 && !_chunkXIOMMGPDcjs.isBlendMode.call(void 0, update.blend) && !(l.type === "group" && update.blend === "pass-through")) {
|
|
2310
2752
|
throw new PaintEditorError(`Unknown blend mode "${update.blend}". Call get_canvas_info for the blend modes.`);
|
|
2311
2753
|
}
|
|
2312
2754
|
if (update.name !== void 0 && !update.name.trim()) throw new PaintEditorError("name is empty; give the layer a name.");
|
|
@@ -2320,66 +2762,66 @@ var PaintEditorController = (_class9 = class {
|
|
|
2320
2762
|
const locks = { ..._nullishCoalesce(l.locks, () => ( {})), ...update.locks };
|
|
2321
2763
|
next.locks = Object.values(locks).some(Boolean) ? locks : void 0;
|
|
2322
2764
|
}
|
|
2323
|
-
this.commit("Layer properties", { ...this.doc, layers:
|
|
2765
|
+
this.commit("Layer properties", { ...this.doc, layers: _chunkPOGBUYWEcjs.mapLayer.call(void 0, this.doc.layers, id, () => next) });
|
|
2324
2766
|
return this.describeLayer(next);
|
|
2325
2767
|
}
|
|
2326
2768
|
removeLayer(id) {
|
|
2327
2769
|
this.requireIdle("removing a layer");
|
|
2328
2770
|
const l = this.requireLayer(id);
|
|
2329
|
-
if (
|
|
2330
|
-
const layers =
|
|
2771
|
+
if (_chunkPOGBUYWEcjs.isLayerLocked.call(void 0, l, "all")) throw new PaintEditorError(`Layer "${l.name}" is fully locked; unlock it before removing it.`);
|
|
2772
|
+
const layers = _chunkPOGBUYWEcjs.mapLayer.call(void 0, this.doc.layers, id, () => null);
|
|
2331
2773
|
let active = this.doc.activeLayerId;
|
|
2332
|
-
if (active && !
|
|
2774
|
+
if (active && !_chunkPOGBUYWEcjs.findLayer.call(void 0, layers, active)) active = _nullishCoalesce(_optionalChain([_chunkPOGBUYWEcjs.flattenLayers.call(void 0, layers), 'access', _54 => _54.filter, 'call', _55 => _55((x) => x.type === "pixel"), 'access', _56 => _56.pop, 'call', _57 => _57(), 'optionalAccess', _58 => _58.id]), () => ( null));
|
|
2333
2775
|
this.commit("Remove layer", { ...this.doc, layers, activeLayerId: active });
|
|
2334
2776
|
return { removed: id, activeLayerId: active };
|
|
2335
2777
|
}
|
|
2336
2778
|
duplicateLayer(id) {
|
|
2337
2779
|
this.requireIdle("duplicating a layer");
|
|
2338
|
-
const loc =
|
|
2780
|
+
const loc = _chunkPOGBUYWEcjs.findLayer.call(void 0, this.doc.layers, id);
|
|
2339
2781
|
if (!loc) throw new PaintEditorError(`Unknown layer "${id}". Call list_layers for the ids.`);
|
|
2340
2782
|
const copy = (l) => l.type === "group" ? { ...l, id: newPaintLayerId(), children: l.children.map(copy) } : { ...l, id: newPaintLayerId() };
|
|
2341
2783
|
const dup = { ...copy(loc.layer), name: `${loc.layer.name} copy` };
|
|
2342
|
-
const layers =
|
|
2784
|
+
const layers = _chunkPOGBUYWEcjs.insertLayer.call(void 0, this.doc.layers, dup, _nullishCoalesce(_optionalChain([loc, 'access', _59 => _59.parent, 'optionalAccess', _60 => _60.id]), () => ( null)), loc.index + 1);
|
|
2343
2785
|
this.commit("Duplicate layer", { ...this.doc, layers, activeLayerId: dup.id });
|
|
2344
2786
|
return this.describeLayer(dup);
|
|
2345
2787
|
}
|
|
2346
2788
|
/** Moves a layer to `index` (0 = bottom) within `parentId` (default its own parent; `null` = top level). */
|
|
2347
2789
|
moveLayer(id, index, parentId) {
|
|
2348
2790
|
this.requireIdle("moving a layer");
|
|
2349
|
-
const loc =
|
|
2791
|
+
const loc = _chunkPOGBUYWEcjs.findLayer.call(void 0, this.doc.layers, id);
|
|
2350
2792
|
if (!loc) throw new PaintEditorError(`Unknown layer "${id}". Call list_layers for the ids.`);
|
|
2351
|
-
const target = parentId === void 0 ? _nullishCoalesce(_optionalChain([loc, 'access',
|
|
2793
|
+
const target = parentId === void 0 ? _nullishCoalesce(_optionalChain([loc, 'access', _61 => _61.parent, 'optionalAccess', _62 => _62.id]), () => ( null)) : parentId;
|
|
2352
2794
|
if (target) {
|
|
2353
2795
|
const g = this.requireLayer(target);
|
|
2354
2796
|
if (g.type !== "group") throw new PaintEditorError(`Layer "${g.name}" is not a group; pass a group id as parentId.`);
|
|
2355
|
-
if (target === id || loc.layer.type === "group" &&
|
|
2797
|
+
if (target === id || loc.layer.type === "group" && _chunkPOGBUYWEcjs.findLayer.call(void 0, loc.layer.children, target)) throw new PaintEditorError("A group cannot move into itself.");
|
|
2356
2798
|
}
|
|
2357
|
-
const without =
|
|
2358
|
-
const siblings = target ?
|
|
2799
|
+
const without = _chunkPOGBUYWEcjs.mapLayer.call(void 0, this.doc.layers, id, () => null);
|
|
2800
|
+
const siblings = target ? _chunkPOGBUYWEcjs.findLayer.call(void 0, without, target).layer.children : without;
|
|
2359
2801
|
if (!Number.isInteger(index) || index < 0 || index > siblings.length) throw new PaintEditorError(`index ${index} is out of range; use 0..${siblings.length} (0 is the bottom).`);
|
|
2360
|
-
const layers =
|
|
2802
|
+
const layers = _chunkPOGBUYWEcjs.insertLayer.call(void 0, without, loc.layer, target, index);
|
|
2361
2803
|
this.commit("Move layer", { ...this.doc, layers });
|
|
2362
2804
|
return this.describeLayer(loc.layer);
|
|
2363
2805
|
}
|
|
2364
2806
|
/** Merges a pixel layer into the pixel layer below it (keeping the lower one's settings). */
|
|
2365
2807
|
mergeDown(id) {
|
|
2366
2808
|
this.requireIdle("merging");
|
|
2367
|
-
const loc =
|
|
2809
|
+
const loc = _chunkPOGBUYWEcjs.findLayer.call(void 0, this.doc.layers, id);
|
|
2368
2810
|
if (!loc) throw new PaintEditorError(`Unknown layer "${id}". Call list_layers for the ids.`);
|
|
2369
2811
|
const siblings = loc.parent ? loc.parent.children : this.doc.layers;
|
|
2370
2812
|
const below = siblings[loc.index - 1];
|
|
2371
2813
|
const upper = loc.layer;
|
|
2372
2814
|
if (!isPixelLayer(upper) || !isPixelLayer(below)) throw new PaintEditorError("Merge down needs a pixel layer directly above another pixel layer in the same group.");
|
|
2373
|
-
if (
|
|
2815
|
+
if (_chunkPOGBUYWEcjs.isLayerLocked.call(void 0, below, "pixels")) throw new PaintEditorError(`Layer "${below.name}" has its pixels locked; unlock it to merge into it.`);
|
|
2374
2816
|
const rect = unionRect(this.describeLayer(below).bounds, this.describeLayer(upper).bounds);
|
|
2375
|
-
let surface =
|
|
2817
|
+
let surface = _chunkXIOMMGPDcjs.emptySurface.call(void 0, 4);
|
|
2376
2818
|
if (rect) {
|
|
2377
2819
|
const base = { ...below, opacity: 1, blend: "normal", visible: true, clipped: void 0, mask: void 0 };
|
|
2378
2820
|
const px = createPaintCompositor().composite([base, upper.visible ? upper : { ...upper, opacity: 0 }], rect, this.sources());
|
|
2379
|
-
surface =
|
|
2821
|
+
surface = _chunkXIOMMGPDcjs.surfaceFromPixels.call(void 0, { width: rect.width, height: rect.height, data: px }, 4, rect.x, rect.y);
|
|
2380
2822
|
}
|
|
2381
2823
|
const merged = { ...below, surface, offset: { x: 0, y: 0 } };
|
|
2382
|
-
const layers =
|
|
2824
|
+
const layers = _chunkPOGBUYWEcjs.mapLayer.call(void 0, _chunkPOGBUYWEcjs.mapLayer.call(void 0, this.doc.layers, upper.id, () => null), below.id, () => merged);
|
|
2383
2825
|
this.commit("Merge down", { ...this.doc, layers, activeLayerId: below.id });
|
|
2384
2826
|
return this.describeLayer(merged);
|
|
2385
2827
|
}
|
|
@@ -2388,7 +2830,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2388
2830
|
this.requireIdle("clearing");
|
|
2389
2831
|
const l = this.paintTarget(layerId, "clear");
|
|
2390
2832
|
const bounds = this.describeLayer(l).bounds;
|
|
2391
|
-
this.commit("Clear layer", { ...this.doc, layers:
|
|
2833
|
+
this.commit("Clear layer", { ...this.doc, layers: _chunkPOGBUYWEcjs.mapLayer.call(void 0, this.doc.layers, l.id, () => ({ ...l, surface: _chunkXIOMMGPDcjs.emptySurface.call(void 0, 4) })) });
|
|
2392
2834
|
return { layerId: l.id, bounds };
|
|
2393
2835
|
}
|
|
2394
2836
|
/**
|
|
@@ -2405,14 +2847,14 @@ var PaintEditorController = (_class9 = class {
|
|
|
2405
2847
|
if (!(grainAmount >= 0 && grainAmount <= 1)) throw new PaintEditorError(`grain ${grainAmount} is out of range; use 0..1.`);
|
|
2406
2848
|
const pressure = _nullishCoalesce(options.pressure, () => ( 0.5));
|
|
2407
2849
|
const blend = _nullishCoalesce(options.blend, () => ( "normal"));
|
|
2408
|
-
if (!
|
|
2850
|
+
if (!_chunkXIOMMGPDcjs.isBlendMode.call(void 0, blend)) throw new PaintEditorError(`Unknown blend mode "${blend}".`);
|
|
2409
2851
|
const { width, height } = this.doc.canvas;
|
|
2410
2852
|
const rect = { x: 0, y: 0, width, height };
|
|
2411
|
-
const coverage = new (0,
|
|
2853
|
+
const coverage = new (0, _chunk7VOHOPLWcjs.CoverageMap)();
|
|
2412
2854
|
const depth = grainAmount * this.doc.paper.depth;
|
|
2413
2855
|
const grain = depth > 0 ? paperGrain(this.doc.paper) : null;
|
|
2414
|
-
for (const { tx, ty } of
|
|
2415
|
-
const r = { x: tx *
|
|
2856
|
+
for (const { tx, ty } of _chunkXIOMMGPDcjs.tilesInRect.call(void 0, rect)) {
|
|
2857
|
+
const r = { x: tx * _chunkXIOMMGPDcjs.TILE, y: ty * _chunkXIOMMGPDcjs.TILE, width: Math.min(_chunkXIOMMGPDcjs.TILE, width - tx * _chunkXIOMMGPDcjs.TILE), height: Math.min(_chunkXIOMMGPDcjs.TILE, height - ty * _chunkXIOMMGPDcjs.TILE) };
|
|
2416
2858
|
const vals = new Float32Array(r.width * r.height);
|
|
2417
2859
|
for (let y = 0, k = 0; y < r.height; y++) for (let x = 0; x < r.width; x++, k++) vals[k] = grain ? grainCatch(grain.at(r.x + x + 0.5, r.y + y + 0.5), depth, pressure) : 1;
|
|
2418
2860
|
coverage.accumulate(r, vals, 1);
|
|
@@ -2423,25 +2865,25 @@ var PaintEditorController = (_class9 = class {
|
|
|
2423
2865
|
background: rgbaOf(this.doc.paper.color),
|
|
2424
2866
|
opacity,
|
|
2425
2867
|
blend,
|
|
2426
|
-
lockTransparency:
|
|
2868
|
+
lockTransparency: _chunkPOGBUYWEcjs.isLayerLocked.call(void 0, l, "transparency"),
|
|
2427
2869
|
selection: null,
|
|
2428
2870
|
offset: l.offset
|
|
2429
2871
|
};
|
|
2430
2872
|
const surface = this.applyCoverage(l, coverage, rect, apply);
|
|
2431
|
-
this.commit("Fill layer", { ...this.doc, layers:
|
|
2873
|
+
this.commit("Fill layer", { ...this.doc, layers: _chunkPOGBUYWEcjs.mapLayer.call(void 0, this.doc.layers, l.id, () => ({ ...l, surface })) });
|
|
2432
2874
|
return { layerId: l.id, bounds: rect };
|
|
2433
2875
|
}
|
|
2434
2876
|
applyCoverage(l, coverage, rect, apply) {
|
|
2435
2877
|
const base = l.surface;
|
|
2436
2878
|
const tiles = new Map(base.tiles);
|
|
2437
|
-
for (const { tx, ty } of
|
|
2438
|
-
const key =
|
|
2439
|
-
const src = _nullishCoalesce(_optionalChain([base, 'access',
|
|
2440
|
-
const tile =
|
|
2879
|
+
for (const { tx, ty } of _chunkXIOMMGPDcjs.tilesInRect.call(void 0, { x: rect.x - l.offset.x, y: rect.y - l.offset.y, width: rect.width, height: rect.height })) {
|
|
2880
|
+
const key = _chunkXIOMMGPDcjs.tileKey.call(void 0, tx, ty);
|
|
2881
|
+
const src = _nullishCoalesce(_optionalChain([base, 'access', _63 => _63.tiles, 'access', _64 => _64.get, 'call', _65 => _65(key), 'optionalAccess', _66 => _66.data]), () => ( new Uint8ClampedArray(_chunkXIOMMGPDcjs.TILE * _chunkXIOMMGPDcjs.TILE * 4)));
|
|
2882
|
+
const tile = _chunkXIOMMGPDcjs.finishTile.call(void 0, _chunk7VOHOPLWcjs.applyStrokeToTile.call(void 0, coverage, src, 4, tx, ty, apply), 4, 0);
|
|
2441
2883
|
if (tile) tiles.set(key, tile);
|
|
2442
2884
|
else tiles.delete(key);
|
|
2443
2885
|
}
|
|
2444
|
-
return new (0,
|
|
2886
|
+
return new (0, _chunkXIOMMGPDcjs.TiledSurface)(4, tiles, 0);
|
|
2445
2887
|
}
|
|
2446
2888
|
// --- Brushes -------------------------------------------------------------------
|
|
2447
2889
|
findBrush(id) {
|
|
@@ -2481,6 +2923,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2481
2923
|
const i = this.userBrushes.findIndex((b) => b.id === options.id);
|
|
2482
2924
|
if (i >= 0) this.userBrushes[i] = saved;
|
|
2483
2925
|
else this.userBrushes.push(saved);
|
|
2926
|
+
this.library = null;
|
|
2484
2927
|
this.emit([]);
|
|
2485
2928
|
return saved;
|
|
2486
2929
|
}
|
|
@@ -2489,6 +2932,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2489
2932
|
const i = this.userBrushes.findIndex((b) => b.id === id);
|
|
2490
2933
|
if (i < 0) throw new PaintEditorError(`Unknown brush "${id}". Call list_brushes for the ids.`);
|
|
2491
2934
|
this.userBrushes.splice(i, 1);
|
|
2935
|
+
this.library = null;
|
|
2492
2936
|
this.emit([]);
|
|
2493
2937
|
}
|
|
2494
2938
|
/** The user's saved brushes. */
|
|
@@ -2529,7 +2973,10 @@ var PaintEditorController = (_class9 = class {
|
|
|
2529
2973
|
else this.userBrushes.push(b);
|
|
2530
2974
|
added.push(b.id);
|
|
2531
2975
|
}
|
|
2532
|
-
if (added.length)
|
|
2976
|
+
if (added.length) {
|
|
2977
|
+
this.library = null;
|
|
2978
|
+
this.emit([]);
|
|
2979
|
+
}
|
|
2533
2980
|
return { added, skipped, problems };
|
|
2534
2981
|
}
|
|
2535
2982
|
/** A thumbnail of a brush: a test stroke on the current paper. */
|
|
@@ -2561,7 +3008,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2561
3008
|
if (options.layerId !== void 0) {
|
|
2562
3009
|
const l = this.requireLayer(options.layerId);
|
|
2563
3010
|
if (!isPixelLayer(l)) throw new PaintEditorError(`Layer "${l.name}" is a group; pick from a pixel layer or the composite.`);
|
|
2564
|
-
const [r, g, b, a] =
|
|
3011
|
+
const [r, g, b, a] = _chunkXIOMMGPDcjs.readPixel.call(void 0, l.surface, px - l.offset.x, py - l.offset.y);
|
|
2565
3012
|
return { color: rgbToHex(r, g, b), alpha: Math.round(a / 255 * 1e3) / 1e3 };
|
|
2566
3013
|
}
|
|
2567
3014
|
const d = this.exportCompositor.composite(this.renderLayers({}), { x: px, y: py, width: 1, height: 1 }, this.sources());
|
|
@@ -2571,13 +3018,13 @@ var PaintEditorController = (_class9 = class {
|
|
|
2571
3018
|
/** Fills the Mixer Brush reservoir with a colour (default the editor colour). */
|
|
2572
3019
|
loadMixerBrush(color) {
|
|
2573
3020
|
const [r, g, b] = hexToRgb8(color === void 0 ? this.color : parseColor(color));
|
|
2574
|
-
this.mixerLoad =
|
|
3021
|
+
this.mixerLoad = _chunk7VOHOPLWcjs.loadMixer.call(void 0, this.mixerLoad, [r / 255, g / 255, b / 255]);
|
|
2575
3022
|
this.emit([]);
|
|
2576
3023
|
return this.mixerLoad;
|
|
2577
3024
|
}
|
|
2578
3025
|
/** Empties the pickup well (and with `emptyReservoir` the loaded paint too). */
|
|
2579
3026
|
cleanMixerBrush(emptyReservoir = false) {
|
|
2580
|
-
this.mixerLoad = emptyReservoir ?
|
|
3027
|
+
this.mixerLoad = emptyReservoir ? _chunk7VOHOPLWcjs.EMPTY_MIXER_LOAD : _chunk7VOHOPLWcjs.cleanMixer.call(void 0, this.mixerLoad);
|
|
2581
3028
|
this.emit([]);
|
|
2582
3029
|
return this.mixerLoad;
|
|
2583
3030
|
}
|
|
@@ -2603,6 +3050,14 @@ var PaintEditorController = (_class9 = class {
|
|
|
2603
3050
|
if (init.brush) brush = normalizePaintBrush({ ...init.brush, id: brush.id }, brush);
|
|
2604
3051
|
return brush;
|
|
2605
3052
|
}
|
|
3053
|
+
/**
|
|
3054
|
+
* After a stroke: a Mixer Brush that loads colour keeps its whole load; any other dirty brush
|
|
3055
|
+
* keeps only the paint it carries (the reservoir stays the Mixer Brush's).
|
|
3056
|
+
*/
|
|
3057
|
+
keepLoad(brush, load) {
|
|
3058
|
+
if (brush.engine === "mixer" && brush.loadsColor) this.mixerLoad = load;
|
|
3059
|
+
else if (brush.behavior.dirty) this.mixerLoad = { ...this.mixerLoad, pickup: load.pickup };
|
|
3060
|
+
}
|
|
2606
3061
|
/** Extra copies every new stroke paints (the painting guides add mirror and kaleidoscope copies). */
|
|
2607
3062
|
strokeCopies() {
|
|
2608
3063
|
return symmetryCopies(this.guides.symmetry);
|
|
@@ -2611,18 +3066,18 @@ var PaintEditorController = (_class9 = class {
|
|
|
2611
3066
|
const layer = (() => {
|
|
2612
3067
|
const id = _nullishCoalesce(init.layerId, () => ( doc.activeLayerId));
|
|
2613
3068
|
if (!id) throw new PaintEditorError("There is no active layer to paint on; add one with add_layer.");
|
|
2614
|
-
const loc =
|
|
3069
|
+
const loc = _chunkPOGBUYWEcjs.findLayer.call(void 0, doc.layers, id);
|
|
2615
3070
|
if (!loc) throw new PaintEditorError(`Unknown layer "${id}". Call list_layers for the ids.`);
|
|
2616
3071
|
const l = loc.layer;
|
|
2617
3072
|
if (!isPixelLayer(l)) throw new PaintEditorError(`Layer "${l.name}" is a group; paint on a pixel layer inside it.`);
|
|
2618
|
-
if (
|
|
3073
|
+
if (_chunkPOGBUYWEcjs.isLayerLocked.call(void 0, l, "pixels")) throw new PaintEditorError(`Layer "${l.name}" has its pixels locked; unlock it (update_layer locks) to paint on it.`);
|
|
2619
3074
|
if (!l.visible) throw new PaintEditorError(`Layer "${l.name}" is hidden; show it (update_layer visible true) to paint on it.`);
|
|
2620
3075
|
return l;
|
|
2621
3076
|
})();
|
|
2622
3077
|
const brush = this.strokeBrush(init);
|
|
2623
3078
|
let color = init.color === void 0 ? this.color : parseColor(init.color);
|
|
2624
3079
|
const tracing = this.guides.tracing;
|
|
2625
|
-
if (init.color === void 0 && init.start && _optionalChain([tracing, 'optionalAccess',
|
|
3080
|
+
if (init.color === void 0 && init.start && _optionalChain([tracing, 'optionalAccess', _67 => _67.visible]) && tracing.sampleColors) color = _nullishCoalesce(this.sampleTracingColor(init.start.x, init.start.y), () => ( color));
|
|
2626
3081
|
const perspective = this.guides.perspective;
|
|
2627
3082
|
const snapper = init.snapToPerspective && perspective.enabled && perspective.snap ? new PerspectiveSnapper(perspective) : null;
|
|
2628
3083
|
const rgba = rgbaOf(color);
|
|
@@ -2632,7 +3087,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2632
3087
|
layerId: layer.id,
|
|
2633
3088
|
surface: layer.surface,
|
|
2634
3089
|
offset: layer.offset,
|
|
2635
|
-
lockTransparency:
|
|
3090
|
+
lockTransparency: _chunkPOGBUYWEcjs.isLayerLocked.call(void 0, layer, "transparency"),
|
|
2636
3091
|
brush,
|
|
2637
3092
|
color: rgba,
|
|
2638
3093
|
background: rgbaOf(doc.paper.color),
|
|
@@ -2643,7 +3098,8 @@ var PaintEditorController = (_class9 = class {
|
|
|
2643
3098
|
affineCopies: copies,
|
|
2644
3099
|
copies: [IDENTITY_AFFINE, ...copies].map(affineCopy),
|
|
2645
3100
|
inputFilter: snapper ? (p) => snapper.snap(p) : void 0,
|
|
2646
|
-
mixerLoad:
|
|
3101
|
+
mixerLoad: _nullishCoalesce(init.load, () => ( brushLoadForStroke(brush, this.mixerLoad, [rgba[0] / 255, rgba[1] / 255, rgba[2] / 255]))),
|
|
3102
|
+
secondary: rgbaOf(this.secondaryColor)
|
|
2647
3103
|
});
|
|
2648
3104
|
return session;
|
|
2649
3105
|
}
|
|
@@ -2658,6 +3114,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2658
3114
|
seed: init.seed
|
|
2659
3115
|
};
|
|
2660
3116
|
if (init.affineCopies.length) stroke.copies = init.affineCopies.map((m) => [...m]);
|
|
3117
|
+
if (init.mixerLoad.reservoir || init.mixerLoad.pickup) stroke.load = JSON.parse(JSON.stringify(init.mixerLoad));
|
|
2661
3118
|
return stroke;
|
|
2662
3119
|
}
|
|
2663
3120
|
/**
|
|
@@ -2676,7 +3133,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2676
3133
|
this.live = null;
|
|
2677
3134
|
this.liveLayers = null;
|
|
2678
3135
|
const brush = s.init.brush;
|
|
2679
|
-
if (!cancelled && _optionalChain([s, 'access',
|
|
3136
|
+
if (!cancelled && _optionalChain([s, 'access', _68 => _68.result, 'optionalAccess', _69 => _69.mixerLoad])) this.keepLoad(brush, s.result.mixerLoad);
|
|
2680
3137
|
if (cancelled || !surface) {
|
|
2681
3138
|
this.emit();
|
|
2682
3139
|
return;
|
|
@@ -2684,7 +3141,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2684
3141
|
const stroke = this.record(s);
|
|
2685
3142
|
s.result.strokeId = stroke.id;
|
|
2686
3143
|
const l = this.getLayer(s.layerId);
|
|
2687
|
-
this.commit(`${brush.name} stroke`, { ...this.doc, layers:
|
|
3144
|
+
this.commit(`${brush.name} stroke`, { ...this.doc, layers: _chunkPOGBUYWEcjs.mapLayer.call(void 0, this.doc.layers, l.id, () => ({ ...l, surface })), strokes: [...this.doc.strokes, stroke] });
|
|
2688
3145
|
}
|
|
2689
3146
|
};
|
|
2690
3147
|
const session = this.sessionFor(this.doc, { snapToPerspective: true, ...init }, host);
|
|
@@ -2706,8 +3163,8 @@ var PaintEditorController = (_class9 = class {
|
|
|
2706
3163
|
if (cancelled || !surface) return;
|
|
2707
3164
|
const stroke = this.record(s);
|
|
2708
3165
|
s.result.strokeId = stroke.id;
|
|
2709
|
-
const l =
|
|
2710
|
-
out = { ...out, layers:
|
|
3166
|
+
const l = _chunkPOGBUYWEcjs.findLayer.call(void 0, out.layers, s.layerId).layer;
|
|
3167
|
+
out = { ...out, layers: _chunkPOGBUYWEcjs.mapLayer.call(void 0, out.layers, l.id, () => ({ ...l, surface })), strokes: [...out.strokes, stroke] };
|
|
2711
3168
|
}
|
|
2712
3169
|
};
|
|
2713
3170
|
const session = this.sessionFor(doc, init, host);
|
|
@@ -2720,7 +3177,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2720
3177
|
this.requireIdle("painting");
|
|
2721
3178
|
const points = this.checkPoints(init.points);
|
|
2722
3179
|
const { doc, result, brush } = this.runStroke(this.doc, { ...init, start: _nullishCoalesce(init.start, () => ( points[0])) }, points);
|
|
2723
|
-
if (result.mixerLoad
|
|
3180
|
+
if (result.mixerLoad) this.keepLoad(brush, result.mixerLoad);
|
|
2724
3181
|
if (doc !== this.doc) this.commit(`${brush.name} stroke`, doc);
|
|
2725
3182
|
else this.emit([]);
|
|
2726
3183
|
return result;
|
|
@@ -2746,7 +3203,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2746
3203
|
*/
|
|
2747
3204
|
replayStrokes(options) {
|
|
2748
3205
|
this.requireIdle("replaying");
|
|
2749
|
-
if (!_optionalChain([options, 'access',
|
|
3206
|
+
if (!_optionalChain([options, 'access', _70 => _70.strokeIds, 'optionalAccess', _71 => _71.length])) throw new PaintEditorError("strokeIds is empty; pass ids from list_strokes.");
|
|
2750
3207
|
const strokes = options.strokeIds.map((id) => {
|
|
2751
3208
|
const s = this.doc.strokes.find((x) => x.id === id);
|
|
2752
3209
|
if (!s) throw new PaintEditorError(`Unknown stroke "${id}". Call list_strokes for the ids.`);
|
|
@@ -2779,8 +3236,13 @@ var PaintEditorController = (_class9 = class {
|
|
|
2779
3236
|
brush = normalizePaintBrush({ tip: { ...b.tip, size: b.tip.size * (v.brush.tip.size / s.brush.tip.size) } }, b);
|
|
2780
3237
|
}
|
|
2781
3238
|
const layerId = _nullishCoalesce(options.layerId, () => ( s.layerId));
|
|
2782
|
-
if (!
|
|
2783
|
-
|
|
3239
|
+
if (!_chunkPOGBUYWEcjs.findLayer.call(void 0, doc.layers, layerId)) throw new PaintEditorError(`Stroke "${s.id}" was painted on layer "${layerId}", which no longer exists; pass layerId to replay it elsewhere.`);
|
|
3240
|
+
let load = s.load;
|
|
3241
|
+
if (load && options.color !== void 0 && load.reservoir) {
|
|
3242
|
+
const [r, g, b] = hexToRgb8(options.color);
|
|
3243
|
+
load = { ...load, reservoir: [r / 255, g / 255, b / 255] };
|
|
3244
|
+
}
|
|
3245
|
+
const run = this.runStroke(doc, { layerId, color: _nullishCoalesce(options.color, () => ( s.color)), brush, seed: s.seed, copies: _nullishCoalesce(v.copies, () => ( [])), ...load && options.brushId === void 0 ? { load } : {} }, v.points);
|
|
2784
3246
|
doc = run.doc;
|
|
2785
3247
|
bounds = unionRect(bounds, run.result.bounds);
|
|
2786
3248
|
if (run.result.strokeId) ids.push(run.result.strokeId);
|
|
@@ -2798,8 +3260,8 @@ var PaintEditorController = (_class9 = class {
|
|
|
2798
3260
|
currentLayers() {
|
|
2799
3261
|
const live = this.live;
|
|
2800
3262
|
if (!live) return this.doc.layers;
|
|
2801
|
-
if (_optionalChain([this, 'access',
|
|
2802
|
-
const layers =
|
|
3263
|
+
if (_optionalChain([this, 'access', _72 => _72.liveLayers, 'optionalAccess', _73 => _73.key]) === live.surface) return this.liveLayers.layers;
|
|
3264
|
+
const layers = _chunkPOGBUYWEcjs.mapLayer.call(void 0, this.doc.layers, live.layerId, (l) => ({ ...l, surface: live.surface }));
|
|
2803
3265
|
this.liveLayers = { key: live.surface, layers };
|
|
2804
3266
|
return layers;
|
|
2805
3267
|
}
|
|
@@ -2813,7 +3275,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2813
3275
|
tracingNode(t) {
|
|
2814
3276
|
const { width, height } = this.doc.canvas;
|
|
2815
3277
|
const key = this.tracingVersion * 100003 + width * 7 + height;
|
|
2816
|
-
if (_optionalChain([this, 'access',
|
|
3278
|
+
if (_optionalChain([this, 'access', _74 => _74.tracingCache, 'optionalAccess', _75 => _75.key]) === key && this.tracingCache.node.opacity === t.opacity) return this.tracingCache.node;
|
|
2817
3279
|
const src = _nullishCoalesce(this.imagePixels.get(t.src), () => ( this.decodeImage(t.src)));
|
|
2818
3280
|
const image = resampleInto(src, t.rect, width, height);
|
|
2819
3281
|
const node = { type: "image", id: TRACING_NODE_ID, name: "Tracing", visible: true, opacity: t.opacity, blend: "normal", image, version: key + Math.round(t.opacity * 1e3) / 1e4 };
|
|
@@ -2838,7 +3300,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2838
3300
|
*/
|
|
2839
3301
|
compositeTiles(rect, onTile, options = {}) {
|
|
2840
3302
|
const dirty = options.dirty === "all" ? void 0 : options.dirty;
|
|
2841
|
-
this.viewCompositor.compositeTiles(this.renderLayers({}, true), rect, this.sources(), onTile, { dirty, checkpointLayerId: _nullishCoalesce(_optionalChain([this, 'access',
|
|
3303
|
+
this.viewCompositor.compositeTiles(this.renderLayers({}, true), rect, this.sources(), onTile, { dirty, checkpointLayerId: _nullishCoalesce(_optionalChain([this, 'access', _76 => _76.live, 'optionalAccess', _77 => _77.layerId]), () => ( this.doc.activeLayerId)) });
|
|
2842
3304
|
}
|
|
2843
3305
|
/** The canvas as canvas-sized RGBA (paper included by default). */
|
|
2844
3306
|
compositePixels(options = {}) {
|
|
@@ -2859,20 +3321,21 @@ var PaintEditorController = (_class9 = class {
|
|
|
2859
3321
|
// --- Save, load and export -------------------------------------------------------
|
|
2860
3322
|
/** The document as JSON with inline PNG assets. */
|
|
2861
3323
|
toJSON(options = {}) {
|
|
2862
|
-
return serializePaintDocument(this.doc, { strokes: options.strokes, extensions: this.extensions, guides: this.guides, swatches: this.swatches });
|
|
3324
|
+
return serializePaintDocument(this.doc, { strokes: options.strokes, extensions: this.extensions, guides: this.guides, swatches: this.swatches, mixingPad: this.mixingPadJSON() });
|
|
2863
3325
|
}
|
|
2864
3326
|
/** The document as JSON with each PNG stored in `store`. */
|
|
2865
3327
|
toJSONExternal(store, options = {}) {
|
|
2866
|
-
return serializePaintDocumentExternal(this.doc, store, { strokes: options.strokes, extensions: this.extensions, guides: this.guides, swatches: this.swatches });
|
|
3328
|
+
return serializePaintDocumentExternal(this.doc, store, { strokes: options.strokes, extensions: this.extensions, guides: this.guides, swatches: this.swatches, mixingPad: this.mixingPadJSON() });
|
|
2867
3329
|
}
|
|
2868
3330
|
adopt(loaded) {
|
|
2869
3331
|
this.extensions = loaded.extensions;
|
|
3332
|
+
this.readMixingPad(loaded.mixingPad, loaded.problems);
|
|
2870
3333
|
this.readGuides(loaded.guides, loaded.state, loaded.problems);
|
|
2871
3334
|
if (Array.isArray(loaded.swatches)) {
|
|
2872
3335
|
this.swatches = loaded.swatches.filter((x) => Boolean(x) && typeof x.color === "string" && /^#[0-9a-f]{6}$/i.test(x.color)).map((x) => typeof x.name === "string" ? { color: x.color.toLowerCase(), name: x.name } : { color: x.color.toLowerCase() });
|
|
2873
3336
|
}
|
|
2874
3337
|
for (const s of loaded.state.strokes) {
|
|
2875
|
-
const n = Number(_nullishCoalesce(_optionalChain([/^s(\d+)$/, 'access',
|
|
3338
|
+
const n = Number(_nullishCoalesce(_optionalChain([/^s(\d+)$/, 'access', _78 => _78.exec, 'call', _79 => _79(s.id), 'optionalAccess', _80 => _80[1]]), () => ( 0)));
|
|
2876
3339
|
if (n >= this.strokeCounter) this.strokeCounter = n + 1;
|
|
2877
3340
|
}
|
|
2878
3341
|
this.undoStack = [];
|
|
@@ -2903,7 +3366,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2903
3366
|
/** The canvas as PNG bytes (paper included by default; `scale` below 1 shrinks it). */
|
|
2904
3367
|
exportPng(options = {}) {
|
|
2905
3368
|
const img = this.scaled(options);
|
|
2906
|
-
return
|
|
3369
|
+
return _chunkPOGBUYWEcjs.encodePng.call(void 0, img.width, img.height, img.data);
|
|
2907
3370
|
}
|
|
2908
3371
|
scaled(options) {
|
|
2909
3372
|
const full = this.compositePixels(options);
|
|
@@ -2945,22 +3408,22 @@ var PaintEditorController = (_class9 = class {
|
|
|
2945
3408
|
opacity: 1,
|
|
2946
3409
|
blend: "normal",
|
|
2947
3410
|
locks: { all: true },
|
|
2948
|
-
surface:
|
|
3411
|
+
surface: _chunkXIOMMGPDcjs.surfaceFromPixels.call(void 0, paperPixels, 4),
|
|
2949
3412
|
offset: { x: 0, y: 0 }
|
|
2950
3413
|
};
|
|
2951
3414
|
const merged = this.compositePixels({ paper: true }).data;
|
|
2952
3415
|
const changed = /* @__PURE__ */ new Map();
|
|
2953
3416
|
const toStandard = (layers2) => layers2.map((l) => {
|
|
2954
|
-
const blend = l.blend === "pass-through" ? l.blend :
|
|
3417
|
+
const blend = l.blend === "pass-through" ? l.blend : _chunkXIOMMGPDcjs.standardBlendMode.call(void 0, l.blend);
|
|
2955
3418
|
if (blend !== l.blend) changed.set(l.blend, [..._nullishCoalesce(changed.get(l.blend), () => ( [])), l.name]);
|
|
2956
3419
|
const next = blend === l.blend ? l : { ...l, blend };
|
|
2957
3420
|
return next.type === "group" ? { ...next, children: toStandard(next.children) } : next;
|
|
2958
3421
|
});
|
|
2959
3422
|
const layers = toStandard(this.doc.layers);
|
|
2960
|
-
const result =
|
|
3423
|
+
const result = _chunkMGL4U5WAcjs.exportPsd.call(void 0, { width, height, layers: [paperLayer, ...layers], merged, resolution: dpi });
|
|
2961
3424
|
for (const [mode, names] of changed) {
|
|
2962
|
-
const label = _nullishCoalesce(_optionalChain([
|
|
2963
|
-
const std = _nullishCoalesce(_optionalChain([
|
|
3425
|
+
const label = _nullishCoalesce(_optionalChain([_chunkXIOMMGPDcjs.BLEND_MODES, 'access', _81 => _81.find, 'call', _82 => _82((m) => m.id === mode), 'optionalAccess', _83 => _83.label]), () => ( mode));
|
|
3426
|
+
const std = _nullishCoalesce(_optionalChain([_chunkXIOMMGPDcjs.BLEND_MODES, 'access', _84 => _84.find, 'call', _85 => _85((m) => m.id === _chunkXIOMMGPDcjs.standardBlendMode.call(void 0, mode)), 'optionalAccess', _86 => _86.label]), () => ( _chunkXIOMMGPDcjs.standardBlendMode.call(void 0, mode)));
|
|
2964
3427
|
const list = names.map((n) => `"${n}"`).join(", ");
|
|
2965
3428
|
const one = names.length === 1;
|
|
2966
3429
|
result.warnings.push(`${label} blending has no PSD equivalent; ${list} ${one ? "was" : "were"} written as ${std}, the nearest Photoshop mode, so ${one ? "it looks" : "they look"} different in Photoshop (the merged image is exact).`);
|
|
@@ -2970,7 +3433,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
2970
3433
|
/** `render_preview` as a base64 PNG. */
|
|
2971
3434
|
previewPngBase64(options = {}) {
|
|
2972
3435
|
const img = this.renderPreview(options);
|
|
2973
|
-
return { data:
|
|
3436
|
+
return { data: _chunkPOGBUYWEcjs.bytesToBase64.call(void 0, _chunkPOGBUYWEcjs.encodePng.call(void 0, img.width, img.height, img.data)), width: img.width, height: img.height };
|
|
2974
3437
|
}
|
|
2975
3438
|
// --- Guides --------------------------------------------------------------------
|
|
2976
3439
|
setGuides(next) {
|
|
@@ -3008,17 +3471,17 @@ var PaintEditorController = (_class9 = class {
|
|
|
3008
3471
|
pixels = this.decodeImage(input.png.trim().startsWith("data:") ? input.png.trim() : `data:image/png;base64,${input.png.trim()}`, what);
|
|
3009
3472
|
} else throw new PaintEditorError(`${what}: pass png (a PNG as base64 or a data URL) or pixels.`);
|
|
3010
3473
|
if (pixels.width > MAX_IMAGE_SIDE || pixels.height > MAX_IMAGE_SIDE) throw new PaintEditorError(`${what} is ${pixels.width}\xD7${pixels.height}; the limit is ${MAX_IMAGE_SIDE} px per side.`);
|
|
3011
|
-
const src =
|
|
3474
|
+
const src = _chunkPOGBUYWEcjs.pngDataUrl.call(void 0, pixels.width, pixels.height, pixels.data);
|
|
3012
3475
|
this.imagePixels.set(src, pixels);
|
|
3013
3476
|
return { src, pixels };
|
|
3014
3477
|
}
|
|
3015
3478
|
decodeImage(src, what = "Image") {
|
|
3016
3479
|
const cached = this.imagePixels.get(src);
|
|
3017
3480
|
if (cached) return cached;
|
|
3018
|
-
const parsed =
|
|
3481
|
+
const parsed = _chunkPOGBUYWEcjs.parseDataUrl.call(void 0, src);
|
|
3019
3482
|
if (!parsed) throw new PaintEditorError(`${what} is not a data URL or base64 PNG.`);
|
|
3020
3483
|
try {
|
|
3021
|
-
const png =
|
|
3484
|
+
const png = _chunkPOGBUYWEcjs.decodePng.call(void 0, parsed.bytes);
|
|
3022
3485
|
const px = { width: png.width, height: png.height, data: png.data };
|
|
3023
3486
|
this.imagePixels.set(src, px);
|
|
3024
3487
|
return px;
|
|
@@ -3076,7 +3539,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
3076
3539
|
const { src, pixels } = this.importImage(options, "The reference image");
|
|
3077
3540
|
const ref = {
|
|
3078
3541
|
id: `ref${this.nextReferenceId++}`,
|
|
3079
|
-
name: _optionalChain([options, 'access',
|
|
3542
|
+
name: _optionalChain([options, 'access', _87 => _87.name, 'optionalAccess', _88 => _88.trim, 'call', _89 => _89()]) || `Reference ${this.guides.references.length + 1}`,
|
|
3080
3543
|
src,
|
|
3081
3544
|
width: pixels.width,
|
|
3082
3545
|
height: pixels.height,
|
|
@@ -3155,7 +3618,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
3155
3618
|
try {
|
|
3156
3619
|
const px = this.decodeImage(ref.src, `Reference "${ref.name}"`);
|
|
3157
3620
|
guides.references.push({ id: ref.id, name: String(_nullishCoalesce(ref.name, () => ( ref.id))), src: ref.src, width: px.width, height: px.height, x: Number(ref.x) || 0, y: Number(ref.y) || 0, displayWidth: Number(ref.displayWidth) || 240, visible: ref.visible !== false });
|
|
3158
|
-
const n = Number(_nullishCoalesce(_optionalChain([/^ref(\d+)$/, 'access',
|
|
3621
|
+
const n = Number(_nullishCoalesce(_optionalChain([/^ref(\d+)$/, 'access', _90 => _90.exec, 'call', _91 => _91(ref.id), 'optionalAccess', _92 => _92[1]]), () => ( 0)));
|
|
3159
3622
|
if (n >= this.nextReferenceId) this.nextReferenceId = n + 1;
|
|
3160
3623
|
} catch (e) {
|
|
3161
3624
|
problems.push(e.message);
|
|
@@ -3175,7 +3638,7 @@ var PaintEditorController = (_class9 = class {
|
|
|
3175
3638
|
}
|
|
3176
3639
|
addSwatch(color, name) {
|
|
3177
3640
|
const c = parseColor(color);
|
|
3178
|
-
this.swatches = [...this.swatches, _optionalChain([name, 'optionalAccess',
|
|
3641
|
+
this.swatches = [...this.swatches, _optionalChain([name, 'optionalAccess', _93 => _93.trim, 'call', _94 => _94()]) ? { color: c, name: name.trim() } : { color: c }];
|
|
3179
3642
|
this.emit([]);
|
|
3180
3643
|
return this.swatches;
|
|
3181
3644
|
}
|
|
@@ -3221,12 +3684,105 @@ var PaintEditorController = (_class9 = class {
|
|
|
3221
3684
|
this.pad.clear();
|
|
3222
3685
|
this.emit([]);
|
|
3223
3686
|
}
|
|
3687
|
+
/**
|
|
3688
|
+
* Mixes paints on the pad like a painter: blobs in proportion to their `parts`, knife passes,
|
|
3689
|
+
* and a fully mixed core of the Kubelka-Munk mix (see `MixingPad.mix`). With `setColor` the
|
|
3690
|
+
* mix becomes the paint colour. Returns the mixed colour and the pile's bounds.
|
|
3691
|
+
*/
|
|
3692
|
+
mixOnPad(paints, options = {}) {
|
|
3693
|
+
const parsed = (_nullishCoalesce(paints, () => ( []))).map((p, i) => ({ color: parseColor(_optionalChain([p, 'optionalAccess', _95 => _95.color]), `paints[${i}].color`), parts: _optionalChain([p, 'optionalAccess', _96 => _96.parts]) }));
|
|
3694
|
+
let r;
|
|
3695
|
+
try {
|
|
3696
|
+
r = this.pad.mix(parsed, options);
|
|
3697
|
+
} catch (e) {
|
|
3698
|
+
throw new PaintEditorError(e.message);
|
|
3699
|
+
}
|
|
3700
|
+
const color = _nullishCoalesce(r.color, () => ( parsed[0].color));
|
|
3701
|
+
this.emit([]);
|
|
3702
|
+
if (options.setColor) this.setColor(color);
|
|
3703
|
+
return { color, bounds: r.bounds };
|
|
3704
|
+
}
|
|
3705
|
+
/** The mixing pad as a PNG data URL (with alpha). */
|
|
3706
|
+
mixingPadPng() {
|
|
3707
|
+
return this.pad.toPng();
|
|
3708
|
+
}
|
|
3709
|
+
/** Replaces the mixing pad with a PNG (data URL or base64, 360 × 220). */
|
|
3710
|
+
setMixingPadPng(png) {
|
|
3711
|
+
try {
|
|
3712
|
+
this.pad.fromPng(png);
|
|
3713
|
+
} catch (e) {
|
|
3714
|
+
throw new PaintEditorError(e.message);
|
|
3715
|
+
}
|
|
3716
|
+
this.emit([]);
|
|
3717
|
+
}
|
|
3718
|
+
/** The mixing pads kept by name. */
|
|
3719
|
+
listMixingPads() {
|
|
3720
|
+
return this.savedPads.map(({ id, name }) => ({ id, name }));
|
|
3721
|
+
}
|
|
3722
|
+
/** Keeps the mixing pad as it is now under `name` (saved with the document). Returns its id. */
|
|
3723
|
+
saveMixingPad(name) {
|
|
3724
|
+
if (this.pad.empty) throw new PaintEditorError("The mixing pad is empty; put paint on it (mixing_pad_stroke or mix_on_pad) before saving it.");
|
|
3725
|
+
const label = (typeof name === "string" && name.trim() ? name.trim() : `Pad ${this.nextPadId}`).slice(0, 80);
|
|
3726
|
+
const pad = { id: `pad${this.nextPadId++}`, name: label, png: this.pad.toPng() };
|
|
3727
|
+
this.savedPads.push(pad);
|
|
3728
|
+
this.emit([]);
|
|
3729
|
+
return { id: pad.id, name: pad.name };
|
|
3730
|
+
}
|
|
3731
|
+
requirePad(id) {
|
|
3732
|
+
const p = this.savedPads.find((x) => x.id === id);
|
|
3733
|
+
if (!p) throw new PaintEditorError(`Unknown mixing pad "${id}". Call list_mixing_pads for the ids.`);
|
|
3734
|
+
return p;
|
|
3735
|
+
}
|
|
3736
|
+
/** Puts a kept pad back on the mixing pad (replacing what is there). */
|
|
3737
|
+
loadMixingPad(id) {
|
|
3738
|
+
const p = this.requirePad(id);
|
|
3739
|
+
this.setMixingPadPng(p.png);
|
|
3740
|
+
return { id: p.id, name: p.name };
|
|
3741
|
+
}
|
|
3742
|
+
/** Forgets a kept pad. */
|
|
3743
|
+
removeMixingPad(id) {
|
|
3744
|
+
const p = this.requirePad(id);
|
|
3745
|
+
this.savedPads = this.savedPads.filter((x) => x !== p);
|
|
3746
|
+
this.emit([]);
|
|
3747
|
+
return { id: p.id, name: p.name };
|
|
3748
|
+
}
|
|
3749
|
+
mixingPadJSON() {
|
|
3750
|
+
if (this.pad.empty && !this.savedPads.length) return void 0;
|
|
3751
|
+
return { ...this.pad.empty ? {} : { current: this.pad.toPng() }, saved: this.savedPads.map((p) => ({ ...p })) };
|
|
3752
|
+
}
|
|
3753
|
+
readMixingPad(raw, problems) {
|
|
3754
|
+
this.savedPads = [];
|
|
3755
|
+
this.nextPadId = 1;
|
|
3756
|
+
this.pad.clear();
|
|
3757
|
+
if (raw === void 0 || raw === null) return;
|
|
3758
|
+
const r = raw;
|
|
3759
|
+
if (typeof r.current === "string") {
|
|
3760
|
+
try {
|
|
3761
|
+
this.pad.fromPng(r.current);
|
|
3762
|
+
} catch (e) {
|
|
3763
|
+
problems.push(`The mixing pad could not be read (${e.message}); it starts empty.`);
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
if (Array.isArray(r.saved)) {
|
|
3767
|
+
r.saved.forEach((p, i) => {
|
|
3768
|
+
const x = p;
|
|
3769
|
+
if (!x || typeof x.png !== "string" || typeof x.name !== "string") {
|
|
3770
|
+
problems.push(`mixingPad.saved[${i}] is not a saved pad; skipped.`);
|
|
3771
|
+
return;
|
|
3772
|
+
}
|
|
3773
|
+
const id = typeof x.id === "string" && /^pad\d+$/.test(x.id) ? x.id : `pad${this.nextPadId}`;
|
|
3774
|
+
const n = Number(id.slice(3));
|
|
3775
|
+
if (n >= this.nextPadId) this.nextPadId = n + 1;
|
|
3776
|
+
this.savedPads.push({ id, name: x.name, png: x.png });
|
|
3777
|
+
});
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3224
3780
|
/** A layer's tile surface (live while stroking), for thumbnails. */
|
|
3225
3781
|
layerSurface(id) {
|
|
3226
|
-
const l = _optionalChain([
|
|
3782
|
+
const l = _optionalChain([_chunkPOGBUYWEcjs.findLayer.call(void 0, this.currentLayers(), id), 'optionalAccess', _97 => _97.layer]);
|
|
3227
3783
|
return isPixelLayer(l) ? l.surface : null;
|
|
3228
3784
|
}
|
|
3229
|
-
},
|
|
3785
|
+
}, _class10);
|
|
3230
3786
|
var MAX_IMAGE_SIDE = 8192;
|
|
3231
3787
|
function resampleInto(src, rect, width, height) {
|
|
3232
3788
|
const out = new Uint8ClampedArray(width * height * 4);
|
|
@@ -3277,8 +3833,8 @@ function strokeSchedule(points) {
|
|
|
3277
3833
|
return total > 0 ? acc.map((d) => d / total) : points.map(() => 1);
|
|
3278
3834
|
}
|
|
3279
3835
|
function strokeDuration(points, max = 1800) {
|
|
3280
|
-
const t0 = _optionalChain([points, 'access',
|
|
3281
|
-
const t1 = _optionalChain([points, 'access',
|
|
3836
|
+
const t0 = _optionalChain([points, 'access', _98 => _98[0], 'optionalAccess', _99 => _99.t]);
|
|
3837
|
+
const t1 = _optionalChain([points, 'access', _100 => _100[points.length - 1], 'optionalAccess', _101 => _101.t]);
|
|
3282
3838
|
let ms;
|
|
3283
3839
|
if (points.every((p) => Number.isFinite(p.t)) && t1 > t0) ms = t1 - t0;
|
|
3284
3840
|
else {
|
|
@@ -3332,7 +3888,7 @@ var PaintDirector = class {
|
|
|
3332
3888
|
}
|
|
3333
3889
|
where(id) {
|
|
3334
3890
|
const a = this.presence.get(id);
|
|
3335
|
-
return { x: _nullishCoalesce(_optionalChain([a, 'optionalAccess',
|
|
3891
|
+
return { x: _nullishCoalesce(_optionalChain([a, 'optionalAccess', _102 => _102.x]), () => ( 0)), y: _nullishCoalesce(_optionalChain([a, 'optionalAccess', _103 => _103.y]), () => ( 0)) };
|
|
3336
3892
|
}
|
|
3337
3893
|
/** Glides to `to`; an actor that just appeared starts there instead of the corner. */
|
|
3338
3894
|
async glide(id, to, ms = 380) {
|
|
@@ -3379,8 +3935,8 @@ var PaintDirector = class {
|
|
|
3379
3935
|
}
|
|
3380
3936
|
case "replay": {
|
|
3381
3937
|
await this.say(id, command.note, 500);
|
|
3382
|
-
const source = c.getStrokes().filter((s) => _optionalChain([command, 'access',
|
|
3383
|
-
const first = _optionalChain([source, 'access',
|
|
3938
|
+
const source = c.getStrokes().filter((s) => _optionalChain([command, 'access', _104 => _104.replay, 'access', _105 => _105.strokeIds, 'optionalAccess', _106 => _106.includes, 'call', _107 => _107(s.id)]));
|
|
3939
|
+
const first = _optionalChain([source, 'access', _108 => _108[0], 'optionalAccess', _109 => _109.points, 'access', _110 => _110[0]]);
|
|
3384
3940
|
if (first) await this.glide(id, first);
|
|
3385
3941
|
const result = c.replayStrokes(command.replay);
|
|
3386
3942
|
if (result.bounds) await this.glide(id, centre(result.bounds), 520);
|
|
@@ -3678,7 +4234,7 @@ function createPaintGuideTools(c) {
|
|
|
3678
4234
|
{
|
|
3679
4235
|
name: "mixing_pad_stroke",
|
|
3680
4236
|
title: "Paint on the mixing pad",
|
|
3681
|
-
description: "Paints on the mixing pad (a 360 \xD7 220 scratch canvas, not the painting): tool paint lays down a colour with a pigment Mixer Brush; tool knife smears what is there like a palette knife, mixing it as paint. Points are pad pixels. Not an undo step. Returns the changed bounds; then pick_mixing_pad_color.",
|
|
4237
|
+
description: "Paints on the mixing pad (a 360 \xD7 220 scratch canvas, not the painting): tool paint lays down a colour with a pigment Mixer Brush; tool knife smears what is there like a palette knife, mixing it as paint. Points are pad pixels. Not an undo step. Returns the changed bounds; then pick_mixing_pad_color. For a measured mix in one call use mix_on_pad.",
|
|
3682
4238
|
inputSchema: {
|
|
3683
4239
|
type: "object",
|
|
3684
4240
|
properties: {
|
|
@@ -3733,6 +4289,73 @@ function createPaintGuideTools(c) {
|
|
|
3733
4289
|
return { cleared: true };
|
|
3734
4290
|
}
|
|
3735
4291
|
},
|
|
4292
|
+
{
|
|
4293
|
+
name: "mix_on_pad",
|
|
4294
|
+
title: "Mix paints on the pad",
|
|
4295
|
+
description: "Mixes paints on the mixing pad as a painter would: blobs whose areas follow their parts, worked with the palette knife, and a fully mixed core of the Kubelka-Munk (pigment) mix, so blue and yellow make green and white tints. One call per mix. Not an undo step (the pad is scratch space). Returns the mixed colour #rrggbb and the pile bounds in pad pixels; with setColor it becomes the paint colour. pick_mixing_pad_color reads the streaks around it.",
|
|
4296
|
+
inputSchema: {
|
|
4297
|
+
type: "object",
|
|
4298
|
+
properties: {
|
|
4299
|
+
paints: {
|
|
4300
|
+
type: "array",
|
|
4301
|
+
minItems: 1,
|
|
4302
|
+
maxItems: 8,
|
|
4303
|
+
description: "The paints, 1 to 8.",
|
|
4304
|
+
items: {
|
|
4305
|
+
type: "object",
|
|
4306
|
+
properties: {
|
|
4307
|
+
color: { ...COLOR, description: "Paint colour #rrggbb." },
|
|
4308
|
+
parts: { type: "number", minimum: 0, description: "Parts of this paint, any scale (1 and 3 for a 1:3 mix); at least one above 0." }
|
|
4309
|
+
},
|
|
4310
|
+
required: ["color", "parts"],
|
|
4311
|
+
additionalProperties: false
|
|
4312
|
+
}
|
|
4313
|
+
},
|
|
4314
|
+
x: { type: "number", minimum: 0, maximum: 360, description: "Centre of the pile, pad x (default 180)." },
|
|
4315
|
+
y: { type: "number", minimum: 0, maximum: 220, description: "Centre of the pile, pad y (default 110)." },
|
|
4316
|
+
radius: { type: "number", minimum: 12, maximum: 100, description: "Radius of the pile in pad pixels (default 40); it must fit on the pad." },
|
|
4317
|
+
passes: { type: "integer", minimum: 1, maximum: 20, description: "Palette-knife passes (default 3; more gives finer streaks around the core)." },
|
|
4318
|
+
clear: { type: "boolean", description: "Wipe the pile's area first (default true); false mixes into the paint already there." },
|
|
4319
|
+
setColor: { type: "boolean", description: "Also make the mix the paint colour (default false)." }
|
|
4320
|
+
},
|
|
4321
|
+
required: ["paints"],
|
|
4322
|
+
additionalProperties: false
|
|
4323
|
+
},
|
|
4324
|
+
annotations: { title: "Mix paints on the pad", ...WRITE },
|
|
4325
|
+
handler: ({ paints, x, y, radius, passes, clear, setColor }) => c.mixOnPad(paints, { x, y, radius, passes, clear, setColor })
|
|
4326
|
+
},
|
|
4327
|
+
{
|
|
4328
|
+
name: "save_mixing_pad",
|
|
4329
|
+
title: "Keep the mixing pad",
|
|
4330
|
+
description: "Keeps the mixing pad as it is now under a name, saved with the document, to come back to with load_mixing_pad. Refused while the pad is empty. Returns the kept pad id and name.",
|
|
4331
|
+
inputSchema: { type: "object", properties: { name: { type: "string", minLength: 1, maxLength: 80, description: 'Name (default "Pad n").' } }, additionalProperties: false },
|
|
4332
|
+
annotations: { title: "Keep the mixing pad", ...WRITE },
|
|
4333
|
+
handler: ({ name }) => c.saveMixingPad(name)
|
|
4334
|
+
},
|
|
4335
|
+
{
|
|
4336
|
+
name: "list_mixing_pads",
|
|
4337
|
+
title: "List kept mixing pads",
|
|
4338
|
+
description: "Lists the mixing pads kept with save_mixing_pad (id, name). Changes nothing.",
|
|
4339
|
+
inputSchema: EMPTY,
|
|
4340
|
+
annotations: { title: "List kept mixing pads", ...READ },
|
|
4341
|
+
handler: () => ({ pads: c.listMixingPads() })
|
|
4342
|
+
},
|
|
4343
|
+
{
|
|
4344
|
+
name: "load_mixing_pad",
|
|
4345
|
+
title: "Load a kept mixing pad",
|
|
4346
|
+
description: "Puts a kept pad (id from list_mixing_pads) back on the mixing pad, replacing what is on it. Returns the pad id and name.",
|
|
4347
|
+
inputSchema: { type: "object", properties: { id: { type: "string", description: "Kept pad id from list_mixing_pads." } }, required: ["id"], additionalProperties: false },
|
|
4348
|
+
annotations: { title: "Load a kept mixing pad", ...DESTRUCTIVE },
|
|
4349
|
+
handler: ({ id }) => c.loadMixingPad(id)
|
|
4350
|
+
},
|
|
4351
|
+
{
|
|
4352
|
+
name: "remove_mixing_pad",
|
|
4353
|
+
title: "Remove a kept mixing pad",
|
|
4354
|
+
description: "Forgets a kept pad (id from list_mixing_pads); the pad on screen is untouched. Returns the removed id and name.",
|
|
4355
|
+
inputSchema: { type: "object", properties: { id: { type: "string", description: "Kept pad id from list_mixing_pads." } }, required: ["id"], additionalProperties: false },
|
|
4356
|
+
annotations: { title: "Remove a kept mixing pad", ...DESTRUCTIVE },
|
|
4357
|
+
handler: ({ id }) => c.removeMixingPad(id)
|
|
4358
|
+
},
|
|
3736
4359
|
{
|
|
3737
4360
|
name: "render_mixing_pad",
|
|
3738
4361
|
title: "Render the mixing pad",
|
|
@@ -3743,7 +4366,7 @@ function createPaintGuideTools(c) {
|
|
|
3743
4366
|
const pad = c.mixingPad;
|
|
3744
4367
|
return {
|
|
3745
4368
|
content: [
|
|
3746
|
-
{ type: "image", data:
|
|
4369
|
+
{ type: "image", data: _chunkPOGBUYWEcjs.bytesToBase64.call(void 0, _chunkPOGBUYWEcjs.encodePng.call(void 0, pad.width, pad.height, pad.pixels())), mimeType: "image/png" },
|
|
3747
4370
|
{ type: "text", text: `Mixing pad ${pad.width}\xD7${pad.height}` }
|
|
3748
4371
|
]
|
|
3749
4372
|
};
|
|
@@ -3764,7 +4387,7 @@ var TIP = {
|
|
|
3764
4387
|
type: "object",
|
|
3765
4388
|
description: "The tip (the shared brush engine). Missing fields keep their current values.",
|
|
3766
4389
|
properties: {
|
|
3767
|
-
size: { type: "number", minimum: 1, maximum:
|
|
4390
|
+
size: { type: "number", minimum: 1, maximum: _chunk7VOHOPLWcjs.MAX_BRUSH_SIZE, description: "Diameter in document pixels." },
|
|
3768
4391
|
hardness: { type: "number", minimum: 0, maximum: 1, description: "Edge hardness, 0 (soft) .. 1 (hard)." },
|
|
3769
4392
|
spacing: { type: "number", minimum: 0.01, maximum: 10, description: "Dab spacing as a fraction of the diameter (0.05 is smooth, 1 is dotted)." },
|
|
3770
4393
|
angle: { type: "number", minimum: -360, maximum: 360, description: "Tip angle in degrees, counter-clockwise; with rotation stroke or barrel it is added to the stroke direction or pen rotation." },
|
|
@@ -3776,7 +4399,7 @@ var TIP = {
|
|
|
3776
4399
|
pressureOpacity: { type: "boolean", description: "Pressure scales the opacity." },
|
|
3777
4400
|
blend: {
|
|
3778
4401
|
type: "string",
|
|
3779
|
-
enum:
|
|
4402
|
+
enum: _chunkXIOMMGPDcjs.BLEND_MODES.map((m) => m.id),
|
|
3780
4403
|
description: "How paint combines with the layer. pigment mixes it in like real paint (blue into yellow gives green); multiply darkens like marker ink; watercolor glazes like a transparent wash; tint, highlight and shadow recolour, light and shade (see list_blend_modes)."
|
|
3781
4404
|
}
|
|
3782
4405
|
},
|
|
@@ -3802,28 +4425,79 @@ var DYNAMICS = {
|
|
|
3802
4425
|
};
|
|
3803
4426
|
var MIXER = {
|
|
3804
4427
|
type: "object",
|
|
3805
|
-
description: "Mixer engine settings (Mixer Brush
|
|
4428
|
+
description: "Mixer engine settings (the Mixer Brush). Missing fields keep their current values.",
|
|
3806
4429
|
properties: {
|
|
3807
|
-
wet: { type: "number", minimum: 0, maximum: 1, description: "
|
|
4430
|
+
wet: { type: "number", minimum: 0, maximum: 1, description: "Pick-up ratio: how much canvas colour each dab picks up, 0 (dry) .. 1." },
|
|
3808
4431
|
load: { type: "number", minimum: 0, maximum: 1, description: "How much paint the reservoir holds; below 1 it drains per dab, so long strokes run dry." },
|
|
3809
|
-
mix: { type: "number", minimum: 0, maximum: 1, description: "
|
|
4432
|
+
mix: { type: "number", minimum: 0, maximum: 1, description: "Mix ratio: how far each dab's paint moves from the reservoir towards the picked-up colour." },
|
|
3810
4433
|
loadEachStroke: { type: "boolean", description: "Refill the reservoir with the colour before each stroke." },
|
|
3811
4434
|
cleanEachStroke: { type: "boolean", description: "Empty the pickup well after each stroke." },
|
|
3812
|
-
mixing: { type: "string", enum: [...
|
|
4435
|
+
mixing: { type: "string", enum: [..._chunk7VOHOPLWcjs.MIXING_MODELS], description: "rgb: RGB averages. pigment: mixes like paint (Kubelka-Munk)." }
|
|
4436
|
+
},
|
|
4437
|
+
additionalProperties: false
|
|
4438
|
+
};
|
|
4439
|
+
var MIXING = { type: "string", enum: [..._chunk7VOHOPLWcjs.MIXING_MODELS], description: "pigment: mixes like paint (Kubelka-Munk, blue and yellow make green); rgb: straight RGB averages." };
|
|
4440
|
+
var BEHAVIOR = {
|
|
4441
|
+
type: "object",
|
|
4442
|
+
description: "How the brush handles paint on the canvas (dab and mixer engines; on a mixer brush pickup, mix, dirty and mixing are its mixer wet, mix, !cleanEachStroke and mixing). Missing fields keep their current values.",
|
|
4443
|
+
properties: {
|
|
4444
|
+
pickup: { type: "number", minimum: 0, maximum: 1, description: "Pick-up ratio 0..1: how much of the paint under each dab the brush lifts and carries along (0: none, a dry brush)." },
|
|
4445
|
+
mix: { type: "number", minimum: 0, maximum: 1, description: "Mix ratio 0..1: how much of the carried paint goes into what the brush lays down, against the brush's own colour (scaled by pickup)." },
|
|
4446
|
+
dirty: { type: "boolean", description: "Dirty brush: keep the carried paint after the stroke, so the next stroke starts with the last colour it touched (needs pickup above 0). clean_mixer_brush wipes it." },
|
|
4447
|
+
mixing: MIXING,
|
|
4448
|
+
multicolor: {
|
|
4449
|
+
type: "object",
|
|
4450
|
+
description: "Multi-coloured brush. Missing fields keep their current values.",
|
|
4451
|
+
properties: {
|
|
4452
|
+
mode: { type: "string", enum: [...MULTI_COLOR_MODES], description: "off; tip: the colours lie side by side across the tip (stripes along the stroke, like a brush loaded with several paints); dab: the colour changes along the stroke." },
|
|
4453
|
+
colors: { type: "array", maxItems: 8, items: { ...COLOR2 }, description: "2..8 colours #rrggbb; fewer than 2 uses the paint colour and the secondary colour." },
|
|
4454
|
+
order: { type: "string", enum: ["sequence", "random"], description: "dab mode: sequence runs through the colours in order, blending between them; random picks one per run of dabs (seeded)." },
|
|
4455
|
+
length: { type: "integer", minimum: 1, maximum: 500, description: "dab mode: dabs per colour (default 12)." },
|
|
4456
|
+
softness: { type: "number", minimum: 0, maximum: 1, description: "How softly neighbouring colours blend: 0 hard stripes or steps, 1 a smooth gradient." }
|
|
4457
|
+
},
|
|
4458
|
+
additionalProperties: false
|
|
4459
|
+
}
|
|
4460
|
+
},
|
|
4461
|
+
additionalProperties: false
|
|
4462
|
+
};
|
|
4463
|
+
var BLENDER = {
|
|
4464
|
+
type: "object",
|
|
4465
|
+
description: "Blender engine settings. Missing fields keep their current values.",
|
|
4466
|
+
properties: {
|
|
4467
|
+
strength: { type: "number", minimum: 0, maximum: 1, description: "How far each pixel under the tip moves towards the mix of the paint near it, per dab." },
|
|
4468
|
+
distance: { type: "number", minimum: 0, maximum: 1, description: "Blend distance: how far away colours are mixed in, as a fraction of the tip diameter (0.35 default; higher blends wider and softer)." },
|
|
4469
|
+
mixing: MIXING
|
|
4470
|
+
},
|
|
4471
|
+
additionalProperties: false
|
|
4472
|
+
};
|
|
4473
|
+
var SMUDGE = {
|
|
4474
|
+
type: "object",
|
|
4475
|
+
description: "Smudge engine settings. Missing fields keep their current values.",
|
|
4476
|
+
properties: {
|
|
4477
|
+
strength: { type: "number", minimum: 0, maximum: 1, description: "How strongly the dragged paint covers what is under the tip, per dab." },
|
|
4478
|
+
length: { type: "number", minimum: 0, maximum: 1, description: "How far paint is dragged: the share of the carried paint kept from dab to dab (0.85 default; 1 drags it the whole stroke)." },
|
|
4479
|
+
mixing: { ...MIXING, description: "rgb (default): smeared colours average; pigment: they mix like paint." }
|
|
3813
4480
|
},
|
|
3814
4481
|
additionalProperties: false
|
|
3815
4482
|
};
|
|
3816
4483
|
var BRUSH_FIELDS = {
|
|
3817
4484
|
name: { type: "string", minLength: 1, maxLength: 80, description: "Display name." },
|
|
3818
4485
|
category: { type: "string", enum: [...PAINT_BRUSH_CATEGORIES], description: "Library category." },
|
|
3819
|
-
engine: {
|
|
4486
|
+
engine: {
|
|
4487
|
+
type: "string",
|
|
4488
|
+
enum: [...PAINT_BRUSH_ENGINES],
|
|
4489
|
+
description: "dab: paints dabs (with behavior.pickup or multicolor it paints dab by dab, lifting and mixing paint); eraser: removes paint; mixer: the Mixer Brush (a reservoir plus picked-up paint); blender: mixes the colours under and near the tip; smudge: drags paint along the stroke."
|
|
4490
|
+
},
|
|
3820
4491
|
tip: TIP,
|
|
3821
4492
|
grain: { type: "number", minimum: 0, maximum: 1, description: "Paper grain response: 0 ignores the grain, 1 catches only its peaks at light pressure (pencil, pastel, charcoal). Multiplied by the paper depth." },
|
|
3822
4493
|
pressureSmoothing: { type: "number", minimum: 0, maximum: 0.95, description: "Pressure smoothing, separate from the tip position smoothing." },
|
|
3823
4494
|
rotation: { type: "string", enum: [...BRUSH_ROTATIONS], description: "What turns the tip: fixed (tip.angle), stroke (follows the direction of travel) or barrel (the pen barrel rotation, twist)." },
|
|
3824
4495
|
dynamics: DYNAMICS,
|
|
3825
4496
|
mixer: MIXER,
|
|
3826
|
-
loadsColor: { type: "boolean", description: "Mixer engine: load the colour each stroke (false for
|
|
4497
|
+
loadsColor: { type: "boolean", description: "Mixer engine: load the colour each stroke (false for a mixer that only moves paint)." },
|
|
4498
|
+
behavior: BEHAVIOR,
|
|
4499
|
+
blender: BLENDER,
|
|
4500
|
+
smudge: SMUDGE
|
|
3827
4501
|
};
|
|
3828
4502
|
var POINTS = {
|
|
3829
4503
|
type: "array",
|
|
@@ -3852,7 +4526,20 @@ function loadResult(load) {
|
|
|
3852
4526
|
return { reservoir: hex(load.reservoir), amount: Math.round(load.amount * 1e3) / 1e3, pickup: hex(load.pickup) };
|
|
3853
4527
|
}
|
|
3854
4528
|
function brushSummary(b) {
|
|
3855
|
-
return {
|
|
4529
|
+
return {
|
|
4530
|
+
id: b.id,
|
|
4531
|
+
name: b.name,
|
|
4532
|
+
category: b.category,
|
|
4533
|
+
engine: b.engine,
|
|
4534
|
+
size: b.tip.size,
|
|
4535
|
+
grain: b.grain,
|
|
4536
|
+
rotation: b.rotation,
|
|
4537
|
+
wetCapable: isWetCapable(b),
|
|
4538
|
+
pickup: b.behavior.pickup,
|
|
4539
|
+
dirty: b.behavior.dirty,
|
|
4540
|
+
multicolor: b.behavior.multicolor.mode,
|
|
4541
|
+
builtin: Boolean(b.builtin)
|
|
4542
|
+
};
|
|
3856
4543
|
}
|
|
3857
4544
|
function createPaintEditorToolProvider(controller, options = {}) {
|
|
3858
4545
|
const c = controller;
|
|
@@ -3888,7 +4575,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
3888
4575
|
strokes: d.strokes.length,
|
|
3889
4576
|
undo: s.undoLabel,
|
|
3890
4577
|
redo: s.redoLabel,
|
|
3891
|
-
blendModes:
|
|
4578
|
+
blendModes: _chunkXIOMMGPDcjs.BLEND_MODES.map((m) => m.id)
|
|
3892
4579
|
};
|
|
3893
4580
|
}
|
|
3894
4581
|
});
|
|
@@ -4021,7 +4708,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
4021
4708
|
opacity: { type: "number", minimum: 0, maximum: 1, description: "Layer opacity 0..1." },
|
|
4022
4709
|
blend: {
|
|
4023
4710
|
type: "string",
|
|
4024
|
-
enum: [...
|
|
4711
|
+
enum: [..._chunkXIOMMGPDcjs.BLEND_MODES.map((m) => m.id), "pass-through"],
|
|
4025
4712
|
description: "Blend mode (pass-through for groups only). Painterly modes: watercolor (the layer glazes like a transparent wash), tint (recolours keeping lightness), highlight (adds soft coloured light), shadow (shades with ambient fill); see list_blend_modes."
|
|
4026
4713
|
},
|
|
4027
4714
|
clipped: { type: "boolean", description: "Clip to the layer below (paint only shows where it has paint)." },
|
|
@@ -4050,12 +4737,12 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
4050
4737
|
inputSchema: EMPTY2,
|
|
4051
4738
|
annotations: { title: "List blend modes", ...READ2 },
|
|
4052
4739
|
handler: () => ({
|
|
4053
|
-
modes:
|
|
4740
|
+
modes: _chunkXIOMMGPDcjs.BLEND_MODES.map((m) => ({
|
|
4054
4741
|
id: m.id,
|
|
4055
4742
|
label: m.label,
|
|
4056
4743
|
group: m.painterly ? "painterly" : m.id === "pigment" ? "pigment" : "standard",
|
|
4057
4744
|
...m.description ? { description: m.description } : {},
|
|
4058
|
-
psd:
|
|
4745
|
+
psd: _chunkXIOMMGPDcjs.standardBlendMode.call(void 0, m.id)
|
|
4059
4746
|
}))
|
|
4060
4747
|
})
|
|
4061
4748
|
});
|
|
@@ -4123,7 +4810,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
4123
4810
|
opacity: { type: "number", minimum: 0, maximum: 1, description: "Opacity 0..1 (default 1)." },
|
|
4124
4811
|
grain: { type: "number", minimum: 0, maximum: 1, description: "Paper grain response 0..1 (default 0: flat)." },
|
|
4125
4812
|
pressure: { type: "number", minimum: 0, maximum: 1, description: "With grain: how far into the valleys, 0..1 (default 0.5)." },
|
|
4126
|
-
blend: { type: "string", enum:
|
|
4813
|
+
blend: { type: "string", enum: _chunkXIOMMGPDcjs.BLEND_MODES.map((m) => m.id), description: "Blend mode (default normal; pigment mixes like paint, watercolor glazes; see list_blend_modes)." }
|
|
4127
4814
|
},
|
|
4128
4815
|
additionalProperties: false
|
|
4129
4816
|
},
|
|
@@ -4141,18 +4828,18 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
4141
4828
|
add({
|
|
4142
4829
|
name: "list_brushes",
|
|
4143
4830
|
title: "List brushes",
|
|
4144
|
-
description: "Lists the brush library: built-in dry media (pencils, pastels, charcoal, airbrush, marker, ink, erasers, blender, smudge, Mixer Brush) and saved user brushes, with id, name, category, engine, size, grain response and
|
|
4831
|
+
description: "Lists the brush library: built-in dry media (pencils, pastels, charcoal, airbrush, marker, ink, erasers), paint brushes (dirty round, multi-colour flat, palette dabber), blending tools (blender, smudge, pigment smudge, Mixer Brush) and saved user brushes, with id, name, category, engine, size, grain response, rotation mode, whether it can pick up paint (wetCapable), its pick-up ratio, dirty and multi-colour mode, plus the active brush id.",
|
|
4145
4832
|
inputSchema: EMPTY2,
|
|
4146
4833
|
annotations: { title: "List brushes", ...READ2 },
|
|
4147
4834
|
handler: () => {
|
|
4148
4835
|
const s = c.getState();
|
|
4149
|
-
return { activeBrushId: s.brush.id, activeEdited: !s.brush.builtin && Boolean(_optionalChain([c, 'access',
|
|
4836
|
+
return { activeBrushId: s.brush.id, activeEdited: !s.brush.builtin && Boolean(_optionalChain([c, 'access', _111 => _111.getBrush, 'call', _112 => _112(s.brush.id), 'optionalAccess', _113 => _113.builtin])), brushes: s.brushes.map(brushSummary) };
|
|
4150
4837
|
}
|
|
4151
4838
|
});
|
|
4152
4839
|
add({
|
|
4153
4840
|
name: "get_brush",
|
|
4154
4841
|
title: "Get a brush",
|
|
4155
|
-
description: "Reads every setting of a brush (tip, grain, pressure smoothing, rotation, dynamics, mixer). Without id, the active brush as edited.",
|
|
4842
|
+
description: "Reads every setting of a brush (tip, grain, pressure smoothing, rotation, dynamics, mixer, behavior: pick-up / mix / dirty / multi-colour, blender, smudge). Without id, the active brush as edited.",
|
|
4156
4843
|
inputSchema: { type: "object", properties: { id: { type: "string", description: "Brush id from list_brushes (default the active brush)." } }, additionalProperties: false },
|
|
4157
4844
|
annotations: { title: "Get a brush", ...READ2 },
|
|
4158
4845
|
handler: ({ id }) => {
|
|
@@ -4244,7 +4931,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
4244
4931
|
const t = c.brushThumbnail(_nullishCoalesce(id, () => ( c.getState().brush.id)), { width, height, color });
|
|
4245
4932
|
return {
|
|
4246
4933
|
content: [
|
|
4247
|
-
{ type: "image", data:
|
|
4934
|
+
{ type: "image", data: _chunkPOGBUYWEcjs.bytesToBase64.call(void 0, _chunkPOGBUYWEcjs.encodePng.call(void 0, t.width, t.height, t.data)), mimeType: "image/png" },
|
|
4248
4935
|
{ type: "text", text: `Brush ${_nullishCoalesce(id, () => ( c.getState().brush.id))} ${t.width}\xD7${t.height}` }
|
|
4249
4936
|
]
|
|
4250
4937
|
};
|
|
@@ -4287,7 +4974,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
4287
4974
|
add({
|
|
4288
4975
|
name: "get_mixer_brush",
|
|
4289
4976
|
title: "Get the Mixer Brush load",
|
|
4290
|
-
description: "Reads what the
|
|
4977
|
+
description: "Reads what the brush carries between strokes: reservoir #rrggbb or null (the Mixer Brush's loaded paint), amount 0..1 left, pickup #rrggbb or null (paint picked up from the canvas, kept by dirty brushes and a Mixer Brush that does not clean each stroke). Call before painting with mixer-brush when its loadEachStroke is false, or with a dirty brush.",
|
|
4291
4978
|
inputSchema: EMPTY2,
|
|
4292
4979
|
annotations: { title: "Get the Mixer Brush load", ...READ2 },
|
|
4293
4980
|
handler: () => loadResult(c.getState().mixerLoad)
|
|
@@ -4303,7 +4990,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
4303
4990
|
add({
|
|
4304
4991
|
name: "clean_mixer_brush",
|
|
4305
4992
|
title: "Clean the Mixer Brush",
|
|
4306
|
-
description: "
|
|
4993
|
+
description: "Cleans the brush: empties the picked-up paint a dirty brush or the Mixer Brush carries (and with emptyReservoir the Mixer Brush paint too). Editor state, not an undo step. Returns the load.",
|
|
4307
4994
|
inputSchema: { type: "object", properties: { emptyReservoir: { type: "boolean", description: "Also drop the loaded paint (default false)." } }, additionalProperties: false },
|
|
4308
4995
|
annotations: { title: "Clean the Mixer Brush", ...SET2 },
|
|
4309
4996
|
handler: ({ emptyReservoir }) => loadResult(c.cleanMixerBrush(emptyReservoir === true))
|
|
@@ -4311,7 +4998,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
4311
4998
|
add({
|
|
4312
4999
|
name: "paint_stroke",
|
|
4313
5000
|
title: "Paint a brush stroke",
|
|
4314
|
-
description: "Paints one stroke through points (document pixels, with optional pressure, time, tilt and barrel rotation) on a pixel layer with a library brush or the active one, optionally changed for this stroke. Grain-responsive brushes follow the paper; the mixer engine (mixer-brush
|
|
5001
|
+
description: "Paints one stroke through points (document pixels, with optional pressure, time, tilt and barrel rotation) on a pixel layer with a library brush or the active one, optionally changed for this stroke. Grain-responsive brushes follow the paper; the mixer engine (mixer-brush) picks up and mixes paint already on the layer, the blender mixes nearby colours, the smudge drags paint, and dab brushes with behavior.pickup lift and carry paint. The symmetry guide adds its mirror copies; with a tracing image that samples colours, a stroke without color takes the image colour at its first point. With an agent cursor (a director), the stroke is painted live along the points at the pace of their times. One undo step. Returns the layer id, changed bounds, dab count, the recorded strokeId (for replay_strokes) and, for brushes that pick up paint, mixerLoad: what the brush carries after the stroke.",
|
|
4315
5002
|
inputSchema: {
|
|
4316
5003
|
type: "object",
|
|
4317
5004
|
properties: {
|
|
@@ -4366,7 +5053,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
4366
5053
|
color: s.color,
|
|
4367
5054
|
points: ids ? s.points : s.points.length,
|
|
4368
5055
|
seed: s.seed,
|
|
4369
|
-
copies: _nullishCoalesce(_optionalChain([s, 'access',
|
|
5056
|
+
copies: _nullishCoalesce(_optionalChain([s, 'access', _114 => _114.copies, 'optionalAccess', _115 => _115.length]), () => ( 0)),
|
|
4370
5057
|
bounds: { x: Math.floor(Math.min(...xs) - pad), y: Math.floor(Math.min(...ys) - pad), width: Math.ceil(Math.max(...xs) - Math.min(...xs) + 2 * pad), height: Math.ceil(Math.max(...ys) - Math.min(...ys) + 2 * pad) }
|
|
4371
5058
|
};
|
|
4372
5059
|
})
|
|
@@ -4499,7 +5186,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
4499
5186
|
annotations: { title: "Export the painting", ...READ2 },
|
|
4500
5187
|
handler: async ({ format, quality, scale, paper }) => {
|
|
4501
5188
|
const bytes = await c.exportImage({ format, quality, scale, paper });
|
|
4502
|
-
return { mimeType: _nullishCoalesce(format, () => ( "image/png")), bytes: bytes.length, data:
|
|
5189
|
+
return { mimeType: _nullishCoalesce(format, () => ( "image/png")), bytes: bytes.length, data: _chunkPOGBUYWEcjs.bytesToBase64.call(void 0, bytes) };
|
|
4503
5190
|
}
|
|
4504
5191
|
});
|
|
4505
5192
|
add({
|
|
@@ -4510,7 +5197,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
4510
5197
|
annotations: { title: "Export as PSD", ...READ2 },
|
|
4511
5198
|
handler: () => {
|
|
4512
5199
|
const r = c.exportPsd();
|
|
4513
|
-
return { data:
|
|
5200
|
+
return { data: _chunkPOGBUYWEcjs.bytesToBase64.call(void 0, r.bytes), bytes: r.bytes.length, psb: r.psb, warnings: r.warnings };
|
|
4514
5201
|
}
|
|
4515
5202
|
});
|
|
4516
5203
|
add({
|
|
@@ -4534,7 +5221,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
4534
5221
|
}
|
|
4535
5222
|
});
|
|
4536
5223
|
tools.push(...createPaintGuideTools(c));
|
|
4537
|
-
tools.push(...
|
|
5224
|
+
tools.push(..._chunk7VOHOPLWcjs.createPigmentTools.call(void 0, ));
|
|
4538
5225
|
return { id: _nullishCoalesce(options.id, () => ( "paint-editor")), namespace: options.namespace, tools };
|
|
4539
5226
|
}
|
|
4540
5227
|
|
|
@@ -4545,21 +5232,21 @@ var PAINT_TOOLS = [
|
|
|
4545
5232
|
{ id: "pan", label: "Pan", key: "h" },
|
|
4546
5233
|
{ id: "rotate", label: "Rotate view", key: "r" }
|
|
4547
5234
|
];
|
|
4548
|
-
var PaintGesture = (
|
|
4549
|
-
constructor(controller, box) {;
|
|
5235
|
+
var PaintGesture = (_class11 = class {
|
|
5236
|
+
constructor(controller, box) {;_class11.prototype.__init66.call(this);_class11.prototype.__init67.call(this);_class11.prototype.__init68.call(this);_class11.prototype.__init69.call(this);_class11.prototype.__init70.call(this);_class11.prototype.__init71.call(this);
|
|
4550
5237
|
this.controller = controller;
|
|
4551
5238
|
this.box = box;
|
|
4552
5239
|
}
|
|
4553
5240
|
|
|
4554
5241
|
|
|
4555
|
-
|
|
5242
|
+
__init66() {this.tool = "brush"}
|
|
4556
5243
|
/** The last refusal (a hidden or locked layer), for the editor to show. */
|
|
4557
|
-
|
|
5244
|
+
__init67() {this.error = null}
|
|
4558
5245
|
/** The predicted tail of the current stroke in document pixels (draw it as a thin preview). */
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
5246
|
+
__init68() {this.predicted = []}
|
|
5247
|
+
__init69() {this.mode = null}
|
|
5248
|
+
__init70() {this.penAt = null}
|
|
5249
|
+
__init71() {this.penDown = false}
|
|
4563
5250
|
doc(e) {
|
|
4564
5251
|
const b = this.box();
|
|
4565
5252
|
const s = this.controller.getState();
|
|
@@ -4640,12 +5327,12 @@ var PaintGesture = (_class10 = class {
|
|
|
4640
5327
|
if (!m || m.pointerId !== e.pointerId) return;
|
|
4641
5328
|
this.mode = null;
|
|
4642
5329
|
this.predicted = [];
|
|
4643
|
-
if (m.kind === "stroke") _optionalChain([this, 'access',
|
|
5330
|
+
if (m.kind === "stroke") _optionalChain([this, 'access', _116 => _116.controller, 'access', _117 => _117.activeStroke, 'optionalAccess', _118 => _118.end, 'call', _119 => _119()]);
|
|
4644
5331
|
}
|
|
4645
5332
|
cancel() {
|
|
4646
5333
|
this.penDown = false;
|
|
4647
5334
|
this.predicted = [];
|
|
4648
|
-
if (_optionalChain([this, 'access',
|
|
5335
|
+
if (_optionalChain([this, 'access', _120 => _120.mode, 'optionalAccess', _121 => _121.kind]) === "stroke") _optionalChain([this, 'access', _122 => _122.controller, 'access', _123 => _123.activeStroke, 'optionalAccess', _124 => _124.cancel, 'call', _125 => _125()]);
|
|
4649
5336
|
this.mode = null;
|
|
4650
5337
|
}
|
|
4651
5338
|
get active() {
|
|
@@ -4665,7 +5352,7 @@ var PaintGesture = (_class10 = class {
|
|
|
4665
5352
|
const py = e.clientY - b.top - b.height / 2;
|
|
4666
5353
|
this.controller.setView({ zoom, pan: { x: px - (px - s.view.pan.x) * k, y: py - (py - s.view.pan.y) * k } });
|
|
4667
5354
|
}
|
|
4668
|
-
},
|
|
5355
|
+
}, _class11);
|
|
4669
5356
|
function handlePaintShortcut(controller, gesture, e) {
|
|
4670
5357
|
const mod = e.ctrlKey || e.metaKey;
|
|
4671
5358
|
const key = e.key.toLowerCase();
|
|
@@ -4705,14 +5392,14 @@ function handlePaintShortcut(controller, gesture, e) {
|
|
|
4705
5392
|
}
|
|
4706
5393
|
return false;
|
|
4707
5394
|
}
|
|
4708
|
-
var PaintCanvasPresenter = (
|
|
4709
|
-
constructor(controller) {;
|
|
5395
|
+
var PaintCanvasPresenter = (_class12 = class {
|
|
5396
|
+
constructor(controller) {;_class12.prototype.__init72.call(this);_class12.prototype.__init73.call(this);_class12.prototype.__init74.call(this);
|
|
4710
5397
|
this.controller = controller;
|
|
4711
5398
|
}
|
|
4712
5399
|
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
5400
|
+
__init72() {this.uploaded = /* @__PURE__ */ new Map()}
|
|
5401
|
+
__init73() {this.scratch = null}
|
|
5402
|
+
__init74() {this.size = ""}
|
|
4716
5403
|
/** Draws what changed into `ctx` (a canvas the size of the document); returns the tiles drawn. */
|
|
4717
5404
|
render(ctx) {
|
|
4718
5405
|
const { width, height } = this.controller.document.canvas;
|
|
@@ -4729,16 +5416,16 @@ var PaintCanvasPresenter = (_class11 = class {
|
|
|
4729
5416
|
const key = `${tx},${ty}`;
|
|
4730
5417
|
if (this.uploaded.get(key) === data) return;
|
|
4731
5418
|
this.uploaded.set(key, data);
|
|
4732
|
-
if (!this.scratch) this.scratch = ctx.createImageData(
|
|
5419
|
+
if (!this.scratch) this.scratch = ctx.createImageData(_chunkXIOMMGPDcjs.TILE, _chunkXIOMMGPDcjs.TILE);
|
|
4733
5420
|
this.scratch.data.set(data);
|
|
4734
|
-
const ox = tx *
|
|
4735
|
-
const oy = ty *
|
|
4736
|
-
ctx.putImageData(this.scratch, ox, oy, 0, 0, Math.min(
|
|
5421
|
+
const ox = tx * _chunkXIOMMGPDcjs.TILE;
|
|
5422
|
+
const oy = ty * _chunkXIOMMGPDcjs.TILE;
|
|
5423
|
+
ctx.putImageData(this.scratch, ox, oy, 0, 0, Math.min(_chunkXIOMMGPDcjs.TILE, width - ox), Math.min(_chunkXIOMMGPDcjs.TILE, height - oy));
|
|
4737
5424
|
drawn++;
|
|
4738
5425
|
}, { dirty });
|
|
4739
5426
|
return drawn;
|
|
4740
5427
|
}
|
|
4741
|
-
},
|
|
5428
|
+
}, _class12);
|
|
4742
5429
|
function guideOverlay(controller) {
|
|
4743
5430
|
const s = controller.getState();
|
|
4744
5431
|
const size = s.document.canvas;
|
|
@@ -4773,7 +5460,7 @@ var PAPER_SLIDERS = [
|
|
|
4773
5460
|
];
|
|
4774
5461
|
function readPath(obj, path) {
|
|
4775
5462
|
let v = obj;
|
|
4776
|
-
for (const k of path.split(".")) v = _optionalChain([v, 'optionalAccess',
|
|
5463
|
+
for (const k of path.split(".")) v = _optionalChain([v, 'optionalAccess', _126 => _126[k]]);
|
|
4777
5464
|
return typeof v === "number" ? v : 0;
|
|
4778
5465
|
}
|
|
4779
5466
|
function patchPath(obj, path, value) {
|
|
@@ -4815,9 +5502,88 @@ function wheelPick(dx, dy, wheel, color) {
|
|
|
4815
5502
|
return rgbToHexColor(nr, ng, nb);
|
|
4816
5503
|
}
|
|
4817
5504
|
var BLEND_MENU_GROUPS = [
|
|
4818
|
-
{ label: "Standard", modes:
|
|
4819
|
-
{ label: "Paint", modes:
|
|
5505
|
+
{ label: "Standard", modes: _chunkXIOMMGPDcjs.BLEND_MODES.filter((m) => !m.painterly && m.id !== "pigment") },
|
|
5506
|
+
{ label: "Paint", modes: _chunkXIOMMGPDcjs.BLEND_MODES.filter((m) => m.painterly || m.id === "pigment") }
|
|
4820
5507
|
];
|
|
5508
|
+
var PICKUP_SLIDERS = [
|
|
5509
|
+
{ path: "behavior.pickup", label: "Pick-up", min: 0, max: 1, step: 0.01 },
|
|
5510
|
+
{ path: "behavior.mix", label: "Mix", min: 0, max: 1, step: 0.01 }
|
|
5511
|
+
];
|
|
5512
|
+
function brushEngineControls(brush) {
|
|
5513
|
+
switch (brush.engine) {
|
|
5514
|
+
case "blender":
|
|
5515
|
+
return {
|
|
5516
|
+
title: "Blender",
|
|
5517
|
+
sliders: [
|
|
5518
|
+
{ path: "blender.strength", label: "Strength", min: 0, max: 1, step: 0.01 },
|
|
5519
|
+
{ path: "blender.distance", label: "Blend distance", min: 0, max: 1, step: 0.01 }
|
|
5520
|
+
],
|
|
5521
|
+
mixingPath: "blender.mixing",
|
|
5522
|
+
dirty: false,
|
|
5523
|
+
multicolor: false,
|
|
5524
|
+
load: false
|
|
5525
|
+
};
|
|
5526
|
+
case "smudge":
|
|
5527
|
+
return {
|
|
5528
|
+
title: "Smudge",
|
|
5529
|
+
sliders: [
|
|
5530
|
+
{ path: "smudge.strength", label: "Strength", min: 0, max: 1, step: 0.01 },
|
|
5531
|
+
{ path: "smudge.length", label: "Length", min: 0, max: 1, step: 0.01 }
|
|
5532
|
+
],
|
|
5533
|
+
mixingPath: "smudge.mixing",
|
|
5534
|
+
dirty: false,
|
|
5535
|
+
multicolor: false,
|
|
5536
|
+
load: false
|
|
5537
|
+
};
|
|
5538
|
+
case "mixer":
|
|
5539
|
+
return { title: "Mixer Brush", sliders: [...PICKUP_SLIDERS, { path: "mixer.load", label: "Load", min: 0, max: 1, step: 0.01 }], mixingPath: "behavior.mixing", dirty: true, multicolor: true, load: true };
|
|
5540
|
+
case "dab":
|
|
5541
|
+
return { title: "Paint behaviour", sliders: PICKUP_SLIDERS, mixingPath: "behavior.mixing", dirty: true, multicolor: true, load: true };
|
|
5542
|
+
default:
|
|
5543
|
+
return null;
|
|
5544
|
+
}
|
|
5545
|
+
}
|
|
5546
|
+
function multiColorSliders(mode) {
|
|
5547
|
+
if (mode === "off") return [];
|
|
5548
|
+
const soft = { path: "behavior.multicolor.softness", label: "Colour softness", min: 0, max: 1, step: 0.01 };
|
|
5549
|
+
return mode === "dab" ? [soft, { path: "behavior.multicolor.length", label: "Dabs per colour", min: 1, max: 100, step: 1 }] : [soft];
|
|
5550
|
+
}
|
|
5551
|
+
function patchBrushValue(brush, path, value) {
|
|
5552
|
+
const set = (obj, keys) => {
|
|
5553
|
+
const [head2, ...rest2] = keys;
|
|
5554
|
+
if (!rest2.length) return { ...obj, [head2]: value };
|
|
5555
|
+
return { ...obj, [head2]: set(_nullishCoalesce(obj[head2], () => ( {})), rest2) };
|
|
5556
|
+
};
|
|
5557
|
+
const [head, ...rest] = path.split(".");
|
|
5558
|
+
if (!rest.length) return { [head]: value };
|
|
5559
|
+
return { [head]: set({ ..._nullishCoalesce(brush[head], () => ( {})) }, rest) };
|
|
5560
|
+
}
|
|
5561
|
+
function brushLoadColor(load) {
|
|
5562
|
+
const c = _nullishCoalesce(load.pickup, () => ( load.reservoir));
|
|
5563
|
+
return c ? rgbToHexColor(Math.round(c[0] * 255), Math.round(c[1] * 255), Math.round(c[2] * 255)) : null;
|
|
5564
|
+
}
|
|
5565
|
+
var PAD_SAMPLE_SIZES = [1, 3, 5, 9, 15];
|
|
5566
|
+
function padSampleRadius(size) {
|
|
5567
|
+
return Math.max(0, Math.floor((size - 1) / 2));
|
|
5568
|
+
}
|
|
5569
|
+
|
|
5570
|
+
|
|
5571
|
+
|
|
5572
|
+
|
|
5573
|
+
|
|
5574
|
+
|
|
5575
|
+
|
|
5576
|
+
|
|
5577
|
+
|
|
5578
|
+
|
|
5579
|
+
|
|
5580
|
+
|
|
5581
|
+
|
|
5582
|
+
|
|
5583
|
+
|
|
5584
|
+
|
|
5585
|
+
|
|
5586
|
+
|
|
4821
5587
|
|
|
4822
5588
|
|
|
4823
5589
|
|
|
@@ -4965,5 +5731,5 @@ var BLEND_MENU_GROUPS = [
|
|
|
4965
5731
|
|
|
4966
5732
|
|
|
4967
5733
|
|
|
4968
|
-
exports.BLEND_MENU_GROUPS = BLEND_MENU_GROUPS; exports.BLEND_MODES =
|
|
5734
|
+
exports.BLEND_MENU_GROUPS = BLEND_MENU_GROUPS; exports.BLEND_MODES = _chunkXIOMMGPDcjs.BLEND_MODES; exports.BRUSH_ID = BRUSH_ID; exports.BRUSH_ROTATIONS = BRUSH_ROTATIONS; exports.BRUSH_SLIDERS = BRUSH_SLIDERS; exports.BUILTIN_BRUSHES = BUILTIN_BRUSHES; exports.DEFAULT_BEHAVIOR = DEFAULT_BEHAVIOR; exports.DEFAULT_BRUSH_ID = DEFAULT_BRUSH_ID; exports.DEFAULT_DPI = DEFAULT_DPI; exports.DEFAULT_DYNAMICS = DEFAULT_DYNAMICS; exports.DEFAULT_MULTI_COLOR = DEFAULT_MULTI_COLOR; exports.DEFAULT_PAPER = DEFAULT_PAPER; exports.DEFAULT_SWATCHES = DEFAULT_SWATCHES; exports.DEFAULT_VIEW = DEFAULT_VIEW; exports.DryStroke = DryStroke; exports.GRAIN_PATTERNS = GRAIN_PATTERNS; exports.GRAIN_TILE = GRAIN_TILE; exports.HARMONY_KINDS = HARMONY_KINDS; exports.IDENTITY_AFFINE = IDENTITY_AFFINE; exports.IDENTITY_COPY = IDENTITY_COPY; exports.MAX_BRUSH_SIZE = _chunk7VOHOPLWcjs.MAX_BRUSH_SIZE; exports.MAX_CANVAS_SIDE = MAX_CANVAS_SIDE; exports.MAX_COORDINATE = MAX_COORDINATE; exports.MAX_IMAGE_SIDE = MAX_IMAGE_SIDE; exports.MAX_ZOOM = MAX_ZOOM; exports.MIN_CANVAS_SIDE = MIN_CANVAS_SIDE; exports.MIN_ZOOM = MIN_ZOOM; exports.MIXING_PAD_HEIGHT = MIXING_PAD_HEIGHT; exports.MIXING_PAD_WIDTH = MIXING_PAD_WIDTH; exports.MULTI_COLOR_MODES = MULTI_COLOR_MODES; exports.MixingPad = MixingPad; exports.PAD_SAMPLE_SIZES = PAD_SAMPLE_SIZES; exports.PAINTERLY_BLEND_MODES = _chunkXIOMMGPDcjs.PAINTERLY_BLEND_MODES; exports.PAINT_BRUSH_CATEGORIES = PAINT_BRUSH_CATEGORIES; exports.PAINT_BRUSH_ENGINES = PAINT_BRUSH_ENGINES; exports.PAINT_FORMAT = PAINT_FORMAT; exports.PAINT_SCHEMA_VERSION = PAINT_SCHEMA_VERSION; exports.PAINT_TOOLS = PAINT_TOOLS; exports.PAPER_FAMILIES = PAPER_FAMILIES; exports.PAPER_LIBRARY = PAPER_LIBRARY; exports.PAPER_NODE_ID = PAPER_NODE_ID; exports.PAPER_SLIDERS = PAPER_SLIDERS; exports.PaintCanvasPresenter = PaintCanvasPresenter; exports.PaintDabStroke = PaintDabStroke; exports.PaintDirector = PaintDirector; exports.PaintDocumentError = PaintDocumentError; exports.PaintEditorController = PaintEditorController; exports.PaintEditorError = PaintEditorError; exports.PaintGesture = PaintGesture; exports.PaintPresence = PaintPresence; exports.PaintStrokeSession = PaintStrokeSession; exports.PaperGrain = PaperGrain; exports.PenDabWalker = PenDabWalker; exports.PenSmoother = PenSmoother; exports.PerspectiveSnapper = PerspectiveSnapper; exports.SNAP_DECIDE_DISTANCE = SNAP_DECIDE_DISTANCE; exports.SYMMETRY_MODES = SYMMETRY_MODES; exports.TRACING_NODE_ID = TRACING_NODE_ID; exports.TileableNoise = TileableNoise; exports.affineCopy = affineCopy; exports.applyAffine = applyAffine; exports.brushEngineControls = brushEngineControls; exports.brushLoadColor = brushLoadColor; exports.brushLoadForStroke = brushLoadForStroke; exports.brushToJSON = brushToJSON; exports.clonePaper = clonePaper; exports.colorHarmony = colorHarmony; exports.composeAffine = composeAffine; exports.createPaintCompositor = createPaintCompositor; exports.createPaintDocument = createPaintDocument; exports.createPaintEditorToolProvider = createPaintEditorToolProvider; exports.createPaintLayer = createPaintLayer; exports.decodeImageBlob = decodeImageBlob; exports.defaultGuides = defaultGuides; exports.defaultImageEncoder = defaultImageEncoder; exports.deserializePaintDocument = deserializePaintDocument; exports.deserializePaintDocumentAsync = deserializePaintDocumentAsync; exports.documentExtensions = documentExtensions; exports.documentToScreen = documentToScreen; exports.downloadBytes = downloadBytes; exports.downsampleRgba = downsampleRgba; exports.fitZoom = fitZoom; exports.grainCatch = grainCatch; exports.grainHeights = grainHeights; exports.guideOverlay = guideOverlay; exports.handlePaintShortcut = handlePaintShortcut; exports.hash2 = hash2; exports.hexToRgb8 = hexToRgb8; exports.hslToRgb = hslToRgb; exports.hsvToRgb = hsvToRgb; exports.hueToWheel = hueToWheel; exports.invertAffine = invertAffine; exports.isBlendMode = _chunkXIOMMGPDcjs.isBlendMode; exports.isPainterlyBlendMode = _chunkXIOMMGPDcjs.isPainterlyBlendMode; exports.isPalmTouch = isPalmTouch; exports.isWetCapable = isWetCapable; exports.mapPenPoint = mapPenPoint; exports.mix32 = mix32; exports.mixerLoadForStroke = mixerLoadForStroke; exports.mixerTip = mixerTip; exports.multiColorForDab = multiColorForDab; exports.multiColorGradient = multiColorGradient; exports.multiColorPalette = multiColorPalette; exports.multiColorSliders = multiColorSliders; exports.newPaintLayerId = newPaintLayerId; exports.normalizeBehavior = normalizeBehavior; exports.normalizeBrushStroke = normalizeBrushStroke; exports.normalizeCanvas = normalizeCanvas; exports.normalizeDynamics = normalizeDynamics; exports.normalizeMultiColor = normalizeMultiColor; exports.normalizePaintBrush = normalizePaintBrush; exports.normalizePaper = normalizePaper; exports.normalizePerspective = normalizePerspective; exports.normalizeRotation = normalizeRotation; exports.normalizeSymmetry = normalizeSymmetry; exports.normalizeView = normalizeView; exports.padSampleRadius = padSampleRadius; exports.paperGrain = paperGrain; exports.paperKey = paperKey; exports.paperNode = paperNode; exports.paperPreset = paperPreset; exports.parseBrushLibrary = parseBrushLibrary; exports.parseHexColor = parseHexColor; exports.patchBrushValue = patchBrushValue; exports.patchPath = patchPath; exports.penOrientation = penOrientation; exports.perspectiveDirections = perspectiveDirections; exports.perspectiveGuideLines = perspectiveGuideLines; exports.pointerToPenPoint = pointerToPenPoint; exports.rand01 = rand01; exports.randSigned = randSigned; exports.readPath = readPath; exports.renderBrushThumbnail = renderBrushThumbnail; exports.renderColorWheel = renderColorWheel; exports.renderPaper = renderPaper; exports.rgbToHexColor = rgbToHexColor; exports.rgbToHsl = rgbToHsl; exports.rgbToHsv = rgbToHsv; exports.samplePressure = samplePressure; exports.screenToDocument = screenToDocument; exports.seedFromString = seedFromString; exports.serializePaintDocument = serializePaintDocument; exports.serializePaintDocumentExternal = serializePaintDocumentExternal; exports.standardBlendMode = _chunkXIOMMGPDcjs.standardBlendMode; exports.strokeBounds = strokeBounds; exports.strokeDuration = strokeDuration; exports.strokeSchedule = strokeSchedule; exports.sweepPath = sweepPath; exports.symmetryAxisAngles = symmetryAxisAngles; exports.symmetryAxisSegments = symmetryAxisSegments; exports.symmetryCopies = symmetryCopies; exports.thumbnailStroke = thumbnailStroke; exports.tiltToOrientation = tiltToOrientation; exports.toStrokePoint = toStrokePoint; exports.usesPaintExchange = usesPaintExchange; exports.variationAffine = variationAffine; exports.varyStroke = varyStroke; exports.viewCssTransform = viewCssTransform; exports.viewMatrix = viewMatrix; exports.viewPenToDocument = viewPenToDocument; exports.wheelAngle = wheelAngle; exports.wheelPick = wheelPick; exports.wheelPoint = wheelPoint; exports.wheelToHue = wheelToHue;
|
|
4969
5735
|
//# sourceMappingURL=index.cjs.map
|