@thi.ng/geom-trace-bitmap 0.3.39 → 0.3.41
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 +1 -1
- package/README.md +1 -1
- package/api.js +0 -1
- package/border.js +15 -28
- package/extract.js +35 -67
- package/package.json +12 -10
- package/trace.js +121 -126
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
package/api.js
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/border.js
CHANGED
|
@@ -1,31 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* @param w - image width
|
|
5
|
-
* @param h - image width
|
|
6
|
-
*/
|
|
7
|
-
export const borderX = (w) => {
|
|
8
|
-
w--;
|
|
9
|
-
return (p) => p[0] === 0 || p[0] === w;
|
|
1
|
+
const borderX = (w) => {
|
|
2
|
+
w--;
|
|
3
|
+
return (p) => p[0] === 0 || p[0] === w;
|
|
10
4
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* @param w - image width
|
|
15
|
-
* @param h - image width
|
|
16
|
-
*/
|
|
17
|
-
export const borderY = (_, h) => {
|
|
18
|
-
h--;
|
|
19
|
-
return (p) => p[1] === 0 || p[1] === h;
|
|
5
|
+
const borderY = (_, h) => {
|
|
6
|
+
h--;
|
|
7
|
+
return (p) => p[1] === 0 || p[1] === h;
|
|
20
8
|
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return (p) => p[0] === 0 || p[0] === w || p[1] === 0 || p[1] === h;
|
|
9
|
+
const borderXY = (w, h) => {
|
|
10
|
+
w--;
|
|
11
|
+
h--;
|
|
12
|
+
return (p) => p[0] === 0 || p[0] === w || p[1] === 0 || p[1] === h;
|
|
13
|
+
};
|
|
14
|
+
export {
|
|
15
|
+
borderX,
|
|
16
|
+
borderXY,
|
|
17
|
+
borderY
|
|
31
18
|
};
|
package/extract.js
CHANGED
|
@@ -1,72 +1,40 @@
|
|
|
1
1
|
import { comparator2 } from "@thi.ng/vectors/compare";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* (assuming all points are aligned to a grid, e.g. pixel coords). Returns
|
|
5
|
-
* object of `{segments, points}`, where `segments` contains all extracted
|
|
6
|
-
* segments and `points` all remaining/unmatched points.
|
|
7
|
-
*
|
|
8
|
-
* @remarks
|
|
9
|
-
* The given point array will be sorted (in-place!). Line segments will be as
|
|
10
|
-
* long as possible, depending on chosen `maxDist`, which defines max distance
|
|
11
|
-
* between consecutive points. If a point is further away than `maxDist` units
|
|
12
|
-
* from the previous point (or at a new X coord), the current line segment (if
|
|
13
|
-
* any) will be terminated and the new point is potentially becoming the start
|
|
14
|
-
* of the next segment.
|
|
15
|
-
*
|
|
16
|
-
* @param pts
|
|
17
|
-
* @param maxDist
|
|
18
|
-
*/
|
|
19
|
-
export const extractSegmentsX = (pts, maxD = 5) => __extract(pts, maxD, 1);
|
|
20
|
-
/**
|
|
21
|
-
* Similar to {@link extractSegmentsX}, but for extracting vertical line
|
|
22
|
-
* segments (along Y-axis).
|
|
23
|
-
*
|
|
24
|
-
* @param pts
|
|
25
|
-
* @param maxDist
|
|
26
|
-
*/
|
|
27
|
-
export const extractSegmentsY = (pts, maxDist = 5) => __extract(pts, maxDist, 0);
|
|
28
|
-
/**
|
|
29
|
-
* Common implementation for both axis orders.
|
|
30
|
-
*
|
|
31
|
-
* @param pts
|
|
32
|
-
* @param maxD
|
|
33
|
-
* @param order
|
|
34
|
-
*
|
|
35
|
-
* @internal
|
|
36
|
-
*/
|
|
2
|
+
const extractSegmentsX = (pts, maxD = 5) => __extract(pts, maxD, 1);
|
|
3
|
+
const extractSegmentsY = (pts, maxDist = 5) => __extract(pts, maxDist, 0);
|
|
37
4
|
const __extract = (pts, maxD, order) => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
points.push(pts[last]);
|
|
55
|
-
}
|
|
56
|
-
last = i;
|
|
57
|
-
}
|
|
58
|
-
inner = p[1];
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
if (i - last > 1) {
|
|
62
|
-
segments.push([pts[last], pts[i - 1]]);
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
points.push(pts[last]);
|
|
66
|
-
}
|
|
67
|
-
last = i;
|
|
68
|
-
[outer, inner] = p;
|
|
5
|
+
if (pts.length < 2)
|
|
6
|
+
return { segments: [], points: pts };
|
|
7
|
+
const $ = order ? (p) => [p[1], p[0]] : (p) => p;
|
|
8
|
+
pts = pts.sort(comparator2(order, order ^ 1));
|
|
9
|
+
const segments = [];
|
|
10
|
+
const points = [];
|
|
11
|
+
let [outer, inner] = $(pts[0]);
|
|
12
|
+
let last = 0;
|
|
13
|
+
for (let i = 1, n = pts.length - 1; i <= n; i++) {
|
|
14
|
+
const p = $(pts[i]);
|
|
15
|
+
if (p[0] === outer) {
|
|
16
|
+
if (i === n || p[1] - inner > maxD) {
|
|
17
|
+
if (i - last > 1) {
|
|
18
|
+
segments.push([pts[last], pts[i - 1]]);
|
|
19
|
+
} else {
|
|
20
|
+
points.push(pts[last]);
|
|
69
21
|
}
|
|
22
|
+
last = i;
|
|
23
|
+
}
|
|
24
|
+
inner = p[1];
|
|
25
|
+
} else {
|
|
26
|
+
if (i - last > 1) {
|
|
27
|
+
segments.push([pts[last], pts[i - 1]]);
|
|
28
|
+
} else {
|
|
29
|
+
points.push(pts[last]);
|
|
30
|
+
}
|
|
31
|
+
last = i;
|
|
32
|
+
[outer, inner] = p;
|
|
70
33
|
}
|
|
71
|
-
|
|
34
|
+
}
|
|
35
|
+
return { segments, points };
|
|
36
|
+
};
|
|
37
|
+
export {
|
|
38
|
+
extractSegmentsX,
|
|
39
|
+
extractSegmentsY
|
|
72
40
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/geom-trace-bitmap",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.41",
|
|
4
4
|
"description": "Bitmap image to hairline vector and point cloud conversions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"author": "Karsten Schmidt (https://thi.ng)",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"scripts": {
|
|
27
|
-
"build": "yarn
|
|
27
|
+
"build": "yarn build:esbuild && yarn build:decl",
|
|
28
|
+
"build:decl": "tsc --declaration --emitDeclarationOnly",
|
|
29
|
+
"build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
|
|
28
30
|
"clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
|
|
29
31
|
"doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
|
|
30
32
|
"doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
|
|
@@ -33,16 +35,16 @@
|
|
|
33
35
|
"test": "bun test"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"@thi.ng/api": "^8.9.
|
|
37
|
-
"@thi.ng/errors": "^2.4.
|
|
38
|
-
"@thi.ng/grid-iterators": "^4.0.
|
|
39
|
-
"@thi.ng/matrices": "^2.2.
|
|
40
|
-
"@thi.ng/pixel": "^5.0.
|
|
41
|
-
"@thi.ng/vectors": "^7.8.
|
|
38
|
+
"@thi.ng/api": "^8.9.12",
|
|
39
|
+
"@thi.ng/errors": "^2.4.6",
|
|
40
|
+
"@thi.ng/grid-iterators": "^4.0.36",
|
|
41
|
+
"@thi.ng/matrices": "^2.2.13",
|
|
42
|
+
"@thi.ng/pixel": "^5.0.4",
|
|
43
|
+
"@thi.ng/vectors": "^7.8.9"
|
|
42
44
|
},
|
|
43
45
|
"devDependencies": {
|
|
44
46
|
"@microsoft/api-extractor": "^7.38.3",
|
|
45
|
-
"
|
|
47
|
+
"esbuild": "^0.19.8",
|
|
46
48
|
"rimraf": "^5.0.5",
|
|
47
49
|
"tools": "^0.0.1",
|
|
48
50
|
"typedoc": "^0.25.4",
|
|
@@ -99,5 +101,5 @@
|
|
|
99
101
|
"status": "alpha",
|
|
100
102
|
"year": 2022
|
|
101
103
|
},
|
|
102
|
-
"gitHead": "
|
|
104
|
+
"gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
|
|
103
105
|
}
|
package/trace.js
CHANGED
|
@@ -5,139 +5,134 @@ import { rows2d } from "@thi.ng/grid-iterators/rows";
|
|
|
5
5
|
import { flipX, ident } from "@thi.ng/grid-iterators/transforms";
|
|
6
6
|
import { mulV23 } from "@thi.ng/matrices/mulv";
|
|
7
7
|
import { borderX, borderXY, borderY } from "./border.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const mat = opts.mat;
|
|
24
|
-
const { width, height } = opts.img;
|
|
25
|
-
const lines = [];
|
|
26
|
-
const points = [];
|
|
27
|
-
for (let d of opts.dir || ["h", "v", "d1", "d2", "p"]) {
|
|
28
|
-
if (typeof d !== "string") {
|
|
29
|
-
traceLines(opts, d.order, (d.border || borderXY)(width, height), d.tx || ident, lines);
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
switch (d) {
|
|
33
|
-
case "h":
|
|
34
|
-
traceLines(opts, rows2d, borderX(width, height), ident, lines);
|
|
35
|
-
break;
|
|
36
|
-
case "v":
|
|
37
|
-
traceLines(opts, columns2d, borderY(width, height), ident, lines);
|
|
38
|
-
break;
|
|
39
|
-
case "d1":
|
|
40
|
-
traceLines(opts, diagonal2d, borderXY(width, height), ident, lines);
|
|
41
|
-
break;
|
|
42
|
-
case "d2":
|
|
43
|
-
traceLines(opts, diagonal2d, borderXY(width, height), flipX, lines);
|
|
44
|
-
break;
|
|
45
|
-
case "p":
|
|
46
|
-
tracePoints(opts, points);
|
|
47
|
-
break;
|
|
48
|
-
default:
|
|
49
|
-
illegalArgs(`invalid trace direction: ${d}`);
|
|
50
|
-
}
|
|
8
|
+
const traceBitmap = (opts) => {
|
|
9
|
+
const mat = opts.mat;
|
|
10
|
+
const { width, height } = opts.img;
|
|
11
|
+
const lines = [];
|
|
12
|
+
const points = [];
|
|
13
|
+
for (let d of opts.dir || ["h", "v", "d1", "d2", "p"]) {
|
|
14
|
+
if (typeof d !== "string") {
|
|
15
|
+
traceLines(
|
|
16
|
+
opts,
|
|
17
|
+
d.order,
|
|
18
|
+
(d.border || borderXY)(width, height),
|
|
19
|
+
d.tx || ident,
|
|
20
|
+
lines
|
|
21
|
+
);
|
|
22
|
+
continue;
|
|
51
23
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
24
|
+
switch (d) {
|
|
25
|
+
case "h":
|
|
26
|
+
traceLines(opts, rows2d, borderX(width, height), ident, lines);
|
|
27
|
+
break;
|
|
28
|
+
case "v":
|
|
29
|
+
traceLines(
|
|
30
|
+
opts,
|
|
31
|
+
columns2d,
|
|
32
|
+
borderY(width, height),
|
|
33
|
+
ident,
|
|
34
|
+
lines
|
|
35
|
+
);
|
|
36
|
+
break;
|
|
37
|
+
case "d1":
|
|
38
|
+
traceLines(
|
|
39
|
+
opts,
|
|
40
|
+
diagonal2d,
|
|
41
|
+
borderXY(width, height),
|
|
42
|
+
ident,
|
|
43
|
+
lines
|
|
44
|
+
);
|
|
45
|
+
break;
|
|
46
|
+
case "d2":
|
|
47
|
+
traceLines(
|
|
48
|
+
opts,
|
|
49
|
+
diagonal2d,
|
|
50
|
+
borderXY(width, height),
|
|
51
|
+
flipX,
|
|
52
|
+
lines
|
|
53
|
+
);
|
|
54
|
+
break;
|
|
55
|
+
case "p":
|
|
56
|
+
tracePoints(opts, points);
|
|
57
|
+
break;
|
|
58
|
+
default:
|
|
59
|
+
illegalArgs(`invalid trace direction: ${d}`);
|
|
58
60
|
}
|
|
59
|
-
|
|
61
|
+
}
|
|
62
|
+
if (mat) {
|
|
63
|
+
lines.forEach((p) => {
|
|
64
|
+
mulV23(null, mat, p[0]);
|
|
65
|
+
mulV23(null, mat, p[1]);
|
|
66
|
+
});
|
|
67
|
+
points.forEach((p) => mulV23(null, mat, p));
|
|
68
|
+
}
|
|
69
|
+
return { lines, points };
|
|
60
70
|
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (isBorder || n >= max) {
|
|
93
|
-
if (n > 0) {
|
|
94
|
-
if (prevBorder) {
|
|
95
|
-
if (n > min)
|
|
96
|
-
$record();
|
|
97
|
-
curr = [p];
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
if (n >= min) {
|
|
101
|
-
curr.push(p);
|
|
102
|
-
$record();
|
|
103
|
-
}
|
|
104
|
-
curr = [];
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
curr.push(p);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
curr.push(p);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
else if (n > 0) {
|
|
116
|
-
if (n > min) {
|
|
117
|
-
if (last)
|
|
118
|
-
curr.push(p);
|
|
119
|
-
$record();
|
|
71
|
+
const traceLines = (opts, order, border, tx, acc = []) => {
|
|
72
|
+
let { img, select, clear, last, min, max } = {
|
|
73
|
+
clear: 0,
|
|
74
|
+
last: true,
|
|
75
|
+
min: 2,
|
|
76
|
+
max: Infinity,
|
|
77
|
+
...opts
|
|
78
|
+
};
|
|
79
|
+
min--;
|
|
80
|
+
let curr = [];
|
|
81
|
+
let prevBorder = false;
|
|
82
|
+
const $record = () => {
|
|
83
|
+
acc.push([curr[0], curr[curr.length - 1]]);
|
|
84
|
+
for (let q of curr)
|
|
85
|
+
img.setAtUnsafe(q[0], q[1], clear);
|
|
86
|
+
};
|
|
87
|
+
for (let p of order({ cols: img.width, rows: img.height, tx })) {
|
|
88
|
+
const c = select(img.getAtUnsafe(p[0], p[1]), p);
|
|
89
|
+
const isBorder = border(p);
|
|
90
|
+
const n = curr.length;
|
|
91
|
+
if (c) {
|
|
92
|
+
if (isBorder || n >= max) {
|
|
93
|
+
if (n > 0) {
|
|
94
|
+
if (prevBorder) {
|
|
95
|
+
if (n > min)
|
|
96
|
+
$record();
|
|
97
|
+
curr = [p];
|
|
98
|
+
} else {
|
|
99
|
+
if (n >= min) {
|
|
100
|
+
curr.push(p);
|
|
101
|
+
$record();
|
|
120
102
|
}
|
|
121
103
|
curr = [];
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
curr.push(p);
|
|
122
107
|
}
|
|
123
|
-
|
|
108
|
+
} else {
|
|
109
|
+
curr.push(p);
|
|
110
|
+
}
|
|
111
|
+
} else if (n > 0) {
|
|
112
|
+
if (n > min) {
|
|
113
|
+
if (last)
|
|
114
|
+
curr.push(p);
|
|
115
|
+
$record();
|
|
116
|
+
}
|
|
117
|
+
curr = [];
|
|
124
118
|
}
|
|
125
|
-
|
|
119
|
+
prevBorder = isBorder;
|
|
120
|
+
}
|
|
121
|
+
return acc;
|
|
126
122
|
};
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (clear === undefined)
|
|
135
|
-
clear = 0;
|
|
136
|
-
for (let i = 0, n = img.data.length, w = img.width; i < n; i++) {
|
|
137
|
-
if (select(img.data[i], [i % w, (i / w) | 0])) {
|
|
138
|
-
acc.push([i % w, (i / w) | 0]);
|
|
139
|
-
img.data[i] = clear;
|
|
140
|
-
}
|
|
123
|
+
const tracePoints = ({ img, select, clear }, acc = []) => {
|
|
124
|
+
if (clear === void 0)
|
|
125
|
+
clear = 0;
|
|
126
|
+
for (let i = 0, n = img.data.length, w = img.width; i < n; i++) {
|
|
127
|
+
if (select(img.data[i], [i % w, i / w | 0])) {
|
|
128
|
+
acc.push([i % w, i / w | 0]);
|
|
129
|
+
img.data[i] = clear;
|
|
141
130
|
}
|
|
142
|
-
|
|
131
|
+
}
|
|
132
|
+
return acc;
|
|
133
|
+
};
|
|
134
|
+
export {
|
|
135
|
+
traceBitmap,
|
|
136
|
+
traceLines,
|
|
137
|
+
tracePoints
|
|
143
138
|
};
|