@thi.ng/geom 6.1.9 → 7.0.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.
- package/CHANGELOG.md +114 -1
- package/README.md +34 -29
- package/api/arc.js +1 -1
- package/api/complex-polygon.d.ts +14 -0
- package/api/complex-polygon.js +44 -0
- package/api/path.d.ts +11 -3
- package/api/path.js +45 -29
- package/apply-transforms.js +5 -10
- package/arc-length.d.ts +1 -0
- package/arc-length.js +12 -0
- package/arc.d.ts +51 -1
- package/arc.js +12 -1
- package/area.d.ts +1 -0
- package/area.js +4 -1
- package/as-cubic.d.ts +2 -0
- package/as-cubic.js +10 -2
- package/as-path.d.ts +19 -3
- package/as-path.js +54 -1
- package/as-polygon.d.ts +7 -5
- package/as-polygon.js +14 -1
- package/as-polyline.d.ts +6 -4
- package/as-polyline.js +19 -5
- package/bounds.js +14 -7
- package/centroid.d.ts +3 -2
- package/centroid.js +10 -1
- package/classify-point.d.ts +5 -2
- package/clip-convex.d.ts +22 -3
- package/clip-convex.js +39 -12
- package/closest-point.d.ts +2 -0
- package/closest-point.js +34 -0
- package/complex-polygon-from-path.d.ts +14 -0
- package/complex-polygon-from-path.js +9 -0
- package/complex-polygon.d.ts +23 -0
- package/complex-polygon.js +6 -0
- package/convex-hull.d.ts +1 -0
- package/convex-hull.js +2 -0
- package/edges.d.ts +2 -0
- package/edges.js +7 -3
- package/fit-into-bounds.js +5 -10
- package/flip.d.ts +1 -0
- package/flip.js +5 -0
- package/index.d.ts +5 -0
- package/index.js +5 -0
- package/internal/bounds.js +3 -6
- package/internal/copy.d.ts +3 -1
- package/internal/copy.js +9 -3
- package/internal/transform.d.ts +20 -7
- package/internal/transform.js +10 -0
- package/intersects.js +3 -6
- package/package.json +48 -33
- package/path-builder.d.ts +21 -1
- package/path-builder.js +31 -14
- package/path-from-svg.d.ts +13 -1
- package/path-from-svg.js +10 -14
- package/path.d.ts +59 -2
- package/path.js +44 -19
- package/point-inside.d.ts +12 -9
- package/point-inside.js +3 -0
- package/proximity.d.ts +11 -0
- package/proximity.js +9 -0
- package/resample.d.ts +1 -0
- package/resample.js +7 -1
- package/rotate.d.ts +1 -0
- package/rotate.js +13 -10
- package/scale-with-center.d.ts +20 -0
- package/scale-with-center.js +7 -0
- package/scale.d.ts +1 -0
- package/scale.js +12 -9
- package/scatter.js +1 -2
- package/simplify.d.ts +4 -3
- package/simplify.js +39 -27
- package/split-arclength.d.ts +1 -1
- package/split-arclength.js +4 -6
- package/subdiv-curve.d.ts +5 -1
- package/subdiv-curve.js +10 -8
- package/transform-vertices.d.ts +1 -0
- package/transform-vertices.js +19 -19
- package/transform.d.ts +1 -0
- package/transform.js +17 -16
- package/translate.d.ts +1 -0
- package/translate.js +17 -12
- package/vertices.d.ts +18 -17
- package/vertices.js +22 -11
- package/with-attribs.d.ts +1 -1
- package/with-attribs.js +1 -1
package/path-from-svg.js
CHANGED
|
@@ -4,8 +4,8 @@ import { WS } from "@thi.ng/strings/groups";
|
|
|
4
4
|
import { PathBuilder } from "./path-builder.js";
|
|
5
5
|
const CMD_RE = /[achlmqstvz]/i;
|
|
6
6
|
const WSC = { ...WS, ",": true };
|
|
7
|
-
const pathFromSvg = (svg) => {
|
|
8
|
-
const b = new PathBuilder();
|
|
7
|
+
const pathFromSvg = (svg, attribs) => {
|
|
8
|
+
const b = new PathBuilder(attribs);
|
|
9
9
|
try {
|
|
10
10
|
let cmd = "";
|
|
11
11
|
for (let n = svg.length, i = 0; i < n; ) {
|
|
@@ -63,7 +63,7 @@ const pathFromSvg = (svg) => {
|
|
|
63
63
|
break;
|
|
64
64
|
}
|
|
65
65
|
case "z":
|
|
66
|
-
b.
|
|
66
|
+
b.close();
|
|
67
67
|
break;
|
|
68
68
|
default:
|
|
69
69
|
throw new Error(
|
|
@@ -71,15 +71,15 @@ const pathFromSvg = (svg) => {
|
|
|
71
71
|
);
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
|
|
74
|
+
const [main, ...subPaths] = b.paths;
|
|
75
|
+
return main.addSubPaths(...subPaths.map((p) => p.segments));
|
|
75
76
|
} catch (e) {
|
|
76
77
|
throw e instanceof Error ? e : new Error(`illegal char '${svg.charAt(e)}' @ ${e}`);
|
|
77
78
|
}
|
|
78
79
|
};
|
|
79
80
|
const skipWS = (src, i) => {
|
|
80
81
|
const n = src.length;
|
|
81
|
-
while (i < n && WSC[src.charAt(i)])
|
|
82
|
-
i++;
|
|
82
|
+
while (i < n && WSC[src.charAt(i)]) i++;
|
|
83
83
|
return i;
|
|
84
84
|
};
|
|
85
85
|
const readPoint = (src, index) => {
|
|
@@ -113,28 +113,24 @@ const readFloat = (src, index) => {
|
|
|
113
113
|
continue;
|
|
114
114
|
}
|
|
115
115
|
if (c === "-" || c === "+") {
|
|
116
|
-
if (!signOk)
|
|
117
|
-
break;
|
|
116
|
+
if (!signOk) break;
|
|
118
117
|
signOk = false;
|
|
119
118
|
continue;
|
|
120
119
|
}
|
|
121
120
|
if (c === ".") {
|
|
122
|
-
if (!dotOk)
|
|
123
|
-
break;
|
|
121
|
+
if (!dotOk) break;
|
|
124
122
|
dotOk = false;
|
|
125
123
|
continue;
|
|
126
124
|
}
|
|
127
125
|
if (c === "e") {
|
|
128
|
-
if (!expOk)
|
|
129
|
-
throw i;
|
|
126
|
+
if (!expOk) throw i;
|
|
130
127
|
expOk = false;
|
|
131
128
|
dotOk = false;
|
|
132
129
|
signOk = true;
|
|
133
130
|
continue;
|
|
134
131
|
}
|
|
135
132
|
if (c === ",") {
|
|
136
|
-
if (!commaOk)
|
|
137
|
-
throw i;
|
|
133
|
+
if (!commaOk) throw i;
|
|
138
134
|
i++;
|
|
139
135
|
}
|
|
140
136
|
break;
|
package/path.d.ts
CHANGED
|
@@ -2,8 +2,65 @@ import type { Attribs, PathSegment } from "@thi.ng/geom-api";
|
|
|
2
2
|
import type { Vec } from "@thi.ng/vectors";
|
|
3
3
|
import type { Cubic } from "./api/cubic.js";
|
|
4
4
|
import { Path } from "./api/path.js";
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Creates a new {@link Path} instance, optional with given `segments`,
|
|
7
|
+
* `subPaths` and `attribs`.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* Segments and sub-paths can also be later added via {@link Path.addSegments}
|
|
11
|
+
* or {@link Path.addSubPaths}.
|
|
12
|
+
*
|
|
13
|
+
* @param segments
|
|
14
|
+
* @param subPaths
|
|
15
|
+
* @param attribs
|
|
16
|
+
*/
|
|
17
|
+
export declare const path: (segments?: Iterable<PathSegment>, subPaths?: Iterable<PathSegment[]>, attribs?: Attribs) => Path;
|
|
18
|
+
/**
|
|
19
|
+
* Constructs a {@link Path} from given sequence of cubic curves, with optional
|
|
20
|
+
* `attribs`.
|
|
21
|
+
*
|
|
22
|
+
* @remarks
|
|
23
|
+
* If no `attribs` are given, those from the first curve will be used.
|
|
24
|
+
*
|
|
25
|
+
* For each successive curve segment, if the start point of the current curve is
|
|
26
|
+
* not the same as the last point of the previous curve, a new sub path will be
|
|
27
|
+
* started.
|
|
28
|
+
*
|
|
29
|
+
* Also see {@link normalizedPath}.
|
|
30
|
+
*
|
|
31
|
+
* @param cubics
|
|
32
|
+
* @param attribs
|
|
33
|
+
*/
|
|
6
34
|
export declare const pathFromCubics: (cubics: Cubic[], attribs?: Attribs) => Path;
|
|
35
|
+
/**
|
|
36
|
+
* Converts given path into a new one with all segments converted to
|
|
37
|
+
* {@link Cubic} bezier segments.
|
|
38
|
+
*
|
|
39
|
+
* @remarks
|
|
40
|
+
* Also see {@link pathFromCubics}.
|
|
41
|
+
*
|
|
42
|
+
* @param path
|
|
43
|
+
*/
|
|
7
44
|
export declare const normalizedPath: (path: Path) => Path;
|
|
8
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Creates a new rounded rect {@link Path}, using the given corner radius or
|
|
47
|
+
* radii.
|
|
48
|
+
*
|
|
49
|
+
* @remarks
|
|
50
|
+
* If multiple `radii` are given, the interpretation logic is the same as:
|
|
51
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/roundRect
|
|
52
|
+
*
|
|
53
|
+
* - number: all corners
|
|
54
|
+
* - `[top-left-and-bottom-right, top-right-and-bottom-left]`
|
|
55
|
+
* - `[top-left, top-right-and-bottom-left, bottom-right]`
|
|
56
|
+
* - `[top-left, top-right, bottom-right, bottom-left]`
|
|
57
|
+
*
|
|
58
|
+
* No arc segments will be generated for those corners where the radius <= 0
|
|
59
|
+
*
|
|
60
|
+
* @param pos
|
|
61
|
+
* @param size
|
|
62
|
+
* @param radii
|
|
63
|
+
* @param attribs
|
|
64
|
+
*/
|
|
65
|
+
export declare const roundedRect: (pos: Vec, [w, h]: Vec, radii: number | [number, number] | [number, number, number] | [number, number, number, number], attribs?: Attribs) => Path;
|
|
9
66
|
//# sourceMappingURL=path.d.ts.map
|
package/path.js
CHANGED
|
@@ -1,33 +1,58 @@
|
|
|
1
1
|
import { isNumber } from "@thi.ng/checks/is-number";
|
|
2
2
|
import { map } from "@thi.ng/transducers/map";
|
|
3
3
|
import { mapcat } from "@thi.ng/transducers/mapcat";
|
|
4
|
-
import {
|
|
4
|
+
import { equals2 } from "@thi.ng/vectors/equals";
|
|
5
5
|
import { Path } from "./api/path.js";
|
|
6
6
|
import { asCubic } from "./as-cubic.js";
|
|
7
7
|
import { PathBuilder } from "./path-builder.js";
|
|
8
|
-
const path = (segments, attribs) => new Path(segments, attribs);
|
|
8
|
+
const path = (segments, subPaths, attribs) => new Path(segments, subPaths, attribs);
|
|
9
9
|
const pathFromCubics = (cubics, attribs) => {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
let subPaths = [];
|
|
11
|
+
let curr;
|
|
12
|
+
let lastP;
|
|
13
|
+
const $beginPath = (c) => {
|
|
14
|
+
curr = [{ type: "m", point: c.points[0] }];
|
|
15
|
+
subPaths.push(curr);
|
|
16
|
+
};
|
|
12
17
|
for (let c of cubics) {
|
|
13
|
-
|
|
18
|
+
if (!(lastP && equals2(lastP, c.points[0]))) $beginPath(c);
|
|
19
|
+
curr.push({ type: "c", geo: c });
|
|
20
|
+
lastP = c.points[3];
|
|
14
21
|
}
|
|
22
|
+
const path2 = new Path(
|
|
23
|
+
subPaths[0],
|
|
24
|
+
subPaths.slice(1),
|
|
25
|
+
attribs || cubics[0].attribs
|
|
26
|
+
);
|
|
15
27
|
return path2;
|
|
16
28
|
};
|
|
17
|
-
const normalizedPath = (path2) =>
|
|
18
|
-
|
|
19
|
-
(
|
|
20
|
-
(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
const normalizedPath = (path2) => {
|
|
30
|
+
const $normalize = (segments) => [
|
|
31
|
+
...mapcat(
|
|
32
|
+
(s) => s.geo ? map(
|
|
33
|
+
(c) => ({ type: "c", geo: c }),
|
|
34
|
+
asCubic(s.geo)
|
|
35
|
+
) : [{ ...s }],
|
|
36
|
+
segments
|
|
37
|
+
)
|
|
38
|
+
];
|
|
39
|
+
return new Path(
|
|
40
|
+
$normalize(path2.segments),
|
|
41
|
+
path2.subPaths.map($normalize),
|
|
42
|
+
path2.attribs
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
const roundedRect = (pos, [w, h], radii, attribs) => {
|
|
46
|
+
const [tl, tr, br, bl] = isNumber(radii) ? [radii, radii, radii, radii] : radii.length === 2 ? [radii[0], radii[1], radii[0], radii[1]] : radii.length === 3 ? [radii[0], radii[1], radii[2], radii[1]] : radii;
|
|
47
|
+
const b = new PathBuilder(attribs).moveTo([pos[0] + tl, pos[1]]).hlineTo(w - tl - tr, true);
|
|
48
|
+
if (tr > 0) b.arcTo([tr, tr], [tr, tr], 0, false, true, true);
|
|
49
|
+
b.vlineTo(h - tr - br, true);
|
|
50
|
+
if (br > 0) b.arcTo([-br, br], [br, br], 0, false, true, true);
|
|
51
|
+
b.hlineTo(-(w - br - bl), true);
|
|
52
|
+
if (bl > 0) b.arcTo([-bl, -bl], [bl, bl], 0, false, true, true);
|
|
53
|
+
b.vlineTo(-(h - bl - tl), true);
|
|
54
|
+
if (tl > 0) b.arcTo([tl, -tl], [tl, tl], 0, false, true, true);
|
|
55
|
+
return b.current().close();
|
|
31
56
|
};
|
|
32
57
|
export {
|
|
33
58
|
normalizedPath,
|
package/point-inside.d.ts
CHANGED
|
@@ -7,15 +7,18 @@ import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
|
7
7
|
* @remarks
|
|
8
8
|
* Currently implemented for:
|
|
9
9
|
*
|
|
10
|
-
* - AABB
|
|
11
|
-
* - Circle
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
14
|
-
* -
|
|
15
|
-
* -
|
|
16
|
-
* -
|
|
17
|
-
* -
|
|
18
|
-
* -
|
|
10
|
+
* - {@link AABB}
|
|
11
|
+
* - {@link Circle}
|
|
12
|
+
* - {@link ComplexPolygon}
|
|
13
|
+
* - {@link Line} (if `p` is on line segment)
|
|
14
|
+
* - {@link Points} (i.e. if `p` is one of the points in the cloud)
|
|
15
|
+
* - {@link Points3} (same as w/ Points)
|
|
16
|
+
* - {@link Polygon}
|
|
17
|
+
* - {@link Polyline} (if `p` is on any of the line segments)
|
|
18
|
+
* - {@link Quad}
|
|
19
|
+
* - {@link Rect}
|
|
20
|
+
* - {@link Sphere}
|
|
21
|
+
* - {@link Triangle}
|
|
19
22
|
*
|
|
20
23
|
* @param shape
|
|
21
24
|
* @param p
|
package/point-inside.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
pointInPolygon2,
|
|
6
6
|
pointInRect,
|
|
7
7
|
pointInSegment,
|
|
8
|
+
pointInSegments,
|
|
8
9
|
pointInTriangle2
|
|
9
10
|
} from "@thi.ng/geom-isec/point";
|
|
10
11
|
import { isInArray } from "@thi.ng/vectors/eqdelta";
|
|
@@ -19,9 +20,11 @@ const pointInside = defmulti(
|
|
|
19
20
|
{
|
|
20
21
|
aabb: ($, p) => pointInAABB(p, $.pos, $.size),
|
|
21
22
|
circle: ($, p) => pointInCircle(p, $.pos, $.r),
|
|
23
|
+
complexpoly: ($, p) => pointInPolygon2(p, $.boundary.points) ? !$.children.some((child) => pointInPolygon2(p, child.points)) : false,
|
|
22
24
|
line: ($, p) => pointInSegment(p, $.points[0], $.points[1]),
|
|
23
25
|
points: ({ points }, p) => isInArray(p, points),
|
|
24
26
|
poly: ($, p) => pointInPolygon2(p, $.points) > 0,
|
|
27
|
+
polyline: ($, p) => pointInSegments(p, $.points, false),
|
|
25
28
|
rect: ($, p) => pointInRect(p, $.pos, $.size),
|
|
26
29
|
tri: (tri, p) => pointInTriangle2(p, ...tri.points)
|
|
27
30
|
}
|
package/proximity.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { IShape } from "@thi.ng/geom-api";
|
|
2
|
+
import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
3
|
+
/**
|
|
4
|
+
* Computes {@link closestPoint} on `shape` to `p`, and if successful, returns
|
|
5
|
+
* eucledian distance between that point and `p`.
|
|
6
|
+
*
|
|
7
|
+
* @param shape
|
|
8
|
+
* @param p
|
|
9
|
+
*/
|
|
10
|
+
export declare const proximity: (shape: IShape, p: ReadonlyVec) => number | undefined;
|
|
11
|
+
//# sourceMappingURL=proximity.d.ts.map
|
package/proximity.js
ADDED
package/resample.d.ts
CHANGED
package/resample.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defmulti } from "@thi.ng/defmulti/defmulti";
|
|
2
2
|
import { resample as _resample } from "@thi.ng/geom-resample/resample";
|
|
3
|
+
import { ComplexPolygon } from "./api/complex-polygon.js";
|
|
3
4
|
import { Polygon } from "./api/polygon.js";
|
|
4
5
|
import { Polyline } from "./api/polyline.js";
|
|
5
6
|
import { asPolygon } from "./as-polygon.js";
|
|
@@ -15,7 +16,12 @@ const resample = defmulti(
|
|
|
15
16
|
rect: "circle"
|
|
16
17
|
},
|
|
17
18
|
{
|
|
18
|
-
circle: ($, opts) => asPolygon($, opts),
|
|
19
|
+
circle: ($, opts) => asPolygon($, opts)[0],
|
|
20
|
+
complexpoly: ($, opts) => new ComplexPolygon(
|
|
21
|
+
resample($.boundary, opts),
|
|
22
|
+
$.children.map((child) => resample(child, opts)),
|
|
23
|
+
__attribs($)
|
|
24
|
+
),
|
|
19
25
|
poly: ($, opts) => new Polygon(_resample($.points, opts, true, true), __attribs($)),
|
|
20
26
|
polyline: ($, opts) => new Polyline(_resample($.points, opts, false, true), __attribs($))
|
|
21
27
|
}
|
package/rotate.d.ts
CHANGED
package/rotate.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defmulti } from "@thi.ng/defmulti/defmulti";
|
|
2
2
|
import { rotate as $rotate } from "@thi.ng/vectors/rotate";
|
|
3
3
|
import { Circle } from "./api/circle.js";
|
|
4
|
+
import { ComplexPolygon } from "./api/complex-polygon.js";
|
|
4
5
|
import { Cubic } from "./api/cubic.js";
|
|
5
6
|
import { Line } from "./api/line.js";
|
|
6
7
|
import { Path } from "./api/path.js";
|
|
@@ -17,6 +18,7 @@ import { asPolygon } from "./as-polygon.js";
|
|
|
17
18
|
import { __copyAttribs } from "./internal/copy.js";
|
|
18
19
|
import { __dispatch } from "./internal/dispatch.js";
|
|
19
20
|
import { __rotatedShape as tx } from "./internal/rotate.js";
|
|
21
|
+
import { __segmentTransformer } from "./internal/transform.js";
|
|
20
22
|
const rotate = defmulti(
|
|
21
23
|
__dispatch,
|
|
22
24
|
{},
|
|
@@ -27,21 +29,22 @@ const rotate = defmulti(
|
|
|
27
29
|
return a;
|
|
28
30
|
},
|
|
29
31
|
circle: ($, theta) => new Circle($rotate([], $.pos, theta), $.r, __copyAttribs($)),
|
|
32
|
+
complexpoly: ($, theta) => new ComplexPolygon(
|
|
33
|
+
rotate($.boundary, theta),
|
|
34
|
+
$.children.map((child) => rotate(child, theta))
|
|
35
|
+
),
|
|
30
36
|
cubic: tx(Cubic),
|
|
31
37
|
ellipse: ($, theta) => rotate(asPath($), theta),
|
|
32
38
|
group: ($, theta) => $.copyTransformed((x) => rotate(x, theta)),
|
|
33
39
|
line: tx(Line),
|
|
34
40
|
path: ($, theta) => {
|
|
41
|
+
const $rotateSegments = __segmentTransformer(
|
|
42
|
+
(geo) => rotate(geo, theta),
|
|
43
|
+
(p) => $rotate([], p, theta)
|
|
44
|
+
);
|
|
35
45
|
return new Path(
|
|
36
|
-
$.segments
|
|
37
|
-
|
|
38
|
-
type: s.type,
|
|
39
|
-
geo: rotate(s.geo, theta)
|
|
40
|
-
} : {
|
|
41
|
-
type: s.type,
|
|
42
|
-
point: $rotate([], s.point, theta)
|
|
43
|
-
}
|
|
44
|
-
),
|
|
46
|
+
$rotateSegments($.segments),
|
|
47
|
+
$.subPaths.map($rotateSegments),
|
|
45
48
|
__copyAttribs($)
|
|
46
49
|
);
|
|
47
50
|
},
|
|
@@ -57,7 +60,7 @@ const rotate = defmulti(
|
|
|
57
60
|
__copyAttribs($)
|
|
58
61
|
);
|
|
59
62
|
},
|
|
60
|
-
rect: ($, theta) => rotate(asPolygon($), theta),
|
|
63
|
+
rect: ($, theta) => rotate(asPolygon($)[0], theta),
|
|
61
64
|
text: ($, theta) => new Text($rotate([], $.pos, theta), $.body, __copyAttribs($)),
|
|
62
65
|
tri: tx(Triangle)
|
|
63
66
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { IShape } from "@thi.ng/geom-api";
|
|
2
|
+
import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
3
|
+
/**
|
|
4
|
+
* Applies a sequence of translate and scale operations to return an uniformly
|
|
5
|
+
* scaled version of the given shape using `center` as the point of reference.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* This op is likely slower than using {@link transform} with a equivalent
|
|
9
|
+
* transformation matrix (e.g. using [`scaleWithCenter()` from
|
|
10
|
+
* thi.ng/matrices](https://docs.thi.ng/umbrella/matrices/functions/scaleWithCenter23.html)),
|
|
11
|
+
* but will not change the shape type (as might be the case with `transform()`).
|
|
12
|
+
*
|
|
13
|
+
* Also see: {@link scale}, {@link translate}, {@link applyTransforms}.
|
|
14
|
+
*
|
|
15
|
+
* @param shape
|
|
16
|
+
* @param center
|
|
17
|
+
* @param factor
|
|
18
|
+
*/
|
|
19
|
+
export declare const scaleWithCenter: (shape: IShape, center: ReadonlyVec, factor: number) => IShape<IShape<any>>;
|
|
20
|
+
//# sourceMappingURL=scale-with-center.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { mulN } from "@thi.ng/vectors/muln";
|
|
2
|
+
import { scale } from "./scale.js";
|
|
3
|
+
import { translate } from "./translate.js";
|
|
4
|
+
const scaleWithCenter = (shape, center, factor) => translate(scale(translate(shape, mulN([], center, -1)), factor), center);
|
|
5
|
+
export {
|
|
6
|
+
scaleWithCenter
|
|
7
|
+
};
|
package/scale.d.ts
CHANGED
package/scale.js
CHANGED
|
@@ -6,6 +6,7 @@ import { mulN2, mulN3 } from "@thi.ng/vectors/muln";
|
|
|
6
6
|
import { normalize2 } from "@thi.ng/vectors/normalize";
|
|
7
7
|
import { AABB } from "./api/aabb.js";
|
|
8
8
|
import { Circle } from "./api/circle.js";
|
|
9
|
+
import { ComplexPolygon } from "./api/complex-polygon.js";
|
|
9
10
|
import { Cubic } from "./api/cubic.js";
|
|
10
11
|
import { Ellipse } from "./api/ellipse.js";
|
|
11
12
|
import { Line } from "./api/line.js";
|
|
@@ -24,6 +25,7 @@ import { __asVec } from "./internal/args.js";
|
|
|
24
25
|
import { __copyAttribs } from "./internal/copy.js";
|
|
25
26
|
import { __dispatch } from "./internal/dispatch.js";
|
|
26
27
|
import { __scaledShape as tx } from "./internal/scale.js";
|
|
28
|
+
import { __segmentTransformer } from "./internal/transform.js";
|
|
27
29
|
const scale = defmulti(
|
|
28
30
|
__dispatch,
|
|
29
31
|
{},
|
|
@@ -52,6 +54,10 @@ const scale = defmulti(
|
|
|
52
54
|
mulN2([], delta, $.r),
|
|
53
55
|
__copyAttribs($)
|
|
54
56
|
),
|
|
57
|
+
complexpoly: ($, delta) => new ComplexPolygon(
|
|
58
|
+
scale($.boundary, delta),
|
|
59
|
+
$.children.map((child) => scale(child, delta))
|
|
60
|
+
),
|
|
55
61
|
cubic: tx(Cubic),
|
|
56
62
|
ellipse: ($, delta) => {
|
|
57
63
|
delta = __asVec(delta);
|
|
@@ -65,16 +71,13 @@ const scale = defmulti(
|
|
|
65
71
|
line: tx(Line),
|
|
66
72
|
path: ($, delta) => {
|
|
67
73
|
delta = __asVec(delta);
|
|
74
|
+
const $scaleSegments = __segmentTransformer(
|
|
75
|
+
(geo) => scale(geo, delta),
|
|
76
|
+
(p) => mul2([], p, delta)
|
|
77
|
+
);
|
|
68
78
|
return new Path(
|
|
69
|
-
$.segments
|
|
70
|
-
|
|
71
|
-
type: s.type,
|
|
72
|
-
geo: scale(s.geo, delta)
|
|
73
|
-
} : {
|
|
74
|
-
type: s.type,
|
|
75
|
-
point: mul2([], s.point, delta)
|
|
76
|
-
}
|
|
77
|
-
),
|
|
79
|
+
$scaleSegments($.segments),
|
|
80
|
+
$.subPaths.map($scaleSegments),
|
|
78
81
|
__copyAttribs($)
|
|
79
82
|
);
|
|
80
83
|
},
|
package/scatter.js
CHANGED
|
@@ -4,8 +4,7 @@ import { bounds } from "./bounds.js";
|
|
|
4
4
|
import { pointInside } from "./point-inside.js";
|
|
5
5
|
const scatter = (shape, num, rnd = SYSTEM, out = []) => {
|
|
6
6
|
const b = bounds(shape);
|
|
7
|
-
if (!b)
|
|
8
|
-
return;
|
|
7
|
+
if (!b) return;
|
|
9
8
|
const mi = b.pos;
|
|
10
9
|
const mx = b.max();
|
|
11
10
|
for (; num-- > 0; ) {
|
package/simplify.d.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MultiFn1O } from "@thi.ng/defmulti";
|
|
2
2
|
import type { IShape } from "@thi.ng/geom-api";
|
|
3
3
|
/**
|
|
4
4
|
* Simplifies given 2D shape boundary using Douglas-Peucker algorithm
|
|
5
5
|
* (implemented by
|
|
6
6
|
* [`simplify()`](https://docs.thi.ng/umbrella/geom-resample/functions/simplify.html))
|
|
7
|
-
* and given `threshold` distance (default:
|
|
7
|
+
* and given `threshold` distance (default: 1e-6, which removes only co-linear
|
|
8
8
|
* vertices).
|
|
9
9
|
*
|
|
10
10
|
* @remarks
|
|
11
11
|
* Currently only implemented for:
|
|
12
12
|
*
|
|
13
|
+
* - {@link ComplexPolygon}
|
|
13
14
|
* - {@link Path}
|
|
14
15
|
* - {@link Polygon}
|
|
15
16
|
* - {@link Polyline}
|
|
@@ -20,5 +21,5 @@ import type { IShape } from "@thi.ng/geom-api";
|
|
|
20
21
|
* @param shape
|
|
21
22
|
* @param threshold
|
|
22
23
|
*/
|
|
23
|
-
export declare const simplify:
|
|
24
|
+
export declare const simplify: MultiFn1O<IShape, number, IShape>;
|
|
24
25
|
//# sourceMappingURL=simplify.d.ts.map
|
package/simplify.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { peek } from "@thi.ng/arrays/peek";
|
|
2
2
|
import { defmulti } from "@thi.ng/defmulti/defmulti";
|
|
3
3
|
import { simplify as _simplify } from "@thi.ng/geom-resample/simplify";
|
|
4
|
+
import { ComplexPolygon } from "./api/complex-polygon.js";
|
|
4
5
|
import { Path } from "./api/path.js";
|
|
5
6
|
import { Polygon } from "./api/polygon.js";
|
|
6
7
|
import { Polyline } from "./api/polyline.js";
|
|
@@ -11,39 +12,50 @@ const simplify = defmulti(
|
|
|
11
12
|
__dispatch,
|
|
12
13
|
{},
|
|
13
14
|
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
15
|
+
complexpoly: ($, eps) => new ComplexPolygon(
|
|
16
|
+
simplify($.boundary, eps),
|
|
17
|
+
$.children.map((child) => simplify(child, eps)),
|
|
18
|
+
__copyAttribs($)
|
|
19
|
+
),
|
|
20
|
+
path: ($, eps = 1e-6) => {
|
|
21
|
+
const $simplifySegments = (segments) => {
|
|
22
|
+
const res = [];
|
|
23
|
+
const n = segments.length;
|
|
24
|
+
let points;
|
|
25
|
+
let lastP;
|
|
26
|
+
for (let i = 0; i < n; i++) {
|
|
27
|
+
const s = segments[i];
|
|
28
|
+
if (s.type === "l" || s.type === "p") {
|
|
29
|
+
points = points ? points.concat(vertices(s.geo)) : vertices(s.geo);
|
|
30
|
+
lastP = peek(points);
|
|
31
|
+
} else if (points) {
|
|
32
|
+
points.push(lastP);
|
|
33
|
+
res.push({
|
|
34
|
+
geo: new Polyline(_simplify(points, eps)),
|
|
35
|
+
type: "p"
|
|
36
|
+
});
|
|
37
|
+
points = null;
|
|
38
|
+
} else {
|
|
39
|
+
res.push({ ...s });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (points) {
|
|
26
43
|
points.push(lastP);
|
|
27
44
|
res.push({
|
|
28
|
-
geo: new Polyline(
|
|
45
|
+
geo: new Polyline(points),
|
|
29
46
|
type: "p"
|
|
30
47
|
});
|
|
31
|
-
points = null;
|
|
32
|
-
} else {
|
|
33
|
-
res.push({ ...s });
|
|
34
48
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
return new Path(res, __copyAttribs($));
|
|
49
|
+
return res;
|
|
50
|
+
};
|
|
51
|
+
return new Path(
|
|
52
|
+
$simplifySegments($.segments),
|
|
53
|
+
$.subPaths.map($simplifySegments),
|
|
54
|
+
__copyAttribs($)
|
|
55
|
+
);
|
|
44
56
|
},
|
|
45
|
-
poly: ($, eps =
|
|
46
|
-
polyline: ($, eps =
|
|
57
|
+
poly: ($, eps = 1e-6) => new Polygon(_simplify($.points, eps, true), __copyAttribs($)),
|
|
58
|
+
polyline: ($, eps = 1e-6) => new Polyline(_simplify($.points, eps), __copyAttribs($))
|
|
47
59
|
}
|
|
48
60
|
);
|
|
49
61
|
export {
|
package/split-arclength.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { Group } from "./api/group.js";
|
|
|
15
15
|
* - {@link Polyline}
|
|
16
16
|
*
|
|
17
17
|
* Other shape types will be attempted to be auto-converted via
|
|
18
|
-
* {@link asPolyline} first.
|
|
18
|
+
* {@link asPolyline} first. Only shapes with a single boundary are supported.
|
|
19
19
|
*
|
|
20
20
|
* Nested groups are NOT supported. Groups are processing their child shapes and
|
|
21
21
|
* forming new child groups of given max. arc lengths and potentially splitting
|
package/split-arclength.js
CHANGED
|
@@ -10,7 +10,7 @@ const splitArcLength = defmulti(
|
|
|
10
10
|
__dispatch,
|
|
11
11
|
{},
|
|
12
12
|
{
|
|
13
|
-
[DEFAULT]: ($, d) => splitArcLength(asPolyline($), d),
|
|
13
|
+
[DEFAULT]: ($, d) => splitArcLength(asPolyline($)[0], d),
|
|
14
14
|
group: ($, d) => {
|
|
15
15
|
const groups = [];
|
|
16
16
|
let curr = [];
|
|
@@ -18,7 +18,7 @@ const splitArcLength = defmulti(
|
|
|
18
18
|
const queue = $.children.slice().reverse();
|
|
19
19
|
while (queue.length) {
|
|
20
20
|
const child = queue.pop();
|
|
21
|
-
const polyline = asPolyline(child);
|
|
21
|
+
const polyline = asPolyline(child)[0];
|
|
22
22
|
const sampler = new Sampler(polyline.points);
|
|
23
23
|
const len = sampler.totalLength();
|
|
24
24
|
if (currLen + len <= d) {
|
|
@@ -34,8 +34,7 @@ const splitArcLength = defmulti(
|
|
|
34
34
|
queue.push(new Polyline(next, __attribs(child)));
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
if (curr.length)
|
|
38
|
-
groups.push(new Group({}, curr));
|
|
37
|
+
if (curr.length) groups.push(new Group({}, curr));
|
|
39
38
|
return new Group(__attribs($), groups);
|
|
40
39
|
},
|
|
41
40
|
polyline: ($, d) => {
|
|
@@ -46,8 +45,7 @@ const splitArcLength = defmulti(
|
|
|
46
45
|
const total = sampler.totalLength();
|
|
47
46
|
if (total > d) {
|
|
48
47
|
const parts = sampler.splitAt(d / total);
|
|
49
|
-
if (!parts)
|
|
50
|
-
break;
|
|
48
|
+
if (!parts) break;
|
|
51
49
|
chunks.push(parts[0]);
|
|
52
50
|
pts = parts[1];
|
|
53
51
|
} else {
|