@logomarks/core 0.1.4 → 0.1.5
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/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/src/edit/index.d.ts +1 -1
- package/src/edit/index.js +1 -1
- package/src/edit/pixelate.d.ts +6 -0
- package/src/edit/pixelate.js +43 -6
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,12 @@ All notable changes to `@logomarks/core` are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.5] — 2026-11-06
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `pixelCoverage(paths, grid)` measures how much of each pixel cell a mark covers from its rings, by scanline, so pixelate runs without a canvas (a server, a Worker); `pixelCells(cov, grid)` returns the filled cells themselves, for drawing them one by one (`@arraypress/covers` uses both for its pixel mosaic). `pixelPath` is unchanged.
|
|
13
|
+
|
|
8
14
|
## [0.1.4] — 2026-11-06
|
|
9
15
|
|
|
10
16
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logomarks/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Geometry, SVG reading, fitting, analysis and similarity for logomarks on a 512 grid: the kit the Logomarks set is drawn with (shapes, real booleans, strokes, tracks, iso solids, badges, letter skeletons), one SVG path parser, the 448 fit, speck and shape-tag analysis, outline signatures, and the industry-symbol parts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
package/src/edit/index.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export type { Adjust, Frame, Similarity, AdjustEnv } from './adjust.js';
|
|
|
3
3
|
export type { PixelGrid } from './pixelate.js';
|
|
4
4
|
export { loadShaper, shaperReady, hasCorners, reshape, pieceCount, shapeInfo, smallSizeInfo } from './reshape.js';
|
|
5
5
|
export { NO_ADJUST, isAdjusted, adjustSuffix, transformPath, adjustPaths } from './adjust.js';
|
|
6
|
-
export { SS, pixelGrid, pixelPath } from './pixelate.js';
|
|
6
|
+
export { SS, pixelGrid, pixelPath, pixelCells, pixelCoverage } from './pixelate.js';
|
package/src/edit/index.js
CHANGED
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export { loadShaper, shaperReady, hasCorners, reshape, pieceCount, shapeInfo, smallSizeInfo } from './reshape.js';
|
|
6
6
|
export { NO_ADJUST, isAdjusted, adjustSuffix, transformPath, adjustPaths } from './adjust.js';
|
|
7
|
-
export { SS, pixelGrid, pixelPath } from './pixelate.js';
|
|
7
|
+
export { SS, pixelGrid, pixelPath, pixelCells, pixelCoverage } from './pixelate.js';
|
package/src/edit/pixelate.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { FillPath } from '../raster/scanline.js';
|
|
2
|
+
|
|
1
3
|
/** A pixel grid over a mark's box. */
|
|
2
4
|
export interface PixelGrid {
|
|
3
5
|
cols: number;
|
|
@@ -11,5 +13,9 @@ export interface PixelGrid {
|
|
|
11
13
|
export const SS: 6;
|
|
12
14
|
/** The grid for a box: 48 cells across its longest side at amount 0, 14 at amount 1, centred. */
|
|
13
15
|
export function pixelGrid(amount: number, box: { x: number; y: number; width: number; height: number }): PixelGrid;
|
|
16
|
+
/** How much of each cell the mark's rings cover (0–1, row by row), by scanline at SS × SS samples a cell. */
|
|
17
|
+
export function pixelCoverage(paths: readonly FillPath[], grid: PixelGrid): Float32Array;
|
|
18
|
+
/** The filled cells (1 each, row by row): half covered, or a fifth where a cell stands alone or bridges two. */
|
|
19
|
+
export function pixelCells(cov: ArrayLike<number>, grid: Pick<PixelGrid, 'cols' | 'rows'>): Uint8Array;
|
|
14
20
|
/** The pixelated mark as one path's d, from each cell's coverage; null when nothing is filled. */
|
|
15
21
|
export function pixelPath(cov: ArrayLike<number>, grid: PixelGrid): string | null;
|
package/src/edit/pixelate.js
CHANGED
|
@@ -11,7 +11,12 @@
|
|
|
11
11
|
* alone (a thin line or a small dot would vanish) or when it bridges two filled cells across it (a diagonal would
|
|
12
12
|
* break into steps that don't touch).
|
|
13
13
|
* - Filled cells are merged into as few rectangles as possible, so the SVG stays small and clean.
|
|
14
|
+
*
|
|
15
|
+
* Where there's no canvas to measure with (a server, a Worker), `pixelCoverage` measures the cells from the mark's
|
|
16
|
+
* rings by scanline; `pixelCells` gives the filled cells themselves, for drawing them one by one (a mosaic).
|
|
14
17
|
*/
|
|
18
|
+
import { fill } from '../raster/scanline.js';
|
|
19
|
+
|
|
15
20
|
/** Samples a cell is measured at, along each side (6 × 6 a cell). */
|
|
16
21
|
export const SS = 6;
|
|
17
22
|
|
|
@@ -32,20 +37,38 @@ export function pixelGrid(amount, box) {
|
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
40
|
+
* How much of each cell the mark covers, measured from its rings by scanline at `SS` × `SS` samples a cell: what a
|
|
41
|
+
* canvas would measure, without one.
|
|
42
|
+
*
|
|
43
|
+
* @param {readonly import('../raster/scanline.js').FillPath[]} paths The mark's paths (rings and fill rules).
|
|
44
|
+
* @param {import('./index.js').PixelGrid} grid The grid.
|
|
45
|
+
* @returns {Float32Array} Coverage 0–1 per cell, row by row.
|
|
46
|
+
*/
|
|
47
|
+
export function pixelCoverage(paths, { cols, rows, cell, x0, y0 }) {
|
|
48
|
+
const bmp = fill(paths, { x0, y0, scale: SS / cell, width: cols * SS, height: rows * SS });
|
|
49
|
+
const cov = new Float32Array(cols * rows);
|
|
50
|
+
for (let j = 0; j < bmp.height; j++)
|
|
51
|
+
for (let i = 0; i < bmp.width; i++)
|
|
52
|
+
if (bmp.data[j * bmp.width + i])
|
|
53
|
+
cov[Math.floor(j / SS) * cols + Math.floor(i / SS)]++;
|
|
54
|
+
for (let k = 0; k < cov.length; k++)
|
|
55
|
+
cov[k] /= SS * SS;
|
|
56
|
+
return cov;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The filled cells, from how much of each the mark covers: half or more, or a fifth or more where a cell stands alone
|
|
61
|
+
* (a thin line or a small dot would vanish) or bridges two filled cells across it (a diagonal would break).
|
|
38
62
|
*
|
|
39
63
|
* @param {ArrayLike<number>} cov Coverage 0–1 per cell, row by row.
|
|
40
64
|
* @param {import('./index.js').PixelGrid} grid The grid.
|
|
41
|
-
* @returns {
|
|
65
|
+
* @returns {Uint8Array} 1 per filled cell, row by row.
|
|
42
66
|
*/
|
|
43
|
-
export function
|
|
67
|
+
export function pixelCells(cov, { cols, rows }) {
|
|
44
68
|
const at = (m, q, r) => (q >= 0 && r >= 0 && q < cols && r < rows ? m[r * cols + q] : 0);
|
|
45
69
|
const on = new Uint8Array(cols * rows);
|
|
46
70
|
for (let i = 0; i < on.length; i++)
|
|
47
71
|
on[i] = cov[i] >= 0.5 ? 1 : 0;
|
|
48
|
-
// keep thin lines and small dots, and bridge diagonals
|
|
49
72
|
const keep = on.slice();
|
|
50
73
|
for (let r = 0; r < rows; r++)
|
|
51
74
|
for (let q = 0; q < cols; q++) {
|
|
@@ -64,6 +87,20 @@ export function pixelPath(cov, { cols, rows, cell, x0, y0 }) {
|
|
|
64
87
|
if (!near || (bridges && cov[i] >= 0.3))
|
|
65
88
|
keep[i] = 1;
|
|
66
89
|
}
|
|
90
|
+
return keep;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The pixelated mark as one path's d, from how much of each cell the mark covers: cells covered half or more are
|
|
95
|
+
* filled, cells covered a fifth or more too when they stand alone or bridge two filled cells, then merged into as few
|
|
96
|
+
* rectangles as possible. Null when nothing is filled.
|
|
97
|
+
*
|
|
98
|
+
* @param {ArrayLike<number>} cov Coverage 0–1 per cell, row by row.
|
|
99
|
+
* @param {import('./index.js').PixelGrid} grid The grid.
|
|
100
|
+
* @returns {string | null} The path data.
|
|
101
|
+
*/
|
|
102
|
+
export function pixelPath(cov, { cols, rows, cell, x0, y0 }) {
|
|
103
|
+
const keep = pixelCells(cov, { cols, rows });
|
|
67
104
|
const done = [];
|
|
68
105
|
let open = new Map();
|
|
69
106
|
for (let r = 0; r <= rows; r++) {
|