@slithy/prim-lib 0.4.0 → 0.5.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/README.md +48 -2
- package/dist/index.d.ts +34 -1
- package/dist/index.js +429 -5
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -17,9 +17,52 @@ Reconstructs an image by iteratively placing geometric shapes. Each step evaluat
|
|
|
17
17
|
|
|
18
18
|
### Shapes
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
Fixed shapes (class constructors, pass directly in `shapeTypes`):
|
|
21
|
+
|
|
21
22
|
- **`Triangle`**, **`Rectangle`**, **`Ellipse`**, **`Circle`**, **`Square`**, **`Hexagon`**, **`Glyph`**, **`Debug`**
|
|
22
23
|
|
|
24
|
+
Configurable shapes (factory functions that return a constructor):
|
|
25
|
+
|
|
26
|
+
- **`makeNGon(opts)`** — N-sided polygon; supports regular and irregular modes
|
|
27
|
+
- **`makeRect(opts)`** — axis-aligned or rotatable rectangle with independent width/height control
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
shapeTypes: [Triangle, makeNGon({ sides: 6, regular: true }), makeRect({ aspectRatio: 1.78 })]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Factory options
|
|
34
|
+
|
|
35
|
+
**`NGonOptions`**
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
interface NGonOptions {
|
|
39
|
+
sides: number // number of vertices (≥ 3, required)
|
|
40
|
+
regular?: boolean // true = equidistant vertices (default: true)
|
|
41
|
+
rotatable?: boolean // regular only: random rotation each instance (default: true)
|
|
42
|
+
startAngle?: number // regular, non-rotatable only: fixed orientation in radians
|
|
43
|
+
// default: -π/2 (top vertex pointing up)
|
|
44
|
+
noise?: number // regular only: 0–1 vertex jitter (default: 0)
|
|
45
|
+
convex?: boolean // irregular only: apply convex hull (default: false)
|
|
46
|
+
sizeRange?: [number, number] // vertex radius range in compute-space px (default: [1, 20])
|
|
47
|
+
// regular: radius from center; irregular: scatter radius from first point
|
|
48
|
+
mutationScale?: number // max mutation step size in px (default: 20)
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**`RectOptions`**
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
interface RectOptions {
|
|
56
|
+
widthRange?: [number, number] // half-width range in compute-space px (default: [5, 40])
|
|
57
|
+
heightRange?: [number, number] // half-height range in compute-space px (default: [5, 40])
|
|
58
|
+
// ignored when aspectRatio is set
|
|
59
|
+
aspectRatio?: number // width ÷ height; locks proportions, derives hh from hw
|
|
60
|
+
// e.g. 1.78 for 16:9, 1.33 for 4:3, 1.0 for square
|
|
61
|
+
rotatable?: boolean // random rotation per instance (default: false)
|
|
62
|
+
mutationScale?: number // max mutation step size in px (default: 20)
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
23
66
|
### Types
|
|
24
67
|
|
|
25
68
|
**`Cfg`** — runtime config (all fields required):
|
|
@@ -56,11 +99,13 @@ interface Cfg {
|
|
|
56
99
|
type StepData =
|
|
57
100
|
| { t: 't'; a: number; c: RGB; pts: [number, number][] } // Triangle
|
|
58
101
|
| { t: 'r'; a: number; c: RGB; pts: [number, number][] } // Rectangle
|
|
102
|
+
| { t: 'p'; a: number; c: RGB; pts: [number, number][] } // makeNGon (regular and irregular)
|
|
59
103
|
| { t: 'e'; a: number; c: RGB; cx: number; cy: number; rx: number; ry: number } // Ellipse
|
|
60
104
|
| { t: 'c'; a: number; c: RGB; cx: number; cy: number; r: number } // Circle
|
|
61
105
|
| { t: 's'; a: number; c: RGB; cx: number; cy: number; r: number } // Square
|
|
62
106
|
| { t: 'h'; a: number; c: RGB; cx: number; cy: number; r: number; angle: number } // Hexagon
|
|
63
107
|
| { t: 'sm'; a: number; c: RGB; cx: number; cy: number; fs: number; text: string } // Glyph
|
|
108
|
+
| { t: 'rc'; a: number; c: RGB; cx: number; cy: number; hw: number; hh: number; angle: number } // makeRect
|
|
64
109
|
```
|
|
65
110
|
|
|
66
111
|
**`SerializedOutput`** — compact, storage-ready representation of a completed run:
|
|
@@ -112,7 +157,7 @@ interface ReplayResult {
|
|
|
112
157
|
- **Architecture split** — the original is a single-layer library. Here, `prim-lib` is the pure algorithm (no knowledge of how images arrive or where results go), and `prim-interface` is the adapter that wires it to the browser
|
|
113
158
|
- **`PreCfg` / `Cfg` distinction** — `Canvas.original()` accepts `PreCfg` (optional `width`/`height`) and resolves to a fully-populated `Cfg`, making the config lifecycle explicit in the types
|
|
114
159
|
- **Serialization** — `StepData` / `SerializedOutput` allow completed runs to be stored compactly and replayed via `replayOutput()` without re-running the optimizer
|
|
115
|
-
- **Additional shapes** — `Circle`, `Square`, `Hexagon`, and `Glyph` are not in the original; `Circle` is a uniform-radius variant of `Ellipse`
|
|
160
|
+
- **Additional shapes** — `Circle`, `Square`, `Hexagon`, and `Glyph` are not in the original; `Circle` is a uniform-radius variant of `Ellipse`; `makeNGon` and `makeRect` are configurable factory shapes also not in the original
|
|
116
161
|
- **Shape weighting** — `shapeWeights` allows biased shape selection with a guaranteed distribution: the optimizer pre-allocates exact per-shape step counts (largest-remainder rounding) and shuffles them, so the final mix always matches the requested weights
|
|
117
162
|
|
|
118
163
|
## Architecture notes
|
|
@@ -122,3 +167,4 @@ interface ReplayResult {
|
|
|
122
167
|
- 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
|
|
123
168
|
- `PreCfg` exists to bridge the gap between call time (dimensions unknown) and runtime (dimensions set by `Canvas.original()` or `Canvas.fromBitmap()`)
|
|
124
169
|
- `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
|
package/dist/index.d.ts
CHANGED
|
@@ -68,6 +68,11 @@ type StepData = {
|
|
|
68
68
|
a: number;
|
|
69
69
|
c: RGB;
|
|
70
70
|
pts: [number, number][];
|
|
71
|
+
} | {
|
|
72
|
+
t: 'p';
|
|
73
|
+
a: number;
|
|
74
|
+
c: RGB;
|
|
75
|
+
pts: [number, number][];
|
|
71
76
|
} | {
|
|
72
77
|
t: 'e';
|
|
73
78
|
a: number;
|
|
@@ -106,6 +111,15 @@ type StepData = {
|
|
|
106
111
|
cy: number;
|
|
107
112
|
fs: number;
|
|
108
113
|
text: string;
|
|
114
|
+
} | {
|
|
115
|
+
t: 'rc';
|
|
116
|
+
a: number;
|
|
117
|
+
c: RGB;
|
|
118
|
+
cx: number;
|
|
119
|
+
cy: number;
|
|
120
|
+
hw: number;
|
|
121
|
+
hh: number;
|
|
122
|
+
angle: number;
|
|
109
123
|
};
|
|
110
124
|
interface SerializedOutput {
|
|
111
125
|
v: 1;
|
|
@@ -280,6 +294,25 @@ declare class Hexagon extends Shape {
|
|
|
280
294
|
toData(a: number, c: RGB): StepData;
|
|
281
295
|
mutate(_cfg?: Partial<Cfg>): ShapeInterface;
|
|
282
296
|
}
|
|
297
|
+
interface NGonOptions {
|
|
298
|
+
sides: number;
|
|
299
|
+
regular?: boolean;
|
|
300
|
+
rotatable?: boolean;
|
|
301
|
+
startAngle?: number;
|
|
302
|
+
convex?: boolean;
|
|
303
|
+
noise?: number;
|
|
304
|
+
sizeRange?: [number, number];
|
|
305
|
+
mutationScale?: number;
|
|
306
|
+
}
|
|
307
|
+
declare function makeNGon(opts: NGonOptions): new (w: number, h: number) => ShapeInterface;
|
|
308
|
+
interface RectOptions {
|
|
309
|
+
widthRange?: [number, number];
|
|
310
|
+
heightRange?: [number, number];
|
|
311
|
+
aspectRatio?: number;
|
|
312
|
+
rotatable?: boolean;
|
|
313
|
+
mutationScale?: number;
|
|
314
|
+
}
|
|
315
|
+
declare function makeRect(opts?: Partial<RectOptions>): new (w: number, h: number) => ShapeInterface;
|
|
283
316
|
declare class Debug extends Shape {
|
|
284
317
|
constructor(w: number, h: number);
|
|
285
318
|
render(ctx: CanvasRenderingContext2D): void;
|
|
@@ -308,4 +341,4 @@ interface ReplayResult {
|
|
|
308
341
|
}
|
|
309
342
|
declare function replayOutput(data: SerializedOutput): ReplayResult;
|
|
310
343
|
|
|
311
|
-
export { type Bbox, Canvas, type Cfg, Circle, type Ctx2D, Debug, Ellipse, Glyph, Hexagon, type ImageDataLike, Optimizer, type Point, type PreCfg, type RGB, Rectangle, type ReplayResult, SVGNS, type SerializedOutput, Shape, type ShapeImageData, type ShapeInterface, Square, State, Step, type StepData, Triangle, clamp, clampColor, computeColorAndDifferenceChange, difference, differenceToDistance, distanceToDifference, getFill, parseColor, renderStepToCtx, replayOutput, stepDataToSVGElement, stepPerf };
|
|
344
|
+
export { type Bbox, Canvas, type Cfg, Circle, type Ctx2D, Debug, Ellipse, Glyph, 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, makeNGon, makeRect, parseColor, renderStepToCtx, replayOutput, stepDataToSVGElement, stepPerf };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
1
4
|
// src/util.ts
|
|
2
5
|
var SVGNS = "http://www.w3.org/2000/svg";
|
|
3
6
|
function parseColor(color) {
|
|
@@ -5,18 +8,23 @@ function parseColor(color) {
|
|
|
5
8
|
if (!m) throw new Error(`Cannot parse color: ${color}`);
|
|
6
9
|
return [parseInt(m[1]), parseInt(m[2]), parseInt(m[3])];
|
|
7
10
|
}
|
|
11
|
+
__name(parseColor, "parseColor");
|
|
8
12
|
function clamp(x, min, max) {
|
|
9
13
|
return Math.max(min, Math.min(max, x));
|
|
10
14
|
}
|
|
15
|
+
__name(clamp, "clamp");
|
|
11
16
|
function clampColor(x) {
|
|
12
17
|
return clamp(x, 0, 255);
|
|
13
18
|
}
|
|
19
|
+
__name(clampColor, "clampColor");
|
|
14
20
|
function distanceToDifference(distance, pixels) {
|
|
15
21
|
return Math.pow(distance * 255, 2) * (3 * pixels);
|
|
16
22
|
}
|
|
23
|
+
__name(distanceToDifference, "distanceToDifference");
|
|
17
24
|
function differenceToDistance(difference2, pixels) {
|
|
18
25
|
return Math.sqrt(difference2 / (3 * pixels)) / 255;
|
|
19
26
|
}
|
|
27
|
+
__name(differenceToDistance, "differenceToDistance");
|
|
20
28
|
function difference(data, dataOther) {
|
|
21
29
|
let sum = 0, dr = 0, dg = 0, db = 0;
|
|
22
30
|
for (let i = 0; i < data.data.length; i += 4) {
|
|
@@ -27,6 +35,7 @@ function difference(data, dataOther) {
|
|
|
27
35
|
}
|
|
28
36
|
return sum;
|
|
29
37
|
}
|
|
38
|
+
__name(difference, "difference");
|
|
30
39
|
function getFill(data) {
|
|
31
40
|
const w = data.width;
|
|
32
41
|
const h = data.height;
|
|
@@ -49,6 +58,7 @@ function getFill(data) {
|
|
|
49
58
|
rgb = rgb.map((x) => ~~(x / count)).map(clampColor);
|
|
50
59
|
return `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`;
|
|
51
60
|
}
|
|
61
|
+
__name(getFill, "getFill");
|
|
52
62
|
function computeColorAndDifferenceChange(offset, imageData, alpha) {
|
|
53
63
|
const { shape, current, target } = imageData;
|
|
54
64
|
const shapeData = shape.data;
|
|
@@ -116,12 +126,14 @@ function computeColorAndDifferenceChange(offset, imageData, alpha) {
|
|
|
116
126
|
}
|
|
117
127
|
return { color: `rgb(${cr}, ${cg}, ${cb})`, differenceChange: sum };
|
|
118
128
|
}
|
|
129
|
+
__name(computeColorAndDifferenceChange, "computeColorAndDifferenceChange");
|
|
119
130
|
|
|
120
131
|
// src/canvas.ts
|
|
121
132
|
function getScale(width, height, limit, allowUpscale = false) {
|
|
122
133
|
const scale = Math.max(width / limit, height / limit);
|
|
123
134
|
return allowUpscale ? scale : Math.max(scale, 1);
|
|
124
135
|
}
|
|
136
|
+
__name(getScale, "getScale");
|
|
125
137
|
function svgRect(w, h) {
|
|
126
138
|
const node = document.createElementNS(SVGNS, "rect");
|
|
127
139
|
node.setAttribute("x", "0");
|
|
@@ -130,7 +142,11 @@ function svgRect(w, h) {
|
|
|
130
142
|
node.setAttribute("height", String(h));
|
|
131
143
|
return node;
|
|
132
144
|
}
|
|
145
|
+
__name(svgRect, "svgRect");
|
|
133
146
|
var Canvas = class _Canvas {
|
|
147
|
+
static {
|
|
148
|
+
__name(this, "Canvas");
|
|
149
|
+
}
|
|
134
150
|
node;
|
|
135
151
|
ctx;
|
|
136
152
|
_imageData;
|
|
@@ -280,6 +296,9 @@ var Canvas = class _Canvas {
|
|
|
280
296
|
|
|
281
297
|
// src/state.ts
|
|
282
298
|
var State = class {
|
|
299
|
+
static {
|
|
300
|
+
__name(this, "State");
|
|
301
|
+
}
|
|
283
302
|
target;
|
|
284
303
|
canvas;
|
|
285
304
|
distance;
|
|
@@ -302,6 +321,9 @@ var stepPerf = {
|
|
|
302
321
|
}
|
|
303
322
|
};
|
|
304
323
|
var Step = class _Step {
|
|
324
|
+
static {
|
|
325
|
+
__name(this, "Step");
|
|
326
|
+
}
|
|
305
327
|
shape;
|
|
306
328
|
cfg;
|
|
307
329
|
alpha;
|
|
@@ -366,6 +388,9 @@ var Step = class _Step {
|
|
|
366
388
|
// src/shape.ts
|
|
367
389
|
var _rasterCanvas = null;
|
|
368
390
|
var Shape = class {
|
|
391
|
+
static {
|
|
392
|
+
__name(this, "Shape");
|
|
393
|
+
}
|
|
369
394
|
bbox;
|
|
370
395
|
static randomPoint(width, height) {
|
|
371
396
|
return [~~(Math.random() * width), ~~(Math.random() * height)];
|
|
@@ -417,12 +442,15 @@ var Shape = class {
|
|
|
417
442
|
ctx.translate(-this.bbox.left, -this.bbox.top);
|
|
418
443
|
this.render(ctx);
|
|
419
444
|
const data = ctx.getImageData(0, 0, w, h);
|
|
420
|
-
return { getImageData: () => data };
|
|
445
|
+
return { getImageData: /* @__PURE__ */ __name(() => data, "getImageData") };
|
|
421
446
|
}
|
|
422
447
|
render(_ctx) {
|
|
423
448
|
}
|
|
424
449
|
};
|
|
425
450
|
var Polygon = class _Polygon extends Shape {
|
|
451
|
+
static {
|
|
452
|
+
__name(this, "Polygon");
|
|
453
|
+
}
|
|
426
454
|
points;
|
|
427
455
|
constructor(w, h, count) {
|
|
428
456
|
super(w, h);
|
|
@@ -496,6 +524,9 @@ var Polygon = class _Polygon extends Shape {
|
|
|
496
524
|
}
|
|
497
525
|
};
|
|
498
526
|
var Triangle = class _Triangle extends Polygon {
|
|
527
|
+
static {
|
|
528
|
+
__name(this, "Triangle");
|
|
529
|
+
}
|
|
499
530
|
constructor(w, h) {
|
|
500
531
|
super(w, h, 3);
|
|
501
532
|
}
|
|
@@ -507,6 +538,9 @@ var Triangle = class _Triangle extends Polygon {
|
|
|
507
538
|
}
|
|
508
539
|
};
|
|
509
540
|
var Rectangle = class _Rectangle extends Polygon {
|
|
541
|
+
static {
|
|
542
|
+
__name(this, "Rectangle");
|
|
543
|
+
}
|
|
510
544
|
constructor(w, h) {
|
|
511
545
|
super(w, h, 4);
|
|
512
546
|
}
|
|
@@ -556,6 +590,9 @@ var Rectangle = class _Rectangle extends Polygon {
|
|
|
556
590
|
}
|
|
557
591
|
};
|
|
558
592
|
var Ellipse = class _Ellipse extends Shape {
|
|
593
|
+
static {
|
|
594
|
+
__name(this, "Ellipse");
|
|
595
|
+
}
|
|
559
596
|
center;
|
|
560
597
|
rx;
|
|
561
598
|
ry;
|
|
@@ -617,6 +654,9 @@ var Ellipse = class _Ellipse extends Shape {
|
|
|
617
654
|
}
|
|
618
655
|
};
|
|
619
656
|
var Circle = class _Circle extends Shape {
|
|
657
|
+
static {
|
|
658
|
+
__name(this, "Circle");
|
|
659
|
+
}
|
|
620
660
|
center;
|
|
621
661
|
r;
|
|
622
662
|
constructor(w, h) {
|
|
@@ -670,6 +710,9 @@ var Circle = class _Circle extends Shape {
|
|
|
670
710
|
}
|
|
671
711
|
};
|
|
672
712
|
var Glyph = class _Glyph extends Shape {
|
|
713
|
+
static {
|
|
714
|
+
__name(this, "Glyph");
|
|
715
|
+
}
|
|
673
716
|
center;
|
|
674
717
|
text;
|
|
675
718
|
fontSize;
|
|
@@ -733,6 +776,9 @@ var Glyph = class _Glyph extends Shape {
|
|
|
733
776
|
}
|
|
734
777
|
};
|
|
735
778
|
var Square = class _Square extends Shape {
|
|
779
|
+
static {
|
|
780
|
+
__name(this, "Square");
|
|
781
|
+
}
|
|
736
782
|
center;
|
|
737
783
|
r;
|
|
738
784
|
constructor(w, h) {
|
|
@@ -785,6 +831,9 @@ var Square = class _Square extends Shape {
|
|
|
785
831
|
}
|
|
786
832
|
};
|
|
787
833
|
var Hexagon = class _Hexagon extends Shape {
|
|
834
|
+
static {
|
|
835
|
+
__name(this, "Hexagon");
|
|
836
|
+
}
|
|
788
837
|
center;
|
|
789
838
|
r;
|
|
790
839
|
angle;
|
|
@@ -866,7 +915,341 @@ var Hexagon = class _Hexagon extends Shape {
|
|
|
866
915
|
return clone.computeBbox();
|
|
867
916
|
}
|
|
868
917
|
};
|
|
918
|
+
function convexHull(pts) {
|
|
919
|
+
if (pts.length <= 3) return [...pts];
|
|
920
|
+
const sorted = [...pts].sort((a, b) => a[0] !== b[0] ? a[0] - b[0] : a[1] - b[1]);
|
|
921
|
+
function cross(O, A, B) {
|
|
922
|
+
return (A[0] - O[0]) * (B[1] - O[1]) - (A[1] - O[1]) * (B[0] - O[0]);
|
|
923
|
+
}
|
|
924
|
+
__name(cross, "cross");
|
|
925
|
+
const lower = [];
|
|
926
|
+
for (const p of sorted) {
|
|
927
|
+
while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], p) <= 0) lower.pop();
|
|
928
|
+
lower.push(p);
|
|
929
|
+
}
|
|
930
|
+
const upper = [];
|
|
931
|
+
for (let i = sorted.length - 1; i >= 0; i--) {
|
|
932
|
+
const p = sorted[i];
|
|
933
|
+
while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], p) <= 0) upper.pop();
|
|
934
|
+
upper.push(p);
|
|
935
|
+
}
|
|
936
|
+
lower.pop();
|
|
937
|
+
upper.pop();
|
|
938
|
+
const hull = [...lower, ...upper];
|
|
939
|
+
return hull.length >= 3 ? hull : [...pts];
|
|
940
|
+
}
|
|
941
|
+
__name(convexHull, "convexHull");
|
|
942
|
+
function makeNGon(opts) {
|
|
943
|
+
const sides = opts.sides;
|
|
944
|
+
const regular = opts.regular ?? true;
|
|
945
|
+
const rotatable = opts.rotatable ?? true;
|
|
946
|
+
const noise = opts.noise ?? 0;
|
|
947
|
+
const convex = opts.convex ?? false;
|
|
948
|
+
const sizeRange = opts.sizeRange ?? [1, 20];
|
|
949
|
+
const mutationScale = opts.mutationScale ?? 20;
|
|
950
|
+
const defaultAngle = rotatable ? 0 : -(Math.PI / 2);
|
|
951
|
+
const startAngle = opts.startAngle ?? defaultAngle;
|
|
952
|
+
if (sides < 3) throw new RangeError("makeNGon requires at least 3 sides");
|
|
953
|
+
if (regular) {
|
|
954
|
+
class NGonRegular extends Shape {
|
|
955
|
+
static {
|
|
956
|
+
__name(this, "NGonRegular");
|
|
957
|
+
}
|
|
958
|
+
static _ngonOpts = { sides, regular: true, rotatable, noise, sizeRange, mutationScale, startAngle };
|
|
959
|
+
center;
|
|
960
|
+
r;
|
|
961
|
+
angle;
|
|
962
|
+
noises;
|
|
963
|
+
_cachedPoints;
|
|
964
|
+
constructor(w, h) {
|
|
965
|
+
super(w, h);
|
|
966
|
+
this.center = Shape.randomPoint(w, h);
|
|
967
|
+
this.r = Math.max(1, sizeRange[0] + ~~(Math.random() * (sizeRange[1] - sizeRange[0])));
|
|
968
|
+
this.angle = rotatable ? Math.random() * (2 * Math.PI / sides) : startAngle;
|
|
969
|
+
this.noises = Array.from({ length: sides }, () => Math.random());
|
|
970
|
+
this._cachedPoints = null;
|
|
971
|
+
this.computeBbox();
|
|
972
|
+
}
|
|
973
|
+
_points() {
|
|
974
|
+
if (!this._cachedPoints) {
|
|
975
|
+
this._cachedPoints = Array.from({ length: sides }, (_, i) => {
|
|
976
|
+
const a = this.angle + i * 2 * Math.PI / sides;
|
|
977
|
+
const ri = Math.max(1, ~~(this.r * (1 + (this.noises[i] - 0.5) * noise)));
|
|
978
|
+
return [
|
|
979
|
+
~~(this.center[0] + ri * Math.cos(a)),
|
|
980
|
+
~~(this.center[1] + ri * Math.sin(a))
|
|
981
|
+
];
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
return this._cachedPoints;
|
|
985
|
+
}
|
|
986
|
+
computeBbox() {
|
|
987
|
+
this._cachedPoints = null;
|
|
988
|
+
const pts = this._points();
|
|
989
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
990
|
+
for (const [x, y] of pts) {
|
|
991
|
+
if (x < minX) minX = x;
|
|
992
|
+
if (y < minY) minY = y;
|
|
993
|
+
if (x > maxX) maxX = x;
|
|
994
|
+
if (y > maxY) maxY = y;
|
|
995
|
+
}
|
|
996
|
+
this.bbox = {
|
|
997
|
+
left: minX,
|
|
998
|
+
top: minY,
|
|
999
|
+
width: maxX - minX || 1,
|
|
1000
|
+
height: maxY - minY || 1
|
|
1001
|
+
};
|
|
1002
|
+
return this;
|
|
1003
|
+
}
|
|
1004
|
+
render(ctx) {
|
|
1005
|
+
const pts = this._points();
|
|
1006
|
+
ctx.beginPath();
|
|
1007
|
+
pts.forEach(([x, y], i) => i ? ctx.lineTo(x, y) : ctx.moveTo(x, y));
|
|
1008
|
+
ctx.closePath();
|
|
1009
|
+
ctx.fill();
|
|
1010
|
+
}
|
|
1011
|
+
toSVG() {
|
|
1012
|
+
const node = document.createElementNS(SVGNS, "polygon");
|
|
1013
|
+
node.setAttribute("points", this._points().map((p) => p.join(",")).join(" "));
|
|
1014
|
+
return node;
|
|
1015
|
+
}
|
|
1016
|
+
toData(a, c) {
|
|
1017
|
+
return { t: "p", a, c, pts: this._points().map(([x, y]) => [x, y]) };
|
|
1018
|
+
}
|
|
1019
|
+
mutate(_cfg) {
|
|
1020
|
+
const clone = new NGonRegular(0, 0);
|
|
1021
|
+
clone.center = [this.center[0], this.center[1]];
|
|
1022
|
+
clone.r = this.r;
|
|
1023
|
+
clone.angle = this.angle;
|
|
1024
|
+
clone.noises = [...this.noises];
|
|
1025
|
+
const mutCount = 2 + (rotatable ? 1 : 0) + (noise > 0 ? 1 : 0);
|
|
1026
|
+
switch (Math.floor(Math.random() * mutCount)) {
|
|
1027
|
+
case 0: {
|
|
1028
|
+
const a = Math.random() * 2 * Math.PI;
|
|
1029
|
+
const d = Math.random() * mutationScale;
|
|
1030
|
+
clone.center[0] += ~~(d * Math.cos(a));
|
|
1031
|
+
clone.center[1] += ~~(d * Math.sin(a));
|
|
1032
|
+
break;
|
|
1033
|
+
}
|
|
1034
|
+
case 1:
|
|
1035
|
+
clone.r += (Math.random() - 0.5) * mutationScale;
|
|
1036
|
+
clone.r = Math.max(1, ~~clone.r);
|
|
1037
|
+
break;
|
|
1038
|
+
case 2:
|
|
1039
|
+
if (rotatable) {
|
|
1040
|
+
clone.angle += (Math.random() - 0.5) * (2 * Math.PI / sides);
|
|
1041
|
+
} else {
|
|
1042
|
+
const ni = Math.floor(Math.random() * sides);
|
|
1043
|
+
clone.noises[ni] = Math.random();
|
|
1044
|
+
}
|
|
1045
|
+
break;
|
|
1046
|
+
case 3: {
|
|
1047
|
+
const ni = Math.floor(Math.random() * sides);
|
|
1048
|
+
clone.noises[ni] = Math.random();
|
|
1049
|
+
break;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
return clone.computeBbox();
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
return NGonRegular;
|
|
1056
|
+
} else {
|
|
1057
|
+
class NGonIrregular extends Shape {
|
|
1058
|
+
static {
|
|
1059
|
+
__name(this, "NGonIrregular");
|
|
1060
|
+
}
|
|
1061
|
+
static _ngonOpts = { sides, regular: false, convex, sizeRange, mutationScale };
|
|
1062
|
+
points;
|
|
1063
|
+
constructor(w, h) {
|
|
1064
|
+
super(w, h);
|
|
1065
|
+
const first = Shape.randomPoint(w, h);
|
|
1066
|
+
this.points = [first];
|
|
1067
|
+
for (let i = 1; i < sides; i++) {
|
|
1068
|
+
const angle = Math.random() * 2 * Math.PI;
|
|
1069
|
+
const radius = sizeRange[0] + Math.random() * (sizeRange[1] - sizeRange[0]);
|
|
1070
|
+
this.points.push([
|
|
1071
|
+
first[0] + ~~(radius * Math.cos(angle)),
|
|
1072
|
+
first[1] + ~~(radius * Math.sin(angle))
|
|
1073
|
+
]);
|
|
1074
|
+
}
|
|
1075
|
+
if (convex) this.points = convexHull(this.points);
|
|
1076
|
+
this.computeBbox();
|
|
1077
|
+
}
|
|
1078
|
+
computeBbox() {
|
|
1079
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
1080
|
+
for (const [x, y] of this.points) {
|
|
1081
|
+
if (x < minX) minX = x;
|
|
1082
|
+
if (y < minY) minY = y;
|
|
1083
|
+
if (x > maxX) maxX = x;
|
|
1084
|
+
if (y > maxY) maxY = y;
|
|
1085
|
+
}
|
|
1086
|
+
this.bbox = {
|
|
1087
|
+
left: minX,
|
|
1088
|
+
top: minY,
|
|
1089
|
+
width: maxX - minX || 1,
|
|
1090
|
+
height: maxY - minY || 1
|
|
1091
|
+
};
|
|
1092
|
+
return this;
|
|
1093
|
+
}
|
|
1094
|
+
render(ctx) {
|
|
1095
|
+
ctx.beginPath();
|
|
1096
|
+
this.points.forEach(([x, y], i) => i ? ctx.lineTo(x, y) : ctx.moveTo(x, y));
|
|
1097
|
+
ctx.closePath();
|
|
1098
|
+
ctx.fill();
|
|
1099
|
+
}
|
|
1100
|
+
toSVG() {
|
|
1101
|
+
const path = document.createElementNS(SVGNS, "path");
|
|
1102
|
+
const d = this.points.map(([x, y], i) => `${i ? "L" : "M"}${x},${y}`).join("") + "Z";
|
|
1103
|
+
path.setAttribute("d", d);
|
|
1104
|
+
return path;
|
|
1105
|
+
}
|
|
1106
|
+
toData(a, c) {
|
|
1107
|
+
return { t: "p", a, c, pts: this.points.map(([x, y]) => [x, y]) };
|
|
1108
|
+
}
|
|
1109
|
+
mutate(_cfg) {
|
|
1110
|
+
const clone = new NGonIrregular(0, 0);
|
|
1111
|
+
clone.points = this.points.map(([x, y]) => [x, y]);
|
|
1112
|
+
const index = Math.floor(Math.random() * clone.points.length);
|
|
1113
|
+
const point = clone.points[index];
|
|
1114
|
+
const angle = Math.random() * 2 * Math.PI;
|
|
1115
|
+
const radius = Math.random() * mutationScale;
|
|
1116
|
+
point[0] += ~~(radius * Math.cos(angle));
|
|
1117
|
+
point[1] += ~~(radius * Math.sin(angle));
|
|
1118
|
+
if (convex) clone.points = convexHull(clone.points);
|
|
1119
|
+
return clone.computeBbox();
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
return NGonIrregular;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
__name(makeNGon, "makeNGon");
|
|
1126
|
+
function makeRect(opts) {
|
|
1127
|
+
const widthRange = opts?.widthRange ?? [5, 40];
|
|
1128
|
+
const heightRange = opts?.heightRange ?? [5, 40];
|
|
1129
|
+
const aspectRatio = opts?.aspectRatio;
|
|
1130
|
+
const rotatable = opts?.rotatable ?? false;
|
|
1131
|
+
const mutationScale = opts?.mutationScale ?? 20;
|
|
1132
|
+
class Rect extends Shape {
|
|
1133
|
+
static {
|
|
1134
|
+
__name(this, "Rect");
|
|
1135
|
+
}
|
|
1136
|
+
static _rectOpts = { widthRange, heightRange, aspectRatio, rotatable, mutationScale };
|
|
1137
|
+
static _shapeSpec = { f: "rect" };
|
|
1138
|
+
center;
|
|
1139
|
+
hw;
|
|
1140
|
+
hh;
|
|
1141
|
+
angle;
|
|
1142
|
+
constructor(w, h) {
|
|
1143
|
+
super(w, h);
|
|
1144
|
+
this.center = Shape.randomPoint(w, h);
|
|
1145
|
+
this.hw = widthRange[0] + ~~(Math.random() * (widthRange[1] - widthRange[0]));
|
|
1146
|
+
if (aspectRatio !== void 0) {
|
|
1147
|
+
this.hh = Math.max(1, Math.round(this.hw / aspectRatio));
|
|
1148
|
+
} else {
|
|
1149
|
+
this.hh = heightRange[0] + ~~(Math.random() * (heightRange[1] - heightRange[0]));
|
|
1150
|
+
}
|
|
1151
|
+
this.angle = rotatable ? Math.random() * Math.PI : 0;
|
|
1152
|
+
this.computeBbox();
|
|
1153
|
+
}
|
|
1154
|
+
computeBbox() {
|
|
1155
|
+
const cos = Math.abs(Math.cos(this.angle));
|
|
1156
|
+
const sin = Math.abs(Math.sin(this.angle));
|
|
1157
|
+
const w = ~~(this.hw * cos + this.hh * sin);
|
|
1158
|
+
const h = ~~(this.hw * sin + this.hh * cos);
|
|
1159
|
+
this.bbox = {
|
|
1160
|
+
left: this.center[0] - w,
|
|
1161
|
+
top: this.center[1] - h,
|
|
1162
|
+
width: 2 * w || 1,
|
|
1163
|
+
height: 2 * h || 1
|
|
1164
|
+
};
|
|
1165
|
+
return this;
|
|
1166
|
+
}
|
|
1167
|
+
render(ctx) {
|
|
1168
|
+
const cos = Math.cos(this.angle);
|
|
1169
|
+
const sin = Math.sin(this.angle);
|
|
1170
|
+
const corners = [
|
|
1171
|
+
[this.center[0] - this.hw * cos + this.hh * sin, this.center[1] - this.hw * sin - this.hh * cos],
|
|
1172
|
+
[this.center[0] + this.hw * cos + this.hh * sin, this.center[1] + this.hw * sin - this.hh * cos],
|
|
1173
|
+
[this.center[0] + this.hw * cos - this.hh * sin, this.center[1] + this.hw * sin + this.hh * cos],
|
|
1174
|
+
[this.center[0] - this.hw * cos - this.hh * sin, this.center[1] - this.hw * sin + this.hh * cos]
|
|
1175
|
+
];
|
|
1176
|
+
ctx.beginPath();
|
|
1177
|
+
corners.forEach(([x, y], i) => i ? ctx.lineTo(~~x, ~~y) : ctx.moveTo(~~x, ~~y));
|
|
1178
|
+
ctx.closePath();
|
|
1179
|
+
ctx.fill();
|
|
1180
|
+
}
|
|
1181
|
+
toSVG() {
|
|
1182
|
+
const cos = Math.cos(this.angle);
|
|
1183
|
+
const sin = Math.sin(this.angle);
|
|
1184
|
+
const corners = [
|
|
1185
|
+
[this.center[0] - this.hw * cos + this.hh * sin, this.center[1] - this.hw * sin - this.hh * cos],
|
|
1186
|
+
[this.center[0] + this.hw * cos + this.hh * sin, this.center[1] + this.hw * sin - this.hh * cos],
|
|
1187
|
+
[this.center[0] + this.hw * cos - this.hh * sin, this.center[1] + this.hw * sin + this.hh * cos],
|
|
1188
|
+
[this.center[0] - this.hw * cos - this.hh * sin, this.center[1] - this.hw * sin + this.hh * cos]
|
|
1189
|
+
];
|
|
1190
|
+
const node = document.createElementNS(SVGNS, "polygon");
|
|
1191
|
+
node.setAttribute("points", corners.map((p) => p.join(",")).join(" "));
|
|
1192
|
+
return node;
|
|
1193
|
+
}
|
|
1194
|
+
toData(a, c) {
|
|
1195
|
+
return {
|
|
1196
|
+
t: "rc",
|
|
1197
|
+
a,
|
|
1198
|
+
c,
|
|
1199
|
+
cx: this.center[0],
|
|
1200
|
+
cy: this.center[1],
|
|
1201
|
+
hw: this.hw,
|
|
1202
|
+
hh: this.hh,
|
|
1203
|
+
angle: this.angle
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
mutate(_cfg) {
|
|
1207
|
+
const clone = new Rect(0, 0);
|
|
1208
|
+
clone.center = [this.center[0], this.center[1]];
|
|
1209
|
+
clone.hw = this.hw;
|
|
1210
|
+
clone.hh = this.hh;
|
|
1211
|
+
clone.angle = this.angle;
|
|
1212
|
+
const mutCount = 2 + (rotatable ? 1 : 0) + (aspectRatio === void 0 ? 1 : 0);
|
|
1213
|
+
switch (Math.floor(Math.random() * mutCount)) {
|
|
1214
|
+
case 0: {
|
|
1215
|
+
const a = Math.random() * 2 * Math.PI;
|
|
1216
|
+
const d = Math.random() * mutationScale;
|
|
1217
|
+
clone.center[0] += ~~(d * Math.cos(a));
|
|
1218
|
+
clone.center[1] += ~~(d * Math.sin(a));
|
|
1219
|
+
break;
|
|
1220
|
+
}
|
|
1221
|
+
case 1:
|
|
1222
|
+
clone.hw += (Math.random() - 0.5) * mutationScale;
|
|
1223
|
+
clone.hw = Math.max(1, ~~clone.hw);
|
|
1224
|
+
if (aspectRatio !== void 0) {
|
|
1225
|
+
clone.hh = Math.max(1, Math.round(clone.hw / aspectRatio));
|
|
1226
|
+
}
|
|
1227
|
+
break;
|
|
1228
|
+
case 2:
|
|
1229
|
+
if (rotatable) {
|
|
1230
|
+
clone.angle += (Math.random() - 0.5) * Math.PI / 4;
|
|
1231
|
+
} else if (aspectRatio === void 0) {
|
|
1232
|
+
clone.hh += (Math.random() - 0.5) * mutationScale;
|
|
1233
|
+
clone.hh = Math.max(1, ~~clone.hh);
|
|
1234
|
+
}
|
|
1235
|
+
break;
|
|
1236
|
+
case 3:
|
|
1237
|
+
if (aspectRatio === void 0) {
|
|
1238
|
+
clone.hh += (Math.random() - 0.5) * mutationScale;
|
|
1239
|
+
clone.hh = Math.max(1, ~~clone.hh);
|
|
1240
|
+
}
|
|
1241
|
+
break;
|
|
1242
|
+
}
|
|
1243
|
+
return clone.computeBbox();
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
return Rect;
|
|
1247
|
+
}
|
|
1248
|
+
__name(makeRect, "makeRect");
|
|
869
1249
|
var Debug = class extends Shape {
|
|
1250
|
+
static {
|
|
1251
|
+
__name(this, "Debug");
|
|
1252
|
+
}
|
|
870
1253
|
constructor(w, h) {
|
|
871
1254
|
super(w, h);
|
|
872
1255
|
this.bbox = { left: 0, top: 0, width: w, height: h };
|
|
@@ -893,7 +1276,11 @@ function buildStepPlan(cfg) {
|
|
|
893
1276
|
}
|
|
894
1277
|
return plan;
|
|
895
1278
|
}
|
|
1279
|
+
__name(buildStepPlan, "buildStepPlan");
|
|
896
1280
|
var Optimizer = class {
|
|
1281
|
+
static {
|
|
1282
|
+
__name(this, "Optimizer");
|
|
1283
|
+
}
|
|
897
1284
|
cfg;
|
|
898
1285
|
state;
|
|
899
1286
|
onStep;
|
|
@@ -972,7 +1359,7 @@ var Optimizer = class {
|
|
|
972
1359
|
let resolve;
|
|
973
1360
|
let bestStep = step;
|
|
974
1361
|
const promise = new Promise((r) => resolve = r);
|
|
975
|
-
const tryMutation = () => {
|
|
1362
|
+
const tryMutation = /* @__PURE__ */ __name(() => {
|
|
976
1363
|
if (failedAttempts >= LIMIT) {
|
|
977
1364
|
return resolve(bestStep);
|
|
978
1365
|
}
|
|
@@ -985,7 +1372,7 @@ var Optimizer = class {
|
|
|
985
1372
|
}
|
|
986
1373
|
tryMutation();
|
|
987
1374
|
});
|
|
988
|
-
};
|
|
1375
|
+
}, "tryMutation");
|
|
989
1376
|
tryMutation();
|
|
990
1377
|
return promise;
|
|
991
1378
|
}
|
|
@@ -995,18 +1382,21 @@ var Optimizer = class {
|
|
|
995
1382
|
function rgbString([r, g, b]) {
|
|
996
1383
|
return `rgb(${r}, ${g}, ${b})`;
|
|
997
1384
|
}
|
|
1385
|
+
__name(rgbString, "rgbString");
|
|
998
1386
|
function hexPoints(cx, cy, r, angle) {
|
|
999
1387
|
return Array.from({ length: 6 }, (_, i) => {
|
|
1000
1388
|
const a = angle + i * Math.PI / 3;
|
|
1001
1389
|
return [~~(cx + r * Math.cos(a)), ~~(cy + r * Math.sin(a))];
|
|
1002
1390
|
});
|
|
1003
1391
|
}
|
|
1392
|
+
__name(hexPoints, "hexPoints");
|
|
1004
1393
|
function renderStepToCtx(data, ctx) {
|
|
1005
1394
|
ctx.globalAlpha = data.a;
|
|
1006
1395
|
ctx.fillStyle = rgbString(data.c);
|
|
1007
1396
|
switch (data.t) {
|
|
1008
1397
|
case "t":
|
|
1009
|
-
case "r":
|
|
1398
|
+
case "r":
|
|
1399
|
+
case "p": {
|
|
1010
1400
|
ctx.beginPath();
|
|
1011
1401
|
data.pts.forEach(([x, y], i) => i ? ctx.lineTo(x, y) : ctx.moveTo(x, y));
|
|
1012
1402
|
ctx.closePath();
|
|
@@ -1044,15 +1434,30 @@ function renderStepToCtx(data, ctx) {
|
|
|
1044
1434
|
ctx.fillText(data.text, data.cx, data.cy);
|
|
1045
1435
|
break;
|
|
1046
1436
|
}
|
|
1437
|
+
case "rc": {
|
|
1438
|
+
const { cx, cy, hw, hh, angle } = data;
|
|
1439
|
+
const cos = Math.cos(angle);
|
|
1440
|
+
const sin = Math.sin(angle);
|
|
1441
|
+
ctx.beginPath();
|
|
1442
|
+
ctx.moveTo(cx - hw * cos + hh * sin, cy - hw * sin - hh * cos);
|
|
1443
|
+
ctx.lineTo(cx + hw * cos + hh * sin, cy + hw * sin - hh * cos);
|
|
1444
|
+
ctx.lineTo(cx + hw * cos - hh * sin, cy + hw * sin + hh * cos);
|
|
1445
|
+
ctx.lineTo(cx - hw * cos - hh * sin, cy - hw * sin + hh * cos);
|
|
1446
|
+
ctx.closePath();
|
|
1447
|
+
ctx.fill();
|
|
1448
|
+
break;
|
|
1449
|
+
}
|
|
1047
1450
|
}
|
|
1048
1451
|
}
|
|
1452
|
+
__name(renderStepToCtx, "renderStepToCtx");
|
|
1049
1453
|
function stepDataToSVGElement(data) {
|
|
1050
1454
|
const color = rgbString(data.c);
|
|
1051
1455
|
const opacity = data.a.toFixed(2);
|
|
1052
1456
|
let node;
|
|
1053
1457
|
switch (data.t) {
|
|
1054
1458
|
case "t":
|
|
1055
|
-
case "r":
|
|
1459
|
+
case "r":
|
|
1460
|
+
case "p": {
|
|
1056
1461
|
node = document.createElementNS(SVGNS, "path");
|
|
1057
1462
|
const d = data.pts.map(([x, y], i) => `${i ? "L" : "M"}${x},${y}`).join("") + "Z";
|
|
1058
1463
|
node.setAttribute("d", d);
|
|
@@ -1098,11 +1503,27 @@ function stepDataToSVGElement(data) {
|
|
|
1098
1503
|
node.setAttribute("y", String(data.cy));
|
|
1099
1504
|
break;
|
|
1100
1505
|
}
|
|
1506
|
+
case "rc": {
|
|
1507
|
+
const { cx, cy, hw, hh, angle } = data;
|
|
1508
|
+
const cos = Math.cos(angle);
|
|
1509
|
+
const sin = Math.sin(angle);
|
|
1510
|
+
const fmt = /* @__PURE__ */ __name((n) => n.toFixed(2), "fmt");
|
|
1511
|
+
const pts = [
|
|
1512
|
+
[cx - hw * cos + hh * sin, cy - hw * sin - hh * cos],
|
|
1513
|
+
[cx + hw * cos + hh * sin, cy + hw * sin - hh * cos],
|
|
1514
|
+
[cx + hw * cos - hh * sin, cy + hw * sin + hh * cos],
|
|
1515
|
+
[cx - hw * cos - hh * sin, cy - hw * sin + hh * cos]
|
|
1516
|
+
];
|
|
1517
|
+
node = document.createElementNS(SVGNS, "polygon");
|
|
1518
|
+
node.setAttribute("points", pts.map(([x, y]) => `${fmt(x)},${fmt(y)}`).join(" "));
|
|
1519
|
+
break;
|
|
1520
|
+
}
|
|
1101
1521
|
}
|
|
1102
1522
|
node.setAttribute("fill", color);
|
|
1103
1523
|
node.setAttribute("fill-opacity", opacity);
|
|
1104
1524
|
return node;
|
|
1105
1525
|
}
|
|
1526
|
+
__name(stepDataToSVGElement, "stepDataToSVGElement");
|
|
1106
1527
|
function replayOutput(data) {
|
|
1107
1528
|
const fill = rgbString(data.fill);
|
|
1108
1529
|
const vw = data.w * data.scale;
|
|
@@ -1120,6 +1541,7 @@ function replayOutput(data) {
|
|
|
1120
1541
|
const svgString = new XMLSerializer().serializeToString(svg);
|
|
1121
1542
|
return { raster: raster.node, svg, svgString };
|
|
1122
1543
|
}
|
|
1544
|
+
__name(replayOutput, "replayOutput");
|
|
1123
1545
|
export {
|
|
1124
1546
|
Canvas,
|
|
1125
1547
|
Circle,
|
|
@@ -1142,6 +1564,8 @@ export {
|
|
|
1142
1564
|
differenceToDistance,
|
|
1143
1565
|
distanceToDifference,
|
|
1144
1566
|
getFill,
|
|
1567
|
+
makeNGon,
|
|
1568
|
+
makeRect,
|
|
1145
1569
|
parseColor,
|
|
1146
1570
|
renderStepToCtx,
|
|
1147
1571
|
replayOutput,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slithy/prim-lib",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Core engine for primitive-based image reconstruction.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
46
|
"clean": "rm -rf dist",
|
|
47
|
-
"build": "rm -rf dist && tsup src/index.ts --format esm --dts",
|
|
48
|
-
"dev": "tsup src/index.ts --format esm --watch",
|
|
47
|
+
"build": "rm -rf dist && tsup src/index.ts --format esm --dts --keep-names",
|
|
48
|
+
"dev": "tsup src/index.ts --format esm --watch --keep-names",
|
|
49
49
|
"typecheck": "tsc --noEmit",
|
|
50
50
|
"lint": "eslint .",
|
|
51
51
|
"test": "vitest run",
|