@woosh/meep-engine 2.76.3 → 2.77.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 +202 -621
- package/build/meep.min.js +1 -1
- package/build/meep.module.js +202 -621
- package/editor/view/ecs/components/TerrainController.js +9 -16
- package/package.json +1 -1
- package/src/core/collection/heap/Uint32Heap.js +10 -1
- package/src/core/graph/Edge.js +20 -0
- package/src/core/graph/Graph.js +1 -0
- package/src/core/graph/SquareMatrix.js +4 -2
- package/src/core/graph/WeightedEdge.js +5 -9
- package/src/core/graph/coloring/validateGraphColoring.js +1 -1
- package/src/core/graph/eigen/matrix_eigenvalues_in_place.js +21 -0
- package/src/core/graph/eigen/{eigen.spec.js → matrix_eigenvalues_in_place.spec.js} +2 -2
- package/src/core/graph/eigen/matrix_householder_in_place.js +92 -0
- package/src/core/graph/eigen/{eigen.js → matrix_qr_in_place.js} +2 -113
- package/src/core/graph/v2/Graph.js +16 -9
- package/src/core/graph/v2/NodeContainer.js +120 -22
- package/src/engine/ecs/storage/binary/BinarySerializationRegistry.js +8 -6
- package/src/engine/graphics/particles/node-based/codegen/modules/FunctionModuleRegistry.js +1 -1
- package/src/engine/navigation/grid/find_path_on_grid_astar.js +30 -27
- package/src/engine/navigation/grid/find_path_on_grid_astar.spec.js +2 -2
- package/src/generation/grid/generation/road/GridTaskGenerateRoads.js +17 -33
- package/src/core/graph/GraphUtils.js +0 -284
- package/src/engine/ecs/terrain/ecs/splat/SplatMapMaterialPatch.js +0 -464
- package/src/engine/ecs/terrain/ecs/splat/SplatMapOptimizer.js +0 -622
- package/src/engine/ecs/terrain/ecs/splat/SplatMapOptimizerDebugger.js +0 -383
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assert } from "../../../../core/assert.js";
|
|
2
|
-
import { Graph } from "../../../../core/graph/Graph.js";
|
|
2
|
+
import { Graph } from "../../../../core/graph/v2/Graph.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Contains serializers for various data types as well as data upgraders which enable support for serialization format changes
|
|
@@ -39,25 +39,27 @@ export class BinarySerializationRegistry {
|
|
|
39
39
|
assert.defined(adapter, 'adapter');
|
|
40
40
|
assert.ok(adapter.isBinaryClassSerializationAdapter, 'adapter is not a BinaryClassSerializationAdapter');
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
let _className = className;
|
|
43
|
+
|
|
44
|
+
if (_className === undefined) {
|
|
43
45
|
const klass = adapter.getClass();
|
|
44
46
|
|
|
45
47
|
if (typeof klass.typeName === "string") {
|
|
46
|
-
|
|
48
|
+
_className = klass.typeName;
|
|
47
49
|
} else {
|
|
48
50
|
throw new Error(`className not specified, could not infer class name from the class itself`);
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
if (this.serializers.has(
|
|
54
|
+
if (this.serializers.has(_className)) {
|
|
53
55
|
//a serializer already exists
|
|
54
56
|
|
|
55
|
-
console.warn(`Serializer for class '${
|
|
57
|
+
console.warn(`Serializer for class '${_className}' already exists, ignoring request`);
|
|
56
58
|
|
|
57
59
|
return false;
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
this.serializers.set(
|
|
62
|
+
this.serializers.set(_className, adapter);
|
|
61
63
|
|
|
62
64
|
return true;
|
|
63
65
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { assert } from "../../../../../../core/assert.js";
|
|
2
|
-
import Graph from "../../../../../../core/graph/Graph.js";
|
|
3
2
|
import { Edge, EdgeDirectionType } from "../../../../../../core/graph/Edge.js";
|
|
3
|
+
import { Graph } from "../../../../../../core/graph/v2/Graph.js";
|
|
4
4
|
import { FunctionModuleReference } from "./FunctionModuleReference.js";
|
|
5
5
|
|
|
6
6
|
export class FunctionModuleRegistry {
|
|
@@ -26,7 +26,7 @@ function index2point(result, index, width) {
|
|
|
26
26
|
* @param {number} height
|
|
27
27
|
* @returns {number}
|
|
28
28
|
*/
|
|
29
|
-
function
|
|
29
|
+
function compute_neighbors(result, index, width, height) {
|
|
30
30
|
const x = index % width;
|
|
31
31
|
const y = (index / width) | 0;
|
|
32
32
|
|
|
@@ -36,14 +36,17 @@ function computeNeighbors(result, index, width, height) {
|
|
|
36
36
|
result[neighbour_count] = index - width;
|
|
37
37
|
neighbour_count++
|
|
38
38
|
}
|
|
39
|
+
|
|
39
40
|
if (x > 0) {
|
|
40
41
|
result[neighbour_count] = index - 1;
|
|
41
42
|
neighbour_count++;
|
|
42
43
|
}
|
|
44
|
+
|
|
43
45
|
if (x < width - 1) {
|
|
44
46
|
result[neighbour_count] = index + 1;
|
|
45
47
|
neighbour_count++;
|
|
46
48
|
}
|
|
49
|
+
|
|
47
50
|
if (y < height - 1) {
|
|
48
51
|
result[neighbour_count] = index + width;
|
|
49
52
|
neighbour_count++
|
|
@@ -60,7 +63,7 @@ function computeNeighbors(result, index, width, height) {
|
|
|
60
63
|
* @param {number} height
|
|
61
64
|
* @returns {number[]}
|
|
62
65
|
*/
|
|
63
|
-
function
|
|
66
|
+
function compute_path(node, g_score, width, height) {
|
|
64
67
|
let v_previous = new Vector2(-1, -1);
|
|
65
68
|
let v_current = new Vector2();
|
|
66
69
|
|
|
@@ -111,7 +114,7 @@ function pathTo(node, g_score, width, height) {
|
|
|
111
114
|
v_current = t;
|
|
112
115
|
|
|
113
116
|
// find next point
|
|
114
|
-
const neighbour_count =
|
|
117
|
+
const neighbour_count = compute_neighbors(neighbors, current, width, height);
|
|
115
118
|
|
|
116
119
|
current = -1;
|
|
117
120
|
|
|
@@ -166,15 +169,15 @@ function heuristic(index0, index1, width) {
|
|
|
166
169
|
const x1 = index0 % width;
|
|
167
170
|
const y1 = (index0 / width) | 0;
|
|
168
171
|
|
|
169
|
-
//
|
|
170
172
|
const x2 = index1 % width;
|
|
171
173
|
const y2 = (index1 / width) | 0;
|
|
172
174
|
|
|
173
|
-
//
|
|
174
|
-
const dx =
|
|
175
|
-
const dy =
|
|
175
|
+
// deltas
|
|
176
|
+
const dx = x2 - x1;
|
|
177
|
+
const dy = y2 - y1;
|
|
176
178
|
|
|
177
|
-
|
|
179
|
+
// we skip expensive SQRT, but still get a good guiding heuristic which has the same monotonic order
|
|
180
|
+
return dx * dx + dy * dy;
|
|
178
181
|
}
|
|
179
182
|
|
|
180
183
|
|
|
@@ -190,16 +193,16 @@ closed.preventShrink();
|
|
|
190
193
|
* @param {number} height
|
|
191
194
|
* @param {number} start
|
|
192
195
|
* @param {number} goal
|
|
193
|
-
* @param {number}
|
|
194
|
-
* @param {number}
|
|
196
|
+
* @param {number} crossing_penalty
|
|
197
|
+
* @param {number} block_value value in the field that signifies impassible obstacle
|
|
195
198
|
* @returns {Array.<number>} array of indices representing path from start to end
|
|
196
199
|
*/
|
|
197
200
|
export function find_path_on_grid_astar(
|
|
198
201
|
field,
|
|
199
202
|
width, height,
|
|
200
203
|
start, goal,
|
|
201
|
-
|
|
202
|
-
|
|
204
|
+
crossing_penalty,
|
|
205
|
+
block_value
|
|
203
206
|
) {
|
|
204
207
|
|
|
205
208
|
assert.defined(field, 'field');
|
|
@@ -208,8 +211,8 @@ export function find_path_on_grid_astar(
|
|
|
208
211
|
assert.isNonNegativeInteger(start, "start");
|
|
209
212
|
assert.isNonNegativeInteger(goal, "goal");
|
|
210
213
|
|
|
211
|
-
assert.isNumber(
|
|
212
|
-
assert.isNumber(
|
|
214
|
+
assert.isNumber(crossing_penalty, 'crossingPenalty');
|
|
215
|
+
assert.isNumber(block_value, 'blockValue');
|
|
213
216
|
|
|
214
217
|
// prepare sets
|
|
215
218
|
open.clear();
|
|
@@ -232,15 +235,15 @@ export function find_path_on_grid_astar(
|
|
|
232
235
|
const currentNode = open.pop_min();
|
|
233
236
|
|
|
234
237
|
if (currentNode === goal) {
|
|
235
|
-
// End case
|
|
236
|
-
return
|
|
238
|
+
// End case - result has been found, return the traced path.
|
|
239
|
+
return compute_path(currentNode, g_score, width, height);
|
|
237
240
|
}
|
|
238
241
|
|
|
239
242
|
// Normal case -- move currentNode from open to closed, process each of its neighbors.
|
|
240
243
|
closed.set(currentNode, true);
|
|
241
244
|
|
|
242
245
|
// Find all neighbors for the current node.
|
|
243
|
-
const neighbour_count =
|
|
246
|
+
const neighbour_count = compute_neighbors(neighbors, currentNode, width, height);
|
|
244
247
|
|
|
245
248
|
for (let i = 0; i < neighbour_count; ++i) {
|
|
246
249
|
const neighbor = neighbors[i];
|
|
@@ -252,39 +255,39 @@ export function find_path_on_grid_astar(
|
|
|
252
255
|
|
|
253
256
|
// The g score is the shortest distance from start to current node.
|
|
254
257
|
// We need to check if the path we have arrived at this neighbor is the shortest one we have seen yet.
|
|
255
|
-
const
|
|
258
|
+
const neighbor_value = field[neighbor];
|
|
256
259
|
|
|
257
|
-
if (
|
|
260
|
+
if (neighbor_value === block_value) {
|
|
258
261
|
//cell is blocked, close and continue
|
|
259
262
|
closed.set(neighbor, true);
|
|
260
263
|
continue;
|
|
261
264
|
}
|
|
262
265
|
|
|
263
266
|
// Cost of traversing to this neighbour
|
|
264
|
-
const transition_cost =
|
|
267
|
+
const transition_cost = neighbor_value * crossing_penalty + 1;
|
|
265
268
|
|
|
266
269
|
// updated path cost
|
|
267
|
-
const
|
|
270
|
+
const cost_so_far = g_score[currentNode] + transition_cost;
|
|
268
271
|
|
|
269
272
|
const index_in_open_set = open.find_index_by_id(neighbor);
|
|
270
273
|
|
|
271
274
|
const not_in_open_set = index_in_open_set === -1;
|
|
272
275
|
|
|
273
|
-
if (not_in_open_set ||
|
|
276
|
+
if (not_in_open_set || cost_so_far < g_score[neighbor]) {
|
|
274
277
|
|
|
275
278
|
// update refined score
|
|
276
|
-
g_score[neighbor] =
|
|
279
|
+
g_score[neighbor] = cost_so_far;
|
|
277
280
|
|
|
278
|
-
const
|
|
281
|
+
const remaining_heuristic = heuristic(neighbor, goal, width);
|
|
279
282
|
|
|
280
|
-
const
|
|
283
|
+
const refined_heuristic = cost_so_far + remaining_heuristic;
|
|
281
284
|
|
|
282
285
|
if (not_in_open_set) {
|
|
283
286
|
// Pushing to heap will put it in proper place based on the 'f' value.
|
|
284
|
-
open.insert(neighbor,
|
|
287
|
+
open.insert(neighbor, refined_heuristic);
|
|
285
288
|
} else {
|
|
286
289
|
// Already seen the node, but since it has been re-scored we need to reorder it in the heap
|
|
287
|
-
open.__update_score_by_index(index_in_open_set,
|
|
290
|
+
open.__update_score_by_index(index_in_open_set, refined_heuristic);
|
|
288
291
|
}
|
|
289
292
|
}
|
|
290
293
|
}
|
|
@@ -33,7 +33,7 @@ test("path on open 3x3 grid", () => {
|
|
|
33
33
|
0, 0, 0
|
|
34
34
|
], 3, 3, 0, 8, 0, 1);
|
|
35
35
|
|
|
36
|
-
expect(path).toEqual([0,
|
|
36
|
+
expect(path).toEqual([0, 3, 5, 8]);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
test("performance, 256x256 random", () => {
|
|
@@ -45,7 +45,7 @@ test("performance, 256x256 random", () => {
|
|
|
45
45
|
|
|
46
46
|
const last_field_index = field.length - 1;
|
|
47
47
|
|
|
48
|
-
for (let i = 0; i < field.length * 0.
|
|
48
|
+
for (let i = 0; i < field.length * 0.15; i++) {
|
|
49
49
|
const index = randomIntegerBetween(random, 0, last_field_index);
|
|
50
50
|
|
|
51
51
|
field[index] = 255;
|
|
@@ -1,46 +1,30 @@
|
|
|
1
|
+
import { GridTags } from "../../../../../samples/generation/grid/GridTags.js";
|
|
2
|
+
import { MirGridLayers } from "../../../../../samples/generation/grid/MirGridLayers.js";
|
|
3
|
+
import { matcher_tag_traversable } from "../../../../../samples/generation/rules/matcher_tag_traversable.js";
|
|
4
|
+
import { BitSet } from "../../../../core/binary/BitSet.js";
|
|
5
|
+
import { groupArrayBy } from "../../../../core/collection/array/groupArrayBy.js";
|
|
6
|
+
import { collectIteratorValueToArray } from "../../../../core/collection/collectIteratorValueToArray.js";
|
|
7
|
+
import BinaryHeap from "../../../../core/collection/heap/FastBinaryHeap.js";
|
|
8
|
+
import { QuadTreeNode } from "../../../../core/geom/2d/quad-tree/QuadTreeNode.js";
|
|
9
|
+
import { Graph } from "../../../../core/graph/v2/Graph.js";
|
|
1
10
|
import { seededRandom } from "../../../../core/math/random/seededRandom.js";
|
|
2
|
-
import {
|
|
11
|
+
import { remap } from "../../../../core/math/remap.js";
|
|
3
12
|
import TaskGroup from "../../../../core/process/task/TaskGroup.js";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import { BitSet } from "../../../../core/binary/BitSet.js";
|
|
7
|
-
import { matcher_tag_traversable } from "../../../../../samples/generation/rules/matcher_tag_traversable.js";
|
|
8
|
-
import { buildPathFromDistanceMap } from "../util/buildPathFromDistanceMap.js";
|
|
13
|
+
import { actionTask } from "../../../../core/process/task/util/actionTask.js";
|
|
14
|
+
import { countTask } from "../../../../core/process/task/util/countTask.js";
|
|
9
15
|
import { GridCellActionPlaceTags } from "../../../placement/action/GridCellActionPlaceTags.js";
|
|
10
|
-
import { GridTags } from "../../../../../samples/generation/grid/GridTags.js";
|
|
11
16
|
import { CellMatcher } from "../../../rules/CellMatcher.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
17
|
+
import { CellMatcherNot } from "../../../rules/logic/CellMatcherNot.js";
|
|
18
|
+
import { GridTaskGenerator } from "../../GridTaskGenerator.js";
|
|
19
|
+
import { buildPathFromDistanceMap } from "../util/buildPathFromDistanceMap.js";
|
|
20
|
+
import { buildUnsignedDistanceField } from "../util/buildUnsignedDistanceField.js";
|
|
15
21
|
import { PathEndPoint } from "./PathEndPoint.js";
|
|
16
|
-
import { RoadConnection } from "./RoadConnection.js";
|
|
17
22
|
import { readMarkerNodeGroupId } from "./readMarkerNodeGroupId.js";
|
|
18
|
-
import {
|
|
19
|
-
import { CellMatcherNot } from "../../../rules/logic/CellMatcherNot.js";
|
|
23
|
+
import { RoadConnection } from "./RoadConnection.js";
|
|
20
24
|
import { RoadConnectionNetwork } from "./RoadConnectionNetwork.js";
|
|
21
|
-
import { MirGridLayers } from "../../../../../samples/generation/grid/MirGridLayers.js";
|
|
22
|
-
import { actionTask } from "../../../../core/process/task/util/actionTask.js";
|
|
23
|
-
import { countTask } from "../../../../core/process/task/util/countTask.js";
|
|
24
|
-
import { groupArrayBy } from "../../../../core/collection/array/groupArrayBy.js";
|
|
25
|
-
import { remap } from "../../../../core/math/remap.js";
|
|
26
25
|
|
|
27
26
|
const NODE_TYPE_ROAD_CONNECTOR = 'Road Connector';
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
*
|
|
32
|
-
* @param {QuadTreeNode} tree
|
|
33
|
-
* @param {RoadConnection} path
|
|
34
|
-
* @param {number} gridWidth
|
|
35
|
-
*/
|
|
36
|
-
function addPathToQuadTree(tree, path, gridWidth) {
|
|
37
|
-
const bounds = new AABB2();
|
|
38
|
-
|
|
39
|
-
path.computeBounds(bounds, gridWidth);
|
|
40
|
-
|
|
41
|
-
tree.add(path, bounds.x0, bounds.y0, bounds.x1, bounds.y1);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
28
|
/**
|
|
45
29
|
*
|
|
46
30
|
* @param {MarkerNode[]} array
|
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Created by Alex on 05/02/14.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
line_segment_compute_line_segment_intersection_vectors_array_2d
|
|
8
|
-
} from "../geom/2d/line/line_segment_compute_line_segment_intersection_vectors_array_2d.js";
|
|
9
|
-
|
|
10
|
-
const Utils = {};
|
|
11
|
-
|
|
12
|
-
function pointBetweenEdges(edge1, edge2, node, thickness) {
|
|
13
|
-
const other1 = edge1.other(node);
|
|
14
|
-
const other2 = edge2.other(node);
|
|
15
|
-
const delta1 = other1.clone().sub(node);
|
|
16
|
-
const delta2 = other2.clone().sub(node);
|
|
17
|
-
const sum = delta2.normalize().clone().add(delta1.normalize());
|
|
18
|
-
let angle = edge2.angle() - edge1.angle();
|
|
19
|
-
if (angle < 0) {
|
|
20
|
-
angle += Math.PI * 2;
|
|
21
|
-
}
|
|
22
|
-
// console.log(angle * (57.295));
|
|
23
|
-
if (angle === 0) {
|
|
24
|
-
//parallel edges
|
|
25
|
-
sum.set(delta1.y, delta1.x);
|
|
26
|
-
sum.multiplyScalar(thickness / 2);
|
|
27
|
-
console.log(">");
|
|
28
|
-
} else {
|
|
29
|
-
const scalar = (thickness / 2) * (1 + sum.length() / 4);
|
|
30
|
-
sum.normalize().multiplyScalar(scalar);
|
|
31
|
-
if (angle > Math.PI) {
|
|
32
|
-
console.log("<");
|
|
33
|
-
sum.negate();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return sum;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function angleDifference(edge1, edge2) {
|
|
40
|
-
return edge1.angle() - edge2.angle();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function trace2(graph, thickness) {
|
|
44
|
-
//clone graph
|
|
45
|
-
const g = graph.clone();
|
|
46
|
-
let closedNodes = [];
|
|
47
|
-
let closedEdges = [];
|
|
48
|
-
//edge needs to be covered twice to be closed
|
|
49
|
-
let prevEdge, prevNode,
|
|
50
|
-
currentEdge, currentNode;
|
|
51
|
-
while (g.edges.length > 0) {
|
|
52
|
-
//pick next edge
|
|
53
|
-
const node = prevEdge.other(prevNode);
|
|
54
|
-
const neighbours = g.getAttachedEdges(node);
|
|
55
|
-
neighbours.sort(angleDifference);
|
|
56
|
-
let index = neighbours.indexOf(prevEdge);
|
|
57
|
-
index = (index + 1) % neighbours.length;
|
|
58
|
-
const edge = neighbours[index];
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function makeCap(node, edge, thickness, type) {
|
|
63
|
-
const result = [];
|
|
64
|
-
//get direction
|
|
65
|
-
const other = edge.other(node);
|
|
66
|
-
const first = node.clone();
|
|
67
|
-
const second = other.clone();
|
|
68
|
-
const delta = second.clone().sub(first);
|
|
69
|
-
const inverseDelta = delta.clone();
|
|
70
|
-
//
|
|
71
|
-
inverseDelta.x = delta.y;
|
|
72
|
-
inverseDelta.y = -delta.x;
|
|
73
|
-
|
|
74
|
-
inverseDelta.normalize();
|
|
75
|
-
//projecting cap
|
|
76
|
-
if (type === "projecting") {
|
|
77
|
-
const half_thickness = thickness / 2;
|
|
78
|
-
const sid = inverseDelta.clone().multiplyScalar(half_thickness);
|
|
79
|
-
//projecting offset
|
|
80
|
-
const offset = delta.clone().normalize().multiplyScalar(half_thickness);
|
|
81
|
-
const anchor = node.clone().sub(offset);
|
|
82
|
-
result.push(sid.clone().add(anchor));
|
|
83
|
-
result.push(sid.negate().add(anchor));
|
|
84
|
-
}
|
|
85
|
-
return result;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function makeJoint2(node, edges, thickness) {
|
|
89
|
-
const result = [];
|
|
90
|
-
//sort edges by angle
|
|
91
|
-
edges.sort(angleDifference);
|
|
92
|
-
|
|
93
|
-
for (let i = 0; i < edges.length; i++) {
|
|
94
|
-
const j = (i + 1) % edges.length;
|
|
95
|
-
const edge1 = edges[i];
|
|
96
|
-
const edge2 = edges[j];
|
|
97
|
-
const sum = pointBetweenEdges(edge1, edge2, node, thickness);
|
|
98
|
-
result.push({
|
|
99
|
-
point: sum,
|
|
100
|
-
edges: [edge1, edge2]
|
|
101
|
-
})
|
|
102
|
-
}
|
|
103
|
-
result.forEach(function (element) {
|
|
104
|
-
element.point.add(node);
|
|
105
|
-
});
|
|
106
|
-
return result;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function graph2paths(graph, thickness) {
|
|
110
|
-
const points = [];
|
|
111
|
-
const nodes = graph.nodes;
|
|
112
|
-
//generating outline points
|
|
113
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
114
|
-
const node = nodes[i];
|
|
115
|
-
const attachedEdges = graph.getAttachedEdges(node);
|
|
116
|
-
const length = attachedEdges.length;
|
|
117
|
-
if (length === 0) {
|
|
118
|
-
//this node is not attached to any edge
|
|
119
|
-
console.warn("unconnected node, not representable");
|
|
120
|
-
} else if (length === 1) {
|
|
121
|
-
//this is an end point
|
|
122
|
-
const attachedEdge = attachedEdges[0];
|
|
123
|
-
const cap = makeCap(node, attachedEdge, thickness, "projecting");
|
|
124
|
-
cap.forEach(function (point) {
|
|
125
|
-
points.push({ node: node, edges: [attachedEdge], position: point });
|
|
126
|
-
});
|
|
127
|
-
} else if (length > 1) {
|
|
128
|
-
//this is a joint
|
|
129
|
-
const joint = makeJoint2(node, attachedEdges, thickness, null);
|
|
130
|
-
joint.forEach(function (data) {
|
|
131
|
-
points.push({ node: node, edges: data.edges, position: data.point })
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
let path;
|
|
136
|
-
|
|
137
|
-
function getCommonEdge(point1, point2) {
|
|
138
|
-
const edges1 = point1.edges;
|
|
139
|
-
const edges2 = point2.edges;
|
|
140
|
-
const l1 = edges1.length;
|
|
141
|
-
const l2 = edges2.length;
|
|
142
|
-
for (let i = 0; i < l1; i++) {
|
|
143
|
-
const edge1 = edges1[i];
|
|
144
|
-
for (let j = 0; j < l2; j++) {
|
|
145
|
-
const edge2 = edges2[j];
|
|
146
|
-
if (edge1 === edge2) {
|
|
147
|
-
return edge1;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function lineIntersectsGraph(from, to, graph) {
|
|
155
|
-
const edges = graph.edges;
|
|
156
|
-
let i = 0;
|
|
157
|
-
const l = edges.length;
|
|
158
|
-
for (; i < l; i++) {
|
|
159
|
-
const edge = edges[i];
|
|
160
|
-
if (line_segment_compute_line_segment_intersection_vectors_array_2d(from, to, edge.first, edge.second)) {
|
|
161
|
-
return true;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function getNextPoint2(from) {
|
|
168
|
-
//filter points based on common edge
|
|
169
|
-
const edges0 = from.edges;
|
|
170
|
-
const l0 = edges0.length;
|
|
171
|
-
const candidates = [];
|
|
172
|
-
points.forEach(function (point, index) {
|
|
173
|
-
const edges1 = point.edges;
|
|
174
|
-
const l1 = edges1.length;
|
|
175
|
-
for (let i = 0; i < l0; i++) {
|
|
176
|
-
const e0 = edges0[i];
|
|
177
|
-
for (let j = 0; j < l1; j++) {
|
|
178
|
-
const e1 = edges1[j];
|
|
179
|
-
if (e0 === e1) {
|
|
180
|
-
//now filter out those that cross the graph
|
|
181
|
-
if (!lineIntersectsGraph(from.position, point.position, graph)) {
|
|
182
|
-
candidates.push(index);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
if (candidates.length > 0) {
|
|
189
|
-
const index = candidates[0];
|
|
190
|
-
const point = points[index];
|
|
191
|
-
points.splice(index, 1);
|
|
192
|
-
return point;
|
|
193
|
-
} else {
|
|
194
|
-
console.warn("No next point from ", from);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function getNextPoint(from) {
|
|
200
|
-
for (let i = 0; i < points.length; i++) {
|
|
201
|
-
const p = points[i];
|
|
202
|
-
const commonEdge = getCommonEdge(p, from);
|
|
203
|
-
if (commonEdge != null) {
|
|
204
|
-
//make sure line would not intersect the edge
|
|
205
|
-
// var intersects = getLineIntersection(p.position, from.position, commonEdge.first, commonEdge.second);
|
|
206
|
-
const intersects = lineIntersectsGraph(p.position, from.position, graph);
|
|
207
|
-
if (intersects) {
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
points.splice(i, 1);
|
|
211
|
-
return p;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
console.warn("No next point from", from);
|
|
215
|
-
return null;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function trace() {
|
|
219
|
-
//pick a point
|
|
220
|
-
let currentPoint = points.pop();
|
|
221
|
-
let nextPoint;
|
|
222
|
-
path.moveTo(currentPoint.position.x, currentPoint.position.y);
|
|
223
|
-
// console.log("path.moveTo("+currentPoint.position.x+","+currentPoint.position.y+");");
|
|
224
|
-
const firstPoint = currentPoint;
|
|
225
|
-
for (nextPoint = getNextPoint2(currentPoint); nextPoint != null; nextPoint = getNextPoint2(currentPoint)) {
|
|
226
|
-
path.lineTo(nextPoint.position.x, nextPoint.position.y);
|
|
227
|
-
// console.log("path.lineTo("+nextPoint.position.x+","+nextPoint.position.y+");");
|
|
228
|
-
currentPoint = nextPoint;
|
|
229
|
-
}
|
|
230
|
-
path.lineTo(firstPoint.position.x, firstPoint.position.y);
|
|
231
|
-
// console.log("path.lineTo("+firstPoint.position.x+","+firstPoint.position.y+");");
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const paths = [];
|
|
235
|
-
while (points.length > 0) {
|
|
236
|
-
path = new THREE.Path();
|
|
237
|
-
trace();
|
|
238
|
-
paths.push(path);
|
|
239
|
-
}
|
|
240
|
-
return paths;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function graph2rects(graph, thickness) {
|
|
244
|
-
const edges = graph.edges;
|
|
245
|
-
const paths = [];
|
|
246
|
-
for (let i = 0; i < edges.length; i++) {
|
|
247
|
-
const path = new THREE.Path();
|
|
248
|
-
const edge = edges[i];
|
|
249
|
-
//outline edge
|
|
250
|
-
const cap1 = makeCap(edge.first, edge, thickness, "projecting");
|
|
251
|
-
const cap2 = makeCap(edge.second, edge, thickness, "projecting");
|
|
252
|
-
const points = cap1.concat(cap2);
|
|
253
|
-
let point = points[0];
|
|
254
|
-
path.moveTo(point.x, point.y);
|
|
255
|
-
let j = 1;
|
|
256
|
-
const l = points.length;
|
|
257
|
-
for (; j < l + 1; j++) {
|
|
258
|
-
point = points[j % l];
|
|
259
|
-
path.lineTo(point.x, point.y);
|
|
260
|
-
}
|
|
261
|
-
paths.push(path);
|
|
262
|
-
}
|
|
263
|
-
return paths;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
Utils.makeJoint2 = makeJoint2;
|
|
267
|
-
|
|
268
|
-
Utils.paths2shapes = function (paths) {
|
|
269
|
-
const shapes = [];
|
|
270
|
-
paths.forEach(function (path) {
|
|
271
|
-
const r = path.toShapes();
|
|
272
|
-
Array.prototype.push.apply(shapes, r);
|
|
273
|
-
});
|
|
274
|
-
return shapes;
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
Utils.graph2shapes = function (graph, thickness) {
|
|
278
|
-
const paths = Utils.graph2paths(graph, thickness);
|
|
279
|
-
return Utils.paths2shapes(paths);
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
Utils.graph2paths = graph2paths;
|
|
283
|
-
|
|
284
|
-
export default Utils;
|