@matboks/utilities 0.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/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/geometry/aabb.d.ts +40 -0
- package/dist/geometry/aabb.js +143 -0
- package/dist/geometry/cut-polygon.d.ts +7 -0
- package/dist/geometry/cut-polygon.js +116 -0
- package/dist/geometry/hollowgon.d.ts +24 -0
- package/dist/geometry/hollowgon.js +156 -0
- package/dist/geometry/index.d.ts +13 -0
- package/dist/geometry/index.js +14 -0
- package/dist/geometry/line.d.ts +10 -0
- package/dist/geometry/line.js +26 -0
- package/dist/geometry/polygon-operations.d.ts +31 -0
- package/dist/geometry/polygon-operations.js +159 -0
- package/dist/geometry/polygon.d.ts +55 -0
- package/dist/geometry/polygon.js +353 -0
- package/dist/geometry/polyline.d.ts +23 -0
- package/dist/geometry/polyline.js +100 -0
- package/dist/geometry/ray.d.ts +6 -0
- package/dist/geometry/ray.js +6 -0
- package/dist/geometry/ray3.d.ts +7 -0
- package/dist/geometry/ray3.js +10 -0
- package/dist/geometry/segment.d.ts +16 -0
- package/dist/geometry/segment.js +50 -0
- package/dist/geometry/shared.d.ts +5 -0
- package/dist/geometry/shared.js +60 -0
- package/dist/geometry/transformations.d.ts +7 -0
- package/dist/geometry/transformations.js +28 -0
- package/dist/geometry/vec2.d.ts +71 -0
- package/dist/geometry/vec2.js +225 -0
- package/dist/geometry/vec3.d.ts +102 -0
- package/dist/geometry/vec3.js +256 -0
- package/dist/geometry/vec4.d.ts +71 -0
- package/dist/geometry/vec4.js +238 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -0
- package/dist/math/clamp.d.ts +1 -0
- package/dist/math/clamp.js +5 -0
- package/dist/math/fract.d.ts +1 -0
- package/dist/math/fract.js +3 -0
- package/dist/math/index.d.ts +8 -0
- package/dist/math/index.js +8 -0
- package/dist/math/mix.d.ts +1 -0
- package/dist/math/mix.js +3 -0
- package/dist/math/mod.d.ts +1 -0
- package/dist/math/mod.js +5 -0
- package/dist/math/signed.d.ts +1 -0
- package/dist/math/signed.js +3 -0
- package/dist/math/smoothstep.d.ts +3 -0
- package/dist/math/smoothstep.js +24 -0
- package/dist/math/split-into-int-and-fract.d.ts +1 -0
- package/dist/math/split-into-int-and-fract.js +5 -0
- package/dist/math/units.d.ts +2 -0
- package/dist/math/units.js +6 -0
- package/dist/noise/idw.d.ts +11 -0
- package/dist/noise/idw.js +10 -0
- package/dist/noise/index.d.ts +6 -0
- package/dist/noise/index.js +6 -0
- package/dist/noise/perlin.d.ts +5 -0
- package/dist/noise/perlin.js +29 -0
- package/dist/noise/random.d.ts +13 -0
- package/dist/noise/random.js +39 -0
- package/dist/noise/value.d.ts +4 -0
- package/dist/noise/value.js +20 -0
- package/dist/noise/voronoise.d.ts +10 -0
- package/dist/noise/voronoise.js +26 -0
- package/dist/noise/worley.d.ts +4 -0
- package/dist/noise/worley.js +39 -0
- package/dist/shader-modules/index.d.ts +2 -0
- package/dist/shader-modules/index.js +2 -0
- package/dist/shader-modules/library.d.ts +2 -0
- package/dist/shader-modules/library.js +36 -0
- package/dist/shader-modules/modules/camera.d.ts +1 -0
- package/dist/shader-modules/modules/camera.js +43 -0
- package/dist/shader-modules/modules/color/blend.d.ts +1 -0
- package/dist/shader-modules/modules/color/blend.js +108 -0
- package/dist/shader-modules/modules/color/index.d.ts +1 -0
- package/dist/shader-modules/modules/color/index.js +135 -0
- package/dist/shader-modules/modules/constants.d.ts +1 -0
- package/dist/shader-modules/modules/constants.js +14 -0
- package/dist/shader-modules/modules/geometry.d.ts +1 -0
- package/dist/shader-modules/modules/geometry.js +110 -0
- package/dist/shader-modules/modules/math.d.ts +1 -0
- package/dist/shader-modules/modules/math.js +19 -0
- package/dist/shader-modules/modules/noise.d.ts +1 -0
- package/dist/shader-modules/modules/noise.js +410 -0
- package/dist/shader-modules/modules/random.d.ts +1 -0
- package/dist/shader-modules/modules/random.js +147 -0
- package/dist/shader-modules/modules/ray-marching.d.ts +1 -0
- package/dist/shader-modules/modules/ray-marching.js +54 -0
- package/dist/shader-modules/modules/sdf/index.d.ts +1 -0
- package/dist/shader-modules/modules/sdf/index.js +183 -0
- package/dist/shader-modules/modules/sdf/operations.d.ts +1 -0
- package/dist/shader-modules/modules/sdf/operations.js +77 -0
- package/dist/shader-modules/modules/utils.d.ts +1 -0
- package/dist/shader-modules/modules/utils.js +115 -0
- package/dist/shader-modules/registry.d.ts +2 -0
- package/dist/shader-modules/registry.js +7 -0
- package/dist/shader-modules/shaders.d.ts +1 -0
- package/dist/shader-modules/shaders.js +109 -0
- package/dist/shader-renderer/helpers.d.ts +10 -0
- package/dist/shader-renderer/helpers.js +19 -0
- package/dist/shader-renderer/index.d.ts +2 -0
- package/dist/shader-renderer/index.js +2 -0
- package/dist/shader-renderer/pixel-shader.d.ts +32 -0
- package/dist/shader-renderer/pixel-shader.js +172 -0
- package/dist/shader-renderer/pixel-vertex-shader.d.ts +1 -0
- package/dist/shader-renderer/pixel-vertex-shader.js +14 -0
- package/dist/shader-renderer/texture.d.ts +28 -0
- package/dist/shader-renderer/texture.js +97 -0
- package/dist/shader-renderer/types.d.ts +17 -0
- package/dist/shader-renderer/types.js +4 -0
- package/dist/shader-renderer/uniform.d.ts +16 -0
- package/dist/shader-renderer/uniform.js +134 -0
- package/dist/tilings/delaunay.d.ts +11 -0
- package/dist/tilings/delaunay.js +28 -0
- package/dist/tilings/grid.d.ts +5 -0
- package/dist/tilings/grid.js +29 -0
- package/dist/tilings/hexagononal.d.ts +4 -0
- package/dist/tilings/hexagononal.js +40 -0
- package/dist/tilings/index.d.ts +12 -0
- package/dist/tilings/index.js +12 -0
- package/dist/tilings/line.d.ts +5 -0
- package/dist/tilings/line.js +19 -0
- package/dist/tilings/mediterranean.d.ts +3 -0
- package/dist/tilings/mediterranean.js +49 -0
- package/dist/tilings/pythagorean.d.ts +3 -0
- package/dist/tilings/pythagorean.js +40 -0
- package/dist/tilings/random-cuts.d.ts +6 -0
- package/dist/tilings/random-cuts.js +16 -0
- package/dist/tilings/recursive-cuts.d.ts +5 -0
- package/dist/tilings/recursive-cuts.js +11 -0
- package/dist/tilings/rhombille.d.ts +3 -0
- package/dist/tilings/rhombille.js +16 -0
- package/dist/tilings/rightangled-triangle.d.ts +3 -0
- package/dist/tilings/rightangled-triangle.js +29 -0
- package/dist/tilings/triangle.d.ts +3 -0
- package/dist/tilings/triangle.js +17 -0
- package/dist/tilings/voronoi.d.ts +11 -0
- package/dist/tilings/voronoi.js +29 -0
- package/dist/tilings/weaving.d.ts +1 -0
- package/dist/tilings/weaving.js +8 -0
- package/dist/transforms/camera/camera.d.ts +32 -0
- package/dist/transforms/camera/camera.js +60 -0
- package/dist/transforms/camera/index.d.ts +3 -0
- package/dist/transforms/camera/index.js +3 -0
- package/dist/transforms/camera/orthographic.d.ts +15 -0
- package/dist/transforms/camera/orthographic.js +31 -0
- package/dist/transforms/camera/perspective.d.ts +16 -0
- package/dist/transforms/camera/perspective.js +40 -0
- package/dist/transforms/index.d.ts +2 -0
- package/dist/transforms/index.js +2 -0
- package/dist/transforms/normalize-transform.d.ts +12 -0
- package/dist/transforms/normalize-transform.js +20 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.js +1 -0
- package/dist/utilities/create-frame.d.ts +2 -0
- package/dist/utilities/create-frame.js +6 -0
- package/dist/utilities/ensure-array.d.ts +1 -0
- package/dist/utilities/ensure-array.js +3 -0
- package/dist/utilities/idw-interpolator.d.ts +9 -0
- package/dist/utilities/idw-interpolator.js +18 -0
- package/dist/utilities/index.d.ts +8 -0
- package/dist/utilities/index.js +8 -0
- package/dist/utilities/marching-squares.d.ts +51 -0
- package/dist/utilities/marching-squares.js +177 -0
- package/dist/utilities/point-sampler.d.ts +12 -0
- package/dist/utilities/point-sampler.js +24 -0
- package/dist/utilities/poisson/grid.d.ts +21 -0
- package/dist/utilities/poisson/grid.js +51 -0
- package/dist/utilities/poisson/poissonnier.d.ts +50 -0
- package/dist/utilities/poisson/poissonnier.js +118 -0
- package/dist/utilities/resolution.d.ts +10 -0
- package/dist/utilities/resolution.js +14 -0
- package/dist/utilities/rng.d.ts +13 -0
- package/dist/utilities/rng.js +80 -0
- package/package.json +28 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import * as polyclip from "polyclip-ts";
|
|
2
|
+
import { Hollowgon } from "./hollowgon.js";
|
|
3
|
+
import { Line } from "./line.js";
|
|
4
|
+
import { Polygon } from "./polygon.js";
|
|
5
|
+
import { Polyline } from "./polyline.js";
|
|
6
|
+
import { Ray } from "./ray.js";
|
|
7
|
+
import { Segment } from "./segment.js";
|
|
8
|
+
import { Vec2, vec2 } from "./vec2.js";
|
|
9
|
+
import { ensureArray } from "../utilities/ensure-array.js";
|
|
10
|
+
polyclip.setPrecision(1e-12);
|
|
11
|
+
export function subdivide(shape, subdivisionObject) {
|
|
12
|
+
const shapes = ensureArray(shape);
|
|
13
|
+
let subdividedShapes = [];
|
|
14
|
+
for (const shape of shapes) {
|
|
15
|
+
const subdivideBy = typeof subdivisionObject === "function" ? subdivisionObject(shape) : subdivisionObject;
|
|
16
|
+
const subdivisionPolygon = createSubdivisionPolygon(shape, subdivideBy);
|
|
17
|
+
if (!subdivisionPolygon) {
|
|
18
|
+
subdividedShapes.push(shape);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const results = polyclip.difference(shapeToArray(shape), [subdivisionPolygon.toArray()]);
|
|
22
|
+
for (const result of results) {
|
|
23
|
+
subdividedShapes.push(processPolyclipShape(result));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return subdividedShapes;
|
|
27
|
+
}
|
|
28
|
+
export function intersect(shapes) {
|
|
29
|
+
const [first, ...rest] = shapes.map(shapeToArray);
|
|
30
|
+
const result = polyclip.intersection(first, rest);
|
|
31
|
+
return result.map(processPolyclipShape);
|
|
32
|
+
}
|
|
33
|
+
export function union(shapes) {
|
|
34
|
+
const [first, ...rest] = shapes.map(shapeToArray);
|
|
35
|
+
const result = polyclip.union(first, rest);
|
|
36
|
+
return result.map(processPolyclipShape);
|
|
37
|
+
}
|
|
38
|
+
export function difference(shapes) {
|
|
39
|
+
const [first, ...rest] = shapes.map(shapeToArray);
|
|
40
|
+
const result = polyclip.difference(first, rest);
|
|
41
|
+
return result.map(processPolyclipShape);
|
|
42
|
+
}
|
|
43
|
+
export function xor(shapes) {
|
|
44
|
+
const [first, ...rest] = shapes.map(shapeToArray);
|
|
45
|
+
const result = polyclip.xor(first, rest);
|
|
46
|
+
return result.map(processPolyclipShape);
|
|
47
|
+
}
|
|
48
|
+
export function clip(shapes, clipShapes) {
|
|
49
|
+
shapes = ensureArray(shapes);
|
|
50
|
+
clipShapes = ensureArray(clipShapes);
|
|
51
|
+
const clipUnion = clipShapes.length > 1 ? union(clipShapes) : clipShapes;
|
|
52
|
+
let clippedPolygons = [];
|
|
53
|
+
for (const shape of shapes) {
|
|
54
|
+
clippedPolygons = clippedPolygons.concat(intersect([shape, ...clipUnion]));
|
|
55
|
+
}
|
|
56
|
+
return clippedPolygons;
|
|
57
|
+
}
|
|
58
|
+
export const polygonOperations = {
|
|
59
|
+
subdivide,
|
|
60
|
+
intersect,
|
|
61
|
+
union,
|
|
62
|
+
difference,
|
|
63
|
+
xor,
|
|
64
|
+
clip
|
|
65
|
+
};
|
|
66
|
+
function shapeToArray(shape) {
|
|
67
|
+
return shape instanceof Polygon ? [shape.toArray()] : shape.toArray();
|
|
68
|
+
}
|
|
69
|
+
const subdivisionPolygonPadding = 1e-5;
|
|
70
|
+
export function createSubdivisionPolygon(shape, subdivisionObject) {
|
|
71
|
+
if (subdivisionObject instanceof Polyline) {
|
|
72
|
+
const { vertices } = subdivisionObject;
|
|
73
|
+
const n = vertices.length;
|
|
74
|
+
if (n < 2) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
else if (n === 2) {
|
|
78
|
+
subdivisionObject = new Segment(vertices[0], vertices[1]);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
return createPolylinePolygon(shape, subdivisionObject);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const intersections = shape.computeIntersections(subdivisionObject);
|
|
85
|
+
const n = intersections.length;
|
|
86
|
+
if (n < 2)
|
|
87
|
+
return null;
|
|
88
|
+
let { position: start } = intersections[0];
|
|
89
|
+
let { position: end } = intersections[n - 1];
|
|
90
|
+
if (subdivisionObject instanceof Segment) {
|
|
91
|
+
start = (shape.isPointInside(subdivisionObject.start) ? intersections[1] : intersections[0]).position;
|
|
92
|
+
end = (shape.isPointInside(subdivisionObject.end) ? intersections[n - 2] : intersections[n - 1]).position;
|
|
93
|
+
}
|
|
94
|
+
else if (subdivisionObject instanceof Ray) {
|
|
95
|
+
start = (shape.isPointInside(subdivisionObject.point) ? intersections[1] : intersections[0]).position;
|
|
96
|
+
}
|
|
97
|
+
const center = Vec2.midpoint(start, end);
|
|
98
|
+
const length = Vec2.distance(start, end);
|
|
99
|
+
const angle = Vec2.subtract(end, start).angle();
|
|
100
|
+
const subdivisionPolygon = new Polygon([
|
|
101
|
+
vec2(-length / 2 - subdivisionPolygonPadding, -subdivisionPolygonPadding),
|
|
102
|
+
vec2(length / 2 + subdivisionPolygonPadding, -subdivisionPolygonPadding),
|
|
103
|
+
vec2(length / 2 + subdivisionPolygonPadding, subdivisionPolygonPadding),
|
|
104
|
+
vec2(-length / 2 - subdivisionPolygonPadding, subdivisionPolygonPadding),
|
|
105
|
+
]).rotate(angle).translate(center);
|
|
106
|
+
return subdivisionPolygon;
|
|
107
|
+
}
|
|
108
|
+
// TODO finish. Incorrect when polyline starts and/or ends inside shape
|
|
109
|
+
// A very crude offsetting implementation
|
|
110
|
+
function createPolylinePolygon(_shape, polyline) {
|
|
111
|
+
const { vertices } = polyline;
|
|
112
|
+
// const intersections = shape.computeIntersections(polyline);
|
|
113
|
+
const n = polyline.numVertices();
|
|
114
|
+
const edges = polyline.edges();
|
|
115
|
+
const startVector = edges[0].vector.clone().normalize();
|
|
116
|
+
const startNormal = vec2(-startVector.y, startVector.x);
|
|
117
|
+
const firstOffsetA = Vec2.add(vertices[0], Vec2.multiply(startVector, -subdivisionPolygonPadding), Vec2.multiply(startNormal, subdivisionPolygonPadding));
|
|
118
|
+
const firstOffsetB = Vec2.add(vertices[0], Vec2.multiply(startVector, -subdivisionPolygonPadding), Vec2.multiply(startNormal, -subdivisionPolygonPadding));
|
|
119
|
+
let verticesA = [firstOffsetA];
|
|
120
|
+
let verticesB = [firstOffsetB];
|
|
121
|
+
let prevLineA = new Line(firstOffsetA, startVector);
|
|
122
|
+
let prevLineB = new Line(firstOffsetB, startVector);
|
|
123
|
+
for (let i = 1; i < n - 1; i++) {
|
|
124
|
+
const e0 = edges[i];
|
|
125
|
+
const { vector } = e0;
|
|
126
|
+
const normal = vec2(-vector.y, vector.x).normalize();
|
|
127
|
+
const lineA = new Line(Vec2.add(e0.start, Vec2.multiply(normal, subdivisionPolygonPadding)), vector);
|
|
128
|
+
const lineB = new Line(Vec2.add(e0.start, Vec2.multiply(normal, -subdivisionPolygonPadding)), vector);
|
|
129
|
+
verticesA.push(lineA.computeIntersection(prevLineA));
|
|
130
|
+
verticesB.push(lineB.computeIntersection(prevLineB));
|
|
131
|
+
prevLineA = lineA;
|
|
132
|
+
prevLineB = lineB;
|
|
133
|
+
}
|
|
134
|
+
const endVector = edges[n - 2].vector.clone().normalize();
|
|
135
|
+
const endNormal = vec2(-endVector.y, endVector.x);
|
|
136
|
+
verticesA.push(Vec2.add(vertices[n - 1], Vec2.multiply(endVector, subdivisionPolygonPadding), Vec2.multiply(endNormal, subdivisionPolygonPadding)));
|
|
137
|
+
verticesB.push(Vec2.add(vertices[n - 1], Vec2.multiply(endVector, subdivisionPolygonPadding), Vec2.multiply(endNormal, -subdivisionPolygonPadding)));
|
|
138
|
+
const newVertices = verticesA.concat(verticesB.reverse());
|
|
139
|
+
return new Polygon(newVertices);
|
|
140
|
+
}
|
|
141
|
+
function processPolyclipShape(rings) {
|
|
142
|
+
const boundary = parseRingToPolygon(rings[0]);
|
|
143
|
+
const numRings = rings.length;
|
|
144
|
+
if (numRings === 1)
|
|
145
|
+
return boundary;
|
|
146
|
+
const holes = [];
|
|
147
|
+
for (let i = 1; i < numRings; i++) {
|
|
148
|
+
holes.push(parseRingToPolygon(rings[i]));
|
|
149
|
+
}
|
|
150
|
+
return new Hollowgon(boundary, holes);
|
|
151
|
+
}
|
|
152
|
+
function parseRingToPolygon(ring) {
|
|
153
|
+
let vertices = [];
|
|
154
|
+
for (let i = 0, n = ring.length; i < n - 1; i++) {
|
|
155
|
+
const [x, y] = ring[i];
|
|
156
|
+
vertices.push(vec2(x, y));
|
|
157
|
+
}
|
|
158
|
+
return new Polygon(vertices);
|
|
159
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { AABB } from "./aabb.js";
|
|
2
|
+
import { Line } from "./line.js";
|
|
3
|
+
import { Polyline } from "./polyline.js";
|
|
4
|
+
import { Ray } from "./ray.js";
|
|
5
|
+
import { Segment } from "./segment.js";
|
|
6
|
+
import { Vec2 } from "./vec2.js";
|
|
7
|
+
type IndexedTessellation = {
|
|
8
|
+
indices: number[];
|
|
9
|
+
vertices: Vec2[];
|
|
10
|
+
};
|
|
11
|
+
export declare function polygon(vertices?: Vec2[]): Polygon;
|
|
12
|
+
export declare class Polygon {
|
|
13
|
+
#private;
|
|
14
|
+
vertices: Vec2[];
|
|
15
|
+
constructor(vertices?: Vec2[]);
|
|
16
|
+
centroid(): Vec2;
|
|
17
|
+
signedArea(): number;
|
|
18
|
+
aabb(): AABB;
|
|
19
|
+
edges(): Segment[];
|
|
20
|
+
isConvex(): boolean;
|
|
21
|
+
area(): number;
|
|
22
|
+
numVertices(): number;
|
|
23
|
+
clone(): Polygon;
|
|
24
|
+
copy(polygon: Polygon): this;
|
|
25
|
+
getVertex(index: number): Vec2;
|
|
26
|
+
getEdge(index: number): Segment;
|
|
27
|
+
isCounterClockwise(): boolean;
|
|
28
|
+
switchOrientation(): this;
|
|
29
|
+
makeCounterClockwise(): this;
|
|
30
|
+
makeClockwise(): this;
|
|
31
|
+
addHole(hole: Polygon): this;
|
|
32
|
+
translate(translation: Vec2): this;
|
|
33
|
+
rotate(rotation: number, center?: Vec2): this;
|
|
34
|
+
scale(scale: number | Vec2, center?: Vec2): this;
|
|
35
|
+
isPointInside(position: Vec2): boolean;
|
|
36
|
+
distanceToBoundary(position: Vec2): number;
|
|
37
|
+
signedDistanceToBoundary(position: Vec2): number;
|
|
38
|
+
getNearestBoundaryPoint(point: Vec2): Vec2;
|
|
39
|
+
getNearestNormal(point: Vec2): Vec2;
|
|
40
|
+
indexedTessellation(): IndexedTessellation;
|
|
41
|
+
tessellation(): Polygon[];
|
|
42
|
+
computeIntersections(intersectionObject: IntersectionObject): PolygonIntersection[];
|
|
43
|
+
resetMemoized(): void;
|
|
44
|
+
render(ctx: CanvasRenderingContext2D, beginPath?: boolean): void;
|
|
45
|
+
toArray(): [number, number][];
|
|
46
|
+
toGeogebra(digits?: number): string;
|
|
47
|
+
}
|
|
48
|
+
export type PolygonIntersection = {
|
|
49
|
+
index: number;
|
|
50
|
+
position: Vec2;
|
|
51
|
+
edgeTime: number;
|
|
52
|
+
lineTime: number;
|
|
53
|
+
};
|
|
54
|
+
export type IntersectionObject = Line | Segment | Ray | Polyline;
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
+
};
|
|
6
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
10
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
+
};
|
|
12
|
+
var _Polygon_instances, _a, _Polygon_centroid, _Polygon_computeCentroid, _Polygon_signedArea, _Polygon_computeSignedArea, _Polygon_aabb, _Polygon_edges, _Polygon_computeEdges, _Polygon_isConvex, _Polygon_computeIsConvex, _Polygon_indexedTessellation, _Polygon_computeIndexedTessellation, _Polygon_tessellation, _Polygon_computeTessellation, _Polygon_modifyVertices, _Polygon_distanceToBoundary;
|
|
13
|
+
import { EPS } from "../constants.js";
|
|
14
|
+
import { mod } from "../math/mod.js";
|
|
15
|
+
import { AABB } from "./aabb.js";
|
|
16
|
+
import { Line } from "./line.js";
|
|
17
|
+
import { Polyline } from "./polyline.js";
|
|
18
|
+
import { Ray } from "./ray.js";
|
|
19
|
+
import { Segment } from "./segment.js";
|
|
20
|
+
import { computeLineIntersection } from "./shared.js";
|
|
21
|
+
import { ensureVec2, vec2, Vec2 } from "./vec2.js";
|
|
22
|
+
import earcut from "earcut";
|
|
23
|
+
export function polygon(vertices) {
|
|
24
|
+
return new Polygon(vertices);
|
|
25
|
+
}
|
|
26
|
+
export class Polygon {
|
|
27
|
+
constructor(vertices) {
|
|
28
|
+
_Polygon_instances.add(this);
|
|
29
|
+
_Polygon_centroid.set(this, void 0);
|
|
30
|
+
_Polygon_signedArea.set(this, void 0);
|
|
31
|
+
_Polygon_aabb.set(this, void 0);
|
|
32
|
+
_Polygon_edges.set(this, void 0);
|
|
33
|
+
_Polygon_isConvex.set(this, void 0);
|
|
34
|
+
_Polygon_indexedTessellation.set(this, void 0);
|
|
35
|
+
_Polygon_tessellation.set(this, void 0);
|
|
36
|
+
this.vertices = vertices ?? [];
|
|
37
|
+
}
|
|
38
|
+
centroid() { return __classPrivateFieldSet(this, _Polygon_centroid, __classPrivateFieldGet(this, _Polygon_centroid, "f") ?? __classPrivateFieldGet(this, _Polygon_instances, "m", _Polygon_computeCentroid).call(this), "f"); }
|
|
39
|
+
signedArea() { return __classPrivateFieldSet(this, _Polygon_signedArea, __classPrivateFieldGet(this, _Polygon_signedArea, "f") ?? __classPrivateFieldGet(this, _Polygon_instances, "m", _Polygon_computeSignedArea).call(this), "f"); }
|
|
40
|
+
aabb() { return __classPrivateFieldSet(this, _Polygon_aabb, __classPrivateFieldGet(this, _Polygon_aabb, "f") ?? AABB.fromVertices(this.vertices), "f"); }
|
|
41
|
+
edges() { return __classPrivateFieldSet(this, _Polygon_edges, __classPrivateFieldGet(this, _Polygon_edges, "f") ?? __classPrivateFieldGet(this, _Polygon_instances, "m", _Polygon_computeEdges).call(this), "f"); }
|
|
42
|
+
isConvex() { return __classPrivateFieldSet(this, _Polygon_isConvex, __classPrivateFieldGet(this, _Polygon_isConvex, "f") ?? __classPrivateFieldGet(this, _Polygon_instances, "m", _Polygon_computeIsConvex).call(this), "f"); }
|
|
43
|
+
area() { return Math.abs(this.signedArea()); }
|
|
44
|
+
numVertices() { return this.vertices.length; }
|
|
45
|
+
clone() {
|
|
46
|
+
const clonedVertices = this.vertices.map(vertex => vertex.clone());
|
|
47
|
+
return new _a(clonedVertices);
|
|
48
|
+
}
|
|
49
|
+
copy(polygon) {
|
|
50
|
+
return __classPrivateFieldGet(this, _Polygon_instances, "m", _Polygon_modifyVertices).call(this, polygon.vertices.map(vertex => vertex.clone()));
|
|
51
|
+
}
|
|
52
|
+
getVertex(index) {
|
|
53
|
+
return this.vertices[mod(index, this.numVertices())];
|
|
54
|
+
}
|
|
55
|
+
getEdge(index) {
|
|
56
|
+
// TODO computes all edges for a single value. Is this desired?
|
|
57
|
+
return this.edges()[mod(index, this.numVertices())];
|
|
58
|
+
}
|
|
59
|
+
isCounterClockwise() {
|
|
60
|
+
return this.signedArea() > 0;
|
|
61
|
+
}
|
|
62
|
+
switchOrientation() {
|
|
63
|
+
__classPrivateFieldGet(this, _Polygon_instances, "m", _Polygon_modifyVertices).call(this, this.vertices.reverse());
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
makeCounterClockwise() {
|
|
67
|
+
if (!this.isCounterClockwise())
|
|
68
|
+
this.switchOrientation();
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
makeClockwise() {
|
|
72
|
+
if (this.isCounterClockwise())
|
|
73
|
+
this.switchOrientation();
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
addHole(hole) {
|
|
77
|
+
this.resetMemoized();
|
|
78
|
+
for (let i = 0, n = hole.numVertices(); i < n; i++) {
|
|
79
|
+
const holeVertex = hole.getVertex(i);
|
|
80
|
+
for (let j = 0, m = this.numVertices(); j < m; j++) {
|
|
81
|
+
const vertex = this.getVertex(j);
|
|
82
|
+
// Segment from hole vertex to contour vertex. If it doesn't intersect an edge on the contour, we are done
|
|
83
|
+
const segment = new Segment(holeVertex, vertex);
|
|
84
|
+
for (const edge of this.edges()) {
|
|
85
|
+
if (!edge.intersects(segment)) {
|
|
86
|
+
// Found valid hole vertex, at index i.
|
|
87
|
+
// Insert hole vertices starting at index i, at current polygon index j
|
|
88
|
+
// Result should be as follows, where c is this/current polygon and h is the hole:
|
|
89
|
+
// ..., c(j - 1), c(j), h(i), h(i + 1), ..., h(i - 1), h(i), c(j), c(j + 1), ...
|
|
90
|
+
const insertedVertices = [];
|
|
91
|
+
for (let k = 0; k < n; k++)
|
|
92
|
+
insertedVertices.push(hole.getVertex(i + k).clone());
|
|
93
|
+
insertedVertices.push(hole.getVertex(i).clone());
|
|
94
|
+
insertedVertices.push(this.getVertex(j).clone());
|
|
95
|
+
this.vertices.splice(j + 1, 0, ...insertedVertices);
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
translate(translation) {
|
|
104
|
+
return __classPrivateFieldGet(this, _Polygon_instances, "m", _Polygon_modifyVertices).call(this, vertex => vertex.plus(translation));
|
|
105
|
+
}
|
|
106
|
+
rotate(rotation, center) {
|
|
107
|
+
center ?? (center = this.centroid());
|
|
108
|
+
return __classPrivateFieldGet(this, _Polygon_instances, "m", _Polygon_modifyVertices).call(this, vertex => vertex.rotate(rotation, center));
|
|
109
|
+
}
|
|
110
|
+
scale(scale, center) {
|
|
111
|
+
scale = ensureVec2(scale);
|
|
112
|
+
center ?? (center = this.centroid());
|
|
113
|
+
return __classPrivateFieldGet(this, _Polygon_instances, "m", _Polygon_modifyVertices).call(this, vertex => vertex.minus(center).times(scale).plus(center));
|
|
114
|
+
}
|
|
115
|
+
isPointInside(position) {
|
|
116
|
+
// const fuzzedPosition = Vec2.add(position, vec2(rng(-EPS, EPS), rng(-EPS, EPS)));
|
|
117
|
+
if (!this.aabb().isPointInside(position))
|
|
118
|
+
return false;
|
|
119
|
+
// Use raycasting
|
|
120
|
+
let numIntersections = 0;
|
|
121
|
+
for (let i = 0, n = this.numVertices(); i < n; i++) {
|
|
122
|
+
// if (i === 172) debugger
|
|
123
|
+
if (pointInPolygonHelper(position, this.getEdge(i))) {
|
|
124
|
+
numIntersections++;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return numIntersections % 2 === 1;
|
|
128
|
+
}
|
|
129
|
+
distanceToBoundary(position) {
|
|
130
|
+
return __classPrivateFieldGet(this, _Polygon_instances, "m", _Polygon_distanceToBoundary).call(this, position, false);
|
|
131
|
+
}
|
|
132
|
+
signedDistanceToBoundary(position) {
|
|
133
|
+
return __classPrivateFieldGet(this, _Polygon_instances, "m", _Polygon_distanceToBoundary).call(this, position, true);
|
|
134
|
+
}
|
|
135
|
+
getNearestBoundaryPoint(point) {
|
|
136
|
+
let minimumDistance = Infinity;
|
|
137
|
+
let nearestPoint = null;
|
|
138
|
+
for (let i = 0, n = this.numVertices(); i < n; i++) {
|
|
139
|
+
const currentEdge = this.getEdge(i);
|
|
140
|
+
const currentNearestPoint = currentEdge.getNearestPoint(point);
|
|
141
|
+
const currentDistance = point.distance(currentNearestPoint);
|
|
142
|
+
if (currentDistance < minimumDistance) {
|
|
143
|
+
minimumDistance = currentDistance;
|
|
144
|
+
nearestPoint = currentNearestPoint;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (nearestPoint === null)
|
|
148
|
+
throw new Error("Could not find nearestPoint");
|
|
149
|
+
return nearestPoint;
|
|
150
|
+
}
|
|
151
|
+
// Returns vector from nearest point on boundary to point
|
|
152
|
+
// Combines edge normal and distance
|
|
153
|
+
getNearestNormal(point) {
|
|
154
|
+
return Vec2.subtract(point, this.getNearestBoundaryPoint(point));
|
|
155
|
+
}
|
|
156
|
+
indexedTessellation() { return __classPrivateFieldSet(this, _Polygon_indexedTessellation, __classPrivateFieldGet(this, _Polygon_indexedTessellation, "f") ?? __classPrivateFieldGet(this, _Polygon_instances, "m", _Polygon_computeIndexedTessellation).call(this), "f"); }
|
|
157
|
+
tessellation() { return __classPrivateFieldSet(this, _Polygon_tessellation, __classPrivateFieldGet(this, _Polygon_tessellation, "f") ?? __classPrivateFieldGet(this, _Polygon_instances, "m", _Polygon_computeTessellation).call(this), "f"); }
|
|
158
|
+
computeIntersections(intersectionObject) {
|
|
159
|
+
let intersections = [];
|
|
160
|
+
if (intersectionObject instanceof Polyline) {
|
|
161
|
+
for (const edge of intersectionObject.edges()) {
|
|
162
|
+
intersections = intersections.concat(this.computeIntersections(edge));
|
|
163
|
+
}
|
|
164
|
+
return intersections;
|
|
165
|
+
}
|
|
166
|
+
const { line, timeRange } = getIntersectionLine(intersectionObject);
|
|
167
|
+
const [minTime, maxTime] = timeRange ?? [];
|
|
168
|
+
for (let i = 0, n = this.numVertices(); i < n; i++) {
|
|
169
|
+
const edge = this.getEdge(i);
|
|
170
|
+
const intersection = computeLineIntersection(line, new Line(edge.start, edge.vector));
|
|
171
|
+
if (!intersection)
|
|
172
|
+
continue;
|
|
173
|
+
const [lineTime, edgeTime] = intersection;
|
|
174
|
+
if (timeRange && (lineTime < minTime || lineTime > maxTime))
|
|
175
|
+
continue;
|
|
176
|
+
if (edgeTime < -EPS || edgeTime > 1 + EPS)
|
|
177
|
+
continue;
|
|
178
|
+
const position = edge.interpolate(edgeTime);
|
|
179
|
+
intersections.push({ index: i, position, lineTime, edgeTime });
|
|
180
|
+
}
|
|
181
|
+
intersections.sort((a, b) => a.lineTime - b.lineTime);
|
|
182
|
+
intersections = intersections.filter((int, i) => {
|
|
183
|
+
if (i === intersections.length - 1)
|
|
184
|
+
return true;
|
|
185
|
+
const nextIntersection = intersections[i + 1];
|
|
186
|
+
const timeDifference = nextIntersection.lineTime - int.lineTime;
|
|
187
|
+
if (timeDifference > EPS)
|
|
188
|
+
return true;
|
|
189
|
+
return Math.abs(nextIntersection.index - int.index) <= 1;
|
|
190
|
+
});
|
|
191
|
+
return intersections;
|
|
192
|
+
}
|
|
193
|
+
resetMemoized() {
|
|
194
|
+
__classPrivateFieldSet(this, _Polygon_centroid, undefined, "f");
|
|
195
|
+
__classPrivateFieldSet(this, _Polygon_signedArea, undefined, "f");
|
|
196
|
+
__classPrivateFieldSet(this, _Polygon_aabb, undefined, "f");
|
|
197
|
+
__classPrivateFieldSet(this, _Polygon_edges, undefined, "f");
|
|
198
|
+
__classPrivateFieldSet(this, _Polygon_indexedTessellation, undefined, "f");
|
|
199
|
+
__classPrivateFieldSet(this, _Polygon_tessellation, undefined, "f");
|
|
200
|
+
__classPrivateFieldSet(this, _Polygon_isConvex, undefined, "f");
|
|
201
|
+
}
|
|
202
|
+
render(ctx, beginPath = true) {
|
|
203
|
+
const { vertices } = this;
|
|
204
|
+
if (beginPath)
|
|
205
|
+
ctx.beginPath();
|
|
206
|
+
ctx.moveTo(vertices[0].x, vertices[0].y);
|
|
207
|
+
for (let i = 1, n = vertices.length; i < n; i++) {
|
|
208
|
+
const { x, y } = vertices[i];
|
|
209
|
+
ctx.lineTo(x, y);
|
|
210
|
+
}
|
|
211
|
+
ctx.closePath();
|
|
212
|
+
}
|
|
213
|
+
toArray() {
|
|
214
|
+
let points = [];
|
|
215
|
+
for (const v of this.vertices)
|
|
216
|
+
points.push(v.toArray());
|
|
217
|
+
return points;
|
|
218
|
+
}
|
|
219
|
+
toGeogebra(digits = 4) {
|
|
220
|
+
return `Polygon({${this.vertices.map(({ x, y }) => `(${x.toFixed(digits)},${y.toFixed(digits)})`).join(",")}})`;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
_a = Polygon, _Polygon_centroid = new WeakMap(), _Polygon_signedArea = new WeakMap(), _Polygon_aabb = new WeakMap(), _Polygon_edges = new WeakMap(), _Polygon_isConvex = new WeakMap(), _Polygon_indexedTessellation = new WeakMap(), _Polygon_tessellation = new WeakMap(), _Polygon_instances = new WeakSet(), _Polygon_computeCentroid = function _Polygon_computeCentroid() {
|
|
224
|
+
let x = 0, y = 0;
|
|
225
|
+
for (let i = 0, n = this.numVertices(); i < n; i++) {
|
|
226
|
+
const { start, end } = this.getEdge(i);
|
|
227
|
+
const cross = start.cross(end);
|
|
228
|
+
x += (start.x + end.x) * cross;
|
|
229
|
+
y += (start.y + end.y) * cross;
|
|
230
|
+
}
|
|
231
|
+
const divisor = 6 * this.signedArea();
|
|
232
|
+
return vec2(x / divisor, y / divisor);
|
|
233
|
+
}, _Polygon_computeSignedArea = function _Polygon_computeSignedArea() {
|
|
234
|
+
let sum = 0;
|
|
235
|
+
for (let i = 0, n = this.numVertices(); i < n; i++) {
|
|
236
|
+
const { start, end } = this.getEdge(i);
|
|
237
|
+
sum += start.cross(end);
|
|
238
|
+
}
|
|
239
|
+
return sum / 2;
|
|
240
|
+
}, _Polygon_computeEdges = function _Polygon_computeEdges() {
|
|
241
|
+
let edges = [];
|
|
242
|
+
for (let i = 0, n = this.numVertices(); i < n; i++) {
|
|
243
|
+
const start = this.getVertex(i);
|
|
244
|
+
const end = this.getVertex(i + 1);
|
|
245
|
+
edges.push(new Segment(start, end));
|
|
246
|
+
}
|
|
247
|
+
return edges;
|
|
248
|
+
}, _Polygon_computeIsConvex = function _Polygon_computeIsConvex() {
|
|
249
|
+
const n = this.numVertices();
|
|
250
|
+
const sign = this.isCounterClockwise() ? 1 : -1;
|
|
251
|
+
let isConvex = true;
|
|
252
|
+
for (let i = 0; i < n; i++) {
|
|
253
|
+
const previous = this.getVertex(i - 1);
|
|
254
|
+
const current = this.getVertex(i);
|
|
255
|
+
const next = this.getVertex(i + 1);
|
|
256
|
+
const firstVector = Vec2.subtract(previous, current);
|
|
257
|
+
const secondVector = Vec2.subtract(next, current);
|
|
258
|
+
const internalAngle = mod(sign * Vec2.angle(secondVector, firstVector), 2 * Math.PI);
|
|
259
|
+
if (internalAngle > Math.PI) {
|
|
260
|
+
isConvex = false;
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return isConvex;
|
|
265
|
+
}, _Polygon_computeIndexedTessellation = function _Polygon_computeIndexedTessellation() {
|
|
266
|
+
const flatVertices = [];
|
|
267
|
+
const clonedVertices = [];
|
|
268
|
+
for (let i = 0, n = this.vertices.length; i < n; i++) {
|
|
269
|
+
const vertex = this.getVertex(i);
|
|
270
|
+
flatVertices.push(vertex.x, vertex.y);
|
|
271
|
+
clonedVertices.push(vertex.clone());
|
|
272
|
+
}
|
|
273
|
+
const indices = earcut(flatVertices);
|
|
274
|
+
return {
|
|
275
|
+
indices,
|
|
276
|
+
vertices: clonedVertices
|
|
277
|
+
};
|
|
278
|
+
}, _Polygon_computeTessellation = function _Polygon_computeTessellation() {
|
|
279
|
+
const { indices, vertices } = this.indexedTessellation();
|
|
280
|
+
const triangles = [];
|
|
281
|
+
for (let i = 0, n = indices.length; i < n; i += 3) {
|
|
282
|
+
triangles.push(new _a([
|
|
283
|
+
vertices[indices[i]].clone(),
|
|
284
|
+
vertices[indices[i + 1]].clone(),
|
|
285
|
+
vertices[indices[i + 2]].clone(),
|
|
286
|
+
]));
|
|
287
|
+
}
|
|
288
|
+
return triangles;
|
|
289
|
+
}, _Polygon_modifyVertices = function _Polygon_modifyVertices(modification) {
|
|
290
|
+
if (typeof modification === "function") {
|
|
291
|
+
this.vertices.forEach(modification);
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
this.vertices = modification;
|
|
295
|
+
}
|
|
296
|
+
this.resetMemoized();
|
|
297
|
+
return this;
|
|
298
|
+
}, _Polygon_distanceToBoundary = function _Polygon_distanceToBoundary(position, returnSigned) {
|
|
299
|
+
let smallestDistance = Infinity;
|
|
300
|
+
let distanceSign = 1;
|
|
301
|
+
// const fuzzedPosition = Vec2.add(position, vec2(rng(-EPS, EPS), rng(-EPS, EPS)));
|
|
302
|
+
for (let i = 0, n = this.numVertices(); i < n; i++) {
|
|
303
|
+
const edge = this.getEdge(i);
|
|
304
|
+
const edgeDistance = edge.distanceToPoint(position);
|
|
305
|
+
if (edgeDistance < smallestDistance)
|
|
306
|
+
smallestDistance = edgeDistance;
|
|
307
|
+
if (returnSigned && pointInPolygonHelper(position, this.getEdge(i)))
|
|
308
|
+
distanceSign *= -1;
|
|
309
|
+
}
|
|
310
|
+
return smallestDistance * distanceSign;
|
|
311
|
+
};
|
|
312
|
+
function pointInPolygonHelper(point, edge) {
|
|
313
|
+
const { start, end } = edge;
|
|
314
|
+
// Edge case: Point coincides with start or end. Check only start, so that
|
|
315
|
+
// the same vertex is not counted twice (end is start in the next edge)
|
|
316
|
+
// Ensure that false is returned if end point coincides
|
|
317
|
+
if (Math.abs(end.y - point.y) < EPS) {
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
else if (Math.abs(start.y - point.y) < EPS) {
|
|
321
|
+
return point.x < start.x + EPS;
|
|
322
|
+
}
|
|
323
|
+
let bottom, top;
|
|
324
|
+
if (start.y < end.y) {
|
|
325
|
+
bottom = start;
|
|
326
|
+
top = end;
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
bottom = end;
|
|
330
|
+
top = start;
|
|
331
|
+
}
|
|
332
|
+
return point.y <= top.y && point.y >= bottom.y && ((point.x - top.x) * (top.y - bottom.y) > (point.y - top.y) * (top.x - bottom.x));
|
|
333
|
+
}
|
|
334
|
+
function getIntersectionLine(intersectionObject) {
|
|
335
|
+
if (intersectionObject instanceof Segment) {
|
|
336
|
+
return {
|
|
337
|
+
line: new Line(intersectionObject.start, intersectionObject.vector),
|
|
338
|
+
timeRange: [0, 1]
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
else if (intersectionObject instanceof Ray) {
|
|
342
|
+
return {
|
|
343
|
+
line: new Line(intersectionObject.point, intersectionObject.direction),
|
|
344
|
+
timeRange: [0, Infinity]
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
return {
|
|
349
|
+
line: intersectionObject,
|
|
350
|
+
timeRange: undefined
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { AABB } from "./aabb.js";
|
|
2
|
+
import { Polygon } from "./polygon.js";
|
|
3
|
+
import { Segment } from "./segment.js";
|
|
4
|
+
import { Vec2 } from "./vec2.js";
|
|
5
|
+
export declare class Polyline {
|
|
6
|
+
#private;
|
|
7
|
+
vertices: Vec2[];
|
|
8
|
+
edges(): Segment[];
|
|
9
|
+
hull(): Polygon;
|
|
10
|
+
center(): Vec2;
|
|
11
|
+
aabb(): AABB;
|
|
12
|
+
numVertices(): number;
|
|
13
|
+
constructor(vertices: Vec2[]);
|
|
14
|
+
clone(): Polyline;
|
|
15
|
+
translate(translation: Vec2): this;
|
|
16
|
+
rotate(rotation: number, center?: Vec2): this;
|
|
17
|
+
scale(scale: number, center?: Vec2): this;
|
|
18
|
+
nearestPointOn(point: Vec2): Vec2;
|
|
19
|
+
nearestSegmentToPoint(point: Vec2): Segment;
|
|
20
|
+
distanceToPoint(point: Vec2): number;
|
|
21
|
+
render(ctx: CanvasRenderingContext2D, beginPath?: boolean): void;
|
|
22
|
+
private resetMemoized;
|
|
23
|
+
}
|