@pixerate/editor 0.245.0 → 0.248.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/animation-editor/index.cjs +59 -59
- package/dist/animation-editor/index.js +3 -3
- package/dist/{brushstrokeRender-QA5WVIDT.cjs → brushstrokeRender-DTDPFBLC.cjs} +5 -5
- package/dist/{brushstrokeRender-4542T5HP.js → brushstrokeRender-QLCXROJK.js} +2 -2
- package/dist/canvas/index.cjs +8 -8
- package/dist/canvas/index.js +1 -1
- package/dist/{chunk-OSTKS3YN.js → chunk-5GLYUSNT.js} +1 -1
- package/dist/{chunk-4T47Z4J3.cjs → chunk-6G6ZDDDB.cjs} +400 -27
- package/dist/chunk-7T7JWTVE.cjs +192 -0
- package/dist/{chunk-SD23XMVJ.cjs → chunk-IBDU6EGZ.cjs} +35 -35
- package/dist/{chunk-IZL5V5RK.cjs → chunk-L6REDLNB.cjs} +2 -2
- package/dist/{chunk-L5PB7EG3.js → chunk-NZ3O3A5C.js} +1 -1
- package/dist/{chunk-4EQY4TZJ.js → chunk-Q2HA52V4.js} +1 -1
- package/dist/chunk-SC2RZ5EF.js +192 -0
- package/dist/{chunk-HZ547SGQ.cjs → chunk-TPWB4Y6L.cjs} +1 -1
- package/dist/{chunk-TBJEMELS.js → chunk-VNBUDVD4.js} +398 -25
- package/dist/{chunk-M6JORA5Z.cjs → chunk-WVS5KQDH.cjs} +442 -87
- package/dist/{chunk-5RFENYO5.js → chunk-XNRBMYNB.js} +1 -1
- package/dist/{chunk-BDIH4DYR.cjs → chunk-XWTRATIM.cjs} +23 -23
- package/dist/{chunk-7YQNKSQ7.js → chunk-ZFSLEOSQ.js} +357 -2
- package/dist/image-editor/effect-tools.d.cts +37 -4
- package/dist/image-editor/effect-tools.d.ts +37 -4
- package/dist/image-editor/index.cjs +1059 -1552
- package/dist/image-editor/index.d.cts +1 -1
- package/dist/image-editor/index.d.ts +1 -1
- package/dist/image-editor/index.js +275 -768
- package/dist/paint-editor/controller.d.cts +34 -1
- package/dist/paint-editor/controller.d.ts +34 -1
- package/dist/paint-editor/index.cjs +426 -257
- package/dist/paint-editor/index.js +184 -15
- package/dist/paint-editor/procedural.d.cts +82 -5
- package/dist/paint-editor/procedural.d.ts +82 -5
- package/dist/paint-editor/tools.d.cts +1 -1
- package/dist/paint-editor/tools.d.ts +1 -1
- package/dist/paint-editor/ui.d.cts +15 -2
- package/dist/paint-editor/ui.d.ts +15 -2
- package/dist/{image-editor/layers/effects-ui.d.cts → raster/effects/ui.d.cts} +32 -5
- package/dist/{image-editor/layers/effects-ui.d.ts → raster/effects/ui.d.ts} +32 -5
- package/dist/raster/index.cjs +13 -1
- package/dist/raster/index.d.cts +1 -0
- package/dist/raster/index.d.ts +1 -0
- package/dist/raster/index.js +12 -0
- package/dist/vector/index.cjs +962 -962
- package/dist/vector/index.js +4 -4
- package/package.json +1 -1
- package/skills/pixerate-paint-editor/SKILL.md +18 -27
- package/skills/pixerate-paint-editor/references/gradient-paint.md +23 -0
- package/skills/pixerate-paint-editor/references/layer-effects.md +28 -0
- package/skills/pixerate-paint-editor/references/light-paint.md +10 -2
- package/skills/pixerate-paint-editor/references/particle-brushes.md +66 -0
|
@@ -2,21 +2,30 @@ import {
|
|
|
2
2
|
toVideoFfmpeg
|
|
3
3
|
} from "../chunk-L3JFOTBQ.js";
|
|
4
4
|
import {
|
|
5
|
+
createLayerEffectTools,
|
|
5
6
|
deserializeLayers,
|
|
6
7
|
exportPsd,
|
|
7
8
|
resolveExternalAssets,
|
|
8
9
|
serializeLayers,
|
|
9
10
|
serializeLayersExternal
|
|
10
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-ZFSLEOSQ.js";
|
|
11
12
|
import "../chunk-3CI7FRMK.js";
|
|
12
13
|
import "../chunk-ZNA3IQZF.js";
|
|
13
14
|
import {
|
|
15
|
+
LAYER_EFFECT_KINDS,
|
|
16
|
+
LayerEffectError,
|
|
14
17
|
RasterCompositor,
|
|
18
|
+
defaultLayerEffect,
|
|
15
19
|
findLayer,
|
|
16
20
|
flattenLayers,
|
|
17
21
|
insertLayer,
|
|
22
|
+
isLayerEffectKind,
|
|
18
23
|
isLayerLocked,
|
|
19
|
-
|
|
24
|
+
layerEffectLabel,
|
|
25
|
+
mapLayer,
|
|
26
|
+
mergeLayerEffect,
|
|
27
|
+
newEffectId,
|
|
28
|
+
validateLayerEffects
|
|
20
29
|
} from "../chunk-UOOK7HTX.js";
|
|
21
30
|
import "../chunk-OHKOFKBY.js";
|
|
22
31
|
import "../chunk-ZIF4UJJ6.js";
|
|
@@ -55,7 +64,7 @@ import {
|
|
|
55
64
|
realPresenceClock,
|
|
56
65
|
tweenPresenceCursor
|
|
57
66
|
} from "../chunk-IVSWRVB4.js";
|
|
58
|
-
import "../chunk-
|
|
67
|
+
import "../chunk-Q2HA52V4.js";
|
|
59
68
|
import {
|
|
60
69
|
BRUSH_ID,
|
|
61
70
|
BRUSH_ROTATIONS,
|
|
@@ -72,11 +81,14 @@ import {
|
|
|
72
81
|
DEFAULT_PARTICLE,
|
|
73
82
|
DEFAULT_STICKER,
|
|
74
83
|
DEFAULT_WET_BRUSH,
|
|
84
|
+
DOT_ROTATIONS,
|
|
85
|
+
DOT_TEXTURE_ORDERS,
|
|
75
86
|
DryStroke,
|
|
76
87
|
EFFECT_MAP_REPEATS,
|
|
77
88
|
EFFECT_STRIP_HEIGHT,
|
|
78
89
|
IDENTITY_COPY,
|
|
79
90
|
IMPASTO_TOOLS,
|
|
91
|
+
MAX_DOT_TEXTURES,
|
|
80
92
|
MULTI_COLOR_MODES,
|
|
81
93
|
PAINT_BRUSH_CATEGORIES,
|
|
82
94
|
PAINT_BRUSH_ENGINES,
|
|
@@ -87,10 +99,12 @@ import {
|
|
|
87
99
|
ParticleStroke,
|
|
88
100
|
PenDabWalker,
|
|
89
101
|
PenSmoother,
|
|
102
|
+
RIBBON_PAIRS,
|
|
90
103
|
STAMP_KINDS,
|
|
91
104
|
StickerSpray,
|
|
92
105
|
WET_MEDIA,
|
|
93
106
|
WET_TOOLS,
|
|
107
|
+
addPolygonArea,
|
|
94
108
|
brushTakesPaint,
|
|
95
109
|
brushToJSON,
|
|
96
110
|
clonePaper,
|
|
@@ -133,7 +147,7 @@ import {
|
|
|
133
147
|
tiltToOrientation,
|
|
134
148
|
toStrokePoint,
|
|
135
149
|
usesPaintExchange
|
|
136
|
-
} from "../chunk-
|
|
150
|
+
} from "../chunk-VNBUDVD4.js";
|
|
137
151
|
import "../chunk-M6DNZNVY.js";
|
|
138
152
|
import "../chunk-WEIK3UYB.js";
|
|
139
153
|
import "../chunk-PWBZH3FK.js";
|
|
@@ -3500,7 +3514,9 @@ function liveStrokeBounds(stroke) {
|
|
|
3500
3514
|
if (q.y > y1) y1 = q.y;
|
|
3501
3515
|
}
|
|
3502
3516
|
}
|
|
3503
|
-
const
|
|
3517
|
+
const particle = stroke.brush.engine === "particle" ? stroke.brush.particle : null;
|
|
3518
|
+
const wave = particle ? Math.max(particle.waveAmplitude ?? 0, particle.kind === "fuzzy" ? 0.12 : 0) : 0;
|
|
3519
|
+
const pad = stroke.brush.tip.size * (particle ? 2 + wave : 1) + 2;
|
|
3504
3520
|
return { x: Math.floor(x0 - pad), y: Math.floor(y0 - pad), width: Math.ceil(x1 - x0 + 2 * pad), height: Math.ceil(y1 - y0 + 2 * pad) };
|
|
3505
3521
|
}
|
|
3506
3522
|
|
|
@@ -4032,6 +4048,14 @@ function effectLayersToJSON(map, keep) {
|
|
|
4032
4048
|
}
|
|
4033
4049
|
|
|
4034
4050
|
// src/paint-editor/controller.ts
|
|
4051
|
+
function effectGuard(fn) {
|
|
4052
|
+
try {
|
|
4053
|
+
return fn();
|
|
4054
|
+
} catch (e) {
|
|
4055
|
+
if (e instanceof LayerEffectError) throw new PaintEditorError(e.message);
|
|
4056
|
+
throw e;
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4035
4059
|
var PaintEditorError = class extends Error {
|
|
4036
4060
|
constructor(message) {
|
|
4037
4061
|
super(message);
|
|
@@ -4557,6 +4581,7 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
4557
4581
|
active: l.id === this.doc.activeLayerId,
|
|
4558
4582
|
bounds,
|
|
4559
4583
|
...this.doc.liveStrokes?.[l.id] ? { liveStrokes: this.doc.liveStrokes[l.id].length } : {},
|
|
4584
|
+
...l.effects?.items.length ? { effects: l.effects.items.length } : {},
|
|
4560
4585
|
parentId,
|
|
4561
4586
|
depth
|
|
4562
4587
|
};
|
|
@@ -4630,6 +4655,94 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
4630
4655
|
this.commit("Layer properties", { ...this.doc, layers: mapLayer(this.doc.layers, id, () => next) });
|
|
4631
4656
|
return this.describeLayer(next);
|
|
4632
4657
|
}
|
|
4658
|
+
// --- Layer effects (layer styles) ---------------------------------------------
|
|
4659
|
+
/** A layer's live effects (an empty, enabled list when it has none). */
|
|
4660
|
+
getLayerEffects(id) {
|
|
4661
|
+
return this.requireLayer(id).effects ?? { enabled: true, items: [] };
|
|
4662
|
+
}
|
|
4663
|
+
/**
|
|
4664
|
+
* Adds a live effect of `kind` (outer and inner glow, drop and inner shadow, stroke, bevel &
|
|
4665
|
+
* emboss, satin, colour / gradient / pattern overlay) with Photoshop's defaults and `settings`
|
|
4666
|
+
* applied, at `index` in the layer's list (default the end). The effect is drawn from the
|
|
4667
|
+
* layer's pixels when it is composited, so it follows later painting. One undo step.
|
|
4668
|
+
*/
|
|
4669
|
+
addLayerEffect(id, kind, settings = {}, index) {
|
|
4670
|
+
if (!isLayerEffectKind(kind)) throw new PaintEditorError(`Unknown effect kind "${String(kind)}". Kinds: ${LAYER_EFFECT_KINDS.join(", ")}.`);
|
|
4671
|
+
const current = this.effectsLayer(id, "add effects").effects ?? { enabled: true, items: [] };
|
|
4672
|
+
const ids = new Set(current.items.map((e) => e.id));
|
|
4673
|
+
let effectId = newEffectId();
|
|
4674
|
+
while (ids.has(effectId)) effectId = newEffectId();
|
|
4675
|
+
const { id: _id, kind: _kind, ...rest } = settings;
|
|
4676
|
+
const effect = effectGuard(() => mergeLayerEffect(defaultLayerEffect(kind, effectId), rest));
|
|
4677
|
+
const items = [...current.items];
|
|
4678
|
+
const at = index === void 0 ? items.length : Math.max(0, Math.min(items.length, Math.floor(index)));
|
|
4679
|
+
items.splice(at, 0, effect);
|
|
4680
|
+
this.writeLayerEffects(id, { ...current, items }, `Add ${layerEffectLabel(kind)}`, true);
|
|
4681
|
+
return { effect, effects: this.getLayerEffects(id) };
|
|
4682
|
+
}
|
|
4683
|
+
/**
|
|
4684
|
+
* Changes one effect: only the given settings (nested objects replace whole; `null` removes an
|
|
4685
|
+
* optional contour or texture). One undo step, or with `commitToHistory` false folded into the
|
|
4686
|
+
* last change of this effect (a slider drag). Returns the layer's effects.
|
|
4687
|
+
*/
|
|
4688
|
+
updateLayerEffect(id, effectId, changes, commitToHistory = true) {
|
|
4689
|
+
const current = this.effectsLayer(id, "change its effects").effects;
|
|
4690
|
+
const i = this.requireEffect(id, effectId);
|
|
4691
|
+
const items = [...current.items];
|
|
4692
|
+
items[i] = effectGuard(() => mergeLayerEffect(items[i], changes));
|
|
4693
|
+
this.writeLayerEffects(id, { ...current, items }, `Change ${layerEffectLabel(items[i].kind)}`, commitToHistory);
|
|
4694
|
+
return this.getLayerEffects(id);
|
|
4695
|
+
}
|
|
4696
|
+
/** Removes one effect (one undo step). Returns the layer's remaining effects. */
|
|
4697
|
+
removeLayerEffect(id, effectId) {
|
|
4698
|
+
const current = this.effectsLayer(id, "change its effects").effects;
|
|
4699
|
+
const i = this.requireEffect(id, effectId);
|
|
4700
|
+
this.writeLayerEffects(id, { ...current, items: current.items.filter((e) => e.id !== effectId) }, `Remove ${layerEffectLabel(current.items[i].kind)}`, true);
|
|
4701
|
+
return this.getLayerEffects(id);
|
|
4702
|
+
}
|
|
4703
|
+
/** Turns all of a layer's effects on or off and, when given, sets Scale Effects (0.01..10). One undo step. */
|
|
4704
|
+
setLayerEffectsEnabled(id, enabled, scale, commitToHistory = true) {
|
|
4705
|
+
const current = this.effectsLayer(id, "change its effects").effects ?? { enabled: true, items: [] };
|
|
4706
|
+
if (scale !== void 0 && !(Number.isFinite(scale) && scale >= 0.01 && scale <= 10)) throw new PaintEditorError(`scale must be from 0.01 to 10 (1 = 100 %), got ${scale}.`);
|
|
4707
|
+
const next = { ...current, enabled };
|
|
4708
|
+
if (scale !== void 0) {
|
|
4709
|
+
if (scale === 1) delete next.scale;
|
|
4710
|
+
else next.scale = scale;
|
|
4711
|
+
}
|
|
4712
|
+
this.writeLayerEffects(id, next, "Layer effects", commitToHistory);
|
|
4713
|
+
return this.getLayerEffects(id);
|
|
4714
|
+
}
|
|
4715
|
+
/** Replaces a layer's effects, validated (`null` removes them). One undo step. Returns the effects. */
|
|
4716
|
+
setLayerEffects(id, effects, commitToHistory = true) {
|
|
4717
|
+
this.effectsLayer(id, "change its effects");
|
|
4718
|
+
if (effects) effectGuard(() => validateLayerEffects(effects));
|
|
4719
|
+
this.writeLayerEffects(id, effects, "Layer effects", commitToHistory);
|
|
4720
|
+
return this.getLayerEffects(id);
|
|
4721
|
+
}
|
|
4722
|
+
/** Document patterns for pattern effects besides the built-in ones: none (paint documents bring none). */
|
|
4723
|
+
getDocumentPatterns() {
|
|
4724
|
+
return [];
|
|
4725
|
+
}
|
|
4726
|
+
effectsLayer(id, action) {
|
|
4727
|
+
this.requireIdle("changing layer effects");
|
|
4728
|
+
const l = this.requireLayer(id);
|
|
4729
|
+
if (isLayerLocked(l, "all")) throw new PaintEditorError(`Layer "${l.name}" is fully locked, so it can't ${action}; unlock it first (locks {all: false}).`);
|
|
4730
|
+
return l;
|
|
4731
|
+
}
|
|
4732
|
+
requireEffect(id, effectId) {
|
|
4733
|
+
const l = this.requireLayer(id);
|
|
4734
|
+
const items = l.effects?.items ?? [];
|
|
4735
|
+
const i = items.findIndex((e) => e.id === effectId);
|
|
4736
|
+
if (i === -1) throw new PaintEditorError(`Layer "${l.name}" has no effect "${effectId}". Effects: ${items.map((e) => `${e.id} (${e.kind})`).join(", ") || "none"}; call list_layer_effects.`);
|
|
4737
|
+
return i;
|
|
4738
|
+
}
|
|
4739
|
+
/** Writes a layer's effects (dropped when empty, enabled and unscaled), as an undo step or folded into the last one. */
|
|
4740
|
+
writeLayerEffects(id, effects, label, commitToHistory) {
|
|
4741
|
+
const next = { ...this.requireLayer(id) };
|
|
4742
|
+
if (effects && (effects.items.length || effects.psd || effects.scale !== void 0 || !effects.enabled)) next.effects = effects;
|
|
4743
|
+
else delete next.effects;
|
|
4744
|
+
this.commitOrAmend(label, { ...this.doc, layers: mapLayer(this.doc.layers, id, () => next) }, commitToHistory);
|
|
4745
|
+
}
|
|
4633
4746
|
removeLayer(id) {
|
|
4634
4747
|
this.requireIdle("removing a layer");
|
|
4635
4748
|
const l = this.requireLayer(id);
|
|
@@ -9001,17 +9114,39 @@ var PARTICLE_BRUSH = {
|
|
|
9001
9114
|
type: "object",
|
|
9002
9115
|
description: "Particle engine settings (procedural brushes). Missing fields keep their current values.",
|
|
9003
9116
|
properties: {
|
|
9004
|
-
kind: {
|
|
9005
|
-
|
|
9117
|
+
kind: {
|
|
9118
|
+
type: "string",
|
|
9119
|
+
enum: [...PARTICLE_KINDS],
|
|
9120
|
+
description: "string: strands of masses on springs dragged behind the pen (hair, fur, wet string); spring: particles on springs to the pen, overshooting and wobbling; orbit: particles circling the pen (curls); flow: particles thrown off the pen into a swirling flow until they fade; elastic: strands whose links keep a rest length, stretching when the pen is fast and snapping back; fuzzy: winding strings, each strand waving out of step with the others (always waves by at least 0.12 of the tip size)."
|
|
9121
|
+
},
|
|
9122
|
+
render: {
|
|
9123
|
+
type: "string",
|
|
9124
|
+
enum: [...PARTICLE_RENDERS],
|
|
9125
|
+
description: "dot: a mark at each step (round, or dotTextures); line: each particle draws a continuous line; sequence: beads spaced along each path (round, or dotTextures); ribbon: the band between neighbouring particles' paths filled (ribbonFill), with the paths drawn as its edges at the mark size (flame, silk, smoke)."
|
|
9126
|
+
},
|
|
9006
9127
|
count: { type: "integer", minimum: 1, maximum: 64, description: "Particles (flow: how many are thrown per tip of travel)." },
|
|
9007
|
-
size: { type: "number", minimum: 0, maximum: 1, description: "Mark size as a fraction of the tip size." },
|
|
9008
|
-
spread: { type: "number", minimum: 0, maximum: 1, description: "How far apart the particles start, as a fraction of the tip size." },
|
|
9009
|
-
stiffness: { type: "number", minimum: 0, maximum: 1, description: "Spring and
|
|
9128
|
+
size: { type: "number", minimum: 0, maximum: 1, description: "Mark size as a fraction of the tip size (ribbon: the width of its edges)." },
|
|
9129
|
+
spread: { type: "number", minimum: 0, maximum: 1, description: "How far apart the particles start, as a fraction of the tip size (ribbon: how wide it is)." },
|
|
9130
|
+
stiffness: { type: "number", minimum: 0, maximum: 1, description: "Spring, string and elastic stiffness: stiff follows closely (elastic: snaps back faster), soft swings wide." },
|
|
9010
9131
|
damping: { type: "number", minimum: 0, maximum: 1, description: "How quickly motion dies down." },
|
|
9011
9132
|
speed: { type: "number", minimum: 0, maximum: 1, description: "Orbit and flow speed." },
|
|
9012
9133
|
gravity: { type: "number", minimum: -1, maximum: 1, description: "Pull downwards (negative: upwards)." },
|
|
9013
9134
|
life: { type: "number", minimum: 0, maximum: 1, description: "Flow particles: how long they live." },
|
|
9014
|
-
interval: { type: "number", minimum: 0, maximum: 1, description: "Sequence: spacing of the beads." }
|
|
9135
|
+
interval: { type: "number", minimum: 0, maximum: 1, description: "Sequence: spacing of the beads." },
|
|
9136
|
+
ribbonFill: { type: "number", minimum: 0, maximum: 1, description: "Ribbon: opacity of the fill between the paths, relative to their edges (default 0.5; 0 draws only the edges)." },
|
|
9137
|
+
ribbonPairs: { type: "string", enum: [...RIBBON_PAIRS], description: "Ribbon: adjacent (default) joins each particle to its neighbour around the pen; first-last makes one band between the outermost two." },
|
|
9138
|
+
waveAmplitude: { type: "number", minimum: 0, maximum: 1, description: "Wave: how far each particle's drawn place swings sideways to the stroke, as a fraction of the tip size (default 0: no wave). Works with every kind and render." },
|
|
9139
|
+
waveLength: { type: "number", minimum: 4, maximum: 2e3, description: "Wave: wavelength in px of pen travel (default 60)." },
|
|
9140
|
+
waveSpeed: { type: "number", minimum: 0, maximum: 1, description: "Wave: how fast the waves run, by stroke time (0 default: they stand still on the path; 1: about four a second, so slow strokes wave more). Strokes without times run at a nominal 0.5 px/ms." },
|
|
9141
|
+
wavePhaseJitter: { type: "number", minimum: 0, maximum: 1, description: "Wave: how far out of step the particles' waves are (default 0: all in step, a wavy band; 1: random phases, tangled strands)." },
|
|
9142
|
+
dotTextures: {
|
|
9143
|
+
type: "array",
|
|
9144
|
+
maxItems: MAX_DOT_TEXTURES,
|
|
9145
|
+
items: { type: "string", enum: [...STAMP_KINDS], description: "A stamp." },
|
|
9146
|
+
description: `Dot and sequence renders: up to ${MAX_DOT_TEXTURES} sticker stamps (list_stickers) drawn instead of round marks; repeats are allowed (they weight the order). Empty (default): round marks.`
|
|
9147
|
+
},
|
|
9148
|
+
dotTextureOrder: { type: "string", enum: [...DOT_TEXTURE_ORDERS], description: "Textured dots: sequence (default) uses the stamps in turn; random picks one per dot (seeded, so replays are exact)." },
|
|
9149
|
+
dotRotation: { type: "string", enum: [...DOT_ROTATIONS], description: "Textured dots: follow turns each with its particle's motion; random (default) turns each at random; fixed uses the tip angle." }
|
|
9015
9150
|
},
|
|
9016
9151
|
additionalProperties: false
|
|
9017
9152
|
};
|
|
@@ -9431,7 +9566,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
9431
9566
|
add({
|
|
9432
9567
|
name: "list_brushes",
|
|
9433
9568
|
title: "List brushes",
|
|
9434
|
-
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), light paint (category light: glow gel, neon marker, light airbrush, spark trail, glow wash; light is the amount they lay) 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.",
|
|
9569
|
+
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), light paint (category light: glow gel, neon marker, light airbrush, spark trail, light ribbon, flame ribbon, neon strand, smoke ribbon, bokeh dots, twinkle sparks, glow wash, highlighter; light is the amount they lay) 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.",
|
|
9435
9570
|
inputSchema: EMPTY3,
|
|
9436
9571
|
annotations: { title: "List brushes", ...READ6 },
|
|
9437
9572
|
handler: () => {
|
|
@@ -9943,6 +10078,19 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
9943
10078
|
tools.push(...createPaintReplayTools(c));
|
|
9944
10079
|
tools.push(...createPaintStickerTools(c));
|
|
9945
10080
|
tools.push(...createPigmentTools());
|
|
10081
|
+
tools.push(
|
|
10082
|
+
...createLayerEffectTools(
|
|
10083
|
+
{
|
|
10084
|
+
requireLayer: (id) => void c.getLayerEffects(id),
|
|
10085
|
+
getLayerEffects: (id) => c.getLayerEffects(id),
|
|
10086
|
+
addLayerEffect: (id, kind, settings, index) => c.addLayerEffect(id, kind, settings, index),
|
|
10087
|
+
updateLayerEffect: (id, effectId, changes) => c.updateLayerEffect(id, effectId, changes),
|
|
10088
|
+
removeLayerEffect: (id, effectId) => c.removeLayerEffect(id, effectId),
|
|
10089
|
+
setLayerEffectsEnabled: (id, enabled, scale) => c.setLayerEffectsEnabled(id, enabled, scale)
|
|
10090
|
+
},
|
|
10091
|
+
{ layers: "a pixel, stroke, sticker or group layer", error: (m) => new PaintEditorError(m) }
|
|
10092
|
+
)
|
|
10093
|
+
);
|
|
9946
10094
|
const exclude = new Set(options.exclude ?? []);
|
|
9947
10095
|
const description = "Natural-media painting: paper and grain, dry and wet brushes, the brush library, strokes as data, pigment mixing, stencils, guides, layers and export.";
|
|
9948
10096
|
return withPaintPlayback({
|
|
@@ -10413,7 +10561,12 @@ function engineControls(brush) {
|
|
|
10413
10561
|
{ path: "particle.speed", label: "Speed", min: 0, max: 1, step: 0.01 },
|
|
10414
10562
|
{ path: "particle.gravity", label: "Gravity", min: -1, max: 1, step: 0.01 },
|
|
10415
10563
|
{ path: "particle.life", label: "Life", min: 0, max: 1, step: 0.01 },
|
|
10416
|
-
{ path: "particle.interval", label: "Bead spacing", min: 0, max: 1, step: 0.01 }
|
|
10564
|
+
{ path: "particle.interval", label: "Bead spacing", min: 0, max: 1, step: 0.01 },
|
|
10565
|
+
{ path: "particle.ribbonFill", label: "Ribbon fill", min: 0, max: 1, step: 0.01 },
|
|
10566
|
+
{ path: "particle.waveAmplitude", label: "Wave", min: 0, max: 1, step: 0.01 },
|
|
10567
|
+
{ path: "particle.waveLength", label: "Wavelength", min: 4, max: 400, step: 1 },
|
|
10568
|
+
{ path: "particle.waveSpeed", label: "Wave speed", min: 0, max: 1, step: 0.01 },
|
|
10569
|
+
{ path: "particle.wavePhaseJitter", label: "Wave scatter", min: 0, max: 1, step: 0.01 }
|
|
10417
10570
|
],
|
|
10418
10571
|
mixingPath: null,
|
|
10419
10572
|
dirty: false,
|
|
@@ -10628,13 +10781,20 @@ var LIGHT_SLIDERS = [
|
|
|
10628
10781
|
{ path: "elevation", label: "Light height", min: 5, max: 90, step: 1 },
|
|
10629
10782
|
{ path: "relief", label: "Relief", min: 0, max: 1, step: 0.01 }
|
|
10630
10783
|
];
|
|
10631
|
-
var PARTICLE_KIND_LABELS = { string: "Strings", spring: "Springs", orbit: "Orbits", flow: "Flow" };
|
|
10632
|
-
var PARTICLE_RENDER_LABELS = { dot: "Dots", line: "Lines", sequence: "Beads" };
|
|
10784
|
+
var PARTICLE_KIND_LABELS = { string: "Strings", spring: "Springs", orbit: "Orbits", flow: "Flow", elastic: "Elastic", fuzzy: "Fuzzy" };
|
|
10785
|
+
var PARTICLE_RENDER_LABELS = { dot: "Dots", line: "Lines", sequence: "Beads", ribbon: "Ribbon" };
|
|
10786
|
+
var RIBBON_PAIR_LABELS = { adjacent: "Neighbours", "first-last": "Outermost" };
|
|
10787
|
+
var DOT_TEXTURE_ORDER_LABELS = { sequence: "In turn", random: "Random" };
|
|
10788
|
+
var DOT_ROTATION_LABELS = { follow: "Follow motion", random: "Random", fixed: "Tip angle" };
|
|
10633
10789
|
var STAMP_LABELS = { dot: "Dot", star: "Star", heart: "Heart", leaf: "Leaf", flower: "Flower", confetti: "Confetti", splat: "Splat", sparkle: "Sparkle", ring: "Ring" };
|
|
10634
10790
|
function toggleStamp(stamps, stamp) {
|
|
10635
10791
|
const next = stamps.includes(stamp) ? stamps.filter((s) => s !== stamp) : [...stamps, stamp];
|
|
10636
10792
|
return next.length ? next : [stamp];
|
|
10637
10793
|
}
|
|
10794
|
+
function toggleDotTexture(textures, stamp) {
|
|
10795
|
+
if (textures.includes(stamp)) return textures.filter((s) => s !== stamp);
|
|
10796
|
+
return textures.length < MAX_DOT_TEXTURES ? [...textures, stamp] : [...textures];
|
|
10797
|
+
}
|
|
10638
10798
|
var STROKE_SLIDERS = [
|
|
10639
10799
|
{ path: "tip.size", label: "Size", min: 1, max: 400, step: 1 },
|
|
10640
10800
|
{ path: "tip.opacity", label: "Opacity", min: 0, max: 1, step: 0.01 },
|
|
@@ -10957,6 +11117,10 @@ export {
|
|
|
10957
11117
|
DEFAULT_VIEW,
|
|
10958
11118
|
DEFAULT_WET_BRUSH,
|
|
10959
11119
|
DEFAULT_WET_PARAMS,
|
|
11120
|
+
DOT_ROTATIONS,
|
|
11121
|
+
DOT_ROTATION_LABELS,
|
|
11122
|
+
DOT_TEXTURE_ORDERS,
|
|
11123
|
+
DOT_TEXTURE_ORDER_LABELS,
|
|
10960
11124
|
DryStroke,
|
|
10961
11125
|
EFFECT_MAP_REPEATS,
|
|
10962
11126
|
EFFECT_STRIP_HEIGHT,
|
|
@@ -10974,6 +11138,7 @@ export {
|
|
|
10974
11138
|
MAX_BRUSH_SIZE,
|
|
10975
11139
|
MAX_CANVAS_SIDE,
|
|
10976
11140
|
MAX_COORDINATE,
|
|
11141
|
+
MAX_DOT_TEXTURES,
|
|
10977
11142
|
MAX_IMAGE_SIDE,
|
|
10978
11143
|
MAX_LIVE_EFFECT_SOURCES,
|
|
10979
11144
|
MAX_ZOOM,
|
|
@@ -11033,6 +11198,8 @@ export {
|
|
|
11033
11198
|
PenDabWalker,
|
|
11034
11199
|
PenSmoother,
|
|
11035
11200
|
PerspectiveSnapper,
|
|
11201
|
+
RIBBON_PAIRS,
|
|
11202
|
+
RIBBON_PAIR_LABELS,
|
|
11036
11203
|
SNAP_DECIDE_DISTANCE,
|
|
11037
11204
|
STAMP_KINDS,
|
|
11038
11205
|
STAMP_LABELS,
|
|
@@ -11061,6 +11228,7 @@ export {
|
|
|
11061
11228
|
WET_TOOL_LABELS,
|
|
11062
11229
|
WetStrokeDriver,
|
|
11063
11230
|
addPaintGradientStop,
|
|
11231
|
+
addPolygonArea,
|
|
11064
11232
|
affineCopy,
|
|
11065
11233
|
applyAffine,
|
|
11066
11234
|
backlightOf,
|
|
@@ -11238,6 +11406,7 @@ export {
|
|
|
11238
11406
|
timeLapseVideo,
|
|
11239
11407
|
timeLapseZip,
|
|
11240
11408
|
toStrokePoint,
|
|
11409
|
+
toggleDotTexture,
|
|
11241
11410
|
toggleStamp,
|
|
11242
11411
|
translateStencil,
|
|
11243
11412
|
updatePaintGradientStop,
|
|
@@ -6,8 +6,12 @@
|
|
|
6
6
|
* rules. `string`: strands of masses on springs, dragged behind the pen like hair or wet string;
|
|
7
7
|
* `spring`: each particle tied to the pen by a spring, overshooting and wobbling; `orbit`:
|
|
8
8
|
* particles circling the pen (spirograph curls); `flow`: particles thrown off the pen and carried
|
|
9
|
-
* by a swirling flow field until they fade
|
|
10
|
-
* `
|
|
9
|
+
* by a swirling flow field until they fade; `elastic`: strands whose masses keep a rest length,
|
|
10
|
+
* stretching when the pen is fast and snapping back; `fuzzy`: strings that wind, each strand
|
|
11
|
+
* waving out of step with the others. Each is drawn as `dot`s (round, or stamped with up to four
|
|
12
|
+
* textures), a continuous `line`, a `sequence` of beads spaced along its path, or a `ribbon`:
|
|
13
|
+
* the area between neighbouring particles' paths filled, with their paths as its edges. Any kind
|
|
14
|
+
* can wave: each particle's drawn place moves sideways on a sine along the pen's path.
|
|
11
15
|
* - **Bristle brushes** (`engine: 'bristle'`): a tuft of bristles, each its own thin line. Pressure
|
|
12
16
|
* splays the tuft, tilt flattens it, and bristles lag behind the tip by their stiffness, so they
|
|
13
17
|
* bend into curves at turns; each bristle carries its own paint and runs dry at its own pace, so
|
|
@@ -19,11 +23,12 @@
|
|
|
19
23
|
*/
|
|
20
24
|
import { CoverageMap } from '../paint/brush.cjs';
|
|
21
25
|
import { type PaintGradientSource } from '../paint/gradient-paint.cjs';
|
|
22
|
-
import type
|
|
26
|
+
import { type RasterRect } from '../raster/surface.cjs';
|
|
23
27
|
import type { PaintBrush } from './brushes.cjs';
|
|
24
28
|
import { type EffectPaintPixels } from './effect-paint.cjs';
|
|
25
29
|
import { PenDabWalker, PenSmoother, type PenDab, type PenPoint, type StrokeCopy } from './engine.cjs';
|
|
26
30
|
import { type PaperGrain } from './paper.cjs';
|
|
31
|
+
import { type StampKind } from './stickers.cjs';
|
|
27
32
|
export interface ProceduralStrokeOptions {
|
|
28
33
|
grain: PaperGrain | null;
|
|
29
34
|
paperDepth: number;
|
|
@@ -71,13 +76,36 @@ declare abstract class MarkStroke implements CoverageStroke {
|
|
|
71
76
|
protected abstract advance(copy: number, d: PenDab, rects: RasterRect[]): void;
|
|
72
77
|
/** Stamps one round mark of radius `r` at (x, y) with flow `alpha` towards `opacity`. */
|
|
73
78
|
protected mark(x: number, y: number, r: number, alpha: number, opacity: number, pressure: number, rects: RasterRect[]): void;
|
|
79
|
+
/**
|
|
80
|
+
* Stamps a textured mark: `kind` from the sticker stamps over a square of half-size `r`, turned
|
|
81
|
+
* `rotation` radians clockwise, with flow `alpha` towards `opacity`. Small marks are supersampled.
|
|
82
|
+
*/
|
|
83
|
+
protected stamp(x: number, y: number, r: number, kind: StampKind, rotation: number, seed: number, alpha: number, opacity: number, pressure: number, rects: RasterRect[]): void;
|
|
84
|
+
/** A scratch buffer of `n` values (reused; valid until the next mark). */
|
|
85
|
+
protected scratch(n: number): Float32Array;
|
|
86
|
+
/**
|
|
87
|
+
* Lays coverage `vals` over `rect` towards `opacity`: paper grain, then effect paint or the
|
|
88
|
+
* colour of a mark at (x, y) (gradient paint), into the stroke's coverage.
|
|
89
|
+
*/
|
|
90
|
+
protected lay(rect: RasterRect, vals: Float32Array, opacity: number, pressure: number, x: number, y: number, rects: RasterRect[]): void;
|
|
74
91
|
/** Marks along a segment, spaced by a fraction of the radius. */
|
|
75
92
|
protected segment(x0: number, y0: number, x1: number, y1: number, r: number, alpha: number, opacity: number, pressure: number, rects: RasterRect[]): void;
|
|
76
93
|
}
|
|
77
|
-
export type ParticleKind = 'string' | 'spring' | 'orbit' | 'flow';
|
|
94
|
+
export type ParticleKind = 'string' | 'spring' | 'orbit' | 'flow' | 'elastic' | 'fuzzy';
|
|
78
95
|
export declare const PARTICLE_KINDS: readonly ParticleKind[];
|
|
79
|
-
export type ParticleRender = 'dot' | 'line' | 'sequence';
|
|
96
|
+
export type ParticleRender = 'dot' | 'line' | 'sequence' | 'ribbon';
|
|
80
97
|
export declare const PARTICLE_RENDERS: readonly ParticleRender[];
|
|
98
|
+
/** Ribbon: which particles' paths are joined (`adjacent`: each to its neighbour; `first-last`: one band between the outermost two). */
|
|
99
|
+
export type RibbonPairs = 'adjacent' | 'first-last';
|
|
100
|
+
export declare const RIBBON_PAIRS: readonly RibbonPairs[];
|
|
101
|
+
/** Textured dots: the stamps in turn, or one picked at random (seeded) per dot. */
|
|
102
|
+
export type DotTextureOrder = 'sequence' | 'random';
|
|
103
|
+
export declare const DOT_TEXTURE_ORDERS: readonly DotTextureOrder[];
|
|
104
|
+
/** Textured dots: turned with the particle's motion, at random, or at the tip angle. */
|
|
105
|
+
export type DotRotation = 'follow' | 'random' | 'fixed';
|
|
106
|
+
export declare const DOT_ROTATIONS: readonly DotRotation[];
|
|
107
|
+
/** Textured dots: at most this many stamps. */
|
|
108
|
+
export declare const MAX_DOT_TEXTURES = 4;
|
|
81
109
|
/** Particle engine settings (each 0..1 unless noted). */
|
|
82
110
|
export interface ParticleSettings {
|
|
83
111
|
kind: ParticleKind;
|
|
@@ -100,17 +128,66 @@ export interface ParticleSettings {
|
|
|
100
128
|
life: number;
|
|
101
129
|
/** Sequence: spacing of the beads along each path. */
|
|
102
130
|
interval: number;
|
|
131
|
+
/** Ribbon: opacity of the fill between the paths, relative to their edges. */
|
|
132
|
+
ribbonFill: number;
|
|
133
|
+
/** Ribbon: which paths are joined. */
|
|
134
|
+
ribbonPairs: RibbonPairs;
|
|
135
|
+
/** Wave: how far each particle's drawn place swings sideways, as a fraction of the tip size (0: no wave). */
|
|
136
|
+
waveAmplitude: number;
|
|
137
|
+
/** Wave: wavelength in px of pen travel, 4..2000. */
|
|
138
|
+
waveLength: number;
|
|
139
|
+
/** Wave: how fast the waves run (0: they stand still on the path; 1: about four a second while the pen moves). */
|
|
140
|
+
waveSpeed: number;
|
|
141
|
+
/** Wave: how far out of step the particles' waves are (0: all in step; 1: random phases). */
|
|
142
|
+
wavePhaseJitter: number;
|
|
143
|
+
/** Dots and beads: up to four sticker stamps drawn instead of round marks (empty: round). */
|
|
144
|
+
dotTextures: StampKind[];
|
|
145
|
+
dotTextureOrder: DotTextureOrder;
|
|
146
|
+
dotRotation: DotRotation;
|
|
103
147
|
}
|
|
104
148
|
export declare const DEFAULT_PARTICLE: Readonly<ParticleSettings>;
|
|
105
149
|
export declare function normalizeParticle(raw?: Partial<ParticleSettings>, base?: ParticleSettings): ParticleSettings;
|
|
150
|
+
/**
|
|
151
|
+
* Adds the area polygon `pts` (x, y pairs, even-odd) covers in each pixel of `rect`, times `weight`, to `out`,
|
|
152
|
+
* exactly across each of `RIBBON_SUB` sub-rows per row. Points on an edge two polygons share
|
|
153
|
+
* count for exactly one of them, so polygons that tile a region add up to it with no seams.
|
|
154
|
+
*/
|
|
155
|
+
export declare function addPolygonArea(pts: readonly number[], rect: RasterRect, out: Float32Array, weight?: number): void;
|
|
106
156
|
/** A particle-brush stroke. */
|
|
107
157
|
export declare class ParticleStroke extends MarkStroke {
|
|
108
158
|
private readonly sets;
|
|
159
|
+
/** Ribbon pairs per copy: particles in their order around the pen (non-flow kinds). */
|
|
160
|
+
private readonly orders;
|
|
109
161
|
private readonly s;
|
|
110
162
|
private spawn;
|
|
163
|
+
/** Textured dots drawn so far, per copy (sequence order, random picks). */
|
|
164
|
+
private readonly dots;
|
|
165
|
+
/** Ribbon fill: the area each pixel has had so far this stroke (telescoped into the coverage). */
|
|
166
|
+
private readonly filled;
|
|
167
|
+
private pts;
|
|
111
168
|
constructor(brush: PaintBrush, options: ProceduralStrokeOptions);
|
|
169
|
+
/** Strand kinds: a chain of masses behind each particle. */
|
|
170
|
+
private get strands();
|
|
171
|
+
private newParticle;
|
|
112
172
|
private init;
|
|
173
|
+
/** The sideways wave of particle `p` at pen dab `d` (px along the normal to the pen's direction). */
|
|
174
|
+
private wave;
|
|
113
175
|
protected advance(copy: number, d: PenDab, rects: RasterRect[]): void;
|
|
176
|
+
/** A dot (or bead): round, or a textured stamp turned by the particle's motion (mx, my), at random or to the tip angle. */
|
|
177
|
+
private dot;
|
|
178
|
+
/**
|
|
179
|
+
* Ribbon fill for one step: the band between each pair of joined particles, from where they
|
|
180
|
+
* were drawn last step to where they are drawn now. Bands of one step, and of consecutive steps,
|
|
181
|
+
* share edges exactly, so the fill has no seams; it is telescoped into the coverage
|
|
182
|
+
* (`telescope`), so it does not build up at the joins.
|
|
183
|
+
*/
|
|
184
|
+
private ribbon;
|
|
185
|
+
/**
|
|
186
|
+
* Turns the area newly filled over `rect` into coverage to lay towards the fill's ceiling: the
|
|
187
|
+
* share of what was left unfilled, so after any number of bands (in any grouping) a pixel's
|
|
188
|
+
* coverage is the ceiling times the area filled there. False when nothing was added.
|
|
189
|
+
*/
|
|
190
|
+
private telescope;
|
|
114
191
|
}
|
|
115
192
|
/** Bristle engine settings (each 0..1 unless noted). */
|
|
116
193
|
export interface BristleSettings {
|
|
@@ -6,8 +6,12 @@
|
|
|
6
6
|
* rules. `string`: strands of masses on springs, dragged behind the pen like hair or wet string;
|
|
7
7
|
* `spring`: each particle tied to the pen by a spring, overshooting and wobbling; `orbit`:
|
|
8
8
|
* particles circling the pen (spirograph curls); `flow`: particles thrown off the pen and carried
|
|
9
|
-
* by a swirling flow field until they fade
|
|
10
|
-
* `
|
|
9
|
+
* by a swirling flow field until they fade; `elastic`: strands whose masses keep a rest length,
|
|
10
|
+
* stretching when the pen is fast and snapping back; `fuzzy`: strings that wind, each strand
|
|
11
|
+
* waving out of step with the others. Each is drawn as `dot`s (round, or stamped with up to four
|
|
12
|
+
* textures), a continuous `line`, a `sequence` of beads spaced along its path, or a `ribbon`:
|
|
13
|
+
* the area between neighbouring particles' paths filled, with their paths as its edges. Any kind
|
|
14
|
+
* can wave: each particle's drawn place moves sideways on a sine along the pen's path.
|
|
11
15
|
* - **Bristle brushes** (`engine: 'bristle'`): a tuft of bristles, each its own thin line. Pressure
|
|
12
16
|
* splays the tuft, tilt flattens it, and bristles lag behind the tip by their stiffness, so they
|
|
13
17
|
* bend into curves at turns; each bristle carries its own paint and runs dry at its own pace, so
|
|
@@ -19,11 +23,12 @@
|
|
|
19
23
|
*/
|
|
20
24
|
import { CoverageMap } from '../paint/brush.js';
|
|
21
25
|
import { type PaintGradientSource } from '../paint/gradient-paint.js';
|
|
22
|
-
import type
|
|
26
|
+
import { type RasterRect } from '../raster/surface.js';
|
|
23
27
|
import type { PaintBrush } from './brushes.js';
|
|
24
28
|
import { type EffectPaintPixels } from './effect-paint.js';
|
|
25
29
|
import { PenDabWalker, PenSmoother, type PenDab, type PenPoint, type StrokeCopy } from './engine.js';
|
|
26
30
|
import { type PaperGrain } from './paper.js';
|
|
31
|
+
import { type StampKind } from './stickers.js';
|
|
27
32
|
export interface ProceduralStrokeOptions {
|
|
28
33
|
grain: PaperGrain | null;
|
|
29
34
|
paperDepth: number;
|
|
@@ -71,13 +76,36 @@ declare abstract class MarkStroke implements CoverageStroke {
|
|
|
71
76
|
protected abstract advance(copy: number, d: PenDab, rects: RasterRect[]): void;
|
|
72
77
|
/** Stamps one round mark of radius `r` at (x, y) with flow `alpha` towards `opacity`. */
|
|
73
78
|
protected mark(x: number, y: number, r: number, alpha: number, opacity: number, pressure: number, rects: RasterRect[]): void;
|
|
79
|
+
/**
|
|
80
|
+
* Stamps a textured mark: `kind` from the sticker stamps over a square of half-size `r`, turned
|
|
81
|
+
* `rotation` radians clockwise, with flow `alpha` towards `opacity`. Small marks are supersampled.
|
|
82
|
+
*/
|
|
83
|
+
protected stamp(x: number, y: number, r: number, kind: StampKind, rotation: number, seed: number, alpha: number, opacity: number, pressure: number, rects: RasterRect[]): void;
|
|
84
|
+
/** A scratch buffer of `n` values (reused; valid until the next mark). */
|
|
85
|
+
protected scratch(n: number): Float32Array;
|
|
86
|
+
/**
|
|
87
|
+
* Lays coverage `vals` over `rect` towards `opacity`: paper grain, then effect paint or the
|
|
88
|
+
* colour of a mark at (x, y) (gradient paint), into the stroke's coverage.
|
|
89
|
+
*/
|
|
90
|
+
protected lay(rect: RasterRect, vals: Float32Array, opacity: number, pressure: number, x: number, y: number, rects: RasterRect[]): void;
|
|
74
91
|
/** Marks along a segment, spaced by a fraction of the radius. */
|
|
75
92
|
protected segment(x0: number, y0: number, x1: number, y1: number, r: number, alpha: number, opacity: number, pressure: number, rects: RasterRect[]): void;
|
|
76
93
|
}
|
|
77
|
-
export type ParticleKind = 'string' | 'spring' | 'orbit' | 'flow';
|
|
94
|
+
export type ParticleKind = 'string' | 'spring' | 'orbit' | 'flow' | 'elastic' | 'fuzzy';
|
|
78
95
|
export declare const PARTICLE_KINDS: readonly ParticleKind[];
|
|
79
|
-
export type ParticleRender = 'dot' | 'line' | 'sequence';
|
|
96
|
+
export type ParticleRender = 'dot' | 'line' | 'sequence' | 'ribbon';
|
|
80
97
|
export declare const PARTICLE_RENDERS: readonly ParticleRender[];
|
|
98
|
+
/** Ribbon: which particles' paths are joined (`adjacent`: each to its neighbour; `first-last`: one band between the outermost two). */
|
|
99
|
+
export type RibbonPairs = 'adjacent' | 'first-last';
|
|
100
|
+
export declare const RIBBON_PAIRS: readonly RibbonPairs[];
|
|
101
|
+
/** Textured dots: the stamps in turn, or one picked at random (seeded) per dot. */
|
|
102
|
+
export type DotTextureOrder = 'sequence' | 'random';
|
|
103
|
+
export declare const DOT_TEXTURE_ORDERS: readonly DotTextureOrder[];
|
|
104
|
+
/** Textured dots: turned with the particle's motion, at random, or at the tip angle. */
|
|
105
|
+
export type DotRotation = 'follow' | 'random' | 'fixed';
|
|
106
|
+
export declare const DOT_ROTATIONS: readonly DotRotation[];
|
|
107
|
+
/** Textured dots: at most this many stamps. */
|
|
108
|
+
export declare const MAX_DOT_TEXTURES = 4;
|
|
81
109
|
/** Particle engine settings (each 0..1 unless noted). */
|
|
82
110
|
export interface ParticleSettings {
|
|
83
111
|
kind: ParticleKind;
|
|
@@ -100,17 +128,66 @@ export interface ParticleSettings {
|
|
|
100
128
|
life: number;
|
|
101
129
|
/** Sequence: spacing of the beads along each path. */
|
|
102
130
|
interval: number;
|
|
131
|
+
/** Ribbon: opacity of the fill between the paths, relative to their edges. */
|
|
132
|
+
ribbonFill: number;
|
|
133
|
+
/** Ribbon: which paths are joined. */
|
|
134
|
+
ribbonPairs: RibbonPairs;
|
|
135
|
+
/** Wave: how far each particle's drawn place swings sideways, as a fraction of the tip size (0: no wave). */
|
|
136
|
+
waveAmplitude: number;
|
|
137
|
+
/** Wave: wavelength in px of pen travel, 4..2000. */
|
|
138
|
+
waveLength: number;
|
|
139
|
+
/** Wave: how fast the waves run (0: they stand still on the path; 1: about four a second while the pen moves). */
|
|
140
|
+
waveSpeed: number;
|
|
141
|
+
/** Wave: how far out of step the particles' waves are (0: all in step; 1: random phases). */
|
|
142
|
+
wavePhaseJitter: number;
|
|
143
|
+
/** Dots and beads: up to four sticker stamps drawn instead of round marks (empty: round). */
|
|
144
|
+
dotTextures: StampKind[];
|
|
145
|
+
dotTextureOrder: DotTextureOrder;
|
|
146
|
+
dotRotation: DotRotation;
|
|
103
147
|
}
|
|
104
148
|
export declare const DEFAULT_PARTICLE: Readonly<ParticleSettings>;
|
|
105
149
|
export declare function normalizeParticle(raw?: Partial<ParticleSettings>, base?: ParticleSettings): ParticleSettings;
|
|
150
|
+
/**
|
|
151
|
+
* Adds the area polygon `pts` (x, y pairs, even-odd) covers in each pixel of `rect`, times `weight`, to `out`,
|
|
152
|
+
* exactly across each of `RIBBON_SUB` sub-rows per row. Points on an edge two polygons share
|
|
153
|
+
* count for exactly one of them, so polygons that tile a region add up to it with no seams.
|
|
154
|
+
*/
|
|
155
|
+
export declare function addPolygonArea(pts: readonly number[], rect: RasterRect, out: Float32Array, weight?: number): void;
|
|
106
156
|
/** A particle-brush stroke. */
|
|
107
157
|
export declare class ParticleStroke extends MarkStroke {
|
|
108
158
|
private readonly sets;
|
|
159
|
+
/** Ribbon pairs per copy: particles in their order around the pen (non-flow kinds). */
|
|
160
|
+
private readonly orders;
|
|
109
161
|
private readonly s;
|
|
110
162
|
private spawn;
|
|
163
|
+
/** Textured dots drawn so far, per copy (sequence order, random picks). */
|
|
164
|
+
private readonly dots;
|
|
165
|
+
/** Ribbon fill: the area each pixel has had so far this stroke (telescoped into the coverage). */
|
|
166
|
+
private readonly filled;
|
|
167
|
+
private pts;
|
|
111
168
|
constructor(brush: PaintBrush, options: ProceduralStrokeOptions);
|
|
169
|
+
/** Strand kinds: a chain of masses behind each particle. */
|
|
170
|
+
private get strands();
|
|
171
|
+
private newParticle;
|
|
112
172
|
private init;
|
|
173
|
+
/** The sideways wave of particle `p` at pen dab `d` (px along the normal to the pen's direction). */
|
|
174
|
+
private wave;
|
|
113
175
|
protected advance(copy: number, d: PenDab, rects: RasterRect[]): void;
|
|
176
|
+
/** A dot (or bead): round, or a textured stamp turned by the particle's motion (mx, my), at random or to the tip angle. */
|
|
177
|
+
private dot;
|
|
178
|
+
/**
|
|
179
|
+
* Ribbon fill for one step: the band between each pair of joined particles, from where they
|
|
180
|
+
* were drawn last step to where they are drawn now. Bands of one step, and of consecutive steps,
|
|
181
|
+
* share edges exactly, so the fill has no seams; it is telescoped into the coverage
|
|
182
|
+
* (`telescope`), so it does not build up at the joins.
|
|
183
|
+
*/
|
|
184
|
+
private ribbon;
|
|
185
|
+
/**
|
|
186
|
+
* Turns the area newly filled over `rect` into coverage to lay towards the fill's ceiling: the
|
|
187
|
+
* share of what was left unfilled, so after any number of bands (in any grouping) a pixel's
|
|
188
|
+
* coverage is the ceiling times the area filled there. False when nothing was added.
|
|
189
|
+
*/
|
|
190
|
+
private telescope;
|
|
114
191
|
}
|
|
115
192
|
/** Bristle engine settings (each 0..1 unless noted). */
|
|
116
193
|
export interface BristleSettings {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { AgentToolProvider } from '../tools/registry.cjs';
|
|
8
8
|
import { type PaintOrigin } from './hosted.cjs';
|
|
9
|
-
import type
|
|
9
|
+
import { type PaintEditorController } from './controller.cjs';
|
|
10
10
|
import type { PaintDirector } from './presence.cjs';
|
|
11
11
|
import type { PresenceActorInfo } from '../animation/presence.cjs';
|
|
12
12
|
export interface PaintEditorToolProviderOptions {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { AgentToolProvider } from '../tools/registry.js';
|
|
8
8
|
import { type PaintOrigin } from './hosted.js';
|
|
9
|
-
import type
|
|
9
|
+
import { type PaintEditorController } from './controller.js';
|
|
10
10
|
import type { PaintDirector } from './presence.js';
|
|
11
11
|
import type { PresenceActorInfo } from '../animation/presence.js';
|
|
12
12
|
export interface PaintEditorToolProviderOptions {
|