@woosh/meep-engine 2.77.0 → 2.78.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/meep.cjs +41 -2
- package/build/meep.min.js +1 -1
- package/build/meep.module.js +41 -2
- package/package.json +1 -1
- package/src/core/graph/coloring/colorizeGraphGreedy.spec.js +1 -1
- package/src/core/graph/layout/CircleLayout.js +6 -11
- package/src/core/graph/v2/Graph.js +23 -0
- package/src/core/graph/v2/NodeContainer.js +18 -2
- package/src/core/graph/Graph.js +0 -565
package/src/core/graph/Graph.js
DELETED
|
@@ -1,565 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Created by Alex on 29/01/14.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import Signal from "../events/signal/Signal.js";
|
|
7
|
-
import { Edge, EdgeDirectionType } from "./Edge.js";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @callback Graph~visitor
|
|
11
|
-
* @param {*} node
|
|
12
|
-
* @param {Edge} edge
|
|
13
|
-
* @returns {boolean|undefined} if false is returned, traversal should stop
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* @template N
|
|
18
|
-
* @deprecated use v2 `Graph` class instead, it's faster and has a wider range of algorithms built on top of it
|
|
19
|
-
*/
|
|
20
|
-
export class Graph {
|
|
21
|
-
/**
|
|
22
|
-
* @template N
|
|
23
|
-
* @constructor
|
|
24
|
-
*/
|
|
25
|
-
constructor() {
|
|
26
|
-
/**
|
|
27
|
-
* @private
|
|
28
|
-
* @type {N[]}
|
|
29
|
-
*/
|
|
30
|
-
this.__nodes = [];
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Accelerated data structure for faster lookups
|
|
34
|
-
* @type {Set<any>}
|
|
35
|
-
* @private
|
|
36
|
-
*/
|
|
37
|
-
this.__nodes_set = new Set();
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* @private
|
|
41
|
-
* @type {Edge<N>[]}
|
|
42
|
-
*/
|
|
43
|
-
this.__edges = [];
|
|
44
|
-
this.onChange = new Signal();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
*
|
|
49
|
-
* @returns {N[]}
|
|
50
|
-
*/
|
|
51
|
-
get nodes() {
|
|
52
|
-
return this.__nodes;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
*
|
|
57
|
-
* @returns {Edge<N>[]}
|
|
58
|
-
*/
|
|
59
|
-
get edges() {
|
|
60
|
-
return this.__edges;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Converts this graph into a shallow copy of supplied graph
|
|
65
|
-
* @param {Graph<N>} other
|
|
66
|
-
*/
|
|
67
|
-
copy(other) {
|
|
68
|
-
this.clear();
|
|
69
|
-
|
|
70
|
-
this.__nodes = other.__nodes.slice();
|
|
71
|
-
this.__edges = other.__edges.slice();
|
|
72
|
-
|
|
73
|
-
this.__nodes_set = new Set(this.__nodes);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
*
|
|
78
|
-
* @param {N} start
|
|
79
|
-
* @param {N} goal
|
|
80
|
-
* @returns {Array<N>|null} nodes from start to goal in the shortest path including both start and goal.
|
|
81
|
-
*/
|
|
82
|
-
findPath(start, goal) {
|
|
83
|
-
const open = new Set();
|
|
84
|
-
open.add(start);
|
|
85
|
-
|
|
86
|
-
const closed = new Set();
|
|
87
|
-
|
|
88
|
-
const cameFrom = new Map();
|
|
89
|
-
|
|
90
|
-
function constructPath() {
|
|
91
|
-
const result = [];
|
|
92
|
-
let c = goal;
|
|
93
|
-
do {
|
|
94
|
-
result.unshift(c);
|
|
95
|
-
c = cameFrom.get(c);
|
|
96
|
-
} while (c !== undefined);
|
|
97
|
-
|
|
98
|
-
return result;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const graph = this;
|
|
102
|
-
|
|
103
|
-
function expandNode(current) {
|
|
104
|
-
graph.traverseSuccessors(current, function (node, edge) {
|
|
105
|
-
if (closed.has(node)) {
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
if (open.has(node)) {
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
open.add(node);
|
|
112
|
-
cameFrom.set(node, current);
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
while (open.size > 0) {
|
|
117
|
-
const current = open.values().next().value;
|
|
118
|
-
if (current === goal) {
|
|
119
|
-
//reached the goal
|
|
120
|
-
return constructPath();
|
|
121
|
-
}
|
|
122
|
-
open.delete(current);
|
|
123
|
-
closed.add(current);
|
|
124
|
-
|
|
125
|
-
//expand node
|
|
126
|
-
expandNode(current);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
//no path found
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Returns true if there is an edge between two given nodes on this graph
|
|
135
|
-
* @param {N} node1
|
|
136
|
-
* @param {N} node2
|
|
137
|
-
* @returns {boolean}
|
|
138
|
-
*/
|
|
139
|
-
isEdgeBetween(node1, node2) {
|
|
140
|
-
if (!this.containsNode(node1) || !this.containsNode(node2)) {
|
|
141
|
-
return false; // one or both nodes are not part of the graph
|
|
142
|
-
}
|
|
143
|
-
const connectingEdge = this.findConnectingEdge(node1, node2);
|
|
144
|
-
return connectingEdge !== null;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Strictly traversable edge exists from source to target
|
|
149
|
-
* @param {N} source
|
|
150
|
-
* @param {N} target
|
|
151
|
-
* @returns {boolean}
|
|
152
|
-
*/
|
|
153
|
-
edgeExists(source, target) {
|
|
154
|
-
if (!this.containsNode(source) || !this.containsNode(target)) {
|
|
155
|
-
return false; // one or both nodes are not part of the graph
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return this.traversePredecessors(source, function (destination) {
|
|
159
|
-
if (destination === target) {
|
|
160
|
-
//terminate traversal, this will make "traversePredecessors" return true also
|
|
161
|
-
return false;
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
*
|
|
168
|
-
* @param {function(node:N):boolean} visitor
|
|
169
|
-
* @param {*} [thisArg]
|
|
170
|
-
*/
|
|
171
|
-
traverseNodes(visitor, thisArg) {
|
|
172
|
-
const nodes = this.__nodes;
|
|
173
|
-
const l = nodes.length;
|
|
174
|
-
for (let i = 0; i < l; i++) {
|
|
175
|
-
const node = nodes[i];
|
|
176
|
-
if (visitor.call(thisArg, node) === false) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
*
|
|
184
|
-
* @param {N} node
|
|
185
|
-
* @param {function(node:N, edge:Edge<N>):(boolean|void)} visitor
|
|
186
|
-
*/
|
|
187
|
-
traverseSuccessors(node, visitor) {
|
|
188
|
-
const edges = this.__edges;
|
|
189
|
-
let i = 0;
|
|
190
|
-
const l = edges.length;
|
|
191
|
-
|
|
192
|
-
for (; i < l; i++) {
|
|
193
|
-
const edge = edges[i];
|
|
194
|
-
const first = edge.first;
|
|
195
|
-
const second = edge.second;
|
|
196
|
-
|
|
197
|
-
if (first === node && edge.traversableForward()) {
|
|
198
|
-
if (visitor(second, edge) === false) {
|
|
199
|
-
//terminate traversal if visitor returns false
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
} else if (second === node && edge.traversableBackward()) {
|
|
203
|
-
if (visitor(first, edge) === false) {
|
|
204
|
-
//terminate traversal if visitor returns false
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
*
|
|
214
|
-
* @param {function(edge:Edge<N>):boolean} visitor
|
|
215
|
-
*/
|
|
216
|
-
traverseEdges(visitor) {
|
|
217
|
-
const edges = this.__edges;
|
|
218
|
-
let i = 0;
|
|
219
|
-
const l = edges.length;
|
|
220
|
-
for (; i < l; i++) {
|
|
221
|
-
const edge = edges[i];
|
|
222
|
-
if (visitor(edge) === false) {
|
|
223
|
-
//terminate traversal if visitor returns false
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
*
|
|
231
|
-
* @param {N} node
|
|
232
|
-
* @param {function(N,Edge<N>)} visitor
|
|
233
|
-
*/
|
|
234
|
-
traversePredecessors(node, visitor) {
|
|
235
|
-
const edges = this.__edges;
|
|
236
|
-
let i = 0;
|
|
237
|
-
const l = edges.length;
|
|
238
|
-
for (; i < l; i++) {
|
|
239
|
-
const edge = edges[i];
|
|
240
|
-
const first = edge.first;
|
|
241
|
-
const second = edge.second;
|
|
242
|
-
|
|
243
|
-
if (second === node && edge.traversableForward()) {
|
|
244
|
-
if (visitor(first, edge) === false) {
|
|
245
|
-
//terminate traversal if visitor returns false
|
|
246
|
-
return true;
|
|
247
|
-
}
|
|
248
|
-
} else if (first === node && edge.traversableBackward()) {
|
|
249
|
-
if (visitor(second, edge) === false) {
|
|
250
|
-
//terminate traversal if visitor returns false
|
|
251
|
-
return true;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
return false;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
*
|
|
260
|
-
* @param {N} node
|
|
261
|
-
* @param {function(N,Edge):(boolean|void)} visitor
|
|
262
|
-
*/
|
|
263
|
-
traverseAttachedEdges(node, visitor) {
|
|
264
|
-
const edges = this.__edges;
|
|
265
|
-
let i = 0;
|
|
266
|
-
const l = edges.length;
|
|
267
|
-
for (; i < l; i++) {
|
|
268
|
-
const edge = edges[i];
|
|
269
|
-
const first = edge.first;
|
|
270
|
-
const second = edge.second;
|
|
271
|
-
|
|
272
|
-
if (first === node) {
|
|
273
|
-
if (visitor(second, edge) === false) {
|
|
274
|
-
//terminate traversal if visitor returns false
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
} else if (second === node) {
|
|
278
|
-
if (visitor(first, edge) === false) {
|
|
279
|
-
//terminate traversal if visitor returns false
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
*
|
|
288
|
-
* @param {N} source
|
|
289
|
-
* @param {N} target
|
|
290
|
-
* @return {Edge<N>|null}
|
|
291
|
-
*/
|
|
292
|
-
findTraversableEdge(source, target) {
|
|
293
|
-
|
|
294
|
-
const edges = this.__edges;
|
|
295
|
-
|
|
296
|
-
const numEdges = edges.length;
|
|
297
|
-
|
|
298
|
-
for (let i = 0; i < numEdges; i++) {
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
*
|
|
302
|
-
* @type {Edge<N>}
|
|
303
|
-
*/
|
|
304
|
-
const edge = edges[i];
|
|
305
|
-
|
|
306
|
-
if (
|
|
307
|
-
(edge.first === source && edge.second === target && edge.direction !== EdgeDirectionType.Backward)
|
|
308
|
-
|| (edge.second === source && edge.first === target && edge.direction !== EdgeDirectionType.Forward)
|
|
309
|
-
) {
|
|
310
|
-
return edge;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
return null;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
*
|
|
319
|
-
* @param {N} node1
|
|
320
|
-
* @param {N} node2
|
|
321
|
-
* @returns {Edge<N>|null}
|
|
322
|
-
*/
|
|
323
|
-
findConnectingEdge(node1, node2) {
|
|
324
|
-
const edges = this.__edges;
|
|
325
|
-
|
|
326
|
-
const numEdges = edges.length;
|
|
327
|
-
|
|
328
|
-
for (let i = 0; i < numEdges; i++) {
|
|
329
|
-
|
|
330
|
-
const edge = edges[i];
|
|
331
|
-
|
|
332
|
-
if (edge.contains(node1) && edge.contains(node2)) {
|
|
333
|
-
return edge;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
return null;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
*
|
|
343
|
-
* @param {N} source
|
|
344
|
-
* @param {N} target
|
|
345
|
-
* @param {function(Edge<N>):boolean} visitor
|
|
346
|
-
*/
|
|
347
|
-
findTraversableEdges(source, target, visitor) {
|
|
348
|
-
const edges = this.__edges;
|
|
349
|
-
for (let i = 0; i < edges.length; i++) {
|
|
350
|
-
const edge = edges[i];
|
|
351
|
-
if (edge.validateTransition(source, target)) {
|
|
352
|
-
|
|
353
|
-
if (visitor(edge) === false) {
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
*
|
|
362
|
-
* @param {N} node
|
|
363
|
-
* @returns {Edge<N>[]}
|
|
364
|
-
*/
|
|
365
|
-
getAttachedEdges(node) {
|
|
366
|
-
let i = 0;
|
|
367
|
-
|
|
368
|
-
const result = [];
|
|
369
|
-
|
|
370
|
-
const edges = this.__edges;
|
|
371
|
-
const l = edges.length;
|
|
372
|
-
|
|
373
|
-
for (; i < l; i++) {
|
|
374
|
-
const edge = edges[i];
|
|
375
|
-
|
|
376
|
-
if (edge.contains(node)) {
|
|
377
|
-
result.push(edge);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
return result;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
*
|
|
386
|
-
* @param {N} node
|
|
387
|
-
* @returns {N[]}
|
|
388
|
-
*/
|
|
389
|
-
getNeighbours(node) {
|
|
390
|
-
const result = [];
|
|
391
|
-
|
|
392
|
-
const edges = this.edges;
|
|
393
|
-
const nEdges = edges.length;
|
|
394
|
-
|
|
395
|
-
for (let i = 0; i < nEdges; i++) {
|
|
396
|
-
const edge = edges[i];
|
|
397
|
-
|
|
398
|
-
const first = edge.first;
|
|
399
|
-
const second = edge.second;
|
|
400
|
-
|
|
401
|
-
if (first === node && (edge.direction === EdgeDirectionType.Forward || edge.direction === EdgeDirectionType.Undirected)) {
|
|
402
|
-
if (result.indexOf(second) === -1) {
|
|
403
|
-
result.push(second);
|
|
404
|
-
}
|
|
405
|
-
} else if (second === node && (edge.direction === EdgeDirectionType.Backward || edge.direction === EdgeDirectionType.Undirected)) {
|
|
406
|
-
if (result.indexOf(first) === -1) {
|
|
407
|
-
result.push(first);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
return result;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
*
|
|
418
|
-
* @param {N} node
|
|
419
|
-
* @returns {boolean}
|
|
420
|
-
*/
|
|
421
|
-
containsNode(node) {
|
|
422
|
-
return this.__nodes_set.has(node);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* @deprecated
|
|
427
|
-
* @returns {number}
|
|
428
|
-
*/
|
|
429
|
-
length() {
|
|
430
|
-
const edges = this.__edges;
|
|
431
|
-
let result = 0;
|
|
432
|
-
for (let i = 0; i < edges.length; i++) {
|
|
433
|
-
const edge = edges[i];
|
|
434
|
-
result += edge.length;
|
|
435
|
-
}
|
|
436
|
-
return result;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
/**
|
|
440
|
-
*
|
|
441
|
-
* @param {N} node
|
|
442
|
-
*/
|
|
443
|
-
addNode(node) {
|
|
444
|
-
|
|
445
|
-
this.__nodes_set.add(node);
|
|
446
|
-
|
|
447
|
-
this.__nodes.push(node);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
*
|
|
452
|
-
* @param {N} node
|
|
453
|
-
* @returns {boolean}
|
|
454
|
-
*/
|
|
455
|
-
removeNode(node) {
|
|
456
|
-
if (!this.containsNode(node)) {
|
|
457
|
-
return false;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
const i = this.__nodes.indexOf(node);
|
|
461
|
-
|
|
462
|
-
if (i === -1) {
|
|
463
|
-
// this should never happen
|
|
464
|
-
return false;
|
|
465
|
-
} else {
|
|
466
|
-
//remove attached connections
|
|
467
|
-
const attachedEdges = this.getAttachedEdges(node);
|
|
468
|
-
|
|
469
|
-
const n = attachedEdges.length;
|
|
470
|
-
|
|
471
|
-
for (let j = 0; j < n; j++) {
|
|
472
|
-
const edge = attachedEdges[j];
|
|
473
|
-
|
|
474
|
-
this.removeEdge(edge);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
this.__nodes.splice(i, 1);
|
|
478
|
-
this.__nodes_set.delete(node);
|
|
479
|
-
|
|
480
|
-
return true;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
/**
|
|
485
|
-
* Whether or not the graph contains given node
|
|
486
|
-
* NOTE: same as {@link #containsNode}
|
|
487
|
-
* @param {N} node
|
|
488
|
-
* @returns {boolean}
|
|
489
|
-
*/
|
|
490
|
-
hasNode(node) {
|
|
491
|
-
return this.containsNode(node);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
/**
|
|
495
|
-
*
|
|
496
|
-
* @param {Edge<N>} edge
|
|
497
|
-
*/
|
|
498
|
-
addEdge(edge) {
|
|
499
|
-
//assert.ok(this.containsNode(edge.first), `Node Edge.first(=${edge.first}) is not present in the graph`);
|
|
500
|
-
//assert.ok(this.containsNode(edge.second), `Node Edge.second(=${edge.second}) is not present in the graph`);
|
|
501
|
-
|
|
502
|
-
this.__edges.push(edge);
|
|
503
|
-
this.onChange.send1(edge);
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
*
|
|
508
|
-
* @param {N} source
|
|
509
|
-
* @param {N} target
|
|
510
|
-
* @param {EdgeDirectionType} [direction]
|
|
511
|
-
* @returns {Edge<N>}
|
|
512
|
-
*/
|
|
513
|
-
createEdge(source, target, direction = EdgeDirectionType.Undirected) {
|
|
514
|
-
const edge = new Edge(source, target);
|
|
515
|
-
|
|
516
|
-
edge.direction = direction;
|
|
517
|
-
|
|
518
|
-
this.addEdge(edge);
|
|
519
|
-
|
|
520
|
-
return edge;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
/**
|
|
524
|
-
*
|
|
525
|
-
* @param {Edge<N>} edge
|
|
526
|
-
* @returns {boolean}
|
|
527
|
-
*/
|
|
528
|
-
removeEdge(edge) {
|
|
529
|
-
const edges = this.__edges;
|
|
530
|
-
const indexOf = edges.indexOf(edge);
|
|
531
|
-
if (indexOf >= 0) {
|
|
532
|
-
edges.splice(indexOf, 1);
|
|
533
|
-
this.onChange.send1(edge);
|
|
534
|
-
|
|
535
|
-
return true;
|
|
536
|
-
} else {
|
|
537
|
-
console.error("Edge was not found");
|
|
538
|
-
|
|
539
|
-
return false;
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
clear() {
|
|
544
|
-
this.__edges = [];
|
|
545
|
-
this.__nodes = [];
|
|
546
|
-
this.__nodes_set.clear();
|
|
547
|
-
|
|
548
|
-
this.onChange.send0();
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
/**
|
|
552
|
-
*
|
|
553
|
-
* @returns {Graph<N>}
|
|
554
|
-
*/
|
|
555
|
-
clone() {
|
|
556
|
-
const graph = new Graph();
|
|
557
|
-
|
|
558
|
-
graph.copy(this);
|
|
559
|
-
|
|
560
|
-
return graph;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
export default Graph;
|