@slithy/prim-lib 0.8.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.
Files changed (2) hide show
  1. package/README.md +105 -3
  2. package/package.json +16 -17
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 }), makeRect({ aspectRatio: 1.78 })]
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slithy/prim-lib",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Core engine for primitive-based image reconstruction.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -13,24 +13,14 @@
13
13
  "dist"
14
14
  ],
15
15
  "sideEffects": false,
16
- "scripts": {
17
- "clean": "rm -rf dist",
18
- "prepack": "rm -rf dist && tsup src/index.ts --format esm --dts --keep-names",
19
- "build": "rm -rf dist && tsup src/index.ts --format esm --dts --keep-names",
20
- "dev": "tsup src/index.ts --format esm --watch --keep-names",
21
- "typecheck": "tsc --noEmit",
22
- "lint": "eslint .",
23
- "test": "vitest run",
24
- "test:watch": "vitest"
25
- },
26
16
  "devDependencies": {
27
- "@slithy/eslint-config": "workspace:*",
28
- "@slithy/tsconfig": "workspace:*",
29
- "@vitest/coverage-v8": "^4.1.2",
30
- "jsdom": "^29.0.1",
17
+ "@vitest/coverage-v8": "^4.1.7",
18
+ "jsdom": "^29.1.1",
31
19
  "tsup": "^8",
32
20
  "typescript": "^5",
33
- "vitest": "^4.1.2"
21
+ "vitest": "^4.1.7",
22
+ "@slithy/eslint-config": "0.0.0",
23
+ "@slithy/tsconfig": "0.0.0"
34
24
  },
35
25
  "author": {
36
26
  "name": "Matthew Campagna",
@@ -51,5 +41,14 @@
51
41
  },
52
42
  "publishConfig": {
53
43
  "access": "public"
44
+ },
45
+ "scripts": {
46
+ "clean": "rm -rf dist",
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
+ "typecheck": "tsc --noEmit",
50
+ "lint": "eslint .",
51
+ "test": "vitest run",
52
+ "test:watch": "vitest"
54
53
  }
55
- }
54
+ }