@flighthq/path 0.1.0 → 0.2.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/dist/cleanPath.d.ts +3 -0
- package/dist/cleanPath.d.ts.map +1 -0
- package/dist/cleanPath.js +111 -0
- package/dist/cleanPath.js.map +1 -0
- package/dist/{simplifyPath.d.ts → decimatePath.d.ts} +2 -2
- package/dist/decimatePath.d.ts.map +1 -0
- package/dist/{simplifyPath.js → decimatePath.js} +7 -5
- package/dist/decimatePath.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
- package/src/cleanPath.test.ts +138 -0
- package/src/{simplifyPath.test.ts → decimatePath.test.ts} +8 -8
- package/dist/offsetPath.d.ts +0 -3
- package/dist/offsetPath.d.ts.map +0 -1
- package/dist/offsetPath.js +0 -146
- package/dist/offsetPath.js.map +0 -1
- package/dist/simplifyPath.d.ts.map +0 -1
- package/dist/simplifyPath.js.map +0 -1
- package/src/offsetPath.test.ts +0 -78
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cleanPath.d.ts","sourceRoot":"","sources":["../src/cleanPath.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAa5C,wBAAgB,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,gBAAgB,SAAO,GAAG,IAAI,CAiC7G"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { PathCommand } from '@flighthq/types';
|
|
2
|
+
import { flattenPath } from './flattenPath';
|
|
3
|
+
// Cleans `source` by flattening curves and removing redundant vertices from each contour: consecutive
|
|
4
|
+
// coincident points within `tolerance`, near-collinear vertices whose perpendicular distance to the line
|
|
5
|
+
// through their neighbors is within `tolerance`, and zero-area out-and-back spikes. Writes the result into
|
|
6
|
+
// `out`. This is a linear vertex pass (Clipper `CleanPolygons`), not a topological one — it never resolves
|
|
7
|
+
// self-intersections into a clean region (that is `simplifyPath` in `@flighthq/path-boolean`, a
|
|
8
|
+
// kernel-based operation) and it keeps every vertex that carries shape within `tolerance`, unlike the
|
|
9
|
+
// error-bounded Douglas-Peucker reduction of `decimatePath`. Alias-safe: `out` may be the same object as
|
|
10
|
+
// `source`. Curves are first flattened using `flattenTolerance`; closed contours stay closed.
|
|
11
|
+
export function cleanPath(source, tolerance, out, flattenTolerance = 0.25) {
|
|
12
|
+
const contours = flattenPath(source, flattenTolerance);
|
|
13
|
+
const winding = source.winding;
|
|
14
|
+
const toleranceSq = tolerance * tolerance;
|
|
15
|
+
out.commands.length = 0;
|
|
16
|
+
out.data.length = 0;
|
|
17
|
+
out.winding = winding;
|
|
18
|
+
for (const contour of contours) {
|
|
19
|
+
const n = contour.length >> 1;
|
|
20
|
+
if (n < 2)
|
|
21
|
+
continue;
|
|
22
|
+
const closed = n >= 3 && withinTolerance(contour[0], contour[1], contour[n * 2 - 2], contour[n * 2 - 1], toleranceSq);
|
|
23
|
+
// A closed contour from the flattener repeats its start as an explicit closing vertex; drop it so the
|
|
24
|
+
// seam is cleaned like any other corner rather than left as a duplicate.
|
|
25
|
+
const count = closed ? n - 1 : n;
|
|
26
|
+
const kept = [];
|
|
27
|
+
for (let i = 0; i < count; i++)
|
|
28
|
+
pushCleanVertex(kept, contour[i * 2], contour[i * 2 + 1], toleranceSq);
|
|
29
|
+
if (closed)
|
|
30
|
+
collapseClosedSeam(kept, toleranceSq);
|
|
31
|
+
const keptCount = kept.length >> 1;
|
|
32
|
+
if (closed ? keptCount < 3 : keptCount < 2)
|
|
33
|
+
continue;
|
|
34
|
+
out.commands.push(PathCommand.MOVE_TO);
|
|
35
|
+
out.data.push(kept[0], kept[1]);
|
|
36
|
+
for (let i = 1; i < keptCount; i++) {
|
|
37
|
+
out.commands.push(PathCommand.LINE_TO);
|
|
38
|
+
out.data.push(kept[i * 2], kept[i * 2 + 1]);
|
|
39
|
+
}
|
|
40
|
+
if (closed)
|
|
41
|
+
out.commands.push(PathCommand.CLOSE);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Removes vertices from the closed seam that a single forward pass cannot see: the first and last kept
|
|
45
|
+
// vertices become interior once the ring wraps, so re-test each against its cyclic neighbors and drop it
|
|
46
|
+
// when the neighbors reveal it as coincident, collinear, or a spike. Loops until neither end is removable.
|
|
47
|
+
function collapseClosedSeam(kept, toleranceSq) {
|
|
48
|
+
let changed = true;
|
|
49
|
+
while (changed && kept.length >> 1 > 3) {
|
|
50
|
+
changed = false;
|
|
51
|
+
const last = kept.length >> 1;
|
|
52
|
+
// Last vertex, with predecessor (last - 2) and wrap-successor (first).
|
|
53
|
+
if (isMiddleRemovable(kept, last - 2, last - 1, 0, toleranceSq)) {
|
|
54
|
+
kept.length -= 2;
|
|
55
|
+
changed = true;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
// First vertex, with wrap-predecessor (last - 1) and successor (index 1).
|
|
59
|
+
if (isMiddleRemovable(kept, last - 1, 0, 1, toleranceSq)) {
|
|
60
|
+
kept.copyWithin(0, 2);
|
|
61
|
+
kept.length -= 2;
|
|
62
|
+
changed = true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Whether the middle vertex at `mid` is redundant between the outer vertices `prev` and `next` (all three
|
|
67
|
+
// given as vertex indices into the flat `kept` list): true when `prev` and `next` coincide (so `mid` is a
|
|
68
|
+
// spike tip) or when `mid`'s perpendicular distance to the `prev`-`next` line is within tolerance.
|
|
69
|
+
function isMiddleRemovable(kept, prev, mid, next, toleranceSq) {
|
|
70
|
+
const px = kept[prev * 2];
|
|
71
|
+
const py = kept[prev * 2 + 1];
|
|
72
|
+
const mx = kept[mid * 2];
|
|
73
|
+
const my = kept[mid * 2 + 1];
|
|
74
|
+
const sx = kept[next * 2];
|
|
75
|
+
const sy = kept[next * 2 + 1];
|
|
76
|
+
return isRedundantMiddle(px, py, mx, my, sx, sy, toleranceSq);
|
|
77
|
+
}
|
|
78
|
+
// Whether the middle point (mx, my) is redundant between outer points (px, py) and (sx, sy). Coincident
|
|
79
|
+
// outer points make the middle a spike tip; otherwise it is redundant when its squared perpendicular
|
|
80
|
+
// distance to the outer segment is within tolerance (collapsing both collinear runs and colinear spikes).
|
|
81
|
+
function isRedundantMiddle(px, py, mx, my, sx, sy, toleranceSq) {
|
|
82
|
+
const dx = sx - px;
|
|
83
|
+
const dy = sy - py;
|
|
84
|
+
const lengthSq = dx * dx + dy * dy;
|
|
85
|
+
if (lengthSq <= toleranceSq)
|
|
86
|
+
return true;
|
|
87
|
+
const cross = dx * (py - my) - dy * (px - mx);
|
|
88
|
+
return (cross * cross) / lengthSq <= toleranceSq;
|
|
89
|
+
}
|
|
90
|
+
// Appends (x, y) to the forward-built vertex list, first skipping it as a duplicate of the current last
|
|
91
|
+
// vertex, then popping any trailing vertices the new point reveals as redundant (collinear runs and
|
|
92
|
+
// spikes collapse here as each following vertex arrives).
|
|
93
|
+
function pushCleanVertex(kept, x, y, toleranceSq) {
|
|
94
|
+
const k = kept.length;
|
|
95
|
+
if (k >= 2 && withinTolerance(kept[k - 2], kept[k - 1], x, y, toleranceSq))
|
|
96
|
+
return;
|
|
97
|
+
while (kept.length >= 4) {
|
|
98
|
+
const m = kept.length;
|
|
99
|
+
if (!isRedundantMiddle(kept[m - 4], kept[m - 3], kept[m - 2], kept[m - 1], x, y, toleranceSq))
|
|
100
|
+
break;
|
|
101
|
+
kept.length -= 2;
|
|
102
|
+
}
|
|
103
|
+
kept.push(x, y);
|
|
104
|
+
}
|
|
105
|
+
// Whether two points are within `tolerance` (squared) of each other.
|
|
106
|
+
function withinTolerance(ax, ay, bx, by, toleranceSq) {
|
|
107
|
+
const dx = ax - bx;
|
|
108
|
+
const dy = ay - by;
|
|
109
|
+
return dx * dx + dy * dy <= toleranceSq;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=cleanPath.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cleanPath.js","sourceRoot":"","sources":["../src/cleanPath.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { Path } from '@flighthq/types';
|
|
2
|
-
export declare function
|
|
3
|
-
//# sourceMappingURL=
|
|
2
|
+
export declare function decimatePath(source: Readonly<Path>, tolerance: number, out: Path, flattenTolerance?: number): void;
|
|
3
|
+
//# sourceMappingURL=decimatePath.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decimatePath.d.ts","sourceRoot":"","sources":["../src/decimatePath.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAa5C,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,gBAAgB,SAAO,GAAG,IAAI,CAgChH"}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { PathCommand } from '@flighthq/types';
|
|
2
2
|
import { flattenPath } from './flattenPath';
|
|
3
|
-
//
|
|
4
|
-
// contour, removing points that deviate less than `tolerance` from the
|
|
5
|
-
// the result into `out`. Alias-safe: `out` may be the same object as `source`.
|
|
3
|
+
// Decimates `source` by flattening curves and applying Douglas-Peucker point reduction to each
|
|
4
|
+
// contour, removing points that deviate less than `tolerance` from the decimated line. Writes
|
|
5
|
+
// the result into `out`. Alias-safe: `out` may be the same object as `source`. Topological
|
|
6
|
+
// simplification (resolving self-intersections into a clean region) is `simplifyPath` in
|
|
7
|
+
// `@flighthq/path-boolean` — a distinct, kernel-based operation.
|
|
6
8
|
//
|
|
7
9
|
// Curves are first flattened (using `flattenTolerance`), then the resulting polylines are
|
|
8
10
|
// decimated. Closed contours that end at their start point remain closed in the output.
|
|
9
|
-
export function
|
|
11
|
+
export function decimatePath(source, tolerance, out, flattenTolerance = 0.25) {
|
|
10
12
|
const contours = flattenPath(source, flattenTolerance);
|
|
11
13
|
out.commands.length = 0;
|
|
12
14
|
out.data.length = 0;
|
|
@@ -75,4 +77,4 @@ function douglasPeucker(pts, first, last, toleranceSq, keep) {
|
|
|
75
77
|
douglasPeucker(pts, maxIdx, last, toleranceSq, keep);
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
|
-
//# sourceMappingURL=
|
|
80
|
+
//# sourceMappingURL=decimatePath.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decimatePath.js","sourceRoot":"","sources":["../src/decimatePath.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from './cleanPath';
|
|
1
2
|
export * from './containsPathPoint';
|
|
2
3
|
export * from './copyPath';
|
|
3
4
|
export * from './dashPath';
|
|
@@ -11,11 +12,10 @@ export * from './getPathNearestPoint';
|
|
|
11
12
|
export * from './getPathPointAtDistance';
|
|
12
13
|
export * from './getPathSegmentAtParameter';
|
|
13
14
|
export * from './getPathSignedArea';
|
|
14
|
-
export * from './offsetPath';
|
|
15
15
|
export * from './path';
|
|
16
16
|
export * from './pathMeshPool';
|
|
17
17
|
export * from './reversePath';
|
|
18
|
-
export * from './
|
|
18
|
+
export * from './decimatePath';
|
|
19
19
|
export * from './strokePath';
|
|
20
20
|
export * from './tessellatePath';
|
|
21
21
|
export * from './tessellatePathTyped';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,QAAQ,CAAC;AACvB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from './cleanPath';
|
|
1
2
|
export * from './containsPathPoint';
|
|
2
3
|
export * from './copyPath';
|
|
3
4
|
export * from './dashPath';
|
|
@@ -11,11 +12,10 @@ export * from './getPathNearestPoint';
|
|
|
11
12
|
export * from './getPathPointAtDistance';
|
|
12
13
|
export * from './getPathSegmentAtParameter';
|
|
13
14
|
export * from './getPathSignedArea';
|
|
14
|
-
export * from './offsetPath';
|
|
15
15
|
export * from './path';
|
|
16
16
|
export * from './pathMeshPool';
|
|
17
17
|
export * from './reversePath';
|
|
18
|
-
export * from './
|
|
18
|
+
export * from './decimatePath';
|
|
19
19
|
export * from './strokePath';
|
|
20
20
|
export * from './tessellatePath';
|
|
21
21
|
export * from './tessellatePathTyped';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,QAAQ,CAAC;AACvB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/path",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "https://github.com/flighthq/flight.git",
|
|
7
|
+
"directory": "packages/path"
|
|
8
|
+
},
|
|
4
9
|
"type": "module",
|
|
5
10
|
"main": "dist/index.js",
|
|
6
11
|
"types": "dist/index.d.ts",
|
|
@@ -27,7 +32,7 @@
|
|
|
27
32
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
28
33
|
},
|
|
29
34
|
"dependencies": {
|
|
30
|
-
"@flighthq/types": "0.
|
|
35
|
+
"@flighthq/types": "0.2.0"
|
|
31
36
|
},
|
|
32
37
|
"devDependencies": {
|
|
33
38
|
"typescript": "^5.3.0"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { PathCommand } from '@flighthq/types';
|
|
2
|
+
|
|
3
|
+
import { cleanPath } from './cleanPath';
|
|
4
|
+
import { appendPathClose, appendPathLineTo, appendPathMoveTo, createPath } from './path';
|
|
5
|
+
|
|
6
|
+
describe('cleanPath', () => {
|
|
7
|
+
it('removes consecutive duplicate points', () => {
|
|
8
|
+
const source = createPath();
|
|
9
|
+
appendPathMoveTo(source, 0, 0);
|
|
10
|
+
appendPathLineTo(source, 10, 0);
|
|
11
|
+
appendPathLineTo(source, 10, 0);
|
|
12
|
+
appendPathLineTo(source, 10, 10);
|
|
13
|
+
const out = createPath();
|
|
14
|
+
cleanPath(source, 0.001, out);
|
|
15
|
+
expect(out.commands).toStrictEqual([PathCommand.MOVE_TO, PathCommand.LINE_TO, PathCommand.LINE_TO]);
|
|
16
|
+
expect(out.data).toStrictEqual([0, 0, 10, 0, 10, 10]);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('collapses a near-collinear run to its endpoints', () => {
|
|
20
|
+
const source = createPath();
|
|
21
|
+
appendPathMoveTo(source, 0, 0);
|
|
22
|
+
appendPathLineTo(source, 25, 0.1);
|
|
23
|
+
appendPathLineTo(source, 50, -0.1);
|
|
24
|
+
appendPathLineTo(source, 75, 0.05);
|
|
25
|
+
appendPathLineTo(source, 100, 0);
|
|
26
|
+
const out = createPath();
|
|
27
|
+
cleanPath(source, 1, out);
|
|
28
|
+
expect(out.commands).toStrictEqual([PathCommand.MOVE_TO, PathCommand.LINE_TO]);
|
|
29
|
+
expect(out.data).toStrictEqual([0, 0, 100, 0]);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('removes a zero-area out-and-back spike', () => {
|
|
33
|
+
const source = createPath();
|
|
34
|
+
appendPathMoveTo(source, 0, 0);
|
|
35
|
+
appendPathLineTo(source, 10, 0);
|
|
36
|
+
appendPathLineTo(source, 10, 10);
|
|
37
|
+
appendPathLineTo(source, 5, 10);
|
|
38
|
+
appendPathLineTo(source, 5, 15);
|
|
39
|
+
appendPathLineTo(source, 5, 10);
|
|
40
|
+
appendPathLineTo(source, 0, 10);
|
|
41
|
+
appendPathClose(source);
|
|
42
|
+
const out = createPath();
|
|
43
|
+
cleanPath(source, 0.001, out);
|
|
44
|
+
// The spike (5,15) and the now-collinear (5,10) points on the top edge collapse away, leaving the
|
|
45
|
+
// clean unit square outline.
|
|
46
|
+
expect(out.commands).toStrictEqual([
|
|
47
|
+
PathCommand.MOVE_TO,
|
|
48
|
+
PathCommand.LINE_TO,
|
|
49
|
+
PathCommand.LINE_TO,
|
|
50
|
+
PathCommand.LINE_TO,
|
|
51
|
+
PathCommand.CLOSE,
|
|
52
|
+
]);
|
|
53
|
+
expect(out.data).toStrictEqual([0, 0, 10, 0, 10, 10, 0, 10]);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('keeps a non-collinear spike as a genuine feature', () => {
|
|
57
|
+
// A triangular spike whose tip (5, 20) sits 10 units off the y=10 top edge, between distinct,
|
|
58
|
+
// non-coincident neighbors (6, 10) and (4, 10). This is a real protrusion, not vertex noise, so
|
|
59
|
+
// cleanPath (a within-tolerance dedup, not an angle-based needle remover) preserves every vertex.
|
|
60
|
+
// Removing acute needles is a separate simplification concern, deliberately out of cleanPath's scope.
|
|
61
|
+
const source = createPath();
|
|
62
|
+
appendPathMoveTo(source, 0, 0);
|
|
63
|
+
appendPathLineTo(source, 10, 0);
|
|
64
|
+
appendPathLineTo(source, 10, 10);
|
|
65
|
+
appendPathLineTo(source, 6, 10);
|
|
66
|
+
appendPathLineTo(source, 5, 20);
|
|
67
|
+
appendPathLineTo(source, 4, 10);
|
|
68
|
+
appendPathLineTo(source, 0, 10);
|
|
69
|
+
appendPathClose(source);
|
|
70
|
+
const out = createPath();
|
|
71
|
+
cleanPath(source, 0.001, out);
|
|
72
|
+
expect(out.data).toStrictEqual([0, 0, 10, 0, 10, 10, 6, 10, 5, 20, 4, 10, 0, 10]);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('keeps a vertex whose deviation just exceeds tolerance', () => {
|
|
76
|
+
const source = createPath();
|
|
77
|
+
appendPathMoveTo(source, 0, 0);
|
|
78
|
+
appendPathLineTo(source, 10, 1.5);
|
|
79
|
+
appendPathLineTo(source, 20, 0);
|
|
80
|
+
const out = createPath();
|
|
81
|
+
cleanPath(source, 1, out);
|
|
82
|
+
// Perpendicular deviation of (10, 1.5) is 1.5 > tolerance 1, so it is retained.
|
|
83
|
+
expect(out.commands).toStrictEqual([PathCommand.MOVE_TO, PathCommand.LINE_TO, PathCommand.LINE_TO]);
|
|
84
|
+
expect(out.data).toStrictEqual([0, 0, 10, 1.5, 20, 0]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('removes a vertex whose deviation is within tolerance', () => {
|
|
88
|
+
const source = createPath();
|
|
89
|
+
appendPathMoveTo(source, 0, 0);
|
|
90
|
+
appendPathLineTo(source, 10, 0.5);
|
|
91
|
+
appendPathLineTo(source, 20, 0);
|
|
92
|
+
const out = createPath();
|
|
93
|
+
cleanPath(source, 1, out);
|
|
94
|
+
// Perpendicular deviation of (10, 0.5) is 0.5 < tolerance 1, so it is dropped.
|
|
95
|
+
expect(out.commands).toStrictEqual([PathCommand.MOVE_TO, PathCommand.LINE_TO]);
|
|
96
|
+
expect(out.data).toStrictEqual([0, 0, 20, 0]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('keeps a closed contour closed', () => {
|
|
100
|
+
const source = createPath();
|
|
101
|
+
appendPathMoveTo(source, 0, 0);
|
|
102
|
+
appendPathLineTo(source, 10, 0);
|
|
103
|
+
appendPathLineTo(source, 10, 10);
|
|
104
|
+
appendPathLineTo(source, 0, 10);
|
|
105
|
+
appendPathClose(source);
|
|
106
|
+
const out = createPath();
|
|
107
|
+
cleanPath(source, 0.001, out);
|
|
108
|
+
expect(out.commands).toStrictEqual([
|
|
109
|
+
PathCommand.MOVE_TO,
|
|
110
|
+
PathCommand.LINE_TO,
|
|
111
|
+
PathCommand.LINE_TO,
|
|
112
|
+
PathCommand.LINE_TO,
|
|
113
|
+
PathCommand.CLOSE,
|
|
114
|
+
]);
|
|
115
|
+
expect(out.data).toStrictEqual([0, 0, 10, 0, 10, 10, 0, 10]);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('cleans in place when out is the same object as source', () => {
|
|
119
|
+
const path = createPath();
|
|
120
|
+
appendPathMoveTo(path, 0, 0);
|
|
121
|
+
appendPathLineTo(path, 5, 0);
|
|
122
|
+
appendPathLineTo(path, 5, 0);
|
|
123
|
+
appendPathLineTo(path, 10, 0);
|
|
124
|
+
appendPathLineTo(path, 10, 10);
|
|
125
|
+
appendPathLineTo(path, 0, 10);
|
|
126
|
+
appendPathClose(path);
|
|
127
|
+
cleanPath(path, 0.001, path);
|
|
128
|
+
// The duplicate and collinear points on the bottom edge collapse; the clean square remains.
|
|
129
|
+
expect(path.commands).toStrictEqual([
|
|
130
|
+
PathCommand.MOVE_TO,
|
|
131
|
+
PathCommand.LINE_TO,
|
|
132
|
+
PathCommand.LINE_TO,
|
|
133
|
+
PathCommand.LINE_TO,
|
|
134
|
+
PathCommand.CLOSE,
|
|
135
|
+
]);
|
|
136
|
+
expect(path.data).toStrictEqual([0, 0, 10, 0, 10, 10, 0, 10]);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { PathCommand } from '@flighthq/types';
|
|
2
2
|
|
|
3
|
+
import { decimatePath } from './decimatePath';
|
|
3
4
|
import { appendPathClose, appendPathLineTo, appendPathMoveTo, createPath } from './path';
|
|
4
|
-
import { simplifyPath } from './simplifyPath';
|
|
5
5
|
|
|
6
|
-
describe('
|
|
6
|
+
describe('decimatePath', () => {
|
|
7
7
|
it('keeps a straight line unchanged', () => {
|
|
8
8
|
const source = createPath();
|
|
9
9
|
appendPathMoveTo(source, 0, 0);
|
|
10
10
|
appendPathLineTo(source, 50, 0);
|
|
11
11
|
appendPathLineTo(source, 100, 0);
|
|
12
12
|
const out = createPath();
|
|
13
|
-
|
|
13
|
+
decimatePath(source, 1, out);
|
|
14
14
|
expect(out.commands).toStrictEqual([PathCommand.MOVE_TO, PathCommand.LINE_TO]);
|
|
15
15
|
expect(out.data).toStrictEqual([0, 0, 100, 0]);
|
|
16
16
|
});
|
|
@@ -23,7 +23,7 @@ describe('simplifyPath', () => {
|
|
|
23
23
|
appendPathLineTo(source, 75, 0.05);
|
|
24
24
|
appendPathLineTo(source, 100, 0);
|
|
25
25
|
const out = createPath();
|
|
26
|
-
|
|
26
|
+
decimatePath(source, 1, out);
|
|
27
27
|
expect(out.commands).toStrictEqual([PathCommand.MOVE_TO, PathCommand.LINE_TO]);
|
|
28
28
|
expect(out.data[0]).toBe(0);
|
|
29
29
|
expect(out.data[2]).toBe(100);
|
|
@@ -35,7 +35,7 @@ describe('simplifyPath', () => {
|
|
|
35
35
|
appendPathLineTo(source, 50, 10);
|
|
36
36
|
appendPathLineTo(source, 100, 0);
|
|
37
37
|
const out = createPath();
|
|
38
|
-
|
|
38
|
+
decimatePath(source, 0, out);
|
|
39
39
|
expect(out.commands).toHaveLength(3);
|
|
40
40
|
expect(out.data).toStrictEqual([0, 0, 50, 10, 100, 0]);
|
|
41
41
|
});
|
|
@@ -48,7 +48,7 @@ describe('simplifyPath', () => {
|
|
|
48
48
|
appendPathLineTo(source, 0, 100);
|
|
49
49
|
appendPathClose(source);
|
|
50
50
|
const out = createPath();
|
|
51
|
-
|
|
51
|
+
decimatePath(source, 1, out);
|
|
52
52
|
expect(out.commands[out.commands.length - 1]).toBe(PathCommand.CLOSE);
|
|
53
53
|
});
|
|
54
54
|
|
|
@@ -57,13 +57,13 @@ describe('simplifyPath', () => {
|
|
|
57
57
|
appendPathMoveTo(source, 0, 0);
|
|
58
58
|
appendPathLineTo(source, 100, 0);
|
|
59
59
|
const out = createPath();
|
|
60
|
-
|
|
60
|
+
decimatePath(source, 1, out);
|
|
61
61
|
expect(out.winding).toBe('evenOdd');
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
it('handles an empty path', () => {
|
|
65
65
|
const out = createPath();
|
|
66
|
-
|
|
66
|
+
decimatePath(createPath(), 1, out);
|
|
67
67
|
expect(out.commands).toStrictEqual([]);
|
|
68
68
|
});
|
|
69
69
|
});
|
package/dist/offsetPath.d.ts
DELETED
package/dist/offsetPath.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"offsetPath.d.ts","sourceRoot":"","sources":["../src/offsetPath.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAS5C,wBAAgB,UAAU,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,SAAO,GAAG,IAAI,CActG"}
|
package/dist/offsetPath.js
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { PathCommand } from '@flighthq/types';
|
|
2
|
-
import { flattenPath } from './flattenPath';
|
|
3
|
-
// Offsets each contour of `source` by `distance` path units (positive = outset, negative =
|
|
4
|
-
// inset). The result is a polyline approximation of the parallel curve, written into `out`.
|
|
5
|
-
// Curves are first flattened to `tolerance`, then each segment is shifted along its outward
|
|
6
|
-
// normal by `distance`. Closed contours detect winding to ensure positive = outward. Alias-safe.
|
|
7
|
-
export function offsetPath(source, distance, out, tolerance = 0.25) {
|
|
8
|
-
const contours = flattenPath(source, tolerance);
|
|
9
|
-
out.commands.length = 0;
|
|
10
|
-
out.data.length = 0;
|
|
11
|
-
out.winding = source.winding;
|
|
12
|
-
if (distance === 0) {
|
|
13
|
-
copyFlatContours(contours, out);
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
for (const contour of contours) {
|
|
17
|
-
offsetContour(contour, distance, out);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function offsetContour(pts, distance, out) {
|
|
21
|
-
const n = pts.length >> 1;
|
|
22
|
-
if (n < 2)
|
|
23
|
-
return;
|
|
24
|
-
const closed = n >= 3 && pts[0] === pts[(n - 1) * 2] && pts[1] === pts[(n - 1) * 2 + 1];
|
|
25
|
-
const segCount = closed ? n - 1 : n - 1;
|
|
26
|
-
// For closed contours, determine winding so positive distance always outsets.
|
|
27
|
-
// Shoelace area > 0 → CCW (y-up) → left normals point outward.
|
|
28
|
-
// Shoelace area < 0 → CW (y-up, i.e. standard screen CW) → left normals point inward → negate.
|
|
29
|
-
let d = distance;
|
|
30
|
-
if (closed) {
|
|
31
|
-
const area = shoelace(pts, closed ? n - 1 : n);
|
|
32
|
-
if (area > 0)
|
|
33
|
-
d = -d;
|
|
34
|
-
}
|
|
35
|
-
const normals = new Float64Array(segCount * 2);
|
|
36
|
-
for (let i = 0; i < segCount; i++) {
|
|
37
|
-
const dx = pts[(i + 1) * 2] - pts[i * 2];
|
|
38
|
-
const dy = pts[(i + 1) * 2 + 1] - pts[i * 2 + 1];
|
|
39
|
-
const len = Math.sqrt(dx * dx + dy * dy);
|
|
40
|
-
if (len > 0) {
|
|
41
|
-
normals[i * 2] = -dy / len;
|
|
42
|
-
normals[i * 2 + 1] = dx / len;
|
|
43
|
-
}
|
|
44
|
-
else if (i > 0) {
|
|
45
|
-
normals[i * 2] = normals[(i - 1) * 2];
|
|
46
|
-
normals[i * 2 + 1] = normals[(i - 1) * 2 + 1];
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
normals[i * 2] = 0;
|
|
50
|
-
normals[i * 2 + 1] = 1;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
const result = [];
|
|
54
|
-
if (closed) {
|
|
55
|
-
addOffsetJoin(pts, 0, normals, segCount - 1, 0, d, result);
|
|
56
|
-
for (let i = 1; i < segCount; i++) {
|
|
57
|
-
addOffsetJoin(pts, i, normals, i - 1, i, d, result);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
result.push(pts[0] + normals[0] * d, pts[1] + normals[1] * d);
|
|
62
|
-
for (let i = 1; i < n - 1; i++) {
|
|
63
|
-
addOffsetJoin(pts, i, normals, i - 1, i, d, result);
|
|
64
|
-
}
|
|
65
|
-
result.push(pts[(n - 1) * 2] + normals[(segCount - 1) * 2] * d, pts[(n - 1) * 2 + 1] + normals[(segCount - 1) * 2 + 1] * d);
|
|
66
|
-
}
|
|
67
|
-
if (result.length < 4)
|
|
68
|
-
return;
|
|
69
|
-
out.commands.push(PathCommand.MOVE_TO);
|
|
70
|
-
out.data.push(result[0], result[1]);
|
|
71
|
-
for (let i = 2; i < result.length; i += 2) {
|
|
72
|
-
out.commands.push(PathCommand.LINE_TO);
|
|
73
|
-
out.data.push(result[i], result[i + 1]);
|
|
74
|
-
}
|
|
75
|
-
if (closed)
|
|
76
|
-
out.commands.push(PathCommand.CLOSE);
|
|
77
|
-
}
|
|
78
|
-
function addOffsetJoin(pts, ptIdx, normals, seg0, seg1, distance, result) {
|
|
79
|
-
const px = pts[ptIdx * 2];
|
|
80
|
-
const py = pts[ptIdx * 2 + 1];
|
|
81
|
-
const n0x = normals[seg0 * 2];
|
|
82
|
-
const n0y = normals[seg0 * 2 + 1];
|
|
83
|
-
const n1x = normals[seg1 * 2];
|
|
84
|
-
const n1y = normals[seg1 * 2 + 1];
|
|
85
|
-
const cross = n0x * n1y - n0y * n1x;
|
|
86
|
-
if (Math.abs(cross) < 1e-8) {
|
|
87
|
-
result.push(px + n0x * distance, py + n0y * distance);
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
// Segment directions: perpendicular to normal, (ny, -nx).
|
|
91
|
-
const d0x = n0y;
|
|
92
|
-
const d0y = -n0x;
|
|
93
|
-
const ox0 = px + n0x * distance;
|
|
94
|
-
const oy0 = py + n0y * distance;
|
|
95
|
-
const ox1 = px + n1x * distance;
|
|
96
|
-
const oy1 = py + n1y * distance;
|
|
97
|
-
// Intersect: ox0 + s*d0 = ox1 + t*d1
|
|
98
|
-
const dpx = ox1 - ox0;
|
|
99
|
-
const dpy = oy1 - oy0;
|
|
100
|
-
const d1x = n1y;
|
|
101
|
-
const d1y = -n1x;
|
|
102
|
-
const det = d0x * d1y - d0y * d1x;
|
|
103
|
-
if (Math.abs(det) < 1e-12) {
|
|
104
|
-
result.push(px + n0x * distance, py + n0y * distance);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
const s = (dpx * d1y - dpy * d1x) / det;
|
|
108
|
-
const mx = ox0 + s * d0x;
|
|
109
|
-
const my = oy0 + s * d0y;
|
|
110
|
-
const miterDistSq = (mx - px) * (mx - px) + (my - py) * (my - py);
|
|
111
|
-
const absDist = Math.abs(distance);
|
|
112
|
-
const limitSq = absDist * 4 * (absDist * 4);
|
|
113
|
-
if (miterDistSq <= limitSq) {
|
|
114
|
-
result.push(mx, my);
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
result.push(ox0, oy0, ox1, oy1);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
function copyFlatContours(contours, out) {
|
|
121
|
-
for (const contour of contours) {
|
|
122
|
-
const n = contour.length >> 1;
|
|
123
|
-
if (n < 2)
|
|
124
|
-
continue;
|
|
125
|
-
const closed = n >= 3 && contour[0] === contour[(n - 1) * 2] && contour[1] === contour[(n - 1) * 2 + 1];
|
|
126
|
-
out.commands.push(PathCommand.MOVE_TO);
|
|
127
|
-
out.data.push(contour[0], contour[1]);
|
|
128
|
-
const last = closed ? n - 1 : n;
|
|
129
|
-
for (let i = 1; i < last; i++) {
|
|
130
|
-
out.commands.push(PathCommand.LINE_TO);
|
|
131
|
-
out.data.push(contour[i * 2], contour[i * 2 + 1]);
|
|
132
|
-
}
|
|
133
|
-
if (closed)
|
|
134
|
-
out.commands.push(PathCommand.CLOSE);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
function shoelace(pts, n) {
|
|
138
|
-
let area = 0;
|
|
139
|
-
for (let i = 0; i < n; i++) {
|
|
140
|
-
const j = (i + 1) % n;
|
|
141
|
-
area += pts[i * 2] * pts[j * 2 + 1];
|
|
142
|
-
area -= pts[j * 2] * pts[i * 2 + 1];
|
|
143
|
-
}
|
|
144
|
-
return area / 2;
|
|
145
|
-
}
|
|
146
|
-
//# sourceMappingURL=offsetPath.js.map
|
package/dist/offsetPath.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"offsetPath.js","sourceRoot":"","sources":["../src/offsetPath.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,2FAA2F;AAC3F,4FAA4F;AAC5F,4FAA4F;AAC5F,iGAAiG;AACjG,MAAM,UAAU,UAAU,CAAC,MAAsB,EAAE,QAAgB,EAAE,GAAS,EAAE,SAAS,GAAG,IAAI;IAC9F,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAChD,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IACxB,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IACpB,GAAG,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAE7B,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;QACnB,gBAAgB,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QAChC,OAAO;IACT,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,aAAa,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;IACxC,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,GAAuB,EAAE,QAAgB,EAAE,GAAS;IACzE,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC;IAC1B,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO;IAElB,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACxF,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAExC,8EAA8E;IAC9E,+DAA+D;IAC/D,+FAA+F;IAC/F,IAAI,CAAC,GAAG,QAAQ,CAAC;IACjB,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/C,IAAI,IAAI,GAAG,CAAC;YAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;IAC/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACzC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACjD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;QACzC,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;YACZ,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC;YAC3B,OAAO,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC;QAChC,CAAC;aAAM,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACjB,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACtC,OAAO,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACnB,OAAO,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,IAAI,MAAM,EAAE,CAAC;QACX,aAAa,CAAC,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,QAAQ,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QAC3D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;YAClC,aAAa,CAAC,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,aAAa,CAAC,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;QACtD,CAAC;QACD,MAAM,CAAC,IAAI,CACT,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAClD,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAC3D,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO;IAC9B,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACvC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QACvC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,MAAM;QAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,aAAa,CACpB,GAAuB,EACvB,KAAa,EACb,OAA+B,EAC/B,IAAY,EACZ,IAAY,EACZ,QAAgB,EAChB,MAAgB;IAEhB,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAC1B,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAClC,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAElC,MAAM,KAAK,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IACpC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,GAAG,QAAQ,EAAE,EAAE,GAAG,GAAG,GAAG,QAAQ,CAAC,CAAC;QACtD,OAAO;IACT,CAAC;IAED,0DAA0D;IAC1D,MAAM,GAAG,GAAG,GAAG,CAAC;IAChB,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC;IAEjB,MAAM,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,QAAQ,CAAC;IAChC,MAAM,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,QAAQ,CAAC;IAChC,MAAM,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,QAAQ,CAAC;IAChC,MAAM,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,QAAQ,CAAC;IAEhC,qCAAqC;IACrC,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IACtB,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IACtB,MAAM,GAAG,GAAG,GAAG,CAAC;IAChB,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC;IACjB,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAClC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,GAAG,QAAQ,EAAE,EAAE,GAAG,GAAG,GAAG,QAAQ,CAAC,CAAC;QACtD,OAAO;IACT,CAAC;IACD,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;IACxC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;IACzB,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;IAEzB,MAAM,WAAW,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;IAClE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;IAC5C,IAAI,WAAW,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IACtB,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAClC,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,QAA8B,EAAE,GAAS;IACjE,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,GAAG,CAAC;YAAE,SAAS;QACpB,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACxG,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QACvC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9B,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;YACvC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,MAAM;YAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACnD,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,GAAuB,EAAE,CAAS;IAClD,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QACtB,IAAI,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACpC,IAAI,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,GAAG,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"simplifyPath.d.ts","sourceRoot":"","sources":["../src/simplifyPath.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAW5C,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,gBAAgB,SAAO,GAAG,IAAI,CAgChH"}
|
package/dist/simplifyPath.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"simplifyPath.js","sourceRoot":"","sources":["../src/simplifyPath.ts"],"names":[],"mappings":"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"}
|
package/src/offsetPath.test.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import type { RectangleLike } from '@flighthq/types';
|
|
2
|
-
|
|
3
|
-
import { getPathBounds } from './getPathBounds';
|
|
4
|
-
import { offsetPath } from './offsetPath';
|
|
5
|
-
import { appendPathCircle, appendPathRectangle, createPath } from './path';
|
|
6
|
-
|
|
7
|
-
describe('offsetPath', () => {
|
|
8
|
-
it('outsets a rectangle so bounds grow', () => {
|
|
9
|
-
const source = createPath();
|
|
10
|
-
appendPathRectangle(source, 10, 10, 80, 60);
|
|
11
|
-
const out = createPath();
|
|
12
|
-
offsetPath(source, 5, out);
|
|
13
|
-
|
|
14
|
-
const srcBounds: RectangleLike = { x: 0, y: 0, width: 0, height: 0 };
|
|
15
|
-
const outBounds: RectangleLike = { x: 0, y: 0, width: 0, height: 0 };
|
|
16
|
-
getPathBounds(source, srcBounds);
|
|
17
|
-
getPathBounds(out, outBounds);
|
|
18
|
-
|
|
19
|
-
expect(outBounds.width).toBeGreaterThan(srcBounds.width);
|
|
20
|
-
expect(outBounds.height).toBeGreaterThan(srcBounds.height);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('offsets a circle outward', () => {
|
|
24
|
-
const source = createPath();
|
|
25
|
-
appendPathCircle(source, 0, 0, 50);
|
|
26
|
-
const out = createPath();
|
|
27
|
-
offsetPath(source, 10, out);
|
|
28
|
-
|
|
29
|
-
const outBounds: RectangleLike = { x: 0, y: 0, width: 0, height: 0 };
|
|
30
|
-
getPathBounds(out, outBounds);
|
|
31
|
-
expect(outBounds.width).toBeCloseTo(120, -1);
|
|
32
|
-
expect(outBounds.height).toBeCloseTo(120, -1);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('zero offset produces the same geometry', () => {
|
|
36
|
-
const source = createPath();
|
|
37
|
-
appendPathRectangle(source, 0, 0, 100, 50);
|
|
38
|
-
const out = createPath();
|
|
39
|
-
offsetPath(source, 0, out);
|
|
40
|
-
|
|
41
|
-
const srcBounds: RectangleLike = { x: 0, y: 0, width: 0, height: 0 };
|
|
42
|
-
const outBounds: RectangleLike = { x: 0, y: 0, width: 0, height: 0 };
|
|
43
|
-
getPathBounds(source, srcBounds);
|
|
44
|
-
getPathBounds(out, outBounds);
|
|
45
|
-
|
|
46
|
-
expect(outBounds.x).toBeCloseTo(srcBounds.x);
|
|
47
|
-
expect(outBounds.y).toBeCloseTo(srcBounds.y);
|
|
48
|
-
expect(outBounds.width).toBeCloseTo(srcBounds.width);
|
|
49
|
-
expect(outBounds.height).toBeCloseTo(srcBounds.height);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('negative offset insets (shrinks) the path', () => {
|
|
53
|
-
const source = createPath();
|
|
54
|
-
appendPathRectangle(source, 0, 0, 100, 100);
|
|
55
|
-
const out = createPath();
|
|
56
|
-
offsetPath(source, -10, out);
|
|
57
|
-
|
|
58
|
-
const outBounds: RectangleLike = { x: 0, y: 0, width: 0, height: 0 };
|
|
59
|
-
getPathBounds(out, outBounds);
|
|
60
|
-
|
|
61
|
-
expect(outBounds.width).toBeLessThan(100);
|
|
62
|
-
expect(outBounds.height).toBeLessThan(100);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('preserves winding rule', () => {
|
|
66
|
-
const source = createPath('evenOdd');
|
|
67
|
-
appendPathRectangle(source, 0, 0, 50, 50);
|
|
68
|
-
const out = createPath();
|
|
69
|
-
offsetPath(source, 5, out);
|
|
70
|
-
expect(out.winding).toBe('evenOdd');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('handles an empty path', () => {
|
|
74
|
-
const out = createPath();
|
|
75
|
-
offsetPath(createPath(), 10, out);
|
|
76
|
-
expect(out.commands).toStrictEqual([]);
|
|
77
|
-
});
|
|
78
|
-
});
|