delaunay.js 1.0.7 → 1.0.8
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/package.json +5 -3
- package/src/bowyer-watson.js +18 -21
- package/src/triangle.js +43 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "delaunay.js",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Delaunay triangulation using the Bowyer-Watson algorithm, in JavaScript ",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"triangulation",
|
|
@@ -26,9 +26,11 @@
|
|
|
26
26
|
"src/"
|
|
27
27
|
],
|
|
28
28
|
"scripts": {
|
|
29
|
-
"test": "node test/test.js"
|
|
29
|
+
"test": "node test/test.js 10",
|
|
30
|
+
"prof": "node --prof test/test.js 20000",
|
|
31
|
+
"cpu-prof": "node --cpu-prof --cpu-prof-dir=./cpu-prof test/test.js 20000"
|
|
30
32
|
},
|
|
31
33
|
"dependencies": {
|
|
32
|
-
"vectory-lib": "^0.0.
|
|
34
|
+
"vectory-lib": "^0.0.9"
|
|
33
35
|
}
|
|
34
36
|
}
|
package/src/bowyer-watson.js
CHANGED
|
@@ -1,45 +1,42 @@
|
|
|
1
1
|
import Triangle from './triangle.js';
|
|
2
2
|
|
|
3
3
|
export default function bowyerWatson (superTriangle, pointList) {
|
|
4
|
-
// pointList is a set of coordinates defining the
|
|
4
|
+
// pointList is a set of coordinates defining the
|
|
5
5
|
// points to be triangulated
|
|
6
6
|
let triangulation = [];
|
|
7
7
|
|
|
8
|
-
// add super-triangle to triangulation
|
|
9
|
-
// must be large enough to completely contain all
|
|
8
|
+
// add super-triangle to triangulation
|
|
9
|
+
// must be large enough to completely contain all
|
|
10
10
|
// the points in pointList
|
|
11
11
|
triangulation.push(superTriangle);
|
|
12
12
|
|
|
13
13
|
// add all the points one at a time to the triangulation
|
|
14
14
|
pointList.forEach(point => {
|
|
15
15
|
let badTriangles = [];
|
|
16
|
-
|
|
17
|
-
// first find all the triangles that are no
|
|
16
|
+
|
|
17
|
+
// first find all the triangles that are no
|
|
18
18
|
// longer valid due to the insertion
|
|
19
|
-
triangulation.forEach(triangle => {
|
|
19
|
+
triangulation.forEach(triangle => {
|
|
20
20
|
if(triangle.pointIsInsideCircumcircle(point)) {
|
|
21
|
-
badTriangles.push(triangle);
|
|
21
|
+
badTriangles.push(triangle);
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
24
|
let polygon = [];
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
// find the boundary of the polygonal hole
|
|
27
27
|
badTriangles.forEach(triangle => {
|
|
28
28
|
triangle.edges().forEach(edge => {
|
|
29
|
-
let edgeIsShared =
|
|
30
|
-
|
|
31
|
-
if(triangle !== otherTriangle && otherTriangle.hasEdge(edge)) {
|
|
32
|
-
edgeIsShared = true;
|
|
33
|
-
}
|
|
29
|
+
let edgeIsShared = badTriangles.some(otherTriangle => {
|
|
30
|
+
return triangle !== otherTriangle && otherTriangle.hasEdge(edge);
|
|
34
31
|
});
|
|
35
32
|
if(!edgeIsShared) {
|
|
36
|
-
//edge is not shared by any other
|
|
33
|
+
// edge is not shared by any other
|
|
37
34
|
// triangles in badTriangles
|
|
38
35
|
polygon.push(edge);
|
|
39
36
|
}
|
|
40
37
|
});
|
|
41
38
|
});
|
|
42
|
-
|
|
39
|
+
|
|
43
40
|
// remove them from the data structure
|
|
44
41
|
badTriangles.forEach(triangle => {
|
|
45
42
|
let index = triangulation.indexOf(triangle);
|
|
@@ -47,27 +44,27 @@ export default function bowyerWatson (superTriangle, pointList) {
|
|
|
47
44
|
triangulation.splice(index, 1);
|
|
48
45
|
}
|
|
49
46
|
});
|
|
50
|
-
|
|
47
|
+
|
|
51
48
|
// re-triangulate the polygonal hole
|
|
52
49
|
polygon.forEach(edge => {
|
|
53
|
-
//form a triangle from edge to point
|
|
50
|
+
// form a triangle from edge to point
|
|
54
51
|
let newTri = new Triangle(edge[0], edge[1], point);
|
|
55
52
|
triangulation.push(newTri);
|
|
56
53
|
});
|
|
57
54
|
});
|
|
58
|
-
|
|
55
|
+
|
|
59
56
|
// done inserting points, now clean up
|
|
60
57
|
let i = triangulation.length;
|
|
61
58
|
while(i--) {
|
|
62
59
|
let triangle = triangulation[i];
|
|
63
60
|
if(triangle.sharesAVertexWith(superTriangle)) {
|
|
64
|
-
//remove triangle from triangulation
|
|
61
|
+
// remove triangle from triangulation
|
|
65
62
|
let index = triangulation.indexOf(triangle);
|
|
66
63
|
if (index > -1) {
|
|
67
64
|
triangulation.splice(index, 1);
|
|
68
65
|
}
|
|
69
|
-
}
|
|
66
|
+
}
|
|
70
67
|
}
|
|
71
|
-
|
|
68
|
+
|
|
72
69
|
return triangulation;
|
|
73
70
|
}
|
package/src/triangle.js
CHANGED
|
@@ -6,15 +6,11 @@ export default class Triangle {
|
|
|
6
6
|
this.b = b;
|
|
7
7
|
this.c = c;
|
|
8
8
|
}
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
vertexes() {
|
|
11
11
|
return [this.a, this.b, this.c];
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
vertexesAsString() {
|
|
15
|
-
return this.vertexes().map(vertex => `${vertex.x}, ${vertex.y}`).join(", ");
|
|
16
|
-
}
|
|
17
|
-
|
|
18
14
|
edges() {
|
|
19
15
|
return [
|
|
20
16
|
[this.a, this.b],
|
|
@@ -22,7 +18,7 @@ export default class Triangle {
|
|
|
22
18
|
[this.c, this.a]
|
|
23
19
|
];
|
|
24
20
|
}
|
|
25
|
-
|
|
21
|
+
|
|
26
22
|
sharesAVertexWith(triangle) {
|
|
27
23
|
// TODO: optimize me please!
|
|
28
24
|
for(let i = 0; i < 3; i++) {
|
|
@@ -40,51 +36,74 @@ export default class Triangle {
|
|
|
40
36
|
hasEdge(edge) {
|
|
41
37
|
for(let i = 0; i < 3; i++) {
|
|
42
38
|
let e = this.edges()[i];
|
|
43
|
-
if(e[0].equals(edge[0]) && e[1].equals(edge[1]) ||
|
|
39
|
+
if(e[0].equals(edge[0]) && e[1].equals(edge[1]) ||
|
|
44
40
|
e[1].equals(edge[0]) && e[0].equals(edge[1])) {
|
|
45
41
|
return true;
|
|
46
42
|
}
|
|
47
43
|
}
|
|
48
44
|
return false;
|
|
49
45
|
}
|
|
50
|
-
|
|
46
|
+
|
|
51
47
|
get circumcenter() {
|
|
52
48
|
if(!this._circumcenter) {
|
|
53
|
-
let d = 2 * (this.a.x * (this.b.y - this.c.y) +
|
|
54
|
-
this.b.x * (this.c.y - this.a.y) +
|
|
49
|
+
let d = 2 * (this.a.x * (this.b.y - this.c.y) +
|
|
50
|
+
this.b.x * (this.c.y - this.a.y) +
|
|
55
51
|
this.c.x * (this.a.y - this.b.y));
|
|
56
52
|
|
|
57
53
|
let x = 1 / d * ((this.a.x * this.a.x + this.a.y * this.a.y) * (this.b.y - this.c.y) +
|
|
58
|
-
(this.b.x * this.b.x + this.b.y * this.b.y) * (this.c.y - this.a.y) +
|
|
54
|
+
(this.b.x * this.b.x + this.b.y * this.b.y) * (this.c.y - this.a.y) +
|
|
59
55
|
(this.c.x * this.c.x + this.c.y * this.c.y) * (this.a.y - this.b.y));
|
|
60
56
|
|
|
61
|
-
let y = 1 / d * ((this.a.x * this.a.x + this.a.y * this.a.y) * (this.c.x - this.b.x) +
|
|
62
|
-
(this.b.x * this.b.x + this.b.y * this.b.y) * (this.a.x - this.c.x) +
|
|
57
|
+
let y = 1 / d * ((this.a.x * this.a.x + this.a.y * this.a.y) * (this.c.x - this.b.x) +
|
|
58
|
+
(this.b.x * this.b.x + this.b.y * this.b.y) * (this.a.x - this.c.x) +
|
|
63
59
|
(this.c.x * this.c.x + this.c.y * this.c.y) * (this.b.x - this.a.x));
|
|
64
60
|
this._circumcenter = new Vector(x, y);
|
|
65
61
|
}
|
|
66
|
-
|
|
62
|
+
|
|
67
63
|
return this._circumcenter;
|
|
68
|
-
|
|
64
|
+
|
|
69
65
|
}
|
|
70
|
-
|
|
66
|
+
|
|
71
67
|
get centroid() {
|
|
72
68
|
if(!this._centroid) {
|
|
73
69
|
this._centroid = this.a.add(this.b).add(this.c).div(3);
|
|
74
70
|
}
|
|
75
71
|
return this._centroid;
|
|
76
72
|
}
|
|
77
|
-
|
|
78
|
-
get
|
|
79
|
-
if(!this.
|
|
80
|
-
this.
|
|
73
|
+
|
|
74
|
+
get circumradiusSq() {
|
|
75
|
+
if(!this._circumradiusSq) {
|
|
76
|
+
this._circumradiusSq = this.circumcenter.sub(this.a).getLengthSq();
|
|
81
77
|
}
|
|
82
|
-
return this.
|
|
78
|
+
return this._circumradiusSq;
|
|
83
79
|
}
|
|
84
80
|
|
|
85
81
|
pointIsInsideCircumcircle(point) {
|
|
86
|
-
let dist = point.sub(this.circumcenter).
|
|
87
|
-
|
|
88
|
-
return dist < this.
|
|
82
|
+
let dist = point.sub(this.circumcenter).getLengthSq();
|
|
83
|
+
|
|
84
|
+
return dist < this.circumradiusSq;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Methods below are not needed for Delaunay triangulation, but useful for drawing.
|
|
88
|
+
vertexesAsString() {
|
|
89
|
+
return this.vertexes().map(vertex => `${vertex.x}, ${vertex.y}`).join(", ");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
edgeLengths() {
|
|
93
|
+
return this.edges().map(v => v[0].sub(v[1]).getLength());
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
heights() {
|
|
97
|
+
let [a, b, c] = this.edgeLengths();
|
|
98
|
+
function height(a, b, c) {
|
|
99
|
+
const s = (a + b + c) / 2;
|
|
100
|
+
const h = 2 * Math.sqrt(s * (s - a) * (s - b) * (s - c)) / b;
|
|
101
|
+
return h;
|
|
102
|
+
}
|
|
103
|
+
return [height(a, b, c), height(b, c, a), height(c, a, b)];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
shortestHeight() {
|
|
107
|
+
return Math.min(...this.heights());
|
|
89
108
|
}
|
|
90
109
|
}
|