@slithy/prim-lib 0.7.0 → 0.8.1
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/README.md +105 -3
- package/dist/index.d.ts +18 -0
- package/dist/index.js +53 -12
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
Core engine for primitive-based image reconstruction. Consumed by `@slithy/prim-interface`.
|
|
4
4
|
|
|
5
|
+
## License
|
|
6
|
+
|
|
7
|
+
This package is dual-licensed.
|
|
8
|
+
|
|
9
|
+
- Public use is licensed under `PolyForm-Noncommercial-1.0.0` in [LICENSE](./LICENSE).
|
|
10
|
+
- Separate commercial use rights, if any, are granted only by a separate written commercial license. The current repo-tracked commercial grant is in [LICENSE-COMMERCIAL.md](./LICENSE-COMMERCIAL.md).
|
|
11
|
+
|
|
12
|
+
This package also includes upstream MIT attributions for `primitive` and `primitive.js` in its `LICENSE` file.
|
|
13
|
+
|
|
5
14
|
## What it does
|
|
6
15
|
|
|
7
16
|
Reconstructs an image by iteratively placing geometric shapes. Each step evaluates candidate shapes, mutates them, picks the one that most reduces the difference from the target, and draws it onto the working canvas.
|
|
@@ -24,10 +33,13 @@ Fixed shapes (class constructors, pass directly in `shapeTypes`):
|
|
|
24
33
|
Configurable shapes (factory functions that return a constructor):
|
|
25
34
|
|
|
26
35
|
- **`makeNGon(opts)`** — N-sided polygon; supports regular and irregular modes
|
|
27
|
-
- **`makeRect(opts)`** — axis-aligned or rotatable rectangle with independent width/height control
|
|
36
|
+
- **`makeRect(opts?)`** — axis-aligned or rotatable rectangle with independent width/height control
|
|
37
|
+
- **`makeCircle(opts?)`** — circle with configurable size range and pixel-area constraints
|
|
38
|
+
- **`makeEllipse(opts?)`** — ellipse with independent rx/ry ranges and optional aspect ratio lock
|
|
39
|
+
- **`makeGlyph(opts?)`** — Unicode character shape; measures glyph bounds at render time and serializes as SVG `<text>`
|
|
28
40
|
|
|
29
41
|
```ts
|
|
30
|
-
shapeTypes: [Triangle, makeNGon({ sides: 6, regular: true }),
|
|
42
|
+
shapeTypes: [Triangle, makeNGon({ sides: 6, regular: true }), makeCircle({ sizeRange: [5, 20] })]
|
|
31
43
|
```
|
|
32
44
|
|
|
33
45
|
### Factory options
|
|
@@ -46,6 +58,14 @@ interface NGonOptions {
|
|
|
46
58
|
sizeRange?: [number, number] // vertex radius range in compute-space px (default: [1, 20])
|
|
47
59
|
// regular: radius from center; irregular: scatter radius from first point
|
|
48
60
|
mutationScale?: number // max mutation step size in px (default: 20)
|
|
61
|
+
// pixel-area constraints (see Pixel-area constraints)
|
|
62
|
+
tonalRange?: [number, number]
|
|
63
|
+
invertTonal?: boolean
|
|
64
|
+
saturationRange?: [number, number]
|
|
65
|
+
invertSaturation?: boolean
|
|
66
|
+
hueCenter?: number
|
|
67
|
+
hueTolerance?: number
|
|
68
|
+
invertHue?: boolean
|
|
49
69
|
}
|
|
50
70
|
```
|
|
51
71
|
|
|
@@ -60,9 +80,91 @@ interface RectOptions {
|
|
|
60
80
|
// e.g. 1.78 for 16:9, 1.33 for 4:3, 1.0 for square
|
|
61
81
|
rotatable?: boolean // random rotation per instance (default: false)
|
|
62
82
|
mutationScale?: number // max mutation step size in px (default: 20)
|
|
83
|
+
// pixel-area constraints (see Pixel-area constraints)
|
|
84
|
+
tonalRange?: [number, number]
|
|
85
|
+
invertTonal?: boolean
|
|
86
|
+
saturationRange?: [number, number]
|
|
87
|
+
invertSaturation?: boolean
|
|
88
|
+
hueCenter?: number
|
|
89
|
+
hueTolerance?: number
|
|
90
|
+
invertHue?: boolean
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**`CircleOptions`**
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
interface CircleOptions {
|
|
98
|
+
sizeRange?: [number, number] // radius range in compute-space px (default: [1, 20])
|
|
99
|
+
mutationScale?: number // max mutation step size in px (default: 20)
|
|
100
|
+
// pixel-area constraints (see Pixel-area constraints)
|
|
101
|
+
tonalRange?: [number, number]
|
|
102
|
+
invertTonal?: boolean
|
|
103
|
+
saturationRange?: [number, number]
|
|
104
|
+
invertSaturation?: boolean
|
|
105
|
+
hueCenter?: number
|
|
106
|
+
hueTolerance?: number
|
|
107
|
+
invertHue?: boolean
|
|
63
108
|
}
|
|
64
109
|
```
|
|
65
110
|
|
|
111
|
+
**`EllipseOptions`**
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
interface EllipseOptions {
|
|
115
|
+
rxRange?: [number, number] // x-radius range in compute-space px (default: [1, 20])
|
|
116
|
+
ryRange?: [number, number] // y-radius range in compute-space px (default: [1, 20])
|
|
117
|
+
// ignored when aspectRatio is set
|
|
118
|
+
aspectRatio?: number // rx ÷ ry; locks proportions, derives ry from rx
|
|
119
|
+
mutationScale?: number // max mutation step size in px (default: 20)
|
|
120
|
+
// pixel-area constraints (see Pixel-area constraints)
|
|
121
|
+
tonalRange?: [number, number]
|
|
122
|
+
invertTonal?: boolean
|
|
123
|
+
saturationRange?: [number, number]
|
|
124
|
+
invertSaturation?: boolean
|
|
125
|
+
hueCenter?: number
|
|
126
|
+
hueTolerance?: number
|
|
127
|
+
invertHue?: boolean
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**`GlyphOptions`**
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
interface GlyphOptions {
|
|
135
|
+
char?: string // Unicode character (default: '☺')
|
|
136
|
+
fontFamily?: string // CSS font-family string (default: 'sans-serif')
|
|
137
|
+
sizeRange?: [number, number] // font-size range in compute-space px (default: [10, 30])
|
|
138
|
+
mutationScale?: number // max mutation step size in px (default: 20)
|
|
139
|
+
// pixel-area constraints (see Pixel-area constraints)
|
|
140
|
+
tonalRange?: [number, number]
|
|
141
|
+
invertTonal?: boolean
|
|
142
|
+
saturationRange?: [number, number]
|
|
143
|
+
invertSaturation?: boolean
|
|
144
|
+
hueCenter?: number
|
|
145
|
+
hueTolerance?: number
|
|
146
|
+
invertHue?: boolean
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Pixel-area constraints
|
|
151
|
+
|
|
152
|
+
All factory shapes (`makeNGon`, `makeRect`, `makeCircle`, `makeEllipse`, `makeGlyph`) accept optional pixel-area constraint fields. Before rasterizing, the optimizer samples the pixels the shape would cover in the target image. If the area fails any active check, the shape is discarded without consuming the step budget. Shapes placed entirely outside the image bounds skip all checks.
|
|
153
|
+
|
|
154
|
+
**`tonalRange: [min, max]`** — BT.601 luma (0–255). Discard if the covered area's average luminance falls outside `[min, max]`. Set `invertTonal: true` to keep shapes *outside* the range instead.
|
|
155
|
+
|
|
156
|
+
**`saturationRange: [min, max]`** — HSL saturation (0–100). Discard if average saturation falls outside `[min, max]`. Set `invertSaturation: true` to invert.
|
|
157
|
+
|
|
158
|
+
**`hueCenter` / `hueTolerance`** — center hue (0–360°) and allowed angular deviation in degrees. Discard if the covered area's average hue differs from `hueCenter` by more than `hueTolerance`. Set `invertHue: true` to invert. Both fields must be set together to activate the hue check.
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
// circles only in bright areas; glyphs only in desaturated areas
|
|
162
|
+
shapeTypes: [
|
|
163
|
+
makeCircle({ sizeRange: [5, 20], tonalRange: [180, 255] }),
|
|
164
|
+
makeGlyph({ char: '·', saturationRange: [0, 20] }),
|
|
165
|
+
]
|
|
166
|
+
```
|
|
167
|
+
|
|
66
168
|
### Types
|
|
67
169
|
|
|
68
170
|
**`Cfg`** — runtime config (all fields required):
|
|
@@ -167,4 +269,4 @@ interface ReplayResult {
|
|
|
167
269
|
- When `shapeWeights` is provided (same length as `shapeTypes`), `Optimizer` builds a step plan at construction time: each shape type is allocated an exact number of slots proportional to its weight (using largest-remainder rounding), then the plan is Fisher-Yates shuffled. This guarantees the final shape distribution matches the weights, rather than just biasing random selection
|
|
168
270
|
- `PreCfg` exists to bridge the gap between call time (dimensions unknown) and runtime (dimensions set by `Canvas.original()` or `Canvas.fromBitmap()`)
|
|
169
271
|
- `Canvas.svgRoot()` is a static helper shared by `Canvas.empty()` and `replayOutput()` to create the SVG root with clip path and background fill; it uses DOM APIs and is main-thread only
|
|
170
|
-
- Factory shapes (`makeNGon`, `makeRect`) capture their options in a closure and return an inner class. The class carries a static `_shapeSpec` property (`{ f: string, o: opts }`) used by `runWorker` to serialize and reconstruct the factory call inside the worker
|
|
272
|
+
- Factory shapes (`makeNGon`, `makeRect`, `makeCircle`, `makeEllipse`, `makeGlyph`) capture their options in a closure and return an inner class. The class carries a static `_shapeSpec` property (`{ f: string, o: opts }`) used by `runWorker` to serialize and reconstruct the factory call inside the worker
|
package/dist/index.d.ts
CHANGED
|
@@ -24,9 +24,12 @@ interface ShapeImageData {
|
|
|
24
24
|
interface ShapeInterface {
|
|
25
25
|
bbox: Bbox;
|
|
26
26
|
tonalRange?: [number, number];
|
|
27
|
+
invertTonal?: boolean;
|
|
27
28
|
saturationRange?: [number, number];
|
|
29
|
+
invertSaturation?: boolean;
|
|
28
30
|
hueCenter?: number;
|
|
29
31
|
hueTolerance?: number;
|
|
32
|
+
invertHue?: boolean;
|
|
30
33
|
/** cfg is rarely used by implementations; accepts Partial to keep call sites flexible. */
|
|
31
34
|
mutate(cfg?: Partial<Cfg>): ShapeInterface;
|
|
32
35
|
toSVG(): SVGElement | undefined;
|
|
@@ -310,9 +313,12 @@ interface NGonOptions {
|
|
|
310
313
|
sizeRange?: [number, number];
|
|
311
314
|
mutationScale?: number;
|
|
312
315
|
tonalRange?: [number, number];
|
|
316
|
+
invertTonal?: boolean;
|
|
313
317
|
saturationRange?: [number, number];
|
|
318
|
+
invertSaturation?: boolean;
|
|
314
319
|
hueCenter?: number;
|
|
315
320
|
hueTolerance?: number;
|
|
321
|
+
invertHue?: boolean;
|
|
316
322
|
}
|
|
317
323
|
declare function makeNGon(opts: NGonOptions): new (w: number, h: number) => ShapeInterface;
|
|
318
324
|
interface RectOptions {
|
|
@@ -322,18 +328,24 @@ interface RectOptions {
|
|
|
322
328
|
rotatable?: boolean;
|
|
323
329
|
mutationScale?: number;
|
|
324
330
|
tonalRange?: [number, number];
|
|
331
|
+
invertTonal?: boolean;
|
|
325
332
|
saturationRange?: [number, number];
|
|
333
|
+
invertSaturation?: boolean;
|
|
326
334
|
hueCenter?: number;
|
|
327
335
|
hueTolerance?: number;
|
|
336
|
+
invertHue?: boolean;
|
|
328
337
|
}
|
|
329
338
|
declare function makeRect(opts?: Partial<RectOptions>): new (w: number, h: number) => ShapeInterface;
|
|
330
339
|
interface CircleOptions {
|
|
331
340
|
sizeRange?: [number, number];
|
|
332
341
|
mutationScale?: number;
|
|
333
342
|
tonalRange?: [number, number];
|
|
343
|
+
invertTonal?: boolean;
|
|
334
344
|
saturationRange?: [number, number];
|
|
345
|
+
invertSaturation?: boolean;
|
|
335
346
|
hueCenter?: number;
|
|
336
347
|
hueTolerance?: number;
|
|
348
|
+
invertHue?: boolean;
|
|
337
349
|
}
|
|
338
350
|
declare function makeCircle(opts?: Partial<CircleOptions>): new (w: number, h: number) => ShapeInterface;
|
|
339
351
|
interface EllipseOptions {
|
|
@@ -342,9 +354,12 @@ interface EllipseOptions {
|
|
|
342
354
|
aspectRatio?: number;
|
|
343
355
|
mutationScale?: number;
|
|
344
356
|
tonalRange?: [number, number];
|
|
357
|
+
invertTonal?: boolean;
|
|
345
358
|
saturationRange?: [number, number];
|
|
359
|
+
invertSaturation?: boolean;
|
|
346
360
|
hueCenter?: number;
|
|
347
361
|
hueTolerance?: number;
|
|
362
|
+
invertHue?: boolean;
|
|
348
363
|
}
|
|
349
364
|
declare function makeEllipse(opts?: Partial<EllipseOptions>): new (w: number, h: number) => ShapeInterface;
|
|
350
365
|
interface GlyphOptions {
|
|
@@ -353,9 +368,12 @@ interface GlyphOptions {
|
|
|
353
368
|
sizeRange?: [number, number];
|
|
354
369
|
mutationScale?: number;
|
|
355
370
|
tonalRange?: [number, number];
|
|
371
|
+
invertTonal?: boolean;
|
|
356
372
|
saturationRange?: [number, number];
|
|
373
|
+
invertSaturation?: boolean;
|
|
357
374
|
hueCenter?: number;
|
|
358
375
|
hueTolerance?: number;
|
|
376
|
+
invertHue?: boolean;
|
|
359
377
|
}
|
|
360
378
|
declare function makeGlyph(opts?: Partial<GlyphOptions>): new (w: number, h: number) => ShapeInterface;
|
|
361
379
|
declare class Debug extends Shape {
|
package/dist/index.js
CHANGED
|
@@ -382,7 +382,7 @@ var Step = class _Step {
|
|
|
382
382
|
compute(state) {
|
|
383
383
|
const pixels = state.canvas.node.width * state.canvas.node.height;
|
|
384
384
|
const offset = this.shape.bbox;
|
|
385
|
-
const { tonalRange, saturationRange, hueCenter, hueTolerance } = this.shape;
|
|
385
|
+
const { tonalRange, invertTonal, saturationRange, invertSaturation, hueCenter, hueTolerance, invertHue } = this.shape;
|
|
386
386
|
if (tonalRange || saturationRange || hueCenter !== void 0 && hueTolerance !== void 0) {
|
|
387
387
|
const { left, top, width, height } = offset;
|
|
388
388
|
const targetData = state.target.getImageData();
|
|
@@ -405,20 +405,25 @@ var Step = class _Step {
|
|
|
405
405
|
if (count > 0) {
|
|
406
406
|
if (tonalRange) {
|
|
407
407
|
const luma = (0.299 * rSum + 0.587 * gSum + 0.114 * bSum) / count;
|
|
408
|
-
|
|
408
|
+
const inRange = luma >= tonalRange[0] && luma <= tonalRange[1];
|
|
409
|
+
if (invertTonal ? inRange : !inRange) {
|
|
409
410
|
this.distance = Infinity;
|
|
410
411
|
return Promise.resolve(this);
|
|
411
412
|
}
|
|
412
413
|
}
|
|
413
414
|
if (saturationRange || hueCenter !== void 0 && hueTolerance !== void 0) {
|
|
414
415
|
const [h, s] = rgbToHsl(rSum / count, gSum / count, bSum / count);
|
|
415
|
-
if (saturationRange
|
|
416
|
-
|
|
417
|
-
|
|
416
|
+
if (saturationRange) {
|
|
417
|
+
const inRange = s >= saturationRange[0] && s <= saturationRange[1];
|
|
418
|
+
if (invertSaturation ? inRange : !inRange) {
|
|
419
|
+
this.distance = Infinity;
|
|
420
|
+
return Promise.resolve(this);
|
|
421
|
+
}
|
|
418
422
|
}
|
|
419
423
|
if (hueCenter !== void 0 && hueTolerance !== void 0) {
|
|
420
424
|
const diff = Math.abs((h - hueCenter + 180 + 360) % 360 - 180);
|
|
421
|
-
|
|
425
|
+
const inRange = diff <= hueTolerance;
|
|
426
|
+
if (invertHue ? inRange : !inRange) {
|
|
422
427
|
this.distance = Infinity;
|
|
423
428
|
return Promise.resolve(this);
|
|
424
429
|
}
|
|
@@ -1026,12 +1031,15 @@ function makeNGon(opts) {
|
|
|
1026
1031
|
static {
|
|
1027
1032
|
__name(this, "NGonRegular");
|
|
1028
1033
|
}
|
|
1029
|
-
static _ngonOpts = { sides, regular: true, rotatable, noise, sizeRange, mutationScale, startAngle, tonalRange: opts.tonalRange, saturationRange: opts.saturationRange, hueCenter: opts.hueCenter, hueTolerance: opts.hueTolerance };
|
|
1034
|
+
static _ngonOpts = { sides, regular: true, rotatable, noise, sizeRange, mutationScale, startAngle, tonalRange: opts.tonalRange, invertTonal: opts.invertTonal, saturationRange: opts.saturationRange, invertSaturation: opts.invertSaturation, hueCenter: opts.hueCenter, hueTolerance: opts.hueTolerance, invertHue: opts.invertHue };
|
|
1030
1035
|
static _shapeSpec = { f: "ngon", o: NGonRegular._ngonOpts };
|
|
1031
1036
|
tonalRange;
|
|
1037
|
+
invertTonal;
|
|
1032
1038
|
saturationRange;
|
|
1039
|
+
invertSaturation;
|
|
1033
1040
|
hueCenter;
|
|
1034
1041
|
hueTolerance;
|
|
1042
|
+
invertHue;
|
|
1035
1043
|
center;
|
|
1036
1044
|
r;
|
|
1037
1045
|
angle;
|
|
@@ -1040,9 +1048,12 @@ function makeNGon(opts) {
|
|
|
1040
1048
|
constructor(w, h) {
|
|
1041
1049
|
super(w, h);
|
|
1042
1050
|
this.tonalRange = opts.tonalRange;
|
|
1051
|
+
this.invertTonal = opts.invertTonal;
|
|
1043
1052
|
this.saturationRange = opts.saturationRange;
|
|
1053
|
+
this.invertSaturation = opts.invertSaturation;
|
|
1044
1054
|
this.hueCenter = opts.hueCenter;
|
|
1045
1055
|
this.hueTolerance = opts.hueTolerance;
|
|
1056
|
+
this.invertHue = opts.invertHue;
|
|
1046
1057
|
this.center = Shape.randomPoint(w, h);
|
|
1047
1058
|
this.r = Math.max(1, sizeRange[0] + ~~(Math.random() * (sizeRange[1] - sizeRange[0])));
|
|
1048
1059
|
this.angle = rotatable ? Math.random() * (2 * Math.PI / sides) : startAngle;
|
|
@@ -1138,19 +1149,25 @@ function makeNGon(opts) {
|
|
|
1138
1149
|
static {
|
|
1139
1150
|
__name(this, "NGonIrregular");
|
|
1140
1151
|
}
|
|
1141
|
-
static _ngonOpts = { sides, regular: false, convex, sizeRange, mutationScale, tonalRange: opts.tonalRange, saturationRange: opts.saturationRange, hueCenter: opts.hueCenter, hueTolerance: opts.hueTolerance };
|
|
1152
|
+
static _ngonOpts = { sides, regular: false, convex, sizeRange, mutationScale, tonalRange: opts.tonalRange, invertTonal: opts.invertTonal, saturationRange: opts.saturationRange, invertSaturation: opts.invertSaturation, hueCenter: opts.hueCenter, hueTolerance: opts.hueTolerance, invertHue: opts.invertHue };
|
|
1142
1153
|
static _shapeSpec = { f: "ngon", o: NGonIrregular._ngonOpts };
|
|
1143
1154
|
tonalRange;
|
|
1155
|
+
invertTonal;
|
|
1144
1156
|
saturationRange;
|
|
1157
|
+
invertSaturation;
|
|
1145
1158
|
hueCenter;
|
|
1146
1159
|
hueTolerance;
|
|
1160
|
+
invertHue;
|
|
1147
1161
|
points;
|
|
1148
1162
|
constructor(w, h) {
|
|
1149
1163
|
super(w, h);
|
|
1150
1164
|
this.tonalRange = opts.tonalRange;
|
|
1165
|
+
this.invertTonal = opts.invertTonal;
|
|
1151
1166
|
this.saturationRange = opts.saturationRange;
|
|
1167
|
+
this.invertSaturation = opts.invertSaturation;
|
|
1152
1168
|
this.hueCenter = opts.hueCenter;
|
|
1153
1169
|
this.hueTolerance = opts.hueTolerance;
|
|
1170
|
+
this.invertHue = opts.invertHue;
|
|
1154
1171
|
const first = Shape.randomPoint(w, h);
|
|
1155
1172
|
this.points = [first];
|
|
1156
1173
|
for (let i = 1; i < sides; i++) {
|
|
@@ -1222,12 +1239,15 @@ function makeRect(opts) {
|
|
|
1222
1239
|
static {
|
|
1223
1240
|
__name(this, "Rect");
|
|
1224
1241
|
}
|
|
1225
|
-
static _rectOpts = { widthRange, heightRange, aspectRatio, rotatable, mutationScale, tonalRange: opts?.tonalRange, saturationRange: opts?.saturationRange, hueCenter: opts?.hueCenter, hueTolerance: opts?.hueTolerance };
|
|
1242
|
+
static _rectOpts = { widthRange, heightRange, aspectRatio, rotatable, mutationScale, tonalRange: opts?.tonalRange, invertTonal: opts?.invertTonal, saturationRange: opts?.saturationRange, invertSaturation: opts?.invertSaturation, hueCenter: opts?.hueCenter, hueTolerance: opts?.hueTolerance, invertHue: opts?.invertHue };
|
|
1226
1243
|
static _shapeSpec = { f: "rect", o: Rect._rectOpts };
|
|
1227
1244
|
tonalRange;
|
|
1245
|
+
invertTonal;
|
|
1228
1246
|
saturationRange;
|
|
1247
|
+
invertSaturation;
|
|
1229
1248
|
hueCenter;
|
|
1230
1249
|
hueTolerance;
|
|
1250
|
+
invertHue;
|
|
1231
1251
|
center;
|
|
1232
1252
|
hw;
|
|
1233
1253
|
hh;
|
|
@@ -1235,9 +1255,12 @@ function makeRect(opts) {
|
|
|
1235
1255
|
constructor(w, h) {
|
|
1236
1256
|
super(w, h);
|
|
1237
1257
|
this.tonalRange = opts?.tonalRange;
|
|
1258
|
+
this.invertTonal = opts?.invertTonal;
|
|
1238
1259
|
this.saturationRange = opts?.saturationRange;
|
|
1260
|
+
this.invertSaturation = opts?.invertSaturation;
|
|
1239
1261
|
this.hueCenter = opts?.hueCenter;
|
|
1240
1262
|
this.hueTolerance = opts?.hueTolerance;
|
|
1263
|
+
this.invertHue = opts?.invertHue;
|
|
1241
1264
|
this.center = Shape.randomPoint(w, h);
|
|
1242
1265
|
this.hw = widthRange[0] + ~~(Math.random() * (widthRange[1] - widthRange[0]));
|
|
1243
1266
|
if (aspectRatio !== void 0) {
|
|
@@ -1350,20 +1373,26 @@ function makeCircle(opts) {
|
|
|
1350
1373
|
static {
|
|
1351
1374
|
__name(this, "MadeCircle");
|
|
1352
1375
|
}
|
|
1353
|
-
static _circleOpts = { sizeRange, mutationScale, tonalRange: opts?.tonalRange, saturationRange: opts?.saturationRange, hueCenter: opts?.hueCenter, hueTolerance: opts?.hueTolerance };
|
|
1376
|
+
static _circleOpts = { sizeRange, mutationScale, tonalRange: opts?.tonalRange, invertTonal: opts?.invertTonal, saturationRange: opts?.saturationRange, invertSaturation: opts?.invertSaturation, hueCenter: opts?.hueCenter, hueTolerance: opts?.hueTolerance, invertHue: opts?.invertHue };
|
|
1354
1377
|
static _shapeSpec = { f: "circle", o: MadeCircle._circleOpts };
|
|
1355
1378
|
tonalRange;
|
|
1379
|
+
invertTonal;
|
|
1356
1380
|
saturationRange;
|
|
1381
|
+
invertSaturation;
|
|
1357
1382
|
hueCenter;
|
|
1358
1383
|
hueTolerance;
|
|
1384
|
+
invertHue;
|
|
1359
1385
|
center;
|
|
1360
1386
|
r;
|
|
1361
1387
|
constructor(w, h) {
|
|
1362
1388
|
super(w, h);
|
|
1363
1389
|
this.tonalRange = opts?.tonalRange;
|
|
1390
|
+
this.invertTonal = opts?.invertTonal;
|
|
1364
1391
|
this.saturationRange = opts?.saturationRange;
|
|
1392
|
+
this.invertSaturation = opts?.invertSaturation;
|
|
1365
1393
|
this.hueCenter = opts?.hueCenter;
|
|
1366
1394
|
this.hueTolerance = opts?.hueTolerance;
|
|
1395
|
+
this.invertHue = opts?.invertHue;
|
|
1367
1396
|
this.center = Shape.randomPoint(w, h);
|
|
1368
1397
|
this.r = Math.max(1, sizeRange[0] + ~~(Math.random() * (sizeRange[1] - sizeRange[0])));
|
|
1369
1398
|
this.computeBbox();
|
|
@@ -1424,21 +1453,27 @@ function makeEllipse(opts) {
|
|
|
1424
1453
|
static {
|
|
1425
1454
|
__name(this, "MadeEllipse");
|
|
1426
1455
|
}
|
|
1427
|
-
static _ellipseOpts = { rxRange, ryRange, aspectRatio, mutationScale, tonalRange: opts?.tonalRange, saturationRange: opts?.saturationRange, hueCenter: opts?.hueCenter, hueTolerance: opts?.hueTolerance };
|
|
1456
|
+
static _ellipseOpts = { rxRange, ryRange, aspectRatio, mutationScale, tonalRange: opts?.tonalRange, invertTonal: opts?.invertTonal, saturationRange: opts?.saturationRange, invertSaturation: opts?.invertSaturation, hueCenter: opts?.hueCenter, hueTolerance: opts?.hueTolerance, invertHue: opts?.invertHue };
|
|
1428
1457
|
static _shapeSpec = { f: "ellipse", o: MadeEllipse._ellipseOpts };
|
|
1429
1458
|
tonalRange;
|
|
1459
|
+
invertTonal;
|
|
1430
1460
|
saturationRange;
|
|
1461
|
+
invertSaturation;
|
|
1431
1462
|
hueCenter;
|
|
1432
1463
|
hueTolerance;
|
|
1464
|
+
invertHue;
|
|
1433
1465
|
center;
|
|
1434
1466
|
rx;
|
|
1435
1467
|
ry;
|
|
1436
1468
|
constructor(w, h) {
|
|
1437
1469
|
super(w, h);
|
|
1438
1470
|
this.tonalRange = opts?.tonalRange;
|
|
1471
|
+
this.invertTonal = opts?.invertTonal;
|
|
1439
1472
|
this.saturationRange = opts?.saturationRange;
|
|
1473
|
+
this.invertSaturation = opts?.invertSaturation;
|
|
1440
1474
|
this.hueCenter = opts?.hueCenter;
|
|
1441
1475
|
this.hueTolerance = opts?.hueTolerance;
|
|
1476
|
+
this.invertHue = opts?.invertHue;
|
|
1442
1477
|
this.center = Shape.randomPoint(w, h);
|
|
1443
1478
|
this.rx = Math.max(1, rxRange[0] + ~~(Math.random() * (rxRange[1] - rxRange[0])));
|
|
1444
1479
|
this.ry = aspectRatio !== void 0 ? Math.max(1, Math.round(this.rx / aspectRatio)) : Math.max(1, ryRange[0] + ~~(Math.random() * (ryRange[1] - ryRange[0])));
|
|
@@ -1510,20 +1545,26 @@ function makeGlyph(opts) {
|
|
|
1510
1545
|
static {
|
|
1511
1546
|
__name(this, "MadeGlyph");
|
|
1512
1547
|
}
|
|
1513
|
-
static _glyphOpts = { char, fontFamily, sizeRange, mutationScale, tonalRange: opts?.tonalRange, saturationRange: opts?.saturationRange, hueCenter: opts?.hueCenter, hueTolerance: opts?.hueTolerance };
|
|
1548
|
+
static _glyphOpts = { char, fontFamily, sizeRange, mutationScale, tonalRange: opts?.tonalRange, invertTonal: opts?.invertTonal, saturationRange: opts?.saturationRange, invertSaturation: opts?.invertSaturation, hueCenter: opts?.hueCenter, hueTolerance: opts?.hueTolerance, invertHue: opts?.invertHue };
|
|
1514
1549
|
static _shapeSpec = { f: "glyph", o: MadeGlyph._glyphOpts };
|
|
1515
1550
|
tonalRange;
|
|
1551
|
+
invertTonal;
|
|
1516
1552
|
saturationRange;
|
|
1553
|
+
invertSaturation;
|
|
1517
1554
|
hueCenter;
|
|
1518
1555
|
hueTolerance;
|
|
1556
|
+
invertHue;
|
|
1519
1557
|
center;
|
|
1520
1558
|
fontSize;
|
|
1521
1559
|
constructor(w, h) {
|
|
1522
1560
|
super(w, h);
|
|
1523
1561
|
this.tonalRange = opts?.tonalRange;
|
|
1562
|
+
this.invertTonal = opts?.invertTonal;
|
|
1524
1563
|
this.saturationRange = opts?.saturationRange;
|
|
1564
|
+
this.invertSaturation = opts?.invertSaturation;
|
|
1525
1565
|
this.hueCenter = opts?.hueCenter;
|
|
1526
1566
|
this.hueTolerance = opts?.hueTolerance;
|
|
1567
|
+
this.invertHue = opts?.invertHue;
|
|
1527
1568
|
this.center = Shape.randomPoint(w, h);
|
|
1528
1569
|
this.fontSize = Math.max(sizeRange[0], sizeRange[0] + ~~(Math.random() * (sizeRange[1] - sizeRange[0])));
|
|
1529
1570
|
this.computeBbox();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slithy/prim-lib",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Core engine for primitive-based image reconstruction.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -14,11 +14,11 @@
|
|
|
14
14
|
],
|
|
15
15
|
"sideEffects": false,
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@vitest/coverage-v8": "^4.1.
|
|
18
|
-
"jsdom": "^29.
|
|
17
|
+
"@vitest/coverage-v8": "^4.1.7",
|
|
18
|
+
"jsdom": "^29.1.1",
|
|
19
19
|
"tsup": "^8",
|
|
20
20
|
"typescript": "^5",
|
|
21
|
-
"vitest": "^4.1.
|
|
21
|
+
"vitest": "^4.1.7",
|
|
22
22
|
"@slithy/eslint-config": "0.0.0",
|
|
23
23
|
"@slithy/tsconfig": "0.0.0"
|
|
24
24
|
},
|