├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── TriVis.ts
├── example.png
├── package-lock.json
├── package.json
├── rollup.config.js
├── strain.png
├── test.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependency directories
2 | node_modules/
3 | # Optional npm cache directory
4 | .npm
5 | coverage
6 |
7 | # Generated by rollup
8 | lib/
9 | test/
10 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "delaunaytests"]
2 | path = delaunaytests
3 | url = https://github.com/kninnug/delaunaytests.git
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | ISC License
2 |
3 | Copyright 2021, Marco Gunnink
4 |
5 | Permission to use, copy, modify, and/or distribute this software for any purpose
6 | with or without fee is hereby granted, provided that the above copyright notice
7 | and this permission notice appear in all copies.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
11 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
13 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
14 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
15 | THIS SOFTWARE.
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | TriVis
2 | ======
3 |
4 | Compute visibility polygons by Triangular Expansion.
5 |
6 | 
7 |
8 | Example
9 | -------
10 |
11 | // Points to be triangulated
12 | const points = [[53,98],[5,201],[194,288],[280,195],[392,148],[413,43],[278,5],[169,71],[146,171]],
13 | // Edges to be constrained
14 | edges = [[5, 8]],
15 | // Triangulate
16 | del = Delaunator.from(points),
17 | // Constrain the triangulation
18 | con = new Constrainautor(del);
19 | con.constrainAll(edges);
20 |
21 | // Query point
22 | const qx = 162, qy = 262,
23 | // Obstruction callback: use constrained edges as obstructions
24 | obstructs = (edg) => con.isConstrained(edg),
25 | // Left & right end-points of the initial viewing cone (optional)
26 | ilx = 45, ily = 144, irx = 280, iry = 145,
27 | // Compute visibility polygon
28 | poly = triangularExpansion(del, qx, qy, obstructs, ilx, ily, irx, iry);
29 |
30 | for(const [lx, ly, rx, ry] of poly){
31 | drawTriangle(lx, ly, qx, qy, rx, ry);
32 | }
33 |
34 | 
35 |
36 | Install
37 | -------
38 |
39 | Install from NPM:
40 |
41 | npm install @kninnug/trivis
42 |
43 | Use in Node.js:
44 |
45 | const triangularExpansion = require('@kninnug/trivis');
46 |
47 | or as an ECMAScript/ES6 module:
48 |
49 | import triangularExpansion from '@kninnug/trivis';
50 |
51 | or in the browser:
52 |
53 |
54 |
55 | or minified:
56 |
57 |
58 |
59 | The TriVis library does not depend on Delaunator itself, but the input is
60 | expected to be in the format that Delaunator outputs. The ES module variant
61 | (`TriVis.mjs`) depends on [robust-predicates](https://www.npmjs.com/package/robust-predicates)
62 | and [containing-triangle](https://www.npmjs.com/package/@kninnug/containing-triangle),
63 | but the CommonJS, browser, and minified versions (`lib/TriVis.cjs`,
64 | `lib/TriVis.js`, and `TriVis.min.js`) come with these dependencies compiled in,
65 | and can be used standalone. The (source) TypeScript version is in `TriVis.ts`.
66 |
67 | Usage
68 | -----
69 |
70 | ### poly = triangularExpansion(del, qx, qy, obstructs, ilx = NaN, ily = NaN, irx = NaN, iry = NaN)
71 |
72 | Parameters:
73 |
74 | - `del`: The triangulation in the format that Delaunator outputs.
75 | - `qx`, `qy`: The coordinates of the query point.
76 | - `obstructs`: A callback that receives an edge id of the triangulation and must
77 | indicate whether it obstructs the view. Edges on the hull of the triangulation
78 | are always considered to be obstructing.
79 | - `ilx`, `ily`, `irx`, `iry`: If given, i.e. not `NaN`, the coordinates of the
80 | left and right points restricting the viewing cone. The angles between these
81 | points and the query point should not be greater than 180°. If these arguments
82 | are not given, the visibility polygon is computed in all directions.
83 |
84 | Return value:
85 |
86 | An array of 4-element arrays, `[lx, ly, rx, ry]` with the coordinates of the
87 | left- and right-hand side end-points of the segments that make up the visibility
88 | polygon. Each triplet `(lx, ly) (qx, qy) (rx, ry)` forms a counter-clockwise
89 | triangle that is entirely visible from the query point. The segments are also
90 | ordered counter-clockwise around `(qx, qy)`.
91 |
92 | Changes
93 | -------
94 |
95 | ### 2.0.0
96 | - Convert to TypeScript.
97 | - Move built files to `lib/`.
98 |
99 | ### 1.0.1
100 | - Update dependencies.
101 | - Move test files to separate repository.
102 |
103 | ### 1.0.0
104 | - Initial version.
105 |
106 | Attributions
107 | ------------
108 |
109 | - The Triangular Expansion algorithm is adapted from [Efficient Computation of
110 | Visibility Polygons](https://arxiv.org/abs/1403.3905), March 18, 2014,
111 | Francisc Bungiu, Michael Hemmer, John Hershberger, Kan Huang, Alexander Kröller.
112 | - Uses Vladimir Agafonkin's [robust-predicates](https://github.com/mourner/robust-predicates) port
113 | of Jonathan Shewchuk's [Adaptive Precision Floating-Point Arithmetic and Fast Robust Predicates
114 | for Computational Geometry](http://www.cs.cmu.edu/~quake/robust.html).
115 | - Ray-segment intersection computation adapted from Nicky Case's
116 | [Sight & Light tutorial](https://ncase.me/sight-and-light/).
117 | - The example image and initial idea for this library were inspired by Amit
118 | Patel's article on [2D visibility](https://www.redblobgames.com/articles/visibility/).
119 |
--------------------------------------------------------------------------------
/TriVis.ts:
--------------------------------------------------------------------------------
1 | import {orient2d} from 'robust-predicates';
2 | import containingTriangle from '@kninnug/containing-triangle';
3 |
4 | export interface DelaunatorLike {
5 | coords: {readonly length: number, readonly [n: number]: number};
6 | triangles: {readonly length: number, [n: number]: number};
7 | halfedges: {readonly length: number, [n: number]: number};
8 | hull: {readonly length: number, readonly [n: number]: number};
9 | };
10 |
11 | /** A 4-tuple indicating the left-x/-y and right-x/-y of a segment. */
12 | export type Segment = [number, number, number, number];
13 |
14 | /**
15 | * Compute if and where a ray, or half-line, intersects with a segment. Returns
16 | * the relative location of the intersection point on the ray. I.e, if it
17 | * intersects:
18 | *
19 | * const r = segIntersectRay(s1x, s1y, s2x, s2y, r1x, r1y, r2x, r2y),
20 | * dx = r2x - r1x,
21 | * dy = r2y - r1y,
22 | * // intersection is at:
23 | * x = r1x + r * dx,
24 | * y = r1y + r * dy;
25 | *
26 | * @source https://ncase.me/sight-and-light/
27 | * @param s1x The x coordinate of point 1 of the segment.
28 | * @param s1y The y coordinate of point 1 of the segment.
29 | * @param s2x The x coordinate of point 2 of the segment.
30 | * @param s2y The y coordinate of point 2 of the segment.
31 | * @param r1x The x coordinate of the origin of the ray.
32 | * @param r1y The y coordinate of the origin of the ray.
33 | * @param r2x The x coordinate of a second point along the ray.
34 | * @param r2y The y coordinate of a second point along the ray.
35 | * @return The relative point of the intersection, or Infinity if the
36 | * ray never intersects the segment.
37 | */
38 | function segIntersectRay(s1x: number, s1y: number, s2x: number, s2y: number, r1x: number, r1y: number, r2x: number, r2y: number){
39 | const rdx = r2x - r1x,
40 | rdy = r2y - r1y,
41 |
42 | sdx = s2x - s1x,
43 | sdy = s2y - s1y,
44 |
45 | rmag = Math.sqrt(rdx * rdx + rdy * rdy),
46 | smag = Math.sqrt(sdx * sdx + sdy * sdy);
47 |
48 | if(rdx / rmag === sdx / smag && rdy / rmag === sdy / smag){
49 | return Infinity;
50 | }
51 |
52 | const T2 = (rdx * (s1y - r1y) + rdy * (r1x - s1x)) / (sdx * rdy - sdy * rdx),
53 | T1 = rdx ? (s1x + sdx*T2 - r1x) / rdx : (s1y + sdy*T2 - r1y) / rdy;
54 |
55 | if(T1 < 0 || T2 < 0 || T2 > 1.0){
56 | return Infinity;
57 | }
58 |
59 | return T1;
60 | }
61 |
62 | /**
63 | * Square distance between two points of a triangulation.
64 | *
65 | * @param x1 First point x coordinate.
66 | * @param y1 First point y coordinate.
67 | * @param x2 Second point x coordinate.
68 | * @param y2 Second point y coordinate.
69 | * @return The squared distance.
70 | */
71 | function sqdist(x1: number, y1: number, x2: number, y2: number){
72 | const dx = x2 - x1,
73 | dy = y2 - y1;
74 |
75 | return dx * dx + dy * dy;
76 | }
77 |
78 | /**
79 | * Next half-edge counter-clockwise in a triangle.
80 | *
81 | * @param e Half-edge id.
82 | * @return Id of the next half-edge.
83 | */
84 | function nextEdge(e: number){ return (e % 3 === 2) ? e - 2 : e + 1; }
85 | /**
86 | * Previous half-edge counter-clockwise in a triangle.
87 | *
88 | * @param e Half-edge id.
89 | * @return Id of the previous half-edge.
90 | */
91 | function prevEdge(e: number){ return (e % 3 === 0) ? e + 2 : e - 1; }
92 | /**
93 | * Half-edges of the given triangle.
94 | *
95 | * @param t Triangle id.
96 | * @return Ids of the half-edges.
97 | */
98 | function edgesOfTri(t: number){ return [t * 3, t * 3 + 1, t * 3 + 2]; }
99 | /**
100 | * Point indices of the given triangle.
101 | *
102 | * @param del The triangulation.
103 | * @param t Triangle id.
104 | * @return Indices into the points array of the triangle's points.
105 | */
106 | function pointsOfTri(del: DelaunatorLike, t: number){
107 | const p1 = del.triangles[t * 3],
108 | p2 = del.triangles[t * 3 + 1],
109 | p3 = del.triangles[t * 3 + 2];
110 | return [p1, p2, p3];
111 | }
112 |
113 | /**
114 | * Whether a point is left of the line defined by two other points.
115 | *
116 | * @param x1 The x coordinate of the first point on the line.
117 | * @param y1 The y coordinate of the first point on the line.
118 | * @param x2 The x coordinate of the second point on the line.
119 | * @param y2 The y coordinate of the second point on the line.
120 | * @param px The x coordinate of the query point.
121 | * @param py The y coordinate of the query point.
122 | * @return True if (px, py) is strictly to the left of the segment.
123 | */
124 | function isLeftOf(x1: number, y1: number, x2: number, y2: number, px: number, py: number){
125 | return orient2d(x1, y1, x2, y2, px, py) > 0;
126 | }
127 |
128 | /**
129 | * Whether a point is right of the line defined by two other points.
130 | *
131 | * @param x1 The x coordinate of the first point on the line.
132 | * @param y1 The y coordinate of the first point on the line.
133 | * @param x2 The x coordinate of the second point on the line.
134 | * @param y2 The y coordinate of the second point on the line.
135 | * @param px The x coordinate of the query point.
136 | * @param py The y coordinate of the query point.
137 | * @return True if (px, py) is strictly to the right of the segment.
138 | */
139 | function isRightOf(x1: number, y1: number, x2: number, y2: number, px: number, py: number){
140 | return orient2d(x1, y1, x2, y2, px, py) < 0;
141 | }
142 |
143 | /**
144 | * Compute the centroid of a triangle in a triangulation.
145 | *
146 | * @param del The triangulation.
147 | * @param tri The triangle id.
148 | * @return The coordinates of the centroid.
149 | */
150 | function centroid(del: DelaunatorLike, tri: number){
151 | const [p1, p2, p3] = pointsOfTri(del, tri),
152 | p1x = del.coords[p1 * 2],
153 | p1y = del.coords[p1 * 2 + 1],
154 | p2x = del.coords[p2 * 2],
155 | p2y = del.coords[p2 * 2 + 1],
156 | p3x = del.coords[p3 * 2],
157 | p3y = del.coords[p3 * 2 + 1];
158 | return [(p1x + p2x + p3x) / 3, (p1y + p2y + p3y) / 3];
159 | }
160 |
161 | /**
162 | * Order two points with respect to a query point.
163 | *
164 | * @param qx The x coordinate of the query point.
165 | * @param qy The y coordinate of the query point.
166 | * @param p1x The x coordinate of the first point.
167 | * @param p1y The y coordinate of the first point.
168 | * @param p2x The x coordinate of the second point.
169 | * @param p2y The y coordinate of the second point.
170 | * @return The coordinates of the two points in order [left-x,
171 | * left-y, right-x, right-y].
172 | */
173 | function orderAngles(qx: number, qy: number, p1x: number, p1y: number, p2x: number, p2y: number){
174 | const segLeft = isLeftOf(qx, qy, p2x, p2y, p1x, p1y),
175 | segRight = !segLeft,
176 | lx = segLeft ? p1x : p2x,
177 | ly = segLeft ? p1y : p2y,
178 | rx = segRight ? p1x : p2x,
179 | ry = segRight ? p1y : p2y;
180 | return [lx, ly, rx, ry];
181 | }
182 |
183 | /**
184 | * Order two points of a triangulation with respect to a query point.
185 | *
186 | * @param del The triangulation.
187 | * @param qx The x coordinate of the query point.
188 | * @param qy The y coordinate of the query point.
189 | * @param p1 The id of the first point.
190 | * @param p2 The id of the second point.
191 | * @return The coordinates of the two points in order [left-x,
192 | * left-y, right-x, right-y].
193 | */
194 | function orderDelAngles(del: DelaunatorLike, qx: number, qy: number, p1: number, p2: number){
195 | const p1x = del.coords[p1 * 2],
196 | p1y = del.coords[p1 * 2 + 1],
197 | p2x = del.coords[p2 * 2],
198 | p2y = del.coords[p2 * 2 + 1];
199 | return orderAngles(qx, qy, p1x, p1y, p2x, p2y);
200 | }
201 |
202 | /**
203 | * Whether a segment is within a viewing cone.
204 | *
205 | * @param px The x coordinate of the query point.
206 | * @param py The y coordinate of the query point.
207 | * @param slx The x coordinate of the segment's left point.
208 | * @param sly The y coordinate of the segment's left point.
209 | * @param srx The x coordinate of the segment's right point.
210 | * @param sry The y coordinate of the segment's right point.
211 | * @param rlx The x coordinate of the viewing cone's left point.
212 | * @param rly The y coordinate of the viewing cone's left point.
213 | * @param rrx The x coordinate of the viewing cone's right point.
214 | * @param rry The y coordinate of the viewing cone's right point.
215 | * @return True if the segment is within the viewing cone.
216 | */
217 | function isWithinCone(px: number, py: number, slx: number, sly: number, srx: number, sry: number, rlx: number, rly: number, rrx: number, rry: number){
218 | // o >----- o #====# o -----< o
219 | // rl sl sr rr --> (rl leftOf sl && sl leftOf rr) || (rl leftOf sr && sr leftOf rr)
220 | // o #===== o >====# o -----< o
221 | // sl rl sr rr --> sl leftOr rl && rl leftOf sr
222 | // o >----- o #====< o =====# o
223 | // rl sl rr sr --> rl leftOf sl && sl leftOf rr
224 | // o #===== o >====< o =====# o
225 | // sl rl rr sr --> (sl leftOf rl && rl leftOf sr) || (sl leftOf rr && rr leftOf sr)
226 |
227 | // o >----< o ------ o #====# o
228 | // rl rr sl sr --> rr leftOf sl
229 | if(isLeftOf(px, py, slx, sly, rrx, rry)){
230 | return false;
231 | }
232 | // o #====# o ------ o >----< o
233 | // sl sr rl rr --> sr leftOf rl
234 | if(isLeftOf(px, py, rlx, rly, srx, sry)){
235 | return false;
236 | }
237 | // o >--------< o #========# o
238 | // rl rr sl sr -->
239 | if(rrx === slx && rry === sly){
240 | return false;
241 | }
242 | // o #========# o >--------< o
243 | // sl sr rl rr -->
244 | if(srx === rlx && sry === rly){
245 | return false;
246 | }
247 |
248 | return true;
249 | }
250 |
251 | /**
252 | * Restrict a viewing cone by an edge.
253 | *
254 | * @param px The x coordinate of the query point.
255 | * @param py The y coordinate of the query point.
256 | * @param slx The x coordinate of the segment's left point.
257 | * @param sly The y coordinate of the segment's left point.
258 | * @param srx The x coordinate of the segment's right point.
259 | * @param sry The y coordinate of the segment's right point.
260 | * @param rlx The x coordinate of the viewing cone's left point.
261 | * @param rly The y coordinate of the viewing cone's left point.
262 | * @param rrx The x coordinate of the viewing cone's right point.
263 | * @param rry The y coordinate of the viewing cone's right point.
264 | * @return The coordinates of the new viewing cone, as
265 | * well as two booleans indicating whether the left and/or the right
266 | * points were changed from the original cone to the segment.
267 | */
268 | function restrictAngles(px: number, py: number,
269 | slx: number, sly: number, srx: number, sry: number,
270 | rlx: number, rly: number, rrx: number, rry: number): [...Segment, boolean, boolean] {
271 | let nlx = rlx,
272 | nly = rly,
273 | resLeft = false;
274 | if(isRightOf(px, py, rlx, rly, slx, sly)){
275 | nlx = slx;
276 | nly = sly;
277 | resLeft = true;
278 | }
279 |
280 | let nrx = rrx,
281 | nry = rry,
282 | resRight = false;
283 | if(isLeftOf(px, py, rrx, rry, srx, sry)){
284 | nrx = srx;
285 | nry = sry;
286 | resRight = true;
287 | }
288 |
289 | return [nlx, nly, nrx, nry, resLeft, resRight];
290 | }
291 |
292 | /**
293 | * Compute a visibility polygon of line segments that are visible from a given
294 | * query point in a triangulation.
295 | *
296 | * @source "Efficient Computation of Visibility Polygons" - Francisc Bungiu et al.
297 | * @param del The triangulation.
298 | * @param qx The x coordinate of the query point.
299 | * @param qy The y coordinate of the query point.
300 | * @param obstructs A callback that indicates whether an edge in the
301 | * triangulation obstructs visibility. Called with an edge id and
302 | * expected to return a truthy value.
303 | * @param ilx The x coordinate of the left point restricting the viewing cone.
304 | * @param ily The y coordinate of the left point restricting the viewing cone.
305 | * @param irx The x coordinate of the right point restricting the viewing cone.
306 | * @param iry The y coordinate of the right point restricting the viewing cone.
307 | * @return An array of number quadruples that define the
308 | * line segments making up the visibility polygon.
309 | */
310 | function triangularExpansion(del: DelaunatorLike, qx: number, qy: number, obstructs: (edge: number) => boolean,
311 | ilx = NaN, ily = NaN, irx = NaN, iry = NaN): Segment[] {
312 | /**
313 | * Expand the visibility polygon through the next triangle.
314 | *
315 | * @param edgIn The edge in this triangle through which we came in.
316 | * @param rlx The x coordinate of the left restricting point.
317 | * @param rly The y coordinate of the left restricting point.
318 | * @param rrx The x coordinate of the right restricting point.
319 | * @param rry The y coordinate of the right restricting point.
320 | * @return The segment quadruples of this subsection of
321 | * the visibility polygon.
322 | */
323 | function expand(edgIn: number, rlx: number, rly: number, rrx: number, rry: number): Segment[] {
324 | const ret: Segment[] = [],
325 | edges = [nextEdge(edgIn), prevEdge(edgIn)];
326 |
327 | for(let i = 0; i < 2; i++){
328 | const edg = edges[i],
329 | p1 = del.triangles[edg],
330 | p2 = del.triangles[nextEdge(edg)],
331 | adjOut = del.halfedges[edg];
332 | // Segment left and right
333 | let [slx, sly, srx, sry] = orderDelAngles(del, qx, qy, p1, p2);
334 |
335 | // Edge outside viewing cone
336 | if(!isWithinCone(qx, qy, slx, sly, srx, sry, rlx, rly, rrx, rry)){
337 | continue;
338 | }
339 |
340 | // Next restricting left & right points
341 | const [nlx, nly, nrx, nry, resL, resR] = restrictAngles(qx, qy,
342 | slx, sly, srx, sry, rlx, rly, rrx, rry);
343 |
344 | // Viewing cone degenerated to zero-area triangle
345 | if(orient2d(qx, qy, nrx, nry, nlx, nly) <= 0.0){
346 | continue;
347 | }
348 |
349 | if(adjOut !== -1 && !obstructs(edg)){
350 | // Expand into the next triangle
351 | ret.push(...expand(adjOut, nlx, nly, nrx, nry));
352 | continue;
353 | }
354 | // Hit a wall, or the hull
355 |
356 | // Left restricting point is now on the segment
357 | if(!resL){
358 | const int = segIntersectRay(slx, sly, srx, sry, qx, qy, rlx, rly);
359 | if(int !== Infinity){
360 | const dx = rlx - qx,
361 | dy = rly - qy;
362 | slx = qx + int * dx;
363 | sly = qy + int * dy;
364 | }
365 | }
366 | // Right restricting point is now on the segment
367 | if(!resR){
368 | const int = segIntersectRay(slx, sly, srx, sry, qx, qy, rrx, rry);
369 | if(int !== Infinity){
370 | const dx = rrx - qx,
371 | dy = rry - qy;
372 | srx = qx + int * dx;
373 | sry = qy + int * dy;
374 | }
375 | }
376 |
377 | ret.push([slx, sly, srx, sry]);
378 | }
379 |
380 | return ret;
381 | }
382 |
383 | const triStart = containingTriangle(del, qx, qy);
384 | if(triStart === -1){
385 | return [];
386 | }
387 |
388 | // Order w.r.t. the centroid, rather than the view point to avoid
389 | // instability when the view point is on a triangle edge.
390 | const [cx, cy] = centroid(del, triStart),
391 | startEdges = edgesOfTri(triStart),
392 | ret: Segment[] = [],
393 | prestrict = !isNaN(ilx);
394 |
395 | // Have an initial viewing cone
396 | if(prestrict){ // assume all are given, or none
397 | ([ilx, ily, irx, iry] = orderAngles(qx, qy, ilx, ily, irx, iry));
398 | }
399 |
400 | for(let i = 0; i < 3; i++){
401 | const edg = startEdges[i],
402 | p1 = del.triangles[edg],
403 | p2 = del.triangles[nextEdge(edg)];
404 | let [slx, sly, srx, sry] = orderDelAngles(del, cx, cy, p1, p2);
405 | // Fix for stack-overflows when (qx, qy) is exactly on an end-point
406 | if(sqdist(slx, sly, qx, qy) === 0.0 || sqdist(srx, sry, qx, qy) === 0.0){
407 | return [];
408 | }
409 |
410 | if(prestrict){
411 | const intL = segIntersectRay(slx, sly, srx, sry, qx, qy, ilx, ily);
412 | if(intL !== Infinity){
413 | const dx = ilx - qx,
414 | dy = ily - qy;
415 | ([, , slx, sly] = orderAngles(qx, qy, qx + intL * dx, qy + intL * dy, slx, sly));
416 | }
417 | const intR = segIntersectRay(slx, sly, srx, sry, qx, qy, irx, iry);
418 | if(intR !== Infinity){
419 | const dx = irx - qx,
420 | dy = iry - qy;
421 | ([srx, sry, , ] = orderAngles(qx, qy, srx, sry, qx + intR * dx, qy + intR * dy));
422 | }
423 |
424 | ([slx, sly, srx, sry] = orderAngles(qx, qy, slx, sly, srx, sry));
425 | }else{
426 | ilx = slx;
427 | ily = sly;
428 | irx = srx;
429 | iry = sry;
430 | }
431 |
432 | const [rlx, rly, rrx, rry] = restrictAngles(qx, qy, slx, sly, srx, sry, ilx, ily, irx, iry);
433 |
434 | if(!isWithinCone(qx, qy, slx, sly, srx, sry, ilx, ily, irx, iry)){
435 | continue;
436 | }
437 |
438 | const adj = del.halfedges[edg];
439 | if(adj === -1 || obstructs(edg)){
440 | ret.push([slx, sly, srx, sry]);
441 | }else{
442 | ret.push(...expand(adj, rlx, rly, rrx, rry));
443 | }
444 | }
445 |
446 | return ret;
447 | }
448 |
449 | export default triangularExpansion;
450 |
--------------------------------------------------------------------------------
/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kninnug/TriVis/00c84aad9426b8bdfdee54ec16d453306b0de194/example.png
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@kninnug/trivis",
3 | "version": "1.0.1",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@babel/code-frame": {
8 | "version": "7.12.13",
9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz",
10 | "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==",
11 | "dev": true,
12 | "requires": {
13 | "@babel/highlight": "^7.12.13"
14 | }
15 | },
16 | "@babel/helper-validator-identifier": {
17 | "version": "7.14.0",
18 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz",
19 | "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==",
20 | "dev": true
21 | },
22 | "@babel/highlight": {
23 | "version": "7.14.0",
24 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz",
25 | "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==",
26 | "dev": true,
27 | "requires": {
28 | "@babel/helper-validator-identifier": "^7.14.0",
29 | "chalk": "^2.0.0",
30 | "js-tokens": "^4.0.0"
31 | }
32 | },
33 | "@kninnug/constrainautor": {
34 | "version": "3.0.0",
35 | "resolved": "https://registry.npmjs.org/@kninnug/constrainautor/-/constrainautor-3.0.0.tgz",
36 | "integrity": "sha512-p6j9LBzsCFOPZJ4PdtjR0mjlKkp/3yLj76S4cLf0jXfdcfbs328I7DFsCarTTM9lwWu7nbhpnLqCQqd8t2KSgw==",
37 | "dev": true,
38 | "requires": {
39 | "robust-predicates": "^3.0.1"
40 | }
41 | },
42 | "@kninnug/containing-triangle": {
43 | "version": "2.0.0",
44 | "resolved": "https://registry.npmjs.org/@kninnug/containing-triangle/-/containing-triangle-2.0.0.tgz",
45 | "integrity": "sha512-4VSyET+jlQtJN1NuPOATqQS5T3Wy59+716Lj8Ks38ETBrHmqOHkuUhnujOHTnGa6dy4hysSuWvbYRL15WN2Jsg==",
46 | "requires": {
47 | "robust-predicates": "^3.0.1"
48 | }
49 | },
50 | "@rollup/plugin-commonjs": {
51 | "version": "21.0.1",
52 | "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.1.tgz",
53 | "integrity": "sha512-EA+g22lbNJ8p5kuZJUYyhhDK7WgJckW5g4pNN7n4mAFUM96VuwUnNT3xr2Db2iCZPI1pJPbGyfT5mS9T1dHfMg==",
54 | "dev": true,
55 | "requires": {
56 | "@rollup/pluginutils": "^3.1.0",
57 | "commondir": "^1.0.1",
58 | "estree-walker": "^2.0.1",
59 | "glob": "^7.1.6",
60 | "is-reference": "^1.2.1",
61 | "magic-string": "^0.25.7",
62 | "resolve": "^1.17.0"
63 | },
64 | "dependencies": {
65 | "estree-walker": {
66 | "version": "2.0.2",
67 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
68 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
69 | "dev": true
70 | }
71 | }
72 | },
73 | "@rollup/plugin-node-resolve": {
74 | "version": "11.2.1",
75 | "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz",
76 | "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==",
77 | "dev": true,
78 | "requires": {
79 | "@rollup/pluginutils": "^3.1.0",
80 | "@types/resolve": "1.17.1",
81 | "builtin-modules": "^3.1.0",
82 | "deepmerge": "^4.2.2",
83 | "is-module": "^1.0.0",
84 | "resolve": "^1.19.0"
85 | }
86 | },
87 | "@rollup/plugin-replace": {
88 | "version": "3.0.1",
89 | "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-3.0.1.tgz",
90 | "integrity": "sha512-989J5oRzf3mm0pO/0djTijdfEh9U3n63BIXN5X7T4U9BP+fN4oxQ6DvDuBvFaHA6scaHQRclqmKQEkBhB7k7Hg==",
91 | "dev": true,
92 | "requires": {
93 | "@rollup/pluginutils": "^3.1.0",
94 | "magic-string": "^0.25.7"
95 | }
96 | },
97 | "@rollup/plugin-typescript": {
98 | "version": "8.3.0",
99 | "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.0.tgz",
100 | "integrity": "sha512-I5FpSvLbtAdwJ+naznv+B4sjXZUcIvLLceYpITAn7wAP8W0wqc5noLdGIp9HGVntNhRWXctwPYrSSFQxtl0FPA==",
101 | "dev": true,
102 | "requires": {
103 | "@rollup/pluginutils": "^3.1.0",
104 | "resolve": "^1.17.0"
105 | }
106 | },
107 | "@rollup/pluginutils": {
108 | "version": "3.1.0",
109 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
110 | "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
111 | "dev": true,
112 | "requires": {
113 | "@types/estree": "0.0.39",
114 | "estree-walker": "^1.0.1",
115 | "picomatch": "^2.2.2"
116 | }
117 | },
118 | "@types/delaunator": {
119 | "version": "5.0.0",
120 | "resolved": "https://registry.npmjs.org/@types/delaunator/-/delaunator-5.0.0.tgz",
121 | "integrity": "sha512-00nrXdKbhF9FQsq6MskmdUgEqd5MWzA+x8CoDt8OJqbS4QB528SgkOfEcnQMa2sIg2XTKowwwVQx8iGsAX6CAQ==",
122 | "dev": true
123 | },
124 | "@types/estree": {
125 | "version": "0.0.39",
126 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
127 | "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
128 | "dev": true
129 | },
130 | "@types/node": {
131 | "version": "15.0.1",
132 | "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.1.tgz",
133 | "integrity": "sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA==",
134 | "dev": true
135 | },
136 | "@types/resolve": {
137 | "version": "1.17.1",
138 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
139 | "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==",
140 | "dev": true,
141 | "requires": {
142 | "@types/node": "*"
143 | }
144 | },
145 | "@types/tape": {
146 | "version": "4.13.2",
147 | "resolved": "https://registry.npmjs.org/@types/tape/-/tape-4.13.2.tgz",
148 | "integrity": "sha512-V1ez/RtYRGN9cNYApw5xf27DpMkTB0033X6a2i3KUmKhSojBfbWN0i3EgZxboUG96WJLHLdOyZ01aiZwVW5aSA==",
149 | "dev": true,
150 | "requires": {
151 | "@types/node": "*"
152 | }
153 | },
154 | "ansi-styles": {
155 | "version": "3.2.1",
156 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
157 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
158 | "dev": true,
159 | "requires": {
160 | "color-convert": "^1.9.0"
161 | }
162 | },
163 | "array-filter": {
164 | "version": "1.0.0",
165 | "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz",
166 | "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=",
167 | "dev": true
168 | },
169 | "available-typed-arrays": {
170 | "version": "1.0.2",
171 | "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz",
172 | "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==",
173 | "dev": true,
174 | "requires": {
175 | "array-filter": "^1.0.0"
176 | }
177 | },
178 | "balanced-match": {
179 | "version": "1.0.2",
180 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
181 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
182 | "dev": true
183 | },
184 | "brace-expansion": {
185 | "version": "1.1.11",
186 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
187 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
188 | "dev": true,
189 | "requires": {
190 | "balanced-match": "^1.0.0",
191 | "concat-map": "0.0.1"
192 | }
193 | },
194 | "buffer-from": {
195 | "version": "1.1.1",
196 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
197 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
198 | "dev": true
199 | },
200 | "builtin-modules": {
201 | "version": "3.2.0",
202 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz",
203 | "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==",
204 | "dev": true
205 | },
206 | "call-bind": {
207 | "version": "1.0.2",
208 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
209 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
210 | "dev": true,
211 | "requires": {
212 | "function-bind": "^1.1.1",
213 | "get-intrinsic": "^1.0.2"
214 | }
215 | },
216 | "chalk": {
217 | "version": "2.4.2",
218 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
219 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
220 | "dev": true,
221 | "requires": {
222 | "ansi-styles": "^3.2.1",
223 | "escape-string-regexp": "^1.0.5",
224 | "supports-color": "^5.3.0"
225 | }
226 | },
227 | "color-convert": {
228 | "version": "1.9.3",
229 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
230 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
231 | "dev": true,
232 | "requires": {
233 | "color-name": "1.1.3"
234 | }
235 | },
236 | "color-name": {
237 | "version": "1.1.3",
238 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
239 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
240 | "dev": true
241 | },
242 | "commander": {
243 | "version": "2.20.3",
244 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
245 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
246 | "dev": true
247 | },
248 | "commondir": {
249 | "version": "1.0.1",
250 | "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
251 | "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
252 | "dev": true
253 | },
254 | "concat-map": {
255 | "version": "0.0.1",
256 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
257 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
258 | "dev": true
259 | },
260 | "deep-equal": {
261 | "version": "2.0.5",
262 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz",
263 | "integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==",
264 | "dev": true,
265 | "requires": {
266 | "call-bind": "^1.0.0",
267 | "es-get-iterator": "^1.1.1",
268 | "get-intrinsic": "^1.0.1",
269 | "is-arguments": "^1.0.4",
270 | "is-date-object": "^1.0.2",
271 | "is-regex": "^1.1.1",
272 | "isarray": "^2.0.5",
273 | "object-is": "^1.1.4",
274 | "object-keys": "^1.1.1",
275 | "object.assign": "^4.1.2",
276 | "regexp.prototype.flags": "^1.3.0",
277 | "side-channel": "^1.0.3",
278 | "which-boxed-primitive": "^1.0.1",
279 | "which-collection": "^1.0.1",
280 | "which-typed-array": "^1.1.2"
281 | }
282 | },
283 | "deepmerge": {
284 | "version": "4.2.2",
285 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
286 | "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
287 | "dev": true
288 | },
289 | "define-properties": {
290 | "version": "1.1.3",
291 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
292 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
293 | "dev": true,
294 | "requires": {
295 | "object-keys": "^1.0.12"
296 | }
297 | },
298 | "defined": {
299 | "version": "1.0.0",
300 | "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
301 | "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
302 | "dev": true
303 | },
304 | "delaunator": {
305 | "version": "5.0.0",
306 | "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
307 | "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
308 | "dev": true,
309 | "requires": {
310 | "robust-predicates": "^3.0.0"
311 | }
312 | },
313 | "dotignore": {
314 | "version": "0.1.2",
315 | "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz",
316 | "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==",
317 | "dev": true,
318 | "requires": {
319 | "minimatch": "^3.0.4"
320 | }
321 | },
322 | "es-abstract": {
323 | "version": "1.18.0",
324 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz",
325 | "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==",
326 | "dev": true,
327 | "requires": {
328 | "call-bind": "^1.0.2",
329 | "es-to-primitive": "^1.2.1",
330 | "function-bind": "^1.1.1",
331 | "get-intrinsic": "^1.1.1",
332 | "has": "^1.0.3",
333 | "has-symbols": "^1.0.2",
334 | "is-callable": "^1.2.3",
335 | "is-negative-zero": "^2.0.1",
336 | "is-regex": "^1.1.2",
337 | "is-string": "^1.0.5",
338 | "object-inspect": "^1.9.0",
339 | "object-keys": "^1.1.1",
340 | "object.assign": "^4.1.2",
341 | "string.prototype.trimend": "^1.0.4",
342 | "string.prototype.trimstart": "^1.0.4",
343 | "unbox-primitive": "^1.0.0"
344 | }
345 | },
346 | "es-get-iterator": {
347 | "version": "1.1.2",
348 | "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz",
349 | "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==",
350 | "dev": true,
351 | "requires": {
352 | "call-bind": "^1.0.2",
353 | "get-intrinsic": "^1.1.0",
354 | "has-symbols": "^1.0.1",
355 | "is-arguments": "^1.1.0",
356 | "is-map": "^2.0.2",
357 | "is-set": "^2.0.2",
358 | "is-string": "^1.0.5",
359 | "isarray": "^2.0.5"
360 | }
361 | },
362 | "es-to-primitive": {
363 | "version": "1.2.1",
364 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
365 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
366 | "dev": true,
367 | "requires": {
368 | "is-callable": "^1.1.4",
369 | "is-date-object": "^1.0.1",
370 | "is-symbol": "^1.0.2"
371 | }
372 | },
373 | "escape-string-regexp": {
374 | "version": "1.0.5",
375 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
376 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
377 | "dev": true
378 | },
379 | "estree-walker": {
380 | "version": "1.0.1",
381 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
382 | "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
383 | "dev": true
384 | },
385 | "for-each": {
386 | "version": "0.3.3",
387 | "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
388 | "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
389 | "dev": true,
390 | "requires": {
391 | "is-callable": "^1.1.3"
392 | }
393 | },
394 | "foreach": {
395 | "version": "2.0.5",
396 | "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
397 | "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=",
398 | "dev": true
399 | },
400 | "fs.realpath": {
401 | "version": "1.0.0",
402 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
403 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
404 | "dev": true
405 | },
406 | "fsevents": {
407 | "version": "2.3.2",
408 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
409 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
410 | "dev": true,
411 | "optional": true
412 | },
413 | "function-bind": {
414 | "version": "1.1.1",
415 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
416 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
417 | "dev": true
418 | },
419 | "get-intrinsic": {
420 | "version": "1.1.1",
421 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
422 | "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
423 | "dev": true,
424 | "requires": {
425 | "function-bind": "^1.1.1",
426 | "has": "^1.0.3",
427 | "has-symbols": "^1.0.1"
428 | }
429 | },
430 | "glob": {
431 | "version": "7.1.6",
432 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
433 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
434 | "dev": true,
435 | "requires": {
436 | "fs.realpath": "^1.0.0",
437 | "inflight": "^1.0.4",
438 | "inherits": "2",
439 | "minimatch": "^3.0.4",
440 | "once": "^1.3.0",
441 | "path-is-absolute": "^1.0.0"
442 | }
443 | },
444 | "has": {
445 | "version": "1.0.3",
446 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
447 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
448 | "dev": true,
449 | "requires": {
450 | "function-bind": "^1.1.1"
451 | }
452 | },
453 | "has-bigints": {
454 | "version": "1.0.1",
455 | "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
456 | "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==",
457 | "dev": true
458 | },
459 | "has-flag": {
460 | "version": "3.0.0",
461 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
462 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
463 | "dev": true
464 | },
465 | "has-symbols": {
466 | "version": "1.0.2",
467 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
468 | "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
469 | "dev": true
470 | },
471 | "inflight": {
472 | "version": "1.0.6",
473 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
474 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
475 | "dev": true,
476 | "requires": {
477 | "once": "^1.3.0",
478 | "wrappy": "1"
479 | }
480 | },
481 | "inherits": {
482 | "version": "2.0.4",
483 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
484 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
485 | "dev": true
486 | },
487 | "is-arguments": {
488 | "version": "1.1.0",
489 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz",
490 | "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==",
491 | "dev": true,
492 | "requires": {
493 | "call-bind": "^1.0.0"
494 | }
495 | },
496 | "is-bigint": {
497 | "version": "1.0.1",
498 | "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz",
499 | "integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==",
500 | "dev": true
501 | },
502 | "is-boolean-object": {
503 | "version": "1.1.0",
504 | "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz",
505 | "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==",
506 | "dev": true,
507 | "requires": {
508 | "call-bind": "^1.0.0"
509 | }
510 | },
511 | "is-callable": {
512 | "version": "1.2.3",
513 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
514 | "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==",
515 | "dev": true
516 | },
517 | "is-core-module": {
518 | "version": "2.3.0",
519 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.3.0.tgz",
520 | "integrity": "sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==",
521 | "dev": true,
522 | "requires": {
523 | "has": "^1.0.3"
524 | }
525 | },
526 | "is-date-object": {
527 | "version": "1.0.2",
528 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
529 | "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
530 | "dev": true
531 | },
532 | "is-map": {
533 | "version": "2.0.2",
534 | "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
535 | "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==",
536 | "dev": true
537 | },
538 | "is-module": {
539 | "version": "1.0.0",
540 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
541 | "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=",
542 | "dev": true
543 | },
544 | "is-negative-zero": {
545 | "version": "2.0.1",
546 | "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz",
547 | "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==",
548 | "dev": true
549 | },
550 | "is-number-object": {
551 | "version": "1.0.4",
552 | "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz",
553 | "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==",
554 | "dev": true
555 | },
556 | "is-reference": {
557 | "version": "1.2.1",
558 | "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
559 | "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
560 | "dev": true,
561 | "requires": {
562 | "@types/estree": "*"
563 | }
564 | },
565 | "is-regex": {
566 | "version": "1.1.2",
567 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz",
568 | "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==",
569 | "dev": true,
570 | "requires": {
571 | "call-bind": "^1.0.2",
572 | "has-symbols": "^1.0.1"
573 | }
574 | },
575 | "is-set": {
576 | "version": "2.0.2",
577 | "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz",
578 | "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==",
579 | "dev": true
580 | },
581 | "is-string": {
582 | "version": "1.0.5",
583 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
584 | "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
585 | "dev": true
586 | },
587 | "is-symbol": {
588 | "version": "1.0.3",
589 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
590 | "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
591 | "dev": true,
592 | "requires": {
593 | "has-symbols": "^1.0.1"
594 | }
595 | },
596 | "is-typed-array": {
597 | "version": "1.1.5",
598 | "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.5.tgz",
599 | "integrity": "sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug==",
600 | "dev": true,
601 | "requires": {
602 | "available-typed-arrays": "^1.0.2",
603 | "call-bind": "^1.0.2",
604 | "es-abstract": "^1.18.0-next.2",
605 | "foreach": "^2.0.5",
606 | "has-symbols": "^1.0.1"
607 | }
608 | },
609 | "is-weakmap": {
610 | "version": "2.0.1",
611 | "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
612 | "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==",
613 | "dev": true
614 | },
615 | "is-weakset": {
616 | "version": "2.0.1",
617 | "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz",
618 | "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==",
619 | "dev": true
620 | },
621 | "isarray": {
622 | "version": "2.0.5",
623 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
624 | "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
625 | "dev": true
626 | },
627 | "jest-worker": {
628 | "version": "26.6.2",
629 | "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
630 | "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
631 | "dev": true,
632 | "requires": {
633 | "@types/node": "*",
634 | "merge-stream": "^2.0.0",
635 | "supports-color": "^7.0.0"
636 | },
637 | "dependencies": {
638 | "has-flag": {
639 | "version": "4.0.0",
640 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
641 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
642 | "dev": true
643 | },
644 | "supports-color": {
645 | "version": "7.2.0",
646 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
647 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
648 | "dev": true,
649 | "requires": {
650 | "has-flag": "^4.0.0"
651 | }
652 | }
653 | }
654 | },
655 | "js-tokens": {
656 | "version": "4.0.0",
657 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
658 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
659 | "dev": true
660 | },
661 | "magic-string": {
662 | "version": "0.25.7",
663 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
664 | "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
665 | "dev": true,
666 | "requires": {
667 | "sourcemap-codec": "^1.4.4"
668 | }
669 | },
670 | "merge-stream": {
671 | "version": "2.0.0",
672 | "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
673 | "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
674 | "dev": true
675 | },
676 | "minimatch": {
677 | "version": "3.0.4",
678 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
679 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
680 | "dev": true,
681 | "requires": {
682 | "brace-expansion": "^1.1.7"
683 | }
684 | },
685 | "minimist": {
686 | "version": "1.2.5",
687 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
688 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
689 | "dev": true
690 | },
691 | "object-inspect": {
692 | "version": "1.10.2",
693 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.2.tgz",
694 | "integrity": "sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA==",
695 | "dev": true
696 | },
697 | "object-is": {
698 | "version": "1.1.5",
699 | "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
700 | "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
701 | "dev": true,
702 | "requires": {
703 | "call-bind": "^1.0.2",
704 | "define-properties": "^1.1.3"
705 | }
706 | },
707 | "object-keys": {
708 | "version": "1.1.1",
709 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
710 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
711 | "dev": true
712 | },
713 | "object.assign": {
714 | "version": "4.1.2",
715 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
716 | "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
717 | "dev": true,
718 | "requires": {
719 | "call-bind": "^1.0.0",
720 | "define-properties": "^1.1.3",
721 | "has-symbols": "^1.0.1",
722 | "object-keys": "^1.1.1"
723 | }
724 | },
725 | "once": {
726 | "version": "1.4.0",
727 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
728 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
729 | "dev": true,
730 | "requires": {
731 | "wrappy": "1"
732 | }
733 | },
734 | "path-is-absolute": {
735 | "version": "1.0.1",
736 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
737 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
738 | "dev": true
739 | },
740 | "path-parse": {
741 | "version": "1.0.6",
742 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
743 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
744 | "dev": true
745 | },
746 | "picomatch": {
747 | "version": "2.2.3",
748 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz",
749 | "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==",
750 | "dev": true
751 | },
752 | "randombytes": {
753 | "version": "2.1.0",
754 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
755 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
756 | "dev": true,
757 | "requires": {
758 | "safe-buffer": "^5.1.0"
759 | }
760 | },
761 | "regexp.prototype.flags": {
762 | "version": "1.3.1",
763 | "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz",
764 | "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==",
765 | "dev": true,
766 | "requires": {
767 | "call-bind": "^1.0.2",
768 | "define-properties": "^1.1.3"
769 | }
770 | },
771 | "resolve": {
772 | "version": "1.20.0",
773 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
774 | "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
775 | "dev": true,
776 | "requires": {
777 | "is-core-module": "^2.2.0",
778 | "path-parse": "^1.0.6"
779 | }
780 | },
781 | "resumer": {
782 | "version": "0.0.0",
783 | "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz",
784 | "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=",
785 | "dev": true,
786 | "requires": {
787 | "through": "~2.3.4"
788 | }
789 | },
790 | "robust-predicates": {
791 | "version": "3.0.1",
792 | "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz",
793 | "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g=="
794 | },
795 | "rollup": {
796 | "version": "2.48.0",
797 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.48.0.tgz",
798 | "integrity": "sha512-wl9ZSSSsi5579oscSDYSzGn092tCS076YB+TQrzsGuSfYyJeep8eEWj0eaRjuC5McuMNmcnR8icBqiE/FWNB1A==",
799 | "dev": true,
800 | "requires": {
801 | "fsevents": "~2.3.1"
802 | }
803 | },
804 | "rollup-plugin-terser": {
805 | "version": "7.0.2",
806 | "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz",
807 | "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==",
808 | "dev": true,
809 | "requires": {
810 | "@babel/code-frame": "^7.10.4",
811 | "jest-worker": "^26.2.1",
812 | "serialize-javascript": "^4.0.0",
813 | "terser": "^5.0.0"
814 | }
815 | },
816 | "safe-buffer": {
817 | "version": "5.2.1",
818 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
819 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
820 | "dev": true
821 | },
822 | "serialize-javascript": {
823 | "version": "4.0.0",
824 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
825 | "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
826 | "dev": true,
827 | "requires": {
828 | "randombytes": "^2.1.0"
829 | }
830 | },
831 | "side-channel": {
832 | "version": "1.0.4",
833 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
834 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
835 | "dev": true,
836 | "requires": {
837 | "call-bind": "^1.0.0",
838 | "get-intrinsic": "^1.0.2",
839 | "object-inspect": "^1.9.0"
840 | }
841 | },
842 | "source-map": {
843 | "version": "0.7.3",
844 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
845 | "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
846 | "dev": true
847 | },
848 | "source-map-support": {
849 | "version": "0.5.19",
850 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
851 | "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
852 | "dev": true,
853 | "requires": {
854 | "buffer-from": "^1.0.0",
855 | "source-map": "^0.6.0"
856 | },
857 | "dependencies": {
858 | "source-map": {
859 | "version": "0.6.1",
860 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
861 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
862 | "dev": true
863 | }
864 | }
865 | },
866 | "sourcemap-codec": {
867 | "version": "1.4.8",
868 | "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
869 | "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
870 | "dev": true
871 | },
872 | "string.prototype.trim": {
873 | "version": "1.2.4",
874 | "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.4.tgz",
875 | "integrity": "sha512-hWCk/iqf7lp0/AgTF7/ddO1IWtSNPASjlzCicV5irAVdE1grjsneK26YG6xACMBEdCvO8fUST0UzDMh/2Qy+9Q==",
876 | "dev": true,
877 | "requires": {
878 | "call-bind": "^1.0.2",
879 | "define-properties": "^1.1.3",
880 | "es-abstract": "^1.18.0-next.2"
881 | }
882 | },
883 | "string.prototype.trimend": {
884 | "version": "1.0.4",
885 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
886 | "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
887 | "dev": true,
888 | "requires": {
889 | "call-bind": "^1.0.2",
890 | "define-properties": "^1.1.3"
891 | }
892 | },
893 | "string.prototype.trimstart": {
894 | "version": "1.0.4",
895 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
896 | "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
897 | "dev": true,
898 | "requires": {
899 | "call-bind": "^1.0.2",
900 | "define-properties": "^1.1.3"
901 | }
902 | },
903 | "supports-color": {
904 | "version": "5.5.0",
905 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
906 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
907 | "dev": true,
908 | "requires": {
909 | "has-flag": "^3.0.0"
910 | }
911 | },
912 | "tape": {
913 | "version": "5.2.2",
914 | "resolved": "https://registry.npmjs.org/tape/-/tape-5.2.2.tgz",
915 | "integrity": "sha512-grXrzPC1ly2kyTMKdqxh5GiLpb0BpNctCuecTB0psHX4Gu0nc+uxWR4xKjTh/4CfQlH4zhvTM2/EXmHXp6v/uA==",
916 | "dev": true,
917 | "requires": {
918 | "call-bind": "^1.0.2",
919 | "deep-equal": "^2.0.5",
920 | "defined": "^1.0.0",
921 | "dotignore": "^0.1.2",
922 | "for-each": "^0.3.3",
923 | "glob": "^7.1.6",
924 | "has": "^1.0.3",
925 | "inherits": "^2.0.4",
926 | "is-regex": "^1.1.2",
927 | "minimist": "^1.2.5",
928 | "object-inspect": "^1.9.0",
929 | "object-is": "^1.1.5",
930 | "object.assign": "^4.1.2",
931 | "resolve": "^2.0.0-next.3",
932 | "resumer": "^0.0.0",
933 | "string.prototype.trim": "^1.2.4",
934 | "through": "^2.3.8"
935 | },
936 | "dependencies": {
937 | "resolve": {
938 | "version": "2.0.0-next.3",
939 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz",
940 | "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==",
941 | "dev": true,
942 | "requires": {
943 | "is-core-module": "^2.2.0",
944 | "path-parse": "^1.0.6"
945 | }
946 | }
947 | }
948 | },
949 | "terser": {
950 | "version": "5.7.0",
951 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.0.tgz",
952 | "integrity": "sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==",
953 | "dev": true,
954 | "requires": {
955 | "commander": "^2.20.0",
956 | "source-map": "~0.7.2",
957 | "source-map-support": "~0.5.19"
958 | }
959 | },
960 | "through": {
961 | "version": "2.3.8",
962 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
963 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
964 | "dev": true
965 | },
966 | "tslib": {
967 | "version": "2.3.1",
968 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
969 | "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
970 | "dev": true
971 | },
972 | "typescript": {
973 | "version": "4.5.4",
974 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz",
975 | "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==",
976 | "dev": true
977 | },
978 | "unbox-primitive": {
979 | "version": "1.0.1",
980 | "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
981 | "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
982 | "dev": true,
983 | "requires": {
984 | "function-bind": "^1.1.1",
985 | "has-bigints": "^1.0.1",
986 | "has-symbols": "^1.0.2",
987 | "which-boxed-primitive": "^1.0.2"
988 | }
989 | },
990 | "which-boxed-primitive": {
991 | "version": "1.0.2",
992 | "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
993 | "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
994 | "dev": true,
995 | "requires": {
996 | "is-bigint": "^1.0.1",
997 | "is-boolean-object": "^1.1.0",
998 | "is-number-object": "^1.0.4",
999 | "is-string": "^1.0.5",
1000 | "is-symbol": "^1.0.3"
1001 | }
1002 | },
1003 | "which-collection": {
1004 | "version": "1.0.1",
1005 | "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz",
1006 | "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==",
1007 | "dev": true,
1008 | "requires": {
1009 | "is-map": "^2.0.1",
1010 | "is-set": "^2.0.1",
1011 | "is-weakmap": "^2.0.1",
1012 | "is-weakset": "^2.0.1"
1013 | }
1014 | },
1015 | "which-typed-array": {
1016 | "version": "1.1.4",
1017 | "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz",
1018 | "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==",
1019 | "dev": true,
1020 | "requires": {
1021 | "available-typed-arrays": "^1.0.2",
1022 | "call-bind": "^1.0.0",
1023 | "es-abstract": "^1.18.0-next.1",
1024 | "foreach": "^2.0.5",
1025 | "function-bind": "^1.1.1",
1026 | "has-symbols": "^1.0.1",
1027 | "is-typed-array": "^1.1.3"
1028 | }
1029 | },
1030 | "wrappy": {
1031 | "version": "1.0.2",
1032 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1033 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
1034 | "dev": true
1035 | }
1036 | }
1037 | }
1038 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@kninnug/trivis",
3 | "version": "1.0.1",
4 | "description": "Compute visibility polygons by Triangular Expansion",
5 | "main": "./lib/TriVis.min.js",
6 | "module": "./lib/TriVis.mjs",
7 | "exports": {
8 | "import": "./lib/TriVis.mjs",
9 | "require": "./lib/TriVis.cjs"
10 | },
11 | "types": "TriVis.ts",
12 | "files": [
13 | "TriVis.ts",
14 | "lib/TriVis.cjs",
15 | "lib/TriVis.mjs",
16 | "lib/TriVis.js",
17 | "lib/TriVis.min.js"
18 | ],
19 | "scripts": {
20 | "test": "npm run build && node test/test.mjs",
21 | "build": "rollup -c",
22 | "prepare": "npm run build",
23 | "clean": "rm -r ./coverage ./lib/ ./test/ || true"
24 | },
25 | "repository": {
26 | "type": "git",
27 | "url": "git+https://github.com/kninnug/TriVis.git"
28 | },
29 | "keywords": [
30 | "visibility",
31 | "polygon",
32 | "triangular",
33 | "expansion",
34 | "Delaunator"
35 | ],
36 | "author": "Marco Gunnink",
37 | "license": "ISC",
38 | "bugs": {
39 | "url": "https://github.com/kninnug/TriVis/issues"
40 | },
41 | "homepage": "https://github.com/kninnug/TriVis#readme",
42 | "dependencies": {
43 | "@kninnug/containing-triangle": "^2.0.0",
44 | "robust-predicates": "^3.0.1"
45 | },
46 | "devDependencies": {
47 | "@kninnug/constrainautor": "^3.0.0",
48 | "@rollup/plugin-commonjs": "^21.0.1",
49 | "@rollup/plugin-node-resolve": "^11.2.1",
50 | "@rollup/plugin-replace": "^3.0.1",
51 | "@rollup/plugin-typescript": "^8.3.0",
52 | "@types/delaunator": "^5.0.0",
53 | "@types/tape": "^4.13.2",
54 | "delaunator": "^5.0.0",
55 | "rollup": "^2.48.0",
56 | "rollup-plugin-terser": "^7.0.2",
57 | "tape": "^5.2.2",
58 | "tslib": "^2.3.1",
59 | "typescript": "^4.5.4"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import {terser} from 'rollup-plugin-terser';
2 | import resolve from '@rollup/plugin-node-resolve';
3 | import commonjs from '@rollup/plugin-commonjs';
4 | import typescript from '@rollup/plugin-typescript';
5 | import replace from '@rollup/plugin-replace';
6 | import * as path from 'path';
7 |
8 | function build(name){
9 | return [
10 | {
11 | input: `${name}.ts`,
12 | output: {
13 | name,
14 | format: 'es',
15 | file: `lib/${name}.mjs`
16 | },
17 | external: ['robust-predicates', '@kninnug/containing-triangle'],
18 | plugins: [typescript()]
19 | },
20 | {
21 | input: `lib/${name}.mjs`, // typescript & resolve don't play well together
22 | output: {
23 | name,
24 | format: 'commonjs',
25 | file: `lib/${name}.cjs`,
26 | exports: 'default'
27 | },
28 | plugins: [resolve(), typescript()]
29 | },
30 | {
31 | input: `lib/${name}.mjs`,
32 | output: {
33 | name,
34 | format: 'umd',
35 | file: `lib/${name}.js`
36 | },
37 | plugins: [commonjs(), resolve()]
38 | },
39 | {
40 | input: `lib/${name}.mjs`,
41 | output: {
42 | name,
43 | format: 'umd',
44 | file: `lib/${name}.min.js`
45 | },
46 | plugins: [resolve(), commonjs(), terser()]
47 | }
48 | ];
49 | }
50 |
51 | function test(name){
52 | return [
53 | {
54 | input: `${name}.ts`,
55 | output: {
56 | name,
57 | format: 'es',
58 | file: `test/${name}.mjs`
59 | },
60 | external: ['robust-predicates', 'robust-segment-intersect', 'tape', 'delaunator',
61 | '@kninnug/constrainautor', '@kninnug/containing-triangle', 'fs', 'path'],
62 | plugins: [replace({
63 | preventAssignment: true,
64 | values: {
65 | 'import.meta.url': function(file){
66 | return JSON.stringify('file://' + file.replace(path.sep, '/'));
67 | }
68 | }
69 | }), typescript(), commonjs()]
70 | }
71 | ]
72 | }
73 |
74 | export default [
75 | ...build('TriVis'),
76 | ...test('test')
77 | ];
78 |
--------------------------------------------------------------------------------
/strain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kninnug/TriVis/00c84aad9426b8bdfdee54ec16d453306b0de194/strain.png
--------------------------------------------------------------------------------
/test.ts:
--------------------------------------------------------------------------------
1 | import tape from 'tape';
2 | import {orient2d} from 'robust-predicates';
3 | import Delaunator from 'delaunator';
4 | import Constrainautor from '@kninnug/constrainautor';
5 | import triangularExpansion from './TriVis';
6 | import {loadTests, findTest, TestFile} from './delaunaytests/loader';
7 |
8 | import type {Segment} from './TriVis';
9 | import type {Test} from 'tape';
10 | type P2 = [number, number];
11 | type TestCase = {
12 | file: string,
13 | query: P2 | [...P2, ...Segment],
14 | output: Segment[]
15 | }
16 |
17 | const EPSILON = 2**-50,
18 | testFiles = loadTests(false);
19 | const references: TestCase[] = [{
20 | file: 'amitexp.json',
21 | query: [563, 241],
22 | output: [
23 | [770, 270, 590, 270],
24 | [780, 188.03149606299212, 780, 271.4009661835749],
25 | [670, 210, 690, 210],
26 | [530, 210, 670, 210],
27 | [510, 210, 530, 210],
28 | [470, 190, 470, 186.60377358490567],
29 | [450, 190, 470, 190],
30 | [446, 190, 450, 190],
31 | [434, 190, 446, 190],
32 | [430, 190, 434, 190],
33 | [410, 190, 430, 190],
34 | [370, 270, 370, 176.66666666666669],
35 | [450, 270, 370, 270],
36 | [450, 290, 450, 270],
37 | [370, 346.60377358490564, 370, 324.69026548672565],
38 | [530, 270, 510, 270],
39 | [530, 430, 530, 270],
40 | [560, 430, 530, 430],
41 | [560, 450, 560, 430],
42 | [600.0745610164117, 780.0000000000011, 555.2631578947369, 779.9999999999975],
43 | [591.3911190204142, 590, 588.233031891709, 607.8446454055273],
44 | [597.3508893593265, 556.3245553203368, 591.3911190204142, 590],
45 | [602.570792255239, 538.3161194238622, 597.3508893593265, 556.3245553203368],
46 | [606.5835921350013, 524.4721359549995, 602.570792255239, 538.3161194238622],
47 | [614.9343910395271, 507.7705381459478, 606.5835921350013, 524.4721359549995],
48 | [617.0585914563812, 503.52213731223964, 614.9343910395271, 507.7705381459478],
49 | [604.4721359549997, 346.58359213500125, 586.5835921350013, 355.52786404500046],
50 | [646.180339887499, 430, 604.4721359549997, 346.58359213500125],
51 | [650, 430, 646.180339887499, 430],
52 | [780, 634.8148148148148, 780, 712.4137931034484],
53 | [590, 270, 590, 290]
54 | ]
55 | }, {
56 | file: 'amitexp.json',
57 | query: [530, 234],
58 | output: [
59 | [770, 270, 590, 270],
60 | [780, 196.5, 780, 271.5],
61 | [670, 210, 690, 210],
62 | [530, 210, 670, 210],
63 | [510, 210, 530, 210],
64 | [470, 170, 470, 162],
65 | [470, 190, 470, 170],
66 | [450, 190, 470, 190],
67 | [446, 190, 450, 190],
68 | [434, 190, 446, 190],
69 | [430, 190, 434, 190],
70 | [410, 190, 430, 190],
71 | [370, 270, 370, 175.33333333333334],
72 | [450, 270, 370, 270],
73 | [450, 290, 450, 270],
74 | [370, 450, 370, 346],
75 | [376.6666666666667, 510, 325.55555555555554, 510],
76 | [530, 270, 510, 270],
77 | [560, 430, 530, 430],
78 | [588.6075312369494, 612.5403531267184, 589.3339210354185, 621.6482840980675],
79 | [588.233031891709, 607.8446454055273, 588.6075312369494, 612.5403531267184],
80 | [591.3911190204142, 590, 588.233031891709, 607.8446454055273],
81 | [597.3508893593265, 556.3245553203368, 591.3911190204142, 590],
82 | [602.570792255239, 538.3161194238622, 597.3508893593265, 556.3245553203368],
83 | [606.5835921350013, 524.4721359549995, 602.570792255239, 538.3161194238622],
84 | [614.9343910395271, 507.7705381459478, 606.5835921350013, 524.4721359549995],
85 | [630, 477.63932022500194, 614.9343910395271, 507.7705381459478],
86 | [630, 448.7758024182177, 630, 477.63932022500194],
87 | [604.4721359549997, 346.58359213500125, 586.5835921350013, 355.52786404500046],
88 | [780, 467.33333333333337, 780, 611.9386433975478],
89 | [590, 270, 590, 290]
90 | ]
91 | }, {
92 | file: 'amitexp.json',
93 | query: [387, 479],
94 | output: [
95 | [430, 510, 290, 510],
96 | [430, 470, 430, 510],
97 | [510, 450, 525.5555555555555, 450],
98 | [510, 430, 510, 450],
99 | [510, 270, 510, 430],
100 | [530, 210, 545.311004784689, 210],
101 | [510, 210, 530, 210],
102 | [510, 190, 510, 210],
103 | [510, 110, 510, 190],
104 | [370, 290, 450, 290],
105 | [370, 450, 370, 290],
106 | [350, 450, 370, 450],
107 | [193.97028311431427, 345.57988645363065, 179.27433670388717, 316.1879936327764],
108 | [199.7103162846646, 357.0599527943313, 198.6824314212446, 348.83687388697126],
109 | [203.41640786499872, 364.47213595499954, 199.7103162846646, 357.0599527943313],
110 | [200.800343220057, 365.7801682774704, 203.41640786499872, 364.47213595499954],
111 | [203.7592105464341, 386.43506512139453, 200.800343220057, 365.7801682774704],
112 | [290, 510, 290, 430]
113 | ]
114 | }, {
115 | file: 'amitexp.json',
116 | query: [530, 233, 740.0107140124046, 496.75082938258225, 266.2491706174178, 443.0107140124046],
117 | output: [
118 | [370, 450, 370, 360.3994638069705],
119 | [380.27027027027026, 510, 325.7603686635945, 510],
120 | [530, 270, 510, 270],
121 | [560, 430, 530, 430],
122 | [588.6075312369494, 612.5403531267184, 589.4976524169139, 623.7012508710677],
123 | [588.233031891709, 607.8446454055273, 588.6075312369494, 612.5403531267184],
124 | [591.3911190204142, 590, 588.233031891709, 607.8446454055273],
125 | [597.3508893593265, 556.3245553203368, 591.3911190204142, 590],
126 | [602.570792255239, 538.3161194238622, 597.3508893593265, 556.3245553203368],
127 | [606.5835921350013, 524.4721359549995, 602.570792255239, 538.3161194238622],
128 | [614.9343910395271, 507.7705381459478, 606.5835921350013, 524.4721359549995],
129 | [630, 477.63932022500194, 614.9343910395271, 507.7705381459478],
130 | [630, 449.5430991950185, 630, 477.63932022500194],
131 | [604.4721359549997, 346.58359213500125, 586.5835921350013, 355.52786404500046],
132 | [780, 546.9730639730642, 780, 614.2956036457548]
133 | ]
134 | }, {
135 | file: 'amitp.json',
136 | query: [550, 536],
137 | output: [
138 | [620, 520, 610, 550],
139 | [640, 480, 620, 520],
140 | [640, 440, 640, 480],
141 | [600, 360, 640, 440],
142 | [600, 280, 622.7272727272727, 279.9999999999999],
143 | [550, 200, 615.625, 200],
144 | [520, 440, 550, 440],
145 | [423.19101123595505, 140, 426.25, 140],
146 | [420, 180, 436, 180],
147 | [323.984375, 10, 357.92134831460675, 10],
148 | [360, 280, 440, 280],
149 | [360, 439.27272727272725, 360, 280],
150 | [440, 520, 440, 480],
151 | [440, 536, 440, 520],
152 | [280, 545.8181818181819, 280, 536],
153 | [440, 556, 440, 540],
154 | [280, 594.9090909090909, 280, 585.0909090909091],
155 | [440, 576, 440, 560],
156 | [280, 644, 280, 634.1818181818182],
157 | [440, 596, 440, 580],
158 | [113.4375, 790, 84.33333333333331, 790],
159 | [440, 616, 440, 600],
160 | [217.3809523809524, 790, 200.75, 790],
161 | [440, 636, 440, 620],
162 | [281.34615384615387, 790, 270.6, 790],
163 | [440, 656, 440, 640],
164 | [324.67741935483866, 790, 317.16666666666663, 790],
165 | [440, 676, 440, 660],
166 | [355.9722222222223, 790, 350.42857142857144, 790],
167 | [440, 760, 440, 680],
168 | [672.9032258064516, 790, 425.26785714285717, 790],
169 | [600, 620, 610, 660],
170 | [600, 600, 600, 620],
171 | [610, 550, 600, 600]
172 | ]
173 | }, {
174 | file: 'amitp.json',
175 | query: [550, 536, 479.28932188134524, 536, 550, 465.28932188134524],
176 | output: [
177 | [520, 440, 550, 440],
178 | [423.19101123595505, 140, 426.25, 140],
179 | [420, 180, 436, 180],
180 | [323.984375, 10, 357.92134831460675, 10],
181 | [360, 280, 440, 280],
182 | [360, 439.27272727272725, 360, 280],
183 | [440, 520, 440, 480],
184 | [440, 536, 440, 520]
185 | ]
186 | }, {
187 | file: 'amitp.json',
188 | query: [332, 386, 380.7903679018718, 290.5405845398161, 427.4594154601839, 434.7903679018718],
189 | output: [[360, 331.2173913043478, 360, 400.31111111111113]]
190 | }, {
191 | file: 'amitp.json',
192 | query: [332, 386, 345.6256212499073, 264.21600086401304, 423.0689887208956, 304.0034190142456],
193 | output: [
194 | [360, 280, 360, 360.7893953820272],
195 | [343.8596520293065, 280.0000000000001, 360, 280]
196 | ]
197 | }, {
198 | file: 'ipa/mei-2.json',
199 | query: [459, 440],
200 | output: [
201 | [332, 462, 450, 388],
202 | [347, 536, 332, 462],
203 | [386, 639, 254.27586206896552, 615.4778325123152],
204 | [459, 602.8724489795918, 386, 639],
205 | [533, 411, 459, 514],
206 | [730.3329161451815, 331.46683354192743, 731.9184706332667, 333.04546421128737],
207 | [561, 313, 609, 380],
208 | [532, 134, 627.9989491583714, 229.57973977340035],
209 | [416.73751474636254, 195.81675186787243, 532, 134]
210 | ]
211 | }, {
212 | file: 'strain.json',
213 | query: [97, 156],
214 | output: [
215 | [5, 201, 53, 98],
216 | [194, 288, 5, 201],
217 | [294.9739572736521, 216.60427263479147, 194, 288],
218 | [413, 43, 146, 171],
219 | [278, 5, 413, 43],
220 | [196.30195510499638, 38.76852522326817, 278, 5],
221 | [53, 98, 196.30195510499638, 38.76852522326817]
222 | ]
223 | }, {
224 | file: 'strain.json',
225 | query: [330, 109],
226 | output: [
227 | [392, 148, 248.2189868368568, 249.66334264060632],
228 | [413, 43, 392, 148],
229 | [146, 171, 413, 43],
230 | [194, 288, 26.963427829474142, 211.11014931832938],
231 | [248.2189868368568, 249.66334264060632, 194, 288]
232 | ]
233 | }, {
234 | file: 'strain.json',
235 | query: [102, 192],
236 | output: [
237 | [194, 288, 5, 201],
238 | [324.46860547150584, 195.74947087873323, 194, 288],
239 | [392, 148, 324.46860547150584, 195.74947087873323],
240 | [412.8743718592965, 43.62814070351757, 392, 148],
241 | [413, 43, 146, 171],
242 | [278, 5, 413, 43],
243 | [184.0410117176336, 43.836381823378105, 278, 5],
244 | [53, 98, 184.0410117176336, 43.836381823378105],
245 | [5, 201, 53, 98]
246 | ]
247 | }, {
248 | file: 'strain.json',
249 | query: [146, 171],
250 | output: []
251 | }, {
252 | file: 'tri.json',
253 | query: [125, 118],
254 | output: []
255 | }, {
256 | file: 'ipa/matisse-nuit.json',
257 | query: [457, 249],
258 | output: [
259 | [453, 234, 460, 250],
260 | [401, 138, 453, 234],
261 | [371, 99, 375.6817975487971, 87.81570585565134],
262 | [344.61708394698087, 89.79086892488957, 371, 99],
263 | [445, 238, 445, 232],
264 | [440, 242, 445, 238],
265 | [421, 275, 440, 242],
266 | [396, 359, 421, 275],
267 | [377, 415, 396, 359],
268 | [362, 472, 377, 415],
269 | [345, 547, 362, 472],
270 | [299, 689, 293.8902272105119, 682.9885026006023],
271 | [309.54466230936816, 701.653594771242, 299, 689],
272 | [337, 637, 328, 645],
273 | [337.19060585432265, 637.0190605854323, 337, 637],
274 | [383, 492, 369, 534],
275 | [401, 446, 383, 492],
276 | [420, 398, 401, 446],
277 | [441, 333, 420, 398],
278 | [455, 281, 441, 333],
279 | [460, 250, 455, 281]
280 | ]
281 | }, {
282 | file: 'ipa/matisse-nuit.json',
283 | query: [352, 602],
284 | output: [
285 | [318, 610, 323, 595],
286 | [271, 623, 271.23023255813956, 621.0046511627907],
287 | [282, 669, 271, 623],
288 | [299, 689, 282, 669],
289 | [301.6601671309192, 692.1922005571031, 299, 689],
290 | [337, 637, 328, 645],
291 | [347, 638, 337, 637],
292 | [355, 647, 347, 638],
293 | [364, 685, 355, 647],
294 | [377, 759, 375.2477064220183, 762.7966360856267],
295 | [389, 749, 377, 759],
296 | [399, 762, 389, 749],
297 | [418, 768, 399, 762],
298 | [430.3447136563877, 757.3386563876652, 418, 768],
299 | [405, 704, 410, 717],
300 | [407, 684, 405, 704],
301 | [412.9230769230769, 672.1538461538462, 407, 684],
302 | [379, 633, 385, 640],
303 | [380, 614, 379, 633],
304 | [395, 576, 380, 614],
305 | [428, 509, 395, 576],
306 | [455, 461, 428, 509],
307 | [500, 378, 455, 461],
308 | [510.28985507246375, 355.36231884057975, 500, 378],
309 | [394.2061855670103, 535.9381443298969, 395, 535],
310 | [365, 551, 375, 566],
311 | [369, 534, 365, 551],
312 | [440, 242, 442.5, 240],
313 | [421, 275, 440, 242],
314 | [396, 359, 421, 275],
315 | [377, 415, 396, 359],
316 | [362, 472, 377, 415],
317 | [345, 547, 362, 472],
318 | [336, 550, 345, 547],
319 | [323, 527, 336, 550],
320 | [302, 520, 305.3826429980276, 481.43786982248525],
321 | [319, 571, 302, 520],
322 | [323, 595, 319, 571]
323 | ]
324 | }, {
325 | file: 'ipa/seidel-3.json',
326 | query: [256, 352],
327 | output: [
328 | [175, 282, 247, 315],
329 | [112, 746, 114.77940300483155, 229.9575087696075],
330 | [198, 657, 112, 746],
331 | [285, 780, 198, 657],
332 | [294, 490, 285, 780],
333 | [384, 583, 294, 490],
334 | [430, 650, 384, 583],
335 | [513, 540, 430, 650],
336 | [616, 516, 582.9042881165919, 591.1362107623319],
337 | [707, 287, 616, 516],
338 | [579, 216, 707, 287],
339 | [594, 101, 579, 216],
340 | [506, 155, 594, 101],
341 | [505, 17, 506, 155],
342 | [451.89786812097174, 70.79176995537927, 505, 17],
343 | [247, 315, 318, 263]
344 | ]
345 | }, {
346 | file: 'ipa/seidel-3.json',
347 | query: [256, 352, 413.2187408195795, 485.0122833993383, 234.99260397048673, 556.8626108201768],
348 | output: [
349 | [285, 780, 221.34034829332205, 689.9984234491795],
350 | [294, 490, 285, 780],
351 | [384, 583, 294, 490],
352 | [430, 650, 384, 583],
353 | [499.44587113039216, 557.9633033211669, 430, 650]
354 | ]
355 | }, {
356 | file: 'ipa/seidel-3.json',
357 | query: [256, 352, 355.5263016439092, 94.86284733417926, 524.4212271396464, 288.9520434839767],
358 | output: [
359 | [579, 216, 655.1581590242263, 258.2439788337506],
360 | [594, 101, 579, 216],
361 | [506, 155, 594, 101],
362 | [505, 17, 506, 155],
363 | [451.89786812097174, 70.79176995537927, 505, 17],
364 | [279.5475259828696, 291.16237533648984, 318, 263]
365 | ]
366 | }, {
367 | file: 'ipa/seidel-3.json',
368 | query: [256, 352, 182.2758425261011, 355.8403912253514, 211.2025977215036, 293.3212751578686],
369 | output: [
370 | [175, 282, 217.393275160481, 301.43025111522047],
371 | [114.08226559954085, 359.3926870185832, 114.77940300483158, 229.95750876960753]
372 | ]
373 | }];
374 |
375 | function sqdist(x1: number, y1: number, x2: number, y2: number){
376 | const dx = x2 - x1,
377 | dy = y2 - y1;
378 |
379 | return dx * dx + dy * dy;
380 | }
381 |
382 | function validatePoly(t: Test, qx: number, qy: number, poly: Segment[]){
383 | const len = poly.length;
384 | if(!len){
385 | return;
386 | }
387 |
388 | let [plx, ply, prx, pry] = poly[0],
389 | failed = false;
390 | for(let i = 1; i < len; i++){
391 | const [lx, ly, rx, ry] = poly[i],
392 | oSeg = orient2d(qx, qy, rx, ry, lx, ly),
393 | oPrev = orient2d(qx, qy, prx, pry, rx, ry);
394 | if(oSeg < 0){
395 | t.fail(`segment ${i} [${lx}, ${ly}, ${rx}, ${ry}] not oriented left-right (${oSeg})`);
396 | failed = true;
397 | }
398 | if(oPrev < 0){
399 | t.fail(`segment ${i}: [${lx}, ${ly}, ${rx}, ${ry}] not left of previous: [${plx}, ${ply}, ${prx}, ${pry}] (${oPrev})`);
400 | failed = true;
401 | }
402 | ([plx, ply, prx, pry] = [lx, ly, rx, ry]);
403 | }
404 |
405 | t.assert(!failed, `segments oriented counter-clockwise`);
406 | return failed;
407 | }
408 |
409 | function compareSegs(p: Segment, r: Segment){
410 | const dl = sqdist(p[0], p[1], r[0], r[1]),
411 | dr = sqdist(p[2], p[3], r[2], r[3]);
412 | return Math.max(dl, dr);
413 | }
414 |
415 | function comparePolys(t: Test, poly: Segment[], ref: Segment[]){
416 | if(poly.length !== ref.length){
417 | t.fail(`wrong number of segments: ${poly.length} !== ${ref.length}`);
418 | return true;
419 | }
420 |
421 | const len = poly.length;
422 | if(!len){
423 | t.pass(`empty polygon as expected`);
424 | return false;
425 | }
426 |
427 | // Find the first segment, as it may be shifted from the reference. As long
428 | // as all the segments are present, we're fine.
429 | let start = 0;
430 | for(; start < len; start++){
431 | const p = poly[start],
432 | r = ref[0],
433 | err = compareSegs(p, r);
434 | if(err <= EPSILON){
435 | break;
436 | }
437 | }
438 |
439 | if(start === len){
440 | t.fail(`starting segment not found`);
441 | return true;
442 | }
443 |
444 | let maxErr = 0,
445 | failed = false;
446 | for(let i = 0, j = start; i < len; i++, j++){
447 | const p = poly[j % len],
448 | r = ref[i],
449 | err = compareSegs(p, r);
450 | if(err > EPSILON){
451 | t.fail(`segment ${i} not equal to reference`);
452 | failed = true;
453 | }
454 | maxErr = Math.max(maxErr, err);
455 | }
456 |
457 | t.assert(!failed, `visiblity polygon equal to reference, maximum error: ${maxErr}`);
458 | return failed;
459 | }
460 |
461 | function testExample(t: Test){
462 | // Points to be triangulated
463 | const points = [[53,98],[5,201],[194,288],[280,195],[392,148],[413,43],[278,5],[169,71],[146,171]],
464 | // Edges to be constrained
465 | edges: P2[] = [[5, 8]],
466 | // Triangulate
467 | del = Delaunator.from(points),
468 | // Constrain the triangulation
469 | con = new Constrainautor(del);
470 | con.constrainAll(edges);
471 |
472 | // Query point
473 | const qx = 162, qy = 262,
474 | // Obstruction callback: use constrained edges as obstructions
475 | obstructs = (edg: number) => con.isConstrained(edg),
476 | // Left & right end-points of the initial viewing cone (optional)
477 | ilx = 45, ily = 144, irx = 280, iry = 145,
478 | // Compute visibility polygon
479 | poly = triangularExpansion(del, qx, qy, obstructs, ilx, ily, irx, iry);
480 |
481 | validatePoly(t, qx, qy, poly);
482 | comparePolys(t, poly, [
483 | [146, 171, 354.6687325689495, 70.96405330027889],
484 | [53, 98, 127.73364294495288, 67.11009424941949],
485 | [35.85927180355631, 134.78114592153543, 53, 98]
486 | ]);
487 | t.end();
488 | }
489 |
490 | function testFile(t: Test, ref: TestCase, test: TestFile){
491 | const {points, edges, name} = test,
492 | {file, query: [qx, qy, ilx = NaN, ily = NaN, irx = NaN, iry = NaN], output} = ref,
493 | del = Delaunator.from(points),
494 | con = new Constrainautor(del);
495 | con.constrainAll(edges);
496 |
497 | const vis = triangularExpansion(del, qx, qy,
498 | edg => con.isConstrained(edg), ilx, ily, irx, iry);
499 |
500 | validatePoly(t, qx, qy, vis);
501 | comparePolys(t, vis, output);
502 |
503 | t.end();
504 | }
505 |
506 | function main(args: string[]){
507 | if(!args.length){
508 | tape("Example", testExample);
509 | }
510 |
511 | for(const ref of references){
512 | tape(ref.file, (t) => testFile(t, ref, findTest(testFiles, ref.file)!));
513 | }
514 | }
515 |
516 | main(process.argv.slice(2));
517 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Basic Options */
4 | "target": "es2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
5 | "module": "es2020", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
6 | // "lib": [], /* Specify library files to be included in the compilation. */
7 | // "allowJs": true, /* Allow javascript files to be compiled. */
8 | // "checkJs": true, /* Report errors in .js files. */
9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
10 | "declaration": false, /* Generates corresponding '.d.ts' file. */
11 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
12 | // "sourceMap": true, /* Generates corresponding '.map' file. */
13 | // "outFile": "./", /* Concatenate and emit output to single file. */
14 | // "outDir": "./", /* Redirect output structure to the directory. */
15 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
16 | // "composite": true, /* Enable project compilation */
17 | // "removeComments": true, /* Do not emit comments to output. */
18 | // "noEmit": true, /* Do not emit outputs. */
19 | "importHelpers": false, /* Import emit helpers from 'tslib'. */
20 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
21 | "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
22 |
23 | /* Strict Type-Checking Options */
24 | "strict": true, /* Enable all strict type-checking options. */
25 | "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
26 | "strictNullChecks": true, /* Enable strict null checks. */
27 | "strictFunctionTypes": true, /* Enable strict checking of function types. */
28 | "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
29 | "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
30 | "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
31 | "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
32 |
33 | /* Additional Checks */
34 | // "noUnusedLocals": true, /* Report errors on unused locals. */
35 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
36 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
37 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
38 |
39 | /* Module Resolution Options */
40 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
41 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
42 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
43 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
44 | // "typeRoots": [], /* List of folders to include type definitions from. */
45 | // "types": [], /* Type declaration files to be included in compilation. */
46 | "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
47 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
48 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
49 |
50 | /* Source Map Options */
51 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
52 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
53 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
54 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
55 |
56 | /* Experimental Options */
57 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
58 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
59 | }
60 | }
--------------------------------------------------------------------------------