@woosh/meep-engine 2.157.0 → 2.159.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/package.json +1 -1
- package/src/core/binary/BitSet.d.ts.map +1 -1
- package/src/core/binary/BitSet.js +6 -1
- package/src/core/binary/float_to_uint8.d.ts +4 -1
- package/src/core/binary/float_to_uint8.d.ts.map +1 -1
- package/src/core/binary/float_to_uint8.js +7 -2
- package/src/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.d.ts +11 -1
- package/src/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.d.ts.map +1 -1
- package/src/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.js +37 -14
- package/src/core/geom/3d/shape/PosedShape3D.d.ts +17 -0
- package/src/core/geom/3d/shape/PosedShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/PosedShape3D.js +50 -0
- package/src/core/math/gaussian.d.ts +7 -6
- package/src/core/math/gaussian.d.ts.map +1 -1
- package/src/core/math/gaussian.js +9 -8
- package/src/engine/graphics/ecs/trail2d/Trail2D.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail2d/Trail2D.js +21 -0
- package/src/engine/graphics/ecs/trail2d/Trail2DFlags.d.ts +1 -0
- package/src/engine/graphics/ecs/trail2d/Trail2DFlags.js +9 -1
- package/src/engine/physics/fluid/FluidField.d.ts +53 -9
- package/src/engine/physics/fluid/FluidField.d.ts.map +1 -1
- package/src/engine/physics/fluid/FluidField.js +684 -600
- package/src/engine/physics/fluid/FluidSimulator.d.ts +53 -38
- package/src/engine/physics/fluid/FluidSimulator.d.ts.map +1 -1
- package/src/engine/physics/fluid/FluidSimulator.js +252 -178
- package/src/engine/physics/fluid/REVIEW_02_PLAN.md +155 -26
- package/src/engine/physics/fluid/ecs/FluidObstacle.d.ts +72 -0
- package/src/engine/physics/fluid/ecs/FluidObstacle.d.ts.map +1 -0
- package/src/engine/physics/fluid/ecs/FluidObstacle.js +97 -0
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +117 -0
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts.map +1 -0
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.js +348 -0
- package/src/engine/physics/fluid/ecs/FluidSystem.d.ts +3 -3
- package/src/engine/physics/fluid/effector/GlobalFluidEffector.d.ts +62 -12
- package/src/engine/physics/fluid/effector/GlobalFluidEffector.d.ts.map +1 -1
- package/src/engine/physics/fluid/effector/GlobalFluidEffector.js +135 -38
- package/src/engine/physics/fluid/effector/ImpulseFluidEffector.d.ts.map +1 -1
- package/src/engine/physics/fluid/effector/ImpulseFluidEffector.js +85 -38
- package/src/engine/physics/fluid/effector/WakeFluidEffector.d.ts.map +1 -1
- package/src/engine/physics/fluid/effector/WakeFluidEffector.js +104 -50
- package/src/engine/physics/fluid/prototype.js +25 -1
- package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.d.ts +30 -0
- package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.js +92 -0
- package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.d.ts +42 -0
- package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.js +319 -0
- package/src/engine/physics/fluid/solver/v3_mac_advect_scalar.d.ts +53 -0
- package/src/engine/physics/fluid/solver/v3_mac_advect_scalar.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_mac_advect_scalar.js +236 -0
- package/src/engine/physics/fluid/solver/v3_mac_advect_sl_velocity.d.ts +46 -0
- package/src/engine/physics/fluid/solver/v3_mac_advect_sl_velocity.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_mac_advect_sl_velocity.js +217 -0
- package/src/engine/physics/fluid/solver/v3_mac_apply_vorticity_confinement.d.ts +40 -0
- package/src/engine/physics/fluid/solver/v3_mac_apply_vorticity_confinement.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_mac_apply_vorticity_confinement.js +165 -0
- package/src/engine/physics/fluid/solver/v3_mac_clip_trace.d.ts +44 -0
- package/src/engine/physics/fluid/solver/v3_mac_clip_trace.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_mac_clip_trace.js +95 -0
- package/src/engine/physics/fluid/solver/v3_mac_compute_divergence.d.ts +38 -0
- package/src/engine/physics/fluid/solver/v3_mac_compute_divergence.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_mac_compute_divergence.js +77 -0
- package/src/engine/physics/fluid/solver/v3_mac_compute_face_solid.d.ts +52 -0
- package/src/engine/physics/fluid/solver/v3_mac_compute_face_solid.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_mac_compute_face_solid.js +131 -0
- package/src/engine/physics/fluid/solver/v3_mac_subtract_pressure_gradient.d.ts +38 -0
- package/src/engine/physics/fluid/solver/v3_mac_subtract_pressure_gradient.d.ts.map +1 -0
- package/src/engine/physics/fluid/solver/v3_mac_subtract_pressure_gradient.js +104 -0
- package/src/engine/physics/fluid/effector/AmbientWindFluidEffector.d.ts +0 -41
- package/src/engine/physics/fluid/effector/AmbientWindFluidEffector.d.ts.map +0 -1
- package/src/engine/physics/fluid/effector/AmbientWindFluidEffector.js +0 -124
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { assert } from "../../../../core/assert.js";
|
|
2
|
+
import { clamp } from "../../../../core/math/clamp.js";
|
|
3
|
+
import { v3_grid_sample_scalar_masked } from "./v3_grid_sample_scalar_masked.js";
|
|
4
|
+
import { v3_mac_clip_trace } from "./v3_mac_clip_trace.js";
|
|
5
|
+
|
|
6
|
+
const scratch_clip = new Float32Array(3);
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Straight-line (loop-free, inlinable) test: is the cell containing the
|
|
10
|
+
* point solid? Same rounding/clamping as v3_mac_clip_trace. Kernels use this
|
|
11
|
+
* inline per trace and call the loop-bearing clip helper only on a hit — see
|
|
12
|
+
* the performance contract on {@link v3_mac_clip_trace}.
|
|
13
|
+
*
|
|
14
|
+
* @param {Uint8Array} solid
|
|
15
|
+
* @param {number} res_x
|
|
16
|
+
* @param {number} res_y
|
|
17
|
+
* @param {number} res_z
|
|
18
|
+
* @param {number} px
|
|
19
|
+
* @param {number} py
|
|
20
|
+
* @param {number} pz
|
|
21
|
+
* @return {boolean}
|
|
22
|
+
*/
|
|
23
|
+
function end_in_solid(solid, res_x, res_y, res_z, px, py, pz) {
|
|
24
|
+
let x = (px + 0.5) | 0;
|
|
25
|
+
let y = (py + 0.5) | 0;
|
|
26
|
+
let z = (pz + 0.5) | 0;
|
|
27
|
+
if (x < 0) x = 0; else if (x >= res_x) x = res_x - 1;
|
|
28
|
+
if (y < 0) y = 0; else if (y >= res_y) y = res_y - 1;
|
|
29
|
+
if (z < 0) z = 0; else if (z >= res_z) z = res_z - 1;
|
|
30
|
+
return solid[z * res_x * res_y + y * res_x + x] !== 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Semi-Lagrangian advection of a cell-centered scalar by a MAC velocity
|
|
36
|
+
* field, solid-aware on both ends of the trace:
|
|
37
|
+
*
|
|
38
|
+
* - the back-trace segment is CLIPPED against solid cells
|
|
39
|
+
* ({@link v3_mac_clip_trace}) so a trace can neither sample inside a wall
|
|
40
|
+
* nor tunnel through a thin one into a disconnected pocket;
|
|
41
|
+
* - the sample at the (clipped) source position uses solid-MASKED
|
|
42
|
+
* interpolation ({@link v3_grid_sample_scalar_masked}) so taps inside
|
|
43
|
+
* walls — frozen values from whenever the cell solidified — contribute
|
|
44
|
+
* nothing. Without this, walls slowly leak their frozen contents into
|
|
45
|
+
* passing fluid and absorb dye from it (the stage-5 scalar-mass defect).
|
|
46
|
+
*
|
|
47
|
+
* The carrier velocity at each cell center is the average of the cell's two
|
|
48
|
+
* faces per axis (exact on the staggered layout). Solid destination cells
|
|
49
|
+
* pass their value through unchanged.
|
|
50
|
+
*
|
|
51
|
+
* @param {Float32Array} output Cell-centered destination. Mutated.
|
|
52
|
+
* @param {Float32Array} input Cell-centered source. Must not alias output.
|
|
53
|
+
* @param {Float32Array} vel_u x-face velocities, (res_x+1)·res_y·res_z.
|
|
54
|
+
* @param {Float32Array} vel_v y-face velocities.
|
|
55
|
+
* @param {Float32Array} vel_w z-face velocities.
|
|
56
|
+
* @param {number} res_x
|
|
57
|
+
* @param {number} res_y
|
|
58
|
+
* @param {number} res_z
|
|
59
|
+
* @param {number} time_delta
|
|
60
|
+
* @param {Uint8Array} solid
|
|
61
|
+
*/
|
|
62
|
+
export function v3_mac_advect_sl_scalar(output, input, vel_u, vel_v, vel_w, res_x, res_y, res_z, time_delta, solid) {
|
|
63
|
+
assert.notEqual(output, input, "output must not alias input");
|
|
64
|
+
const cell_count = res_x * res_y * res_z;
|
|
65
|
+
assert.greaterThanOrEqual(output.length, cell_count, "output covers grid");
|
|
66
|
+
assert.greaterThanOrEqual(input.length, cell_count, "input covers grid");
|
|
67
|
+
|
|
68
|
+
const cell_slice = res_x * res_y;
|
|
69
|
+
const sx = res_x + 1;
|
|
70
|
+
const v_slice = res_x * (res_y + 1);
|
|
71
|
+
|
|
72
|
+
for (let z = 0; z < res_z; z++) {
|
|
73
|
+
const c_z = z * cell_slice;
|
|
74
|
+
const u_z = z * sx * res_y;
|
|
75
|
+
const v_z = z * v_slice;
|
|
76
|
+
for (let y = 0; y < res_y; y++) {
|
|
77
|
+
const c_y = c_z + y * res_x;
|
|
78
|
+
const u_y = u_z + y * sx;
|
|
79
|
+
const v_lo = v_z + y * res_x;
|
|
80
|
+
const w_lo = c_z + y * res_x;
|
|
81
|
+
for (let x = 0; x < res_x; x++) {
|
|
82
|
+
const c = c_y + x;
|
|
83
|
+
if (solid[c] !== 0) {
|
|
84
|
+
output[c] = input[c];
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const cu = 0.5 * (vel_u[u_y + x] + vel_u[u_y + x + 1]);
|
|
88
|
+
const cv = 0.5 * (vel_v[v_lo + x] + vel_v[v_lo + res_x + x]);
|
|
89
|
+
const cw = 0.5 * (vel_w[w_lo + x] + vel_w[w_lo + cell_slice + x]);
|
|
90
|
+
|
|
91
|
+
let bx = x - cu * time_delta;
|
|
92
|
+
let by = y - cv * time_delta;
|
|
93
|
+
let bz = z - cw * time_delta;
|
|
94
|
+
if (end_in_solid(solid, res_x, res_y, res_z, bx, by, bz)) {
|
|
95
|
+
v3_mac_clip_trace(scratch_clip, solid, res_x, res_y, res_z, x, y, z, bx, by, bz);
|
|
96
|
+
bx = scratch_clip[0]; by = scratch_clip[1]; bz = scratch_clip[2];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
output[c] = v3_grid_sample_scalar_masked(input, solid, res_x, res_y, res_z,
|
|
100
|
+
bx, by, bz, input[c]);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Unconditionally stable MacCormack advection of a cell-centered scalar by a
|
|
108
|
+
* MAC velocity field — forward + backward semi-Lagrangian passes, error
|
|
109
|
+
* correction, and Selle's monotone min-max limiter. Solid-aware throughout:
|
|
110
|
+
* both passes clip their traces and sample solid-masked (see
|
|
111
|
+
* {@link v3_mac_advect_sl_scalar}), and the limiter takes its extrema over
|
|
112
|
+
* the FLUID corners around the back-traced position only — when none are
|
|
113
|
+
* fluid the corrected value is discarded in favour of the first-order
|
|
114
|
+
* forward result.
|
|
115
|
+
*
|
|
116
|
+
* @param {Float32Array} output Destination. Mutated.
|
|
117
|
+
* @param {Float32Array} input Source. NOT mutated.
|
|
118
|
+
* @param {Float32Array} forward_scratch Working buffer for the forward pass.
|
|
119
|
+
* @param {Float32Array} vel_u
|
|
120
|
+
* @param {Float32Array} vel_v
|
|
121
|
+
* @param {Float32Array} vel_w
|
|
122
|
+
* @param {number} res_x
|
|
123
|
+
* @param {number} res_y
|
|
124
|
+
* @param {number} res_z
|
|
125
|
+
* @param {number} time_delta
|
|
126
|
+
* @param {Uint8Array} solid
|
|
127
|
+
*/
|
|
128
|
+
export function v3_mac_advect_maccormack_scalar(output, input, forward_scratch, vel_u, vel_v, vel_w, res_x, res_y, res_z, time_delta, solid) {
|
|
129
|
+
assert.notEqual(output, input, "output must not alias input");
|
|
130
|
+
assert.notEqual(output, forward_scratch, "output must not alias forward_scratch");
|
|
131
|
+
assert.notEqual(input, forward_scratch, "input must not alias forward_scratch");
|
|
132
|
+
|
|
133
|
+
const cell_count = res_x * res_y * res_z;
|
|
134
|
+
assert.greaterThanOrEqual(forward_scratch.length, cell_count, "forward_scratch covers grid");
|
|
135
|
+
|
|
136
|
+
const cell_slice = res_x * res_y;
|
|
137
|
+
const sx = res_x + 1;
|
|
138
|
+
const v_slice = res_x * (res_y + 1);
|
|
139
|
+
const last_x = res_x - 1;
|
|
140
|
+
const last_y = res_y - 1;
|
|
141
|
+
const last_z = res_z - 1;
|
|
142
|
+
|
|
143
|
+
const fwd = forward_scratch;
|
|
144
|
+
|
|
145
|
+
// ─── pass 1: forward SL step into the scratch ─────────────────────────────
|
|
146
|
+
v3_mac_advect_sl_scalar(fwd, input, vel_u, vel_v, vel_w, res_x, res_y, res_z, time_delta, solid);
|
|
147
|
+
|
|
148
|
+
// ─── pass 2: backward step, correction, monotone limit ──────────────────
|
|
149
|
+
for (let z = 0; z < res_z; z++) {
|
|
150
|
+
const c_z = z * cell_slice;
|
|
151
|
+
const u_z = z * sx * res_y;
|
|
152
|
+
const v_z = z * v_slice;
|
|
153
|
+
for (let y = 0; y < res_y; y++) {
|
|
154
|
+
const c_y = c_z + y * res_x;
|
|
155
|
+
const u_y = u_z + y * sx;
|
|
156
|
+
const v_lo = v_z + y * res_x;
|
|
157
|
+
const w_lo = c_z + y * res_x;
|
|
158
|
+
for (let x = 0; x < res_x; x++) {
|
|
159
|
+
const c = c_y + x;
|
|
160
|
+
if (solid[c] !== 0) {
|
|
161
|
+
output[c] = input[c];
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const cu = 0.5 * (vel_u[u_y + x] + vel_u[u_y + x + 1]) * time_delta;
|
|
166
|
+
const cv = 0.5 * (vel_v[v_lo + x] + vel_v[v_lo + res_x + x]) * time_delta;
|
|
167
|
+
const cw = 0.5 * (vel_w[w_lo + x] + vel_w[w_lo + cell_slice + x]) * time_delta;
|
|
168
|
+
|
|
169
|
+
// Reverse probe through the forward result, clipped + masked.
|
|
170
|
+
let qx = x + cu;
|
|
171
|
+
let qy = y + cv;
|
|
172
|
+
let qz = z + cw;
|
|
173
|
+
if (end_in_solid(solid, res_x, res_y, res_z, qx, qy, qz)) {
|
|
174
|
+
v3_mac_clip_trace(scratch_clip, solid, res_x, res_y, res_z, x, y, z, qx, qy, qz);
|
|
175
|
+
qx = scratch_clip[0]; qy = scratch_clip[1]; qz = scratch_clip[2];
|
|
176
|
+
}
|
|
177
|
+
const backward = v3_grid_sample_scalar_masked(fwd, solid, res_x, res_y, res_z,
|
|
178
|
+
qx, qy, qz, fwd[c]);
|
|
179
|
+
|
|
180
|
+
const corrected = fwd[c] + 0.5 * (input[c] - backward);
|
|
181
|
+
|
|
182
|
+
// Limiter bounds: extrema of the FLUID cells among the 8 around
|
|
183
|
+
// the (clipped) back-traced position.
|
|
184
|
+
let tx = x - cu;
|
|
185
|
+
let ty = y - cv;
|
|
186
|
+
let tz = z - cw;
|
|
187
|
+
if (end_in_solid(solid, res_x, res_y, res_z, tx, ty, tz)) {
|
|
188
|
+
v3_mac_clip_trace(scratch_clip, solid, res_x, res_y, res_z, x, y, z, tx, ty, tz);
|
|
189
|
+
tx = scratch_clip[0]; ty = scratch_clip[1]; tz = scratch_clip[2];
|
|
190
|
+
}
|
|
191
|
+
const bx = clamp(tx, 0, last_x);
|
|
192
|
+
const by = clamp(ty, 0, last_y);
|
|
193
|
+
const bz = clamp(tz, 0, last_z);
|
|
194
|
+
const x0 = bx | 0;
|
|
195
|
+
const y0 = by | 0;
|
|
196
|
+
const z0 = bz | 0;
|
|
197
|
+
const x1 = bx === x0 ? x0 : x0 + 1;
|
|
198
|
+
const y1 = by === y0 ? y0 : y0 + 1;
|
|
199
|
+
const z1 = bz === z0 ? z0 : z0 + 1;
|
|
200
|
+
|
|
201
|
+
const z0_off = z0 * cell_slice;
|
|
202
|
+
const z1_off = z1 * cell_slice;
|
|
203
|
+
const y0_off = y0 * res_x;
|
|
204
|
+
const y1_off = y1 * res_x;
|
|
205
|
+
|
|
206
|
+
let mn = Infinity;
|
|
207
|
+
let mx = -Infinity;
|
|
208
|
+
let t = z0_off + y0_off + x0;
|
|
209
|
+
if (solid[t] === 0) { const v = input[t]; if (v < mn) mn = v; if (v > mx) mx = v; }
|
|
210
|
+
t = z0_off + y0_off + x1;
|
|
211
|
+
if (solid[t] === 0) { const v = input[t]; if (v < mn) mn = v; if (v > mx) mx = v; }
|
|
212
|
+
t = z0_off + y1_off + x0;
|
|
213
|
+
if (solid[t] === 0) { const v = input[t]; if (v < mn) mn = v; if (v > mx) mx = v; }
|
|
214
|
+
t = z0_off + y1_off + x1;
|
|
215
|
+
if (solid[t] === 0) { const v = input[t]; if (v < mn) mn = v; if (v > mx) mx = v; }
|
|
216
|
+
t = z1_off + y0_off + x0;
|
|
217
|
+
if (solid[t] === 0) { const v = input[t]; if (v < mn) mn = v; if (v > mx) mx = v; }
|
|
218
|
+
t = z1_off + y0_off + x1;
|
|
219
|
+
if (solid[t] === 0) { const v = input[t]; if (v < mn) mn = v; if (v > mx) mx = v; }
|
|
220
|
+
t = z1_off + y1_off + x0;
|
|
221
|
+
if (solid[t] === 0) { const v = input[t]; if (v < mn) mn = v; if (v > mx) mx = v; }
|
|
222
|
+
t = z1_off + y1_off + x1;
|
|
223
|
+
if (solid[t] === 0) { const v = input[t]; if (v < mn) mn = v; if (v > mx) mx = v; }
|
|
224
|
+
|
|
225
|
+
if (mn === Infinity) {
|
|
226
|
+
// No fluid corner to bound against — fall back to the
|
|
227
|
+
// first-order forward value.
|
|
228
|
+
output[c] = fwd[c];
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
output[c] = corrected < mn ? mn : (corrected > mx ? mx : corrected);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semi-Lagrangian advection of a MAC (face-centered) velocity field along a
|
|
3
|
+
* separate MAC carrier field.
|
|
4
|
+
*
|
|
5
|
+
* Each component is updated on its own face lattice: for every face, the full
|
|
6
|
+
* carrier velocity AT the face position is reconstructed (own component read
|
|
7
|
+
* directly, the two cross components trilinearly interpolated from their
|
|
8
|
+
* lattices — the classic MAC 4-face average falls out of the interpolation),
|
|
9
|
+
* the position is traced backward by `carrier · dt`, and the SOURCE component
|
|
10
|
+
* is sampled at the traced position on its own lattice.
|
|
11
|
+
*
|
|
12
|
+
* PINNED faces (either adjacent cell solid — see
|
|
13
|
+
* {@link v3_mac_compute_face_solid}) pass their source value through
|
|
14
|
+
* unchanged: their velocity is a boundary condition owned by the projection,
|
|
15
|
+
* not transported state.
|
|
16
|
+
*
|
|
17
|
+
* Back-traces are CLIPPED against solid cells ({@link v3_mac_clip_trace}):
|
|
18
|
+
* a trace stops at the wall instead of sampling momentum from inside it or
|
|
19
|
+
* from a disconnected pocket on the far side. Sampling the face lattices
|
|
20
|
+
* near walls needs no masking — pinned faces hold valid boundary values.
|
|
21
|
+
*
|
|
22
|
+
* Position convention (cell-center coordinates): x-face `(i, j, k)` sits at
|
|
23
|
+
* `(i − 0.5, j, k)`; sampling component grids therefore offsets by +0.5 on
|
|
24
|
+
* the component's own axis. All positions clamp at the lattice edges.
|
|
25
|
+
*
|
|
26
|
+
* Aliasing: `outputs[i]` must not alias `sources[i]`, and outputs must not
|
|
27
|
+
* alias ANY carrier lattice — the per-face carrier reconstruction samples the
|
|
28
|
+
* OTHER components' lattices spatially, so an output lattice written earlier
|
|
29
|
+
* in the kernel would be re-read as carrier data later. Pass a copy when the
|
|
30
|
+
* carrier is the field being advected into (the simulator's reflection path
|
|
31
|
+
* does exactly that).
|
|
32
|
+
*
|
|
33
|
+
* @param {Float32Array[]} outputs `[out_u, out_v, out_w]` face grids. Mutated.
|
|
34
|
+
* @param {Float32Array[]} sources `[src_u, src_v, src_w]`. Read.
|
|
35
|
+
* @param {Float32Array[]} carrier `[car_u, car_v, car_w]`. Drives the traces.
|
|
36
|
+
* @param {number} res_x
|
|
37
|
+
* @param {number} res_y
|
|
38
|
+
* @param {number} res_z
|
|
39
|
+
* @param {number} time_delta
|
|
40
|
+
* @param {Uint8Array} face_solid_x Pinned-face masks, one per lattice.
|
|
41
|
+
* @param {Uint8Array} face_solid_y
|
|
42
|
+
* @param {Uint8Array} face_solid_z
|
|
43
|
+
* @param {Uint8Array} solid Cell-centered solid flags (trace clipping).
|
|
44
|
+
*/
|
|
45
|
+
export function v3_mac_advect_sl_velocity(outputs: Float32Array[], sources: Float32Array[], carrier: Float32Array[], res_x: number, res_y: number, res_z: number, time_delta: number, face_solid_x: Uint8Array, face_solid_y: Uint8Array, face_solid_z: Uint8Array, solid: Uint8Array): void;
|
|
46
|
+
//# sourceMappingURL=v3_mac_advect_sl_velocity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v3_mac_advect_sl_velocity.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/fluid/solver/v3_mac_advect_sl_velocity.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,mDAZW,YAAY,EAAE,WACd,YAAY,EAAE,WACd,YAAY,EAAE,SACd,MAAM,SACN,MAAM,SACN,MAAM,cACN,MAAM,gBACN,UAAU,gBACV,UAAU,gBACV,UAAU,SACV,UAAU,QAwKpB"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { assert } from "../../../../core/assert.js";
|
|
2
|
+
import { scs3d_sample_linear } from "../../../graphics/texture/3d/scs3d_sample_linear.js";
|
|
3
|
+
import { v3_mac_clip_trace } from "./v3_mac_clip_trace.js";
|
|
4
|
+
|
|
5
|
+
const scratch_clip = new Float32Array(3);
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Semi-Lagrangian advection of a MAC (face-centered) velocity field along a
|
|
9
|
+
* separate MAC carrier field.
|
|
10
|
+
*
|
|
11
|
+
* Each component is updated on its own face lattice: for every face, the full
|
|
12
|
+
* carrier velocity AT the face position is reconstructed (own component read
|
|
13
|
+
* directly, the two cross components trilinearly interpolated from their
|
|
14
|
+
* lattices — the classic MAC 4-face average falls out of the interpolation),
|
|
15
|
+
* the position is traced backward by `carrier · dt`, and the SOURCE component
|
|
16
|
+
* is sampled at the traced position on its own lattice.
|
|
17
|
+
*
|
|
18
|
+
* PINNED faces (either adjacent cell solid — see
|
|
19
|
+
* {@link v3_mac_compute_face_solid}) pass their source value through
|
|
20
|
+
* unchanged: their velocity is a boundary condition owned by the projection,
|
|
21
|
+
* not transported state.
|
|
22
|
+
*
|
|
23
|
+
* Back-traces are CLIPPED against solid cells ({@link v3_mac_clip_trace}):
|
|
24
|
+
* a trace stops at the wall instead of sampling momentum from inside it or
|
|
25
|
+
* from a disconnected pocket on the far side. Sampling the face lattices
|
|
26
|
+
* near walls needs no masking — pinned faces hold valid boundary values.
|
|
27
|
+
*
|
|
28
|
+
* Position convention (cell-center coordinates): x-face `(i, j, k)` sits at
|
|
29
|
+
* `(i − 0.5, j, k)`; sampling component grids therefore offsets by +0.5 on
|
|
30
|
+
* the component's own axis. All positions clamp at the lattice edges.
|
|
31
|
+
*
|
|
32
|
+
* Aliasing: `outputs[i]` must not alias `sources[i]`, and outputs must not
|
|
33
|
+
* alias ANY carrier lattice — the per-face carrier reconstruction samples the
|
|
34
|
+
* OTHER components' lattices spatially, so an output lattice written earlier
|
|
35
|
+
* in the kernel would be re-read as carrier data later. Pass a copy when the
|
|
36
|
+
* carrier is the field being advected into (the simulator's reflection path
|
|
37
|
+
* does exactly that).
|
|
38
|
+
*
|
|
39
|
+
* @param {Float32Array[]} outputs `[out_u, out_v, out_w]` face grids. Mutated.
|
|
40
|
+
* @param {Float32Array[]} sources `[src_u, src_v, src_w]`. Read.
|
|
41
|
+
* @param {Float32Array[]} carrier `[car_u, car_v, car_w]`. Drives the traces.
|
|
42
|
+
* @param {number} res_x
|
|
43
|
+
* @param {number} res_y
|
|
44
|
+
* @param {number} res_z
|
|
45
|
+
* @param {number} time_delta
|
|
46
|
+
* @param {Uint8Array} face_solid_x Pinned-face masks, one per lattice.
|
|
47
|
+
* @param {Uint8Array} face_solid_y
|
|
48
|
+
* @param {Uint8Array} face_solid_z
|
|
49
|
+
* @param {Uint8Array} solid Cell-centered solid flags (trace clipping).
|
|
50
|
+
*/
|
|
51
|
+
export function v3_mac_advect_sl_velocity(outputs, sources, carrier, res_x, res_y, res_z, time_delta, face_solid_x, face_solid_y, face_solid_z, solid) {
|
|
52
|
+
assert.equal(outputs.length, 3, "outputs is a 3-component array");
|
|
53
|
+
assert.equal(sources.length, 3, "sources is a 3-component array");
|
|
54
|
+
assert.equal(carrier.length, 3, "carrier is a 3-component array");
|
|
55
|
+
|
|
56
|
+
const out_u = outputs[0], out_v = outputs[1], out_w = outputs[2];
|
|
57
|
+
const src_u = sources[0], src_v = sources[1], src_w = sources[2];
|
|
58
|
+
const car_u = carrier[0], car_v = carrier[1], car_w = carrier[2];
|
|
59
|
+
|
|
60
|
+
assert.notEqual(out_u, src_u, "out_u must not alias src_u");
|
|
61
|
+
assert.notEqual(out_v, src_v, "out_v must not alias src_v");
|
|
62
|
+
assert.notEqual(out_w, src_w, "out_w must not alias src_w");
|
|
63
|
+
|
|
64
|
+
const sx = res_x + 1;
|
|
65
|
+
const sy = res_y + 1;
|
|
66
|
+
const sz = res_z + 1;
|
|
67
|
+
const cell_slice = res_x * res_y;
|
|
68
|
+
const u_slice = sx * res_y;
|
|
69
|
+
const v_slice = res_x * sy;
|
|
70
|
+
const last_cx = res_x - 1;
|
|
71
|
+
const last_cy = res_y - 1;
|
|
72
|
+
const last_cz = res_z - 1;
|
|
73
|
+
|
|
74
|
+
// Cross-component carrier reconstruction: a face position is grid-aligned
|
|
75
|
+
// in both cross axes, so the "trilinear sample" of the other lattice
|
|
76
|
+
// degenerates to the classic MAC 4-tap average with fixed ¼ weights —
|
|
77
|
+
// computed inline below instead of paying the general sampler. Only the
|
|
78
|
+
// tap index along the face's own axis needs edge clamping (the face
|
|
79
|
+
// lattice extends half a cell past the cross lattice there); the trilinear
|
|
80
|
+
// clamp the sampler would apply reduces to the same duplicated tap.
|
|
81
|
+
|
|
82
|
+
// ─── u (x-faces) ─────────────────────────────────────────────────────────
|
|
83
|
+
for (let z = 0; z < res_z; z++) {
|
|
84
|
+
const f_z = z * u_slice;
|
|
85
|
+
const v_z = z * v_slice;
|
|
86
|
+
const w_z = z * cell_slice;
|
|
87
|
+
for (let y = 0; y < res_y; y++) {
|
|
88
|
+
const f_y = f_z + y * sx;
|
|
89
|
+
const v_lo = v_z + y * res_x;
|
|
90
|
+
const v_hi = v_lo + res_x;
|
|
91
|
+
const w_lo = w_z + y * res_x;
|
|
92
|
+
const w_hi = w_lo + cell_slice;
|
|
93
|
+
for (let x = 0; x <= res_x; x++) {
|
|
94
|
+
const f = f_y + x;
|
|
95
|
+
if (face_solid_x[f] !== 0) {
|
|
96
|
+
out_u[f] = src_u[f];
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
// Cross taps straddle the face in x: cells x−1 and x, clamped
|
|
100
|
+
// to the cross lattice's x range [0, res_x−1].
|
|
101
|
+
const xa = x > 0 ? x - 1 : 0;
|
|
102
|
+
const xb = x < res_x ? x : last_cx;
|
|
103
|
+
const cu = car_u[f];
|
|
104
|
+
const cv = 0.25 * (car_v[v_lo + xa] + car_v[v_lo + xb] + car_v[v_hi + xa] + car_v[v_hi + xb]);
|
|
105
|
+
const cw = 0.25 * (car_w[w_lo + xa] + car_w[w_lo + xb] + car_w[w_hi + xa] + car_w[w_hi + xb]);
|
|
106
|
+
// Face position is (x − 0.5, y, z) in cell coords. Inline the
|
|
107
|
+
// end-cell fluid test (see v3_mac_clip_trace performance
|
|
108
|
+
// contract); the loop-bearing clip helper runs only on a hit.
|
|
109
|
+
const ex = x - 0.5 - cu * time_delta;
|
|
110
|
+
const ey = y - cv * time_delta;
|
|
111
|
+
const ez = z - cw * time_delta;
|
|
112
|
+
let ecx = (ex + 0.5) | 0;
|
|
113
|
+
let ecy = (ey + 0.5) | 0;
|
|
114
|
+
let ecz = (ez + 0.5) | 0;
|
|
115
|
+
if (ecx < 0) ecx = 0; else if (ecx >= res_x) ecx = last_cx;
|
|
116
|
+
if (ecy < 0) ecy = 0; else if (ecy >= res_y) ecy = last_cy;
|
|
117
|
+
if (ecz < 0) ecz = 0; else if (ecz >= res_z) ecz = last_cz;
|
|
118
|
+
if (solid[ecz * cell_slice + ecy * res_x + ecx] === 0) {
|
|
119
|
+
out_u[f] = scs3d_sample_linear(src_u, sx, res_y, res_z, ex + 0.5, ey, ez);
|
|
120
|
+
} else {
|
|
121
|
+
v3_mac_clip_trace(scratch_clip, solid, res_x, res_y, res_z,
|
|
122
|
+
x - 0.5, y, z, ex, ey, ez);
|
|
123
|
+
out_u[f] = scs3d_sample_linear(src_u, sx, res_y, res_z,
|
|
124
|
+
scratch_clip[0] + 0.5, scratch_clip[1], scratch_clip[2]);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ─── v (y-faces) ─────────────────────────────────────────────────────────
|
|
131
|
+
for (let z = 0; z < res_z; z++) {
|
|
132
|
+
const f_z = z * v_slice;
|
|
133
|
+
const u_z = z * u_slice;
|
|
134
|
+
const w_z = z * cell_slice;
|
|
135
|
+
for (let y = 0; y <= res_y; y++) {
|
|
136
|
+
const f_y = f_z + y * res_x;
|
|
137
|
+
const ya = y > 0 ? y - 1 : 0;
|
|
138
|
+
const yb = y < res_y ? y : last_cy;
|
|
139
|
+
const u_lo = u_z + ya * sx;
|
|
140
|
+
const u_hi = u_z + yb * sx;
|
|
141
|
+
const w_lo = w_z + ya * res_x;
|
|
142
|
+
const w_hi = w_z + yb * res_x;
|
|
143
|
+
for (let x = 0; x < res_x; x++) {
|
|
144
|
+
const f = f_y + x;
|
|
145
|
+
if (face_solid_y[f] !== 0) {
|
|
146
|
+
out_v[f] = src_v[f];
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const cu = 0.25 * (car_u[u_lo + x] + car_u[u_lo + x + 1] + car_u[u_hi + x] + car_u[u_hi + x + 1]);
|
|
150
|
+
const cv = car_v[f];
|
|
151
|
+
const cw = 0.25 * (car_w[w_lo + x] + car_w[w_lo + x + cell_slice] + car_w[w_hi + x] + car_w[w_hi + x + cell_slice]);
|
|
152
|
+
const ex = x - cu * time_delta;
|
|
153
|
+
const ey = y - 0.5 - cv * time_delta;
|
|
154
|
+
const ez = z - cw * time_delta;
|
|
155
|
+
let ecx = (ex + 0.5) | 0;
|
|
156
|
+
let ecy = (ey + 0.5) | 0;
|
|
157
|
+
let ecz = (ez + 0.5) | 0;
|
|
158
|
+
if (ecx < 0) ecx = 0; else if (ecx >= res_x) ecx = last_cx;
|
|
159
|
+
if (ecy < 0) ecy = 0; else if (ecy >= res_y) ecy = last_cy;
|
|
160
|
+
if (ecz < 0) ecz = 0; else if (ecz >= res_z) ecz = last_cz;
|
|
161
|
+
if (solid[ecz * cell_slice + ecy * res_x + ecx] === 0) {
|
|
162
|
+
out_v[f] = scs3d_sample_linear(src_v, res_x, sy, res_z, ex, ey + 0.5, ez);
|
|
163
|
+
} else {
|
|
164
|
+
v3_mac_clip_trace(scratch_clip, solid, res_x, res_y, res_z,
|
|
165
|
+
x, y - 0.5, z, ex, ey, ez);
|
|
166
|
+
out_v[f] = scs3d_sample_linear(src_v, res_x, sy, res_z,
|
|
167
|
+
scratch_clip[0], scratch_clip[1] + 0.5, scratch_clip[2]);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ─── w (z-faces) ─────────────────────────────────────────────────────────
|
|
174
|
+
for (let z = 0; z <= res_z; z++) {
|
|
175
|
+
const f_z = z * cell_slice;
|
|
176
|
+
const za = z > 0 ? z - 1 : 0;
|
|
177
|
+
const zb = z < res_z ? z : last_cz;
|
|
178
|
+
const u_lo = za * u_slice;
|
|
179
|
+
const u_hi = zb * u_slice;
|
|
180
|
+
const v_lo = za * v_slice;
|
|
181
|
+
const v_hi = zb * v_slice;
|
|
182
|
+
for (let y = 0; y < res_y; y++) {
|
|
183
|
+
const f_y = f_z + y * res_x;
|
|
184
|
+
const u_row_lo = u_lo + y * sx;
|
|
185
|
+
const u_row_hi = u_hi + y * sx;
|
|
186
|
+
const v_row_lo = v_lo + y * res_x;
|
|
187
|
+
const v_row_hi = v_hi + y * res_x;
|
|
188
|
+
for (let x = 0; x < res_x; x++) {
|
|
189
|
+
const f = f_y + x;
|
|
190
|
+
if (face_solid_z[f] !== 0) {
|
|
191
|
+
out_w[f] = src_w[f];
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const cu = 0.25 * (car_u[u_row_lo + x] + car_u[u_row_lo + x + 1] + car_u[u_row_hi + x] + car_u[u_row_hi + x + 1]);
|
|
195
|
+
const cv = 0.25 * (car_v[v_row_lo + x] + car_v[v_row_lo + x + res_x] + car_v[v_row_hi + x] + car_v[v_row_hi + x + res_x]);
|
|
196
|
+
const cw = car_w[f];
|
|
197
|
+
const ex = x - cu * time_delta;
|
|
198
|
+
const ey = y - cv * time_delta;
|
|
199
|
+
const ez = z - 0.5 - cw * time_delta;
|
|
200
|
+
let ecx = (ex + 0.5) | 0;
|
|
201
|
+
let ecy = (ey + 0.5) | 0;
|
|
202
|
+
let ecz = (ez + 0.5) | 0;
|
|
203
|
+
if (ecx < 0) ecx = 0; else if (ecx >= res_x) ecx = last_cx;
|
|
204
|
+
if (ecy < 0) ecy = 0; else if (ecy >= res_y) ecy = last_cy;
|
|
205
|
+
if (ecz < 0) ecz = 0; else if (ecz >= res_z) ecz = last_cz;
|
|
206
|
+
if (solid[ecz * cell_slice + ecy * res_x + ecx] === 0) {
|
|
207
|
+
out_w[f] = scs3d_sample_linear(src_w, res_x, res_y, sz, ex, ey, ez + 0.5);
|
|
208
|
+
} else {
|
|
209
|
+
v3_mac_clip_trace(scratch_clip, solid, res_x, res_y, res_z,
|
|
210
|
+
x, y, z - 0.5, ex, ey, ez);
|
|
211
|
+
out_w[f] = scs3d_sample_linear(src_w, res_x, res_y, sz,
|
|
212
|
+
scratch_clip[0], scratch_clip[1], scratch_clip[2] + 0.5);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vorticity confinement (Fedkiw, Stam, Jensen 2001) for a MAC velocity field.
|
|
3
|
+
*
|
|
4
|
+
* Three passes:
|
|
5
|
+
*
|
|
6
|
+
* 1. average the face velocities to cell centers (exact on the staggered
|
|
7
|
+
* layout: two faces per axis),
|
|
8
|
+
* 2. ω = ∇×v and |ω| at centers by central differences (interior cells;
|
|
9
|
+
* the boundary ring and solid cells get 0),
|
|
10
|
+
* 3. the confinement force f = ε·(N × ω), N = ∇|ω|/|∇|ω||, is computed per
|
|
11
|
+
* cell and scattered to the cell's six faces at half weight each —
|
|
12
|
+
* interior faces accumulate halves from both neighbours, recovering the
|
|
13
|
+
* face-centered average of the cell forces. Pinned faces (adjacent
|
|
14
|
+
* solid) receive nothing.
|
|
15
|
+
*
|
|
16
|
+
* See {@link v3_grid_apply_vorticity_confinement} for the scheme rationale
|
|
17
|
+
* and the energy note (confinement ADDS energy; pair with damping).
|
|
18
|
+
*
|
|
19
|
+
* @param {Float32Array} vel_u Mutated in place, (res_x+1)·res_y·res_z.
|
|
20
|
+
* @param {Float32Array} vel_v Mutated in place.
|
|
21
|
+
* @param {Float32Array} vel_w Mutated in place.
|
|
22
|
+
* @param {Uint8Array} face_solid_x Pinned-face masks.
|
|
23
|
+
* @param {Uint8Array} face_solid_y
|
|
24
|
+
* @param {Uint8Array} face_solid_z
|
|
25
|
+
* @param {Float32Array} scratch_cu Cell-count working buffers (callers may
|
|
26
|
+
* pass larger, e.g. face-sized, arrays). All mutated.
|
|
27
|
+
* @param {Float32Array} scratch_cv
|
|
28
|
+
* @param {Float32Array} scratch_cw
|
|
29
|
+
* @param {Float32Array} scratch_wx
|
|
30
|
+
* @param {Float32Array} scratch_wy
|
|
31
|
+
* @param {Float32Array} scratch_wz
|
|
32
|
+
* @param {Float32Array} scratch_wmag
|
|
33
|
+
* @param {number} res_x
|
|
34
|
+
* @param {number} res_y
|
|
35
|
+
* @param {number} res_z
|
|
36
|
+
* @param {number} epsilon_dt Confinement strength ε times the timestep.
|
|
37
|
+
* @param {Uint8Array} solid
|
|
38
|
+
*/
|
|
39
|
+
export function v3_mac_apply_vorticity_confinement(vel_u: Float32Array, vel_v: Float32Array, vel_w: Float32Array, face_solid_x: Uint8Array, face_solid_y: Uint8Array, face_solid_z: Uint8Array, scratch_cu: Float32Array, scratch_cv: Float32Array, scratch_cw: Float32Array, scratch_wx: Float32Array, scratch_wy: Float32Array, scratch_wz: Float32Array, scratch_wmag: Float32Array, res_x: number, res_y: number, res_z: number, epsilon_dt: number, solid: Uint8Array): void;
|
|
40
|
+
//# sourceMappingURL=v3_mac_apply_vorticity_confinement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v3_mac_apply_vorticity_confinement.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/fluid/solver/v3_mac_apply_vorticity_confinement.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,0DApBW,YAAY,SACZ,YAAY,SACZ,YAAY,gBACZ,UAAU,gBACV,UAAU,gBACV,UAAU,cACV,YAAY,cAEZ,YAAY,cACZ,YAAY,cACZ,YAAY,cACZ,YAAY,cACZ,YAAY,gBACZ,YAAY,SACZ,MAAM,SACN,MAAM,SACN,MAAM,cACN,MAAM,SACN,UAAU,QA8HpB"}
|