@slithy/prim-lib 0.6.0 → 0.7.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/index.d.ts +20 -1
- package/dist/index.js +88 -14
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -24,6 +24,9 @@ interface ShapeImageData {
|
|
|
24
24
|
interface ShapeInterface {
|
|
25
25
|
bbox: Bbox;
|
|
26
26
|
tonalRange?: [number, number];
|
|
27
|
+
saturationRange?: [number, number];
|
|
28
|
+
hueCenter?: number;
|
|
29
|
+
hueTolerance?: number;
|
|
27
30
|
/** cfg is rarely used by implementations; accepts Partial to keep call sites flexible. */
|
|
28
31
|
mutate(cfg?: Partial<Cfg>): ShapeInterface;
|
|
29
32
|
toSVG(): SVGElement | undefined;
|
|
@@ -307,6 +310,9 @@ interface NGonOptions {
|
|
|
307
310
|
sizeRange?: [number, number];
|
|
308
311
|
mutationScale?: number;
|
|
309
312
|
tonalRange?: [number, number];
|
|
313
|
+
saturationRange?: [number, number];
|
|
314
|
+
hueCenter?: number;
|
|
315
|
+
hueTolerance?: number;
|
|
310
316
|
}
|
|
311
317
|
declare function makeNGon(opts: NGonOptions): new (w: number, h: number) => ShapeInterface;
|
|
312
318
|
interface RectOptions {
|
|
@@ -316,12 +322,18 @@ interface RectOptions {
|
|
|
316
322
|
rotatable?: boolean;
|
|
317
323
|
mutationScale?: number;
|
|
318
324
|
tonalRange?: [number, number];
|
|
325
|
+
saturationRange?: [number, number];
|
|
326
|
+
hueCenter?: number;
|
|
327
|
+
hueTolerance?: number;
|
|
319
328
|
}
|
|
320
329
|
declare function makeRect(opts?: Partial<RectOptions>): new (w: number, h: number) => ShapeInterface;
|
|
321
330
|
interface CircleOptions {
|
|
322
331
|
sizeRange?: [number, number];
|
|
323
332
|
mutationScale?: number;
|
|
324
333
|
tonalRange?: [number, number];
|
|
334
|
+
saturationRange?: [number, number];
|
|
335
|
+
hueCenter?: number;
|
|
336
|
+
hueTolerance?: number;
|
|
325
337
|
}
|
|
326
338
|
declare function makeCircle(opts?: Partial<CircleOptions>): new (w: number, h: number) => ShapeInterface;
|
|
327
339
|
interface EllipseOptions {
|
|
@@ -330,6 +342,9 @@ interface EllipseOptions {
|
|
|
330
342
|
aspectRatio?: number;
|
|
331
343
|
mutationScale?: number;
|
|
332
344
|
tonalRange?: [number, number];
|
|
345
|
+
saturationRange?: [number, number];
|
|
346
|
+
hueCenter?: number;
|
|
347
|
+
hueTolerance?: number;
|
|
333
348
|
}
|
|
334
349
|
declare function makeEllipse(opts?: Partial<EllipseOptions>): new (w: number, h: number) => ShapeInterface;
|
|
335
350
|
interface GlyphOptions {
|
|
@@ -338,6 +353,9 @@ interface GlyphOptions {
|
|
|
338
353
|
sizeRange?: [number, number];
|
|
339
354
|
mutationScale?: number;
|
|
340
355
|
tonalRange?: [number, number];
|
|
356
|
+
saturationRange?: [number, number];
|
|
357
|
+
hueCenter?: number;
|
|
358
|
+
hueTolerance?: number;
|
|
341
359
|
}
|
|
342
360
|
declare function makeGlyph(opts?: Partial<GlyphOptions>): new (w: number, h: number) => ShapeInterface;
|
|
343
361
|
declare class Debug extends Shape {
|
|
@@ -346,6 +364,7 @@ declare class Debug extends Shape {
|
|
|
346
364
|
}
|
|
347
365
|
|
|
348
366
|
declare const SVGNS = "http://www.w3.org/2000/svg";
|
|
367
|
+
declare function rgbToHsl(r: number, g: number, b: number): [number, number, number];
|
|
349
368
|
declare function parseColor(color: string): [number, number, number];
|
|
350
369
|
|
|
351
370
|
declare function clamp(x: number, min: number, max: number): number;
|
|
@@ -368,4 +387,4 @@ interface ReplayResult {
|
|
|
368
387
|
}
|
|
369
388
|
declare function replayOutput(data: SerializedOutput): ReplayResult;
|
|
370
389
|
|
|
371
|
-
export { type Bbox, Canvas, type Cfg, Circle, type CircleOptions, type Ctx2D, Debug, Ellipse, type EllipseOptions, Glyph, type GlyphOptions, Hexagon, type ImageDataLike, type NGonOptions, Optimizer, type Point, type PreCfg, type RGB, type RectOptions, Rectangle, type ReplayResult, SVGNS, type SerializedOutput, Shape, type ShapeImageData, type ShapeInterface, Square, State, Step, type StepData, Triangle, clamp, clampColor, computeColorAndDifferenceChange, difference, differenceToDistance, distanceToDifference, getFill, makeCircle, makeEllipse, makeGlyph, makeNGon, makeRect, parseColor, renderStepToCtx, replayOutput, stepDataToSVGElement, stepPerf };
|
|
390
|
+
export { type Bbox, Canvas, type Cfg, Circle, type CircleOptions, type Ctx2D, Debug, Ellipse, type EllipseOptions, Glyph, type GlyphOptions, Hexagon, type ImageDataLike, type NGonOptions, Optimizer, type Point, type PreCfg, type RGB, type RectOptions, Rectangle, type ReplayResult, SVGNS, type SerializedOutput, Shape, type ShapeImageData, type ShapeInterface, Square, State, Step, type StepData, Triangle, clamp, clampColor, computeColorAndDifferenceChange, difference, differenceToDistance, distanceToDifference, getFill, makeCircle, makeEllipse, makeGlyph, makeNGon, makeRect, parseColor, renderStepToCtx, replayOutput, rgbToHsl, stepDataToSVGElement, stepPerf };
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,25 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
|
|
|
3
3
|
|
|
4
4
|
// src/util.ts
|
|
5
5
|
var SVGNS = "http://www.w3.org/2000/svg";
|
|
6
|
+
function rgbToHsl(r, g, b) {
|
|
7
|
+
const rn = r / 255, gn = g / 255, bn = b / 255;
|
|
8
|
+
const max = Math.max(rn, gn, bn);
|
|
9
|
+
const min = Math.min(rn, gn, bn);
|
|
10
|
+
const l = (max + min) / 2;
|
|
11
|
+
if (max === min) return [0, 0, l * 100];
|
|
12
|
+
const d = max - min;
|
|
13
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
14
|
+
let h;
|
|
15
|
+
if (max === rn) {
|
|
16
|
+
h = (gn - bn) / d + (gn < bn ? 6 : 0);
|
|
17
|
+
} else if (max === gn) {
|
|
18
|
+
h = (bn - rn) / d + 2;
|
|
19
|
+
} else {
|
|
20
|
+
h = (rn - gn) / d + 4;
|
|
21
|
+
}
|
|
22
|
+
return [h * 60, s * 100, l * 100];
|
|
23
|
+
}
|
|
24
|
+
__name(rgbToHsl, "rgbToHsl");
|
|
6
25
|
function parseColor(color) {
|
|
7
26
|
const m = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
|
|
8
27
|
if (!m) throw new Error(`Cannot parse color: ${color}`);
|
|
@@ -363,13 +382,13 @@ var Step = class _Step {
|
|
|
363
382
|
compute(state) {
|
|
364
383
|
const pixels = state.canvas.node.width * state.canvas.node.height;
|
|
365
384
|
const offset = this.shape.bbox;
|
|
366
|
-
const tonalRange = this.shape
|
|
367
|
-
if (tonalRange) {
|
|
385
|
+
const { tonalRange, saturationRange, hueCenter, hueTolerance } = this.shape;
|
|
386
|
+
if (tonalRange || saturationRange || hueCenter !== void 0 && hueTolerance !== void 0) {
|
|
368
387
|
const { left, top, width, height } = offset;
|
|
369
388
|
const targetData = state.target.getImageData();
|
|
370
389
|
const fw = targetData.width;
|
|
371
390
|
const fh = targetData.height;
|
|
372
|
-
let
|
|
391
|
+
let rSum = 0, gSum = 0, bSum = 0, count = 0;
|
|
373
392
|
for (let sy = 0; sy < height; sy++) {
|
|
374
393
|
const fy = top + sy;
|
|
375
394
|
if (fy < 0 || fy >= fh) continue;
|
|
@@ -377,15 +396,33 @@ var Step = class _Step {
|
|
|
377
396
|
const fx = left + sx;
|
|
378
397
|
if (fx < 0 || fx >= fw) continue;
|
|
379
398
|
const i = 4 * (fx + fy * fw);
|
|
380
|
-
|
|
399
|
+
rSum += targetData.data[i];
|
|
400
|
+
gSum += targetData.data[i + 1];
|
|
401
|
+
bSum += targetData.data[i + 2];
|
|
381
402
|
count++;
|
|
382
403
|
}
|
|
383
404
|
}
|
|
384
405
|
if (count > 0) {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
406
|
+
if (tonalRange) {
|
|
407
|
+
const luma = (0.299 * rSum + 0.587 * gSum + 0.114 * bSum) / count;
|
|
408
|
+
if (luma < tonalRange[0] || luma > tonalRange[1]) {
|
|
409
|
+
this.distance = Infinity;
|
|
410
|
+
return Promise.resolve(this);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (saturationRange || hueCenter !== void 0 && hueTolerance !== void 0) {
|
|
414
|
+
const [h, s] = rgbToHsl(rSum / count, gSum / count, bSum / count);
|
|
415
|
+
if (saturationRange && (s < saturationRange[0] || s > saturationRange[1])) {
|
|
416
|
+
this.distance = Infinity;
|
|
417
|
+
return Promise.resolve(this);
|
|
418
|
+
}
|
|
419
|
+
if (hueCenter !== void 0 && hueTolerance !== void 0) {
|
|
420
|
+
const diff = Math.abs((h - hueCenter + 180 + 360) % 360 - 180);
|
|
421
|
+
if (diff > hueTolerance) {
|
|
422
|
+
this.distance = Infinity;
|
|
423
|
+
return Promise.resolve(this);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
389
426
|
}
|
|
390
427
|
}
|
|
391
428
|
}
|
|
@@ -989,9 +1026,12 @@ function makeNGon(opts) {
|
|
|
989
1026
|
static {
|
|
990
1027
|
__name(this, "NGonRegular");
|
|
991
1028
|
}
|
|
992
|
-
static _ngonOpts = { sides, regular: true, rotatable, noise, sizeRange, mutationScale, startAngle, tonalRange: opts.tonalRange };
|
|
1029
|
+
static _ngonOpts = { sides, regular: true, rotatable, noise, sizeRange, mutationScale, startAngle, tonalRange: opts.tonalRange, saturationRange: opts.saturationRange, hueCenter: opts.hueCenter, hueTolerance: opts.hueTolerance };
|
|
993
1030
|
static _shapeSpec = { f: "ngon", o: NGonRegular._ngonOpts };
|
|
994
1031
|
tonalRange;
|
|
1032
|
+
saturationRange;
|
|
1033
|
+
hueCenter;
|
|
1034
|
+
hueTolerance;
|
|
995
1035
|
center;
|
|
996
1036
|
r;
|
|
997
1037
|
angle;
|
|
@@ -1000,6 +1040,9 @@ function makeNGon(opts) {
|
|
|
1000
1040
|
constructor(w, h) {
|
|
1001
1041
|
super(w, h);
|
|
1002
1042
|
this.tonalRange = opts.tonalRange;
|
|
1043
|
+
this.saturationRange = opts.saturationRange;
|
|
1044
|
+
this.hueCenter = opts.hueCenter;
|
|
1045
|
+
this.hueTolerance = opts.hueTolerance;
|
|
1003
1046
|
this.center = Shape.randomPoint(w, h);
|
|
1004
1047
|
this.r = Math.max(1, sizeRange[0] + ~~(Math.random() * (sizeRange[1] - sizeRange[0])));
|
|
1005
1048
|
this.angle = rotatable ? Math.random() * (2 * Math.PI / sides) : startAngle;
|
|
@@ -1095,13 +1138,19 @@ function makeNGon(opts) {
|
|
|
1095
1138
|
static {
|
|
1096
1139
|
__name(this, "NGonIrregular");
|
|
1097
1140
|
}
|
|
1098
|
-
static _ngonOpts = { sides, regular: false, convex, sizeRange, mutationScale, tonalRange: opts.tonalRange };
|
|
1141
|
+
static _ngonOpts = { sides, regular: false, convex, sizeRange, mutationScale, tonalRange: opts.tonalRange, saturationRange: opts.saturationRange, hueCenter: opts.hueCenter, hueTolerance: opts.hueTolerance };
|
|
1099
1142
|
static _shapeSpec = { f: "ngon", o: NGonIrregular._ngonOpts };
|
|
1100
1143
|
tonalRange;
|
|
1144
|
+
saturationRange;
|
|
1145
|
+
hueCenter;
|
|
1146
|
+
hueTolerance;
|
|
1101
1147
|
points;
|
|
1102
1148
|
constructor(w, h) {
|
|
1103
1149
|
super(w, h);
|
|
1104
1150
|
this.tonalRange = opts.tonalRange;
|
|
1151
|
+
this.saturationRange = opts.saturationRange;
|
|
1152
|
+
this.hueCenter = opts.hueCenter;
|
|
1153
|
+
this.hueTolerance = opts.hueTolerance;
|
|
1105
1154
|
const first = Shape.randomPoint(w, h);
|
|
1106
1155
|
this.points = [first];
|
|
1107
1156
|
for (let i = 1; i < sides; i++) {
|
|
@@ -1173,9 +1222,12 @@ function makeRect(opts) {
|
|
|
1173
1222
|
static {
|
|
1174
1223
|
__name(this, "Rect");
|
|
1175
1224
|
}
|
|
1176
|
-
static _rectOpts = { widthRange, heightRange, aspectRatio, rotatable, mutationScale, tonalRange: opts?.tonalRange };
|
|
1225
|
+
static _rectOpts = { widthRange, heightRange, aspectRatio, rotatable, mutationScale, tonalRange: opts?.tonalRange, saturationRange: opts?.saturationRange, hueCenter: opts?.hueCenter, hueTolerance: opts?.hueTolerance };
|
|
1177
1226
|
static _shapeSpec = { f: "rect", o: Rect._rectOpts };
|
|
1178
1227
|
tonalRange;
|
|
1228
|
+
saturationRange;
|
|
1229
|
+
hueCenter;
|
|
1230
|
+
hueTolerance;
|
|
1179
1231
|
center;
|
|
1180
1232
|
hw;
|
|
1181
1233
|
hh;
|
|
@@ -1183,6 +1235,9 @@ function makeRect(opts) {
|
|
|
1183
1235
|
constructor(w, h) {
|
|
1184
1236
|
super(w, h);
|
|
1185
1237
|
this.tonalRange = opts?.tonalRange;
|
|
1238
|
+
this.saturationRange = opts?.saturationRange;
|
|
1239
|
+
this.hueCenter = opts?.hueCenter;
|
|
1240
|
+
this.hueTolerance = opts?.hueTolerance;
|
|
1186
1241
|
this.center = Shape.randomPoint(w, h);
|
|
1187
1242
|
this.hw = widthRange[0] + ~~(Math.random() * (widthRange[1] - widthRange[0]));
|
|
1188
1243
|
if (aspectRatio !== void 0) {
|
|
@@ -1295,14 +1350,20 @@ function makeCircle(opts) {
|
|
|
1295
1350
|
static {
|
|
1296
1351
|
__name(this, "MadeCircle");
|
|
1297
1352
|
}
|
|
1298
|
-
static _circleOpts = { sizeRange, mutationScale, tonalRange: opts?.tonalRange };
|
|
1353
|
+
static _circleOpts = { sizeRange, mutationScale, tonalRange: opts?.tonalRange, saturationRange: opts?.saturationRange, hueCenter: opts?.hueCenter, hueTolerance: opts?.hueTolerance };
|
|
1299
1354
|
static _shapeSpec = { f: "circle", o: MadeCircle._circleOpts };
|
|
1300
1355
|
tonalRange;
|
|
1356
|
+
saturationRange;
|
|
1357
|
+
hueCenter;
|
|
1358
|
+
hueTolerance;
|
|
1301
1359
|
center;
|
|
1302
1360
|
r;
|
|
1303
1361
|
constructor(w, h) {
|
|
1304
1362
|
super(w, h);
|
|
1305
1363
|
this.tonalRange = opts?.tonalRange;
|
|
1364
|
+
this.saturationRange = opts?.saturationRange;
|
|
1365
|
+
this.hueCenter = opts?.hueCenter;
|
|
1366
|
+
this.hueTolerance = opts?.hueTolerance;
|
|
1306
1367
|
this.center = Shape.randomPoint(w, h);
|
|
1307
1368
|
this.r = Math.max(1, sizeRange[0] + ~~(Math.random() * (sizeRange[1] - sizeRange[0])));
|
|
1308
1369
|
this.computeBbox();
|
|
@@ -1363,15 +1424,21 @@ function makeEllipse(opts) {
|
|
|
1363
1424
|
static {
|
|
1364
1425
|
__name(this, "MadeEllipse");
|
|
1365
1426
|
}
|
|
1366
|
-
static _ellipseOpts = { rxRange, ryRange, aspectRatio, mutationScale, tonalRange: opts?.tonalRange };
|
|
1427
|
+
static _ellipseOpts = { rxRange, ryRange, aspectRatio, mutationScale, tonalRange: opts?.tonalRange, saturationRange: opts?.saturationRange, hueCenter: opts?.hueCenter, hueTolerance: opts?.hueTolerance };
|
|
1367
1428
|
static _shapeSpec = { f: "ellipse", o: MadeEllipse._ellipseOpts };
|
|
1368
1429
|
tonalRange;
|
|
1430
|
+
saturationRange;
|
|
1431
|
+
hueCenter;
|
|
1432
|
+
hueTolerance;
|
|
1369
1433
|
center;
|
|
1370
1434
|
rx;
|
|
1371
1435
|
ry;
|
|
1372
1436
|
constructor(w, h) {
|
|
1373
1437
|
super(w, h);
|
|
1374
1438
|
this.tonalRange = opts?.tonalRange;
|
|
1439
|
+
this.saturationRange = opts?.saturationRange;
|
|
1440
|
+
this.hueCenter = opts?.hueCenter;
|
|
1441
|
+
this.hueTolerance = opts?.hueTolerance;
|
|
1375
1442
|
this.center = Shape.randomPoint(w, h);
|
|
1376
1443
|
this.rx = Math.max(1, rxRange[0] + ~~(Math.random() * (rxRange[1] - rxRange[0])));
|
|
1377
1444
|
this.ry = aspectRatio !== void 0 ? Math.max(1, Math.round(this.rx / aspectRatio)) : Math.max(1, ryRange[0] + ~~(Math.random() * (ryRange[1] - ryRange[0])));
|
|
@@ -1443,14 +1510,20 @@ function makeGlyph(opts) {
|
|
|
1443
1510
|
static {
|
|
1444
1511
|
__name(this, "MadeGlyph");
|
|
1445
1512
|
}
|
|
1446
|
-
static _glyphOpts = { char, fontFamily, sizeRange, mutationScale, tonalRange: opts?.tonalRange };
|
|
1513
|
+
static _glyphOpts = { char, fontFamily, sizeRange, mutationScale, tonalRange: opts?.tonalRange, saturationRange: opts?.saturationRange, hueCenter: opts?.hueCenter, hueTolerance: opts?.hueTolerance };
|
|
1447
1514
|
static _shapeSpec = { f: "glyph", o: MadeGlyph._glyphOpts };
|
|
1448
1515
|
tonalRange;
|
|
1516
|
+
saturationRange;
|
|
1517
|
+
hueCenter;
|
|
1518
|
+
hueTolerance;
|
|
1449
1519
|
center;
|
|
1450
1520
|
fontSize;
|
|
1451
1521
|
constructor(w, h) {
|
|
1452
1522
|
super(w, h);
|
|
1453
1523
|
this.tonalRange = opts?.tonalRange;
|
|
1524
|
+
this.saturationRange = opts?.saturationRange;
|
|
1525
|
+
this.hueCenter = opts?.hueCenter;
|
|
1526
|
+
this.hueTolerance = opts?.hueTolerance;
|
|
1454
1527
|
this.center = Shape.randomPoint(w, h);
|
|
1455
1528
|
this.fontSize = Math.max(sizeRange[0], sizeRange[0] + ~~(Math.random() * (sizeRange[1] - sizeRange[0])));
|
|
1456
1529
|
this.computeBbox();
|
|
@@ -1845,6 +1918,7 @@ export {
|
|
|
1845
1918
|
parseColor,
|
|
1846
1919
|
renderStepToCtx,
|
|
1847
1920
|
replayOutput,
|
|
1921
|
+
rgbToHsl,
|
|
1848
1922
|
stepDataToSVGElement,
|
|
1849
1923
|
stepPerf
|
|
1850
1924
|
};
|