@woosh/meep-engine 2.33.37 → 2.33.38
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/core/geom/3d/plane/orient3d_fast.js +15 -9
- package/core/geom/3d/tetrahedra/GetDepthForTetrahedronProbe.glsl +43 -0
- package/core/geom/3d/tetrahedra/compute_delaunay_tetrahedral_mesh.js +20 -169
- package/core/geom/3d/tetrahedra/compute_delaunay_tetrahedral_mesh.spec.js +9 -0
- package/core/geom/3d/tetrahedra/tetrahedra_collection.js +279 -0
- package/core/process/undo/ActionProcessor.js +8 -5
- package/engine/graphics/micron/prototypeVirtualGeometry.js +4 -1
- package/package.json +1 -1
|
@@ -21,23 +21,29 @@ export function orient3d_fast(points, a, b, c, d) {
|
|
|
21
21
|
const a3 = a * 3;
|
|
22
22
|
const d3 = d * 3;
|
|
23
23
|
|
|
24
|
-
const
|
|
24
|
+
const d_x = points[d3];
|
|
25
|
+
|
|
26
|
+
const adx = points[a3] - d_x;
|
|
25
27
|
|
|
26
28
|
const b3 = b * 3;
|
|
27
29
|
|
|
28
|
-
const bdx = points[b3] -
|
|
30
|
+
const bdx = points[b3] - d_x;
|
|
29
31
|
|
|
30
32
|
const c3 = c * 3;
|
|
31
33
|
|
|
32
|
-
const cdx = points[c3] -
|
|
34
|
+
const cdx = points[c3] - d_x;
|
|
35
|
+
|
|
36
|
+
const d_y = points[d3 + 1];
|
|
37
|
+
|
|
38
|
+
const ady = points[a3 + 1] - d_y;
|
|
39
|
+
const bdy = points[b3 + 1] - d_y;
|
|
40
|
+
const cdy = points[c3 + 1] - d_y;
|
|
33
41
|
|
|
34
|
-
const
|
|
35
|
-
const bdy = points[b3 + 1] - points[d3 + 1];
|
|
36
|
-
const cdy = points[c3 + 1] - points[d3 + 1];
|
|
42
|
+
const d_z = points[d3 + 2];
|
|
37
43
|
|
|
38
|
-
const adz = points[a3 + 2] -
|
|
39
|
-
const bdz = points[b3 + 2] -
|
|
40
|
-
const cdz = points[c3 + 2] -
|
|
44
|
+
const adz = points[a3 + 2] - d_z;
|
|
45
|
+
const bdz = points[b3 + 2] - d_z;
|
|
46
|
+
const cdz = points[c3 + 2] - d_z;
|
|
41
47
|
|
|
42
48
|
return adx * (bdy * cdz - bdz * cdy)
|
|
43
49
|
+ bdx * (cdy * adz - cdz * ady)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Sample triangular depth map
|
|
2
|
+
// see CoD SIGGRAPH 2017 "Advances in Real-Time Graphics in Games" course
|
|
3
|
+
float GetDepthForTetrahedronProbe(float2 bary, uint4 encodedDepth)
|
|
4
|
+
{
|
|
5
|
+
float sampleY = floor(bary.y * 4.0f);
|
|
6
|
+
float sampleX = floor(bary.x * 4.0f);
|
|
7
|
+
|
|
8
|
+
int baseIndex = (int)(((11.0f - sampleY) * sampleY) * 0.5f + sampleX + 0.5f);
|
|
9
|
+
int lineLength = 5 - (int)sampleY;
|
|
10
|
+
|
|
11
|
+
float2 baseBary = float2(sampleX * 0.25f, sampleY * 0.25f);
|
|
12
|
+
|
|
13
|
+
int3 indices = int3(baseIndex + 1, baseIndex + lineLength, baseIndex);
|
|
14
|
+
|
|
15
|
+
float3 sampleBary;
|
|
16
|
+
sampleBary.x = (bary.x - baseBary.x) * 4.0f;
|
|
17
|
+
sampleBary.y = (bary.y - baseBary.y) * 4.0f;
|
|
18
|
+
|
|
19
|
+
if (sampleBary.x + sampleBary.y > 1.0f)
|
|
20
|
+
{
|
|
21
|
+
// above the diagonal
|
|
22
|
+
indices = int3(baseIndex + lineLength, baseIndex + 1, baseIndex + lineLength + 1);
|
|
23
|
+
|
|
24
|
+
float3 newSampleBary;
|
|
25
|
+
newSampleBary.x = -(sampleBary.y - 1.0f);
|
|
26
|
+
newSampleBary.y = -(sampleBary.x - 1.0f);
|
|
27
|
+
sampleBary = newSampleBary;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
sampleBary.z = 1.0f - sampleBary.x - sampleBary.y;
|
|
31
|
+
|
|
32
|
+
float result = 0.0f;
|
|
33
|
+
for (int i=0; i<3; ++i)
|
|
34
|
+
{
|
|
35
|
+
uint currIndex = indices[i];
|
|
36
|
+
int intDepth = (encodedDepth[currIndex / 4] >> ((currIndex % 4) * 8)) & 0xFF;
|
|
37
|
+
float depth = (float)intDepth / 255.0f;
|
|
38
|
+
|
|
39
|
+
result += sampleBary[i] * depth;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
@@ -1,199 +1,50 @@
|
|
|
1
1
|
//
|
|
2
2
|
|
|
3
3
|
import { compute_bounding_simplex_3d } from "./compute_bounding_simplex_3d.js";
|
|
4
|
-
import {
|
|
5
|
-
import { tetrahedron_contains_point } from "./tetrahedron_contains_point.js";
|
|
6
|
-
import { assert } from "../../../assert.js";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @readonly
|
|
10
|
-
* @type {number}
|
|
11
|
-
*/
|
|
12
|
-
const LAYOUT_TETRA_BYTE_SIZE = 9 * 4;
|
|
13
|
-
const INVALID_NEIGHBOUR = 0xFFFFFFFF;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
*
|
|
17
|
-
* Layout:
|
|
18
|
-
* vertex_id_a :: uint
|
|
19
|
-
* vertex_id_b :: uint
|
|
20
|
-
* vertex_id_c :: uint
|
|
21
|
-
* vertex_id_d :: uint
|
|
22
|
-
* neighbour_a :: uint - neighbour tetrahedron, opposite to vertex A
|
|
23
|
-
* neighbour_b :: uint - neighbour tetrahedron, opposite to vertex B
|
|
24
|
-
* neighbour_c :: uint - neighbour tetrahedron, opposite to vertex C
|
|
25
|
-
* neighbour_d :: uint - neighbour tetrahedron, opposite to vertex D
|
|
26
|
-
* sub_determinant :: float - ?
|
|
27
|
-
* Layout is similar to [1], but is interleaved for better cache locality
|
|
28
|
-
*/
|
|
29
|
-
class TetrahedraCollection {
|
|
30
|
-
constructor(initial_size) {
|
|
31
|
-
this.__buffer = new ArrayBuffer(initial_size * 4 * 9);
|
|
32
|
-
|
|
33
|
-
this.__view = new DataView(this.__buffer);
|
|
34
|
-
|
|
35
|
-
this.__capacity = initial_size;
|
|
36
|
-
this.__length = 0;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
41
|
-
* @param {number} tetra_index
|
|
42
|
-
* @param {number} neighbour_index
|
|
43
|
-
* @returns {number}
|
|
44
|
-
*/
|
|
45
|
-
getNeighbour(tetra_index, neighbour_index) {
|
|
46
|
-
const tetra_address = LAYOUT_TETRA_BYTE_SIZE * tetra_index;
|
|
47
|
-
return this.__view.getUint32(tetra_address + (4 + neighbour_index) * 4);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
*
|
|
52
|
-
* @param {number[]|Float32Array} points
|
|
53
|
-
* @param {number} a
|
|
54
|
-
* @param {number} b
|
|
55
|
-
* @param {number} c
|
|
56
|
-
* @param {number} d
|
|
57
|
-
*/
|
|
58
|
-
append(points, a, b, c, d) {
|
|
59
|
-
const address = this.__length * LAYOUT_TETRA_BYTE_SIZE;
|
|
60
|
-
|
|
61
|
-
// TODO check if there's enough room
|
|
62
|
-
|
|
63
|
-
const view = this.__view;
|
|
64
|
-
|
|
65
|
-
view.setUint32(address, a);
|
|
66
|
-
view.setUint32(address + 4, b);
|
|
67
|
-
view.setUint32(address + 8, c);
|
|
68
|
-
view.setUint32(address + 12, d);
|
|
69
|
-
|
|
70
|
-
// set neighbours
|
|
71
|
-
view.setUint32(address + 16, INVALID_NEIGHBOUR);
|
|
72
|
-
view.setUint32(address + 20, INVALID_NEIGHBOUR);
|
|
73
|
-
view.setUint32(address + 24, INVALID_NEIGHBOUR);
|
|
74
|
-
view.setUint32(address + 28, INVALID_NEIGHBOUR);
|
|
75
|
-
|
|
76
|
-
this.__length++;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
*
|
|
81
|
-
* @param {number[]} points
|
|
82
|
-
* @param {number} index
|
|
83
|
-
* @returns {number} index of tetra or -1 if no containing tetra found
|
|
84
|
-
*/
|
|
85
|
-
findTetraContainingPoint(points, index) {
|
|
86
|
-
const data_view = this.__view;
|
|
87
|
-
|
|
88
|
-
// TODO improve this
|
|
89
|
-
const n = this.__length;
|
|
90
|
-
for (let i = 0; i < n; i++) {
|
|
91
|
-
|
|
92
|
-
const tet_address = i * LAYOUT_TETRA_BYTE_SIZE;
|
|
93
|
-
|
|
94
|
-
const a = data_view.getUint32(tet_address);
|
|
95
|
-
const b = data_view.getUint32(tet_address + 4);
|
|
96
|
-
const c = data_view.getUint32(tet_address + 8);
|
|
97
|
-
const d = data_view.getUint32(tet_address + 12);
|
|
98
|
-
|
|
99
|
-
if (tetrahedron_contains_point(points, a, b, c, d, index)) {
|
|
100
|
-
return i;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return -1;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
*
|
|
109
|
-
* @param {number[]|Float32Array} points
|
|
110
|
-
* @param {number} index
|
|
111
|
-
*/
|
|
112
|
-
insertPoint(points, index) {
|
|
113
|
-
|
|
114
|
-
// 1. walk to tetrahedron that contains this point
|
|
115
|
-
const containing_tetra = this.findTetraContainingPoint(points, index);
|
|
116
|
-
|
|
117
|
-
assert.isNonNegativeInteger(containing_tetra, 'containing_tetra');
|
|
118
|
-
|
|
119
|
-
const cavity = [];
|
|
120
|
-
const open_set = [
|
|
121
|
-
containing_tetra
|
|
122
|
-
];
|
|
123
|
-
|
|
124
|
-
const data_view = this.__view;
|
|
125
|
-
|
|
126
|
-
// 2. find the Delaunay "cavity", a set of tetrahedrons who's circumsphere overlarlaps the point
|
|
127
|
-
// perform breadth-first expansion on the containing tetra to identify the cavity
|
|
128
|
-
while (open_set.length > 0) {
|
|
129
|
-
const tetra = open_set.pop();
|
|
130
|
-
cavity.push(tetra);
|
|
131
|
-
|
|
132
|
-
// get neighbours
|
|
133
|
-
for (let i = 0; i < 4; i++) {
|
|
134
|
-
const neighbour = this.getNeighbour(tetra, i);
|
|
135
|
-
|
|
136
|
-
if (
|
|
137
|
-
(neighbour === INVALID_NEIGHBOUR) // no neighbour
|
|
138
|
-
|| cavity.includes(neighbour) // already visited
|
|
139
|
-
|| open_set.includes(neighbour) // already in open set
|
|
140
|
-
) {
|
|
141
|
-
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const neighbour_address = neighbour * LAYOUT_TETRA_BYTE_SIZE;
|
|
146
|
-
const a = data_view.getUint32(neighbour_address);
|
|
147
|
-
const b = data_view.getUint32(neighbour_address + 4);
|
|
148
|
-
const c = data_view.getUint32(neighbour_address + 8);
|
|
149
|
-
const d = data_view.getUint32(neighbour_address + 12);
|
|
150
|
-
|
|
151
|
-
// check neighbour
|
|
152
|
-
const is_in_sphere = point_in_tetrahedron_circumsphere(points, a, b, c, d, index);
|
|
153
|
-
|
|
154
|
-
if (is_in_sphere) {
|
|
155
|
-
open_set.push(neighbour);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// 3. remove the cavity tetrahedrons
|
|
161
|
-
|
|
162
|
-
// 4. fill the cavity by creating new tetrahedrons connecting the point to the faces of cavity boundary
|
|
163
|
-
}
|
|
164
|
-
}
|
|
4
|
+
import { TetrahedraCollection } from "./tetrahedra_collection.js";
|
|
165
5
|
|
|
166
6
|
/**
|
|
167
7
|
* @see https://en.wikipedia.org/wiki/Bowyer%E2%80%93Watson_algorithm
|
|
168
8
|
* @see [1] "One machine, one minute, three billion tetrahedra" by Célestin Marot et al
|
|
169
9
|
* @param {number[]} input serialized set of 3d points (x,y,z)
|
|
170
|
-
* @param {number}
|
|
10
|
+
* @param {number} n number of points in the input
|
|
171
11
|
*/
|
|
172
|
-
export function compute_delaunay_tetrahedral_mesh(input,
|
|
12
|
+
export function compute_delaunay_tetrahedral_mesh(input, n) {
|
|
13
|
+
if (n < 4) {
|
|
14
|
+
// no mesh can be created, too few points
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
173
18
|
/**
|
|
174
19
|
* According to [1] number of tetrahedra is around 6 times greater than number of input vertices, we use that fact to pre-allocate memory
|
|
20
|
+
* From the book of Cheng et al. 'Delaunay Mesh Generation' A triangulation of n vertices in R^3 has at most `ntet=(n^2 − 3n − 2)/2` tetrahedra,
|
|
21
|
+
* at most nedg=(n^2 − n)/2` edges (this is simply the 2-combination of n)
|
|
22
|
+
* and at most `ntri=n^2 − 3n = 2*ntet+2` triangles
|
|
23
|
+
*
|
|
24
|
+
* The +7 is for the initial "super" tetrahedron and 6 tetras for intermediate triangulation
|
|
175
25
|
*/
|
|
176
|
-
const
|
|
26
|
+
const tetrahedron_count_upper_bound = Math.ceil((n * n - 3 * n - 2) / 2) + 7;
|
|
27
|
+
const tetrahedrons = new TetrahedraCollection(tetrahedron_count_upper_bound);
|
|
177
28
|
// TODO apply spatial sort to point, to get better cache locality
|
|
178
29
|
|
|
179
30
|
// compute bounds over the input
|
|
180
|
-
const points = new Float32Array((
|
|
31
|
+
const points = new Float32Array((n + 4) * 3);
|
|
181
32
|
|
|
182
33
|
points.set(input);
|
|
183
34
|
|
|
184
35
|
/*
|
|
185
36
|
create super bounding volume
|
|
186
37
|
*/
|
|
187
|
-
compute_bounding_simplex_3d(points,
|
|
38
|
+
compute_bounding_simplex_3d(points, n * 3, points, n, 1);
|
|
188
39
|
|
|
189
|
-
tetrahedrons.append(points,
|
|
40
|
+
tetrahedrons.append(points, n, n + 1, n + 2, n + 3);
|
|
190
41
|
|
|
191
42
|
// add all points to the mesh
|
|
192
|
-
for (let i = 0; i <
|
|
43
|
+
for (let i = 0; i < n; i++) {
|
|
193
44
|
tetrahedrons.insertPoint(points, i);
|
|
194
45
|
}
|
|
195
46
|
|
|
196
47
|
// remove tetras formed by with the super bounding volume
|
|
197
|
-
|
|
48
|
+
tetrahedrons.removeTetrasConnectedToPoints(points, n, n + 3);
|
|
198
49
|
}
|
|
199
50
|
|
|
@@ -3,3 +3,12 @@ import { compute_delaunay_tetrahedral_mesh } from "./compute_delaunay_tetrahedra
|
|
|
3
3
|
test('single point', () => {
|
|
4
4
|
compute_delaunay_tetrahedral_mesh([1, 2, 3], 1);
|
|
5
5
|
});
|
|
6
|
+
|
|
7
|
+
test('exactly 4 points', () => {
|
|
8
|
+
compute_delaunay_tetrahedral_mesh([
|
|
9
|
+
-1, 0, 0,
|
|
10
|
+
1, 0, 0,
|
|
11
|
+
0.5, 1, 0,
|
|
12
|
+
0.5, 0, 1
|
|
13
|
+
], 4);
|
|
14
|
+
});
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { tetrahedron_contains_point } from "./tetrahedron_contains_point.js";
|
|
2
|
+
import { assert } from "../../../assert.js";
|
|
3
|
+
import { point_in_tetrahedron_circumsphere } from "./point_in_tetrahedron_circumsphere.js";
|
|
4
|
+
import { BitSet } from "../../../binary/BitSet.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @readonly
|
|
8
|
+
* @type {number}
|
|
9
|
+
*/
|
|
10
|
+
const LAYOUT_TETRA_BYTE_SIZE = 9 * 4;
|
|
11
|
+
const INVALID_NEIGHBOUR = 0xFFFFFFFF;
|
|
12
|
+
|
|
13
|
+
class Cavity {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.indices = [];
|
|
16
|
+
this.__size = 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* @param {number} i
|
|
22
|
+
*/
|
|
23
|
+
push(i) {
|
|
24
|
+
this.indices[this.__size++] = i;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
*
|
|
29
|
+
* @param {number} i
|
|
30
|
+
* @returns {boolean}
|
|
31
|
+
*/
|
|
32
|
+
includes(i) {
|
|
33
|
+
const n = this.__size;
|
|
34
|
+
for (let j = 0; j < n; j++) {
|
|
35
|
+
if (this.indices[j] === i) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
reset() {
|
|
44
|
+
this.__size = 0;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const SCRATCH_CAVITY = new Cavity();
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
*
|
|
52
|
+
* Layout:
|
|
53
|
+
* vertex_id_a :: uint
|
|
54
|
+
* vertex_id_b :: uint
|
|
55
|
+
* vertex_id_c :: uint
|
|
56
|
+
* vertex_id_d :: uint
|
|
57
|
+
* neighbour_a :: uint - neighbour tetrahedron, opposite to vertex A
|
|
58
|
+
* neighbour_b :: uint - neighbour tetrahedron, opposite to vertex B
|
|
59
|
+
* neighbour_c :: uint - neighbour tetrahedron, opposite to vertex C
|
|
60
|
+
* neighbour_d :: uint - neighbour tetrahedron, opposite to vertex D
|
|
61
|
+
* sub_determinant :: float - ?
|
|
62
|
+
* Layout is similar to [1], but is interleaved for better cache locality
|
|
63
|
+
*/
|
|
64
|
+
export class TetrahedraCollection {
|
|
65
|
+
constructor(initial_size) {
|
|
66
|
+
this.__buffer = new ArrayBuffer(initial_size * 4 * 9);
|
|
67
|
+
|
|
68
|
+
this.__view = new DataView(this.__buffer);
|
|
69
|
+
|
|
70
|
+
this.__capacity = initial_size;
|
|
71
|
+
this.__length = 0;
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Marks which indices are in use
|
|
76
|
+
* @type {BitSet}
|
|
77
|
+
* @private
|
|
78
|
+
*/
|
|
79
|
+
this.__occupancy = new BitSet();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
*
|
|
84
|
+
* @param {number} tetra_index
|
|
85
|
+
* @param {number} neighbour_index
|
|
86
|
+
* @returns {number}
|
|
87
|
+
*/
|
|
88
|
+
getNeighbour(tetra_index, neighbour_index) {
|
|
89
|
+
const tetra_address = LAYOUT_TETRA_BYTE_SIZE * tetra_index;
|
|
90
|
+
return this.__view.getUint32(tetra_address + (4 + neighbour_index) * 4);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
*
|
|
95
|
+
* @param {number[]|Float32Array} points
|
|
96
|
+
* @param {number} a
|
|
97
|
+
* @param {number} b
|
|
98
|
+
* @param {number} c
|
|
99
|
+
* @param {number} d
|
|
100
|
+
*/
|
|
101
|
+
append(points, a, b, c, d) {
|
|
102
|
+
const tetra_index = this.__length;
|
|
103
|
+
const address = tetra_index * LAYOUT_TETRA_BYTE_SIZE;
|
|
104
|
+
|
|
105
|
+
// TODO check if there's enough room
|
|
106
|
+
|
|
107
|
+
const view = this.__view;
|
|
108
|
+
|
|
109
|
+
view.setUint32(address, a);
|
|
110
|
+
view.setUint32(address + 4, b);
|
|
111
|
+
view.setUint32(address + 8, c);
|
|
112
|
+
view.setUint32(address + 12, d);
|
|
113
|
+
|
|
114
|
+
// set neighbours
|
|
115
|
+
view.setUint32(address + 16, INVALID_NEIGHBOUR);
|
|
116
|
+
view.setUint32(address + 20, INVALID_NEIGHBOUR);
|
|
117
|
+
view.setUint32(address + 24, INVALID_NEIGHBOUR);
|
|
118
|
+
view.setUint32(address + 28, INVALID_NEIGHBOUR);
|
|
119
|
+
|
|
120
|
+
this.__occupancy.set(tetra_index, true);
|
|
121
|
+
|
|
122
|
+
this.__length++;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Remove tetrahedron
|
|
127
|
+
* @param {number} tera_index
|
|
128
|
+
*/
|
|
129
|
+
remove(tera_index) {
|
|
130
|
+
this.__occupancy.clear(tera_index);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Remove tetrahedrons who's points touch the "super-tetrahedron's" points that was inserted originally
|
|
135
|
+
* These points are identified by an offset + count parameters
|
|
136
|
+
* @param {number[]|Float32Array} points
|
|
137
|
+
* @param {number} range_start
|
|
138
|
+
* @param {number} range_end
|
|
139
|
+
*/
|
|
140
|
+
removeTetrasConnectedToPoints(points, range_start, range_end) {
|
|
141
|
+
const data_view = this.__view;
|
|
142
|
+
|
|
143
|
+
const n = this.__length;
|
|
144
|
+
|
|
145
|
+
let i = 0;
|
|
146
|
+
let j = 0;
|
|
147
|
+
|
|
148
|
+
for (; i < n; i++) {
|
|
149
|
+
|
|
150
|
+
const tet_address = i * LAYOUT_TETRA_BYTE_SIZE;
|
|
151
|
+
|
|
152
|
+
for (j = 0; j < 4; j++) {
|
|
153
|
+
const point_index = data_view.getUint32(tet_address + 4 * i);
|
|
154
|
+
|
|
155
|
+
if (point_index >= range_start && point_index <= range_end) {
|
|
156
|
+
// point index is in range, tetra should be removed
|
|
157
|
+
this.remove(i);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
*
|
|
165
|
+
* @param {number[]} points
|
|
166
|
+
* @param {number} index
|
|
167
|
+
* @returns {number} index of tetra or -1 if no containing tetra found
|
|
168
|
+
*/
|
|
169
|
+
findTetraContainingPoint(points, index) {
|
|
170
|
+
const data_view = this.__view;
|
|
171
|
+
|
|
172
|
+
// TODO improve this
|
|
173
|
+
const n = this.__length;
|
|
174
|
+
for (let i = 0; i < n; i++) {
|
|
175
|
+
|
|
176
|
+
const tet_address = i * LAYOUT_TETRA_BYTE_SIZE;
|
|
177
|
+
|
|
178
|
+
const a = data_view.getUint32(tet_address);
|
|
179
|
+
const b = data_view.getUint32(tet_address + 4);
|
|
180
|
+
const c = data_view.getUint32(tet_address + 8);
|
|
181
|
+
const d = data_view.getUint32(tet_address + 12);
|
|
182
|
+
|
|
183
|
+
if (tetrahedron_contains_point(points, a, b, c, d, index)) {
|
|
184
|
+
return i;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return -1;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
*
|
|
193
|
+
* @param {Cavity} cavity
|
|
194
|
+
* @param {number[]|Float32Array} points
|
|
195
|
+
* @param {number} index
|
|
196
|
+
*/
|
|
197
|
+
computeCavity(cavity, points, index) {
|
|
198
|
+
|
|
199
|
+
// 1.identify tetrahedron that contains this point
|
|
200
|
+
const containing_tetra = this.findTetraContainingPoint(points, index);
|
|
201
|
+
|
|
202
|
+
assert.isNonNegativeInteger(containing_tetra, 'containing_tetra');
|
|
203
|
+
|
|
204
|
+
cavity.reset();
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
*
|
|
208
|
+
* @type {number[]}
|
|
209
|
+
*/
|
|
210
|
+
const open_set = [
|
|
211
|
+
containing_tetra
|
|
212
|
+
];
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
*
|
|
216
|
+
* @type {number[]}
|
|
217
|
+
*/
|
|
218
|
+
const closed_set = [];
|
|
219
|
+
|
|
220
|
+
const data_view = this.__view;
|
|
221
|
+
|
|
222
|
+
// 2. find the Delaunay "cavity", a set of tetrahedrons who's circumsphere overlarlaps the point
|
|
223
|
+
// perform breadth-first expansion on the containing tetra to identify the cavity
|
|
224
|
+
while (open_set.length > 0) {
|
|
225
|
+
const tetra = open_set.pop();
|
|
226
|
+
cavity.push(tetra);
|
|
227
|
+
closed_set.push(tetra);
|
|
228
|
+
|
|
229
|
+
// get neighbours
|
|
230
|
+
for (let i = 0; i < 4; i++) {
|
|
231
|
+
const neighbour = this.getNeighbour(tetra, i);
|
|
232
|
+
|
|
233
|
+
if (
|
|
234
|
+
(neighbour === INVALID_NEIGHBOUR) // no neighbour
|
|
235
|
+
|| closed_set.includes(neighbour) // already visited
|
|
236
|
+
|| open_set.includes(neighbour) // already in open set
|
|
237
|
+
) {
|
|
238
|
+
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const neighbour_address = neighbour * LAYOUT_TETRA_BYTE_SIZE;
|
|
243
|
+
|
|
244
|
+
const a = data_view.getUint32(neighbour_address);
|
|
245
|
+
const b = data_view.getUint32(neighbour_address + 4);
|
|
246
|
+
const c = data_view.getUint32(neighbour_address + 8);
|
|
247
|
+
const d = data_view.getUint32(neighbour_address + 12);
|
|
248
|
+
|
|
249
|
+
// check neighbour
|
|
250
|
+
const is_in_sphere = point_in_tetrahedron_circumsphere(points, a, b, c, d, index);
|
|
251
|
+
|
|
252
|
+
if (is_in_sphere) {
|
|
253
|
+
open_set.push(neighbour);
|
|
254
|
+
} else {
|
|
255
|
+
// move directly to closed set
|
|
256
|
+
closed_set.push(neighbour);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
*
|
|
266
|
+
* @param {number[]|Float32Array} points
|
|
267
|
+
* @param {number} index
|
|
268
|
+
*/
|
|
269
|
+
insertPoint(points, index) {
|
|
270
|
+
// Compute the cavity, that is the set of tetrahedra that need to be removed and rebuilt
|
|
271
|
+
this.computeCavity(SCRATCH_CAVITY, points, index);
|
|
272
|
+
|
|
273
|
+
// identify cavity "edge" - the set of tetras that are neighbours of the tetras that form the cavity
|
|
274
|
+
|
|
275
|
+
// 3. remove the cavity tetrahedrons
|
|
276
|
+
|
|
277
|
+
// 4. fill the cavity by creating new tetrahedrons connecting the point to the faces of cavity boundary
|
|
278
|
+
}
|
|
279
|
+
}
|
|
@@ -100,20 +100,20 @@ export class ActionProcessor {
|
|
|
100
100
|
*/
|
|
101
101
|
clear() {
|
|
102
102
|
// drop all of the history
|
|
103
|
-
this.dropHistory(
|
|
103
|
+
this.dropHistory(Infinity);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
|
-
* Drop
|
|
107
|
+
* Drop number of records from the history
|
|
108
108
|
*
|
|
109
109
|
* NOTE valid use-cases for this method from the outside are: 1) memory management and 1) removing access to a set of previous actions
|
|
110
110
|
* NOTE this method is used internally by {@link #mark} method
|
|
111
111
|
*
|
|
112
112
|
* @example if there are 2 marks A at record 9 and B at record 11, and requested limit is 10 - 9 records will be kept as records 10 and 11 belong to the same batch and will be removed together.
|
|
113
|
-
* @param {number}
|
|
113
|
+
* @param {number} drop_count maximum number of records to drop
|
|
114
114
|
*/
|
|
115
|
-
dropHistory(
|
|
116
|
-
const recordsToDrop = Math.min(
|
|
115
|
+
dropHistory(drop_count) {
|
|
116
|
+
const recordsToDrop = Math.min(drop_count, this.history.length);
|
|
117
117
|
if (recordsToDrop <= 0) {
|
|
118
118
|
return;
|
|
119
119
|
}
|
|
@@ -144,6 +144,9 @@ export class ActionProcessor {
|
|
|
144
144
|
|
|
145
145
|
// dispatch signal
|
|
146
146
|
dropped_marks.forEach(this.onMarkRemoved.send1, this.onMarkRemoved);
|
|
147
|
+
|
|
148
|
+
// update history
|
|
149
|
+
|
|
147
150
|
}
|
|
148
151
|
|
|
149
152
|
/**
|
|
@@ -456,7 +456,7 @@ function init() {
|
|
|
456
456
|
|
|
457
457
|
// sample_gltf_sponza();
|
|
458
458
|
// sample_gltf_rts_factory();
|
|
459
|
-
sample_lucy();
|
|
459
|
+
// sample_lucy();
|
|
460
460
|
// sample_gltf_hapro_2();
|
|
461
461
|
// sample_gltf_hapro_1();
|
|
462
462
|
// sample_gltf_broken_isiflow();
|
|
@@ -465,6 +465,9 @@ function init() {
|
|
|
465
465
|
// gltf_sample('data/models/moicon/08-clickable-assets/model.gltf');
|
|
466
466
|
// gltf_sample('data/models/moicon/05-ground-floor/model.gltf');
|
|
467
467
|
// gltf_sample('data/models/moicon/isiflow_Oct_15_21/0/model.gltf');
|
|
468
|
+
// gltf_sample('moicon/29_Dec_2021_Nammo/1/model.gltf');
|
|
469
|
+
// gltf_sample('moicon/29_Dec_2021_Nammo/1-onematerial/model.gltf');
|
|
470
|
+
gltf_sample('moicon/29_Dec_2021_Nammo/2/model.gltf');
|
|
468
471
|
|
|
469
472
|
document.addEventListener("mousemove", onDocumentMouseMove);
|
|
470
473
|
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"productName": "Meep",
|
|
6
6
|
"description": "production-ready JavaScript game engine based on Entity Component System Architecture",
|
|
7
7
|
"author": "Alexander Goldring",
|
|
8
|
-
"version": "2.33.
|
|
8
|
+
"version": "2.33.38",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"gl-matrix": "3.4.3",
|
|
11
11
|
"fast-levenshtein": "2.0.6",
|