@slithy/prim-lib 0.8.0 → 0.8.2
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/LICENSE-COMMERCIAL.md +23 -0
- package/README.md +105 -3
- package/package.json +19 -18
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Commercial License for @slithy/prim-lib
|
|
2
|
+
|
|
3
|
+
Copyright Matthew Campagna
|
|
4
|
+
|
|
5
|
+
1. Grant
|
|
6
|
+
|
|
7
|
+
Matthew Campagna grants Matthew Campagna a personal, non-transferable, non-sublicensable license to use, reproduce, modify, distribute, and commercially exploit `@slithy/prim-lib` as part of software products and related internal development activities.
|
|
8
|
+
|
|
9
|
+
2. Scope
|
|
10
|
+
|
|
11
|
+
This commercial license applies only to Matthew Campagna in his personal capacity. It does not extend to any employer, client, contractor, affiliate, assignee, or other third party.
|
|
12
|
+
|
|
13
|
+
3. Public License Unchanged
|
|
14
|
+
|
|
15
|
+
This commercial license is separate from the public license distributed with the package. All other recipients use the software, if at all, under the terms of the public license or another separately granted written license.
|
|
16
|
+
|
|
17
|
+
4. Third-Party Attributions
|
|
18
|
+
|
|
19
|
+
This package includes or is derived from third-party material identified in the package `LICENSE` file. Those third-party attribution and notice requirements remain in effect.
|
|
20
|
+
|
|
21
|
+
5. No Warranty
|
|
22
|
+
|
|
23
|
+
To the maximum extent permitted by law, the software is provided "as is", without warranty of any kind, express or implied.
|
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slithy/prim-lib",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"description": "Core engine for primitive-based image reconstruction.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -10,33 +10,25 @@
|
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
|
-
"dist"
|
|
13
|
+
"dist",
|
|
14
|
+
"LICENSE-COMMERCIAL.md"
|
|
14
15
|
],
|
|
15
16
|
"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
17
|
"devDependencies": {
|
|
27
|
-
"@
|
|
28
|
-
"
|
|
29
|
-
"@vitest/coverage-v8": "^4.1.2",
|
|
30
|
-
"jsdom": "^29.0.1",
|
|
18
|
+
"@vitest/coverage-v8": "^4.1.7",
|
|
19
|
+
"jsdom": "^29.1.1",
|
|
31
20
|
"tsup": "^8",
|
|
32
21
|
"typescript": "^5",
|
|
33
|
-
"vitest": "^4.1.
|
|
22
|
+
"vitest": "^4.1.7",
|
|
23
|
+
"@slithy/eslint-config": "0.0.0",
|
|
24
|
+
"@slithy/tsconfig": "0.0.0"
|
|
34
25
|
},
|
|
35
26
|
"author": {
|
|
36
27
|
"name": "Matthew Campagna",
|
|
37
28
|
"url": "https://github.com/mjcampagna"
|
|
38
29
|
},
|
|
39
30
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
31
|
+
"commercialLicense": "SEE LICENSE-COMMERCIAL.md",
|
|
40
32
|
"keywords": [
|
|
41
33
|
"image",
|
|
42
34
|
"reconstruction",
|
|
@@ -51,5 +43,14 @@
|
|
|
51
43
|
},
|
|
52
44
|
"publishConfig": {
|
|
53
45
|
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"clean": "rm -rf dist",
|
|
49
|
+
"build": "rm -rf dist && tsup src/index.ts --format esm --dts --keep-names",
|
|
50
|
+
"dev": "tsup src/index.ts --format esm --watch --keep-names",
|
|
51
|
+
"typecheck": "tsc --noEmit",
|
|
52
|
+
"lint": "eslint .",
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"test:watch": "vitest"
|
|
54
55
|
}
|
|
55
|
-
}
|
|
56
|
+
}
|