@woosh/meep-engine 2.157.0 → 2.158.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/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/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,92 @@
|
|
|
1
|
+
import { clamp } from "../../../../core/math/clamp.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Trilinearly sample a cell-centered scalar at a fractional position,
|
|
5
|
+
* EXCLUDING solid cells from the interpolation: solid taps contribute zero
|
|
6
|
+
* weight and the remaining weights are renormalized. This is the
|
|
7
|
+
* free-boundary-aware sampling of Bridson §3.x — without it, a back-trace
|
|
8
|
+
* landing NEAR a wall blends the frozen in-wall value into the fluid, which
|
|
9
|
+
* reads as dye bleeding out of (or being eaten by) walls.
|
|
10
|
+
*
|
|
11
|
+
* When every tap is solid (a trace clipped into a fully walled corner) there
|
|
12
|
+
* is nothing valid to interpolate; `fallback` — typically the destination
|
|
13
|
+
* cell's own previous value, i.e. "no transport this step" — is returned.
|
|
14
|
+
*
|
|
15
|
+
* Positions clamp to the cell lattice exactly like
|
|
16
|
+
* {@link scs3d_sample_linear}; with a zero-filled `solid` grid the result
|
|
17
|
+
* matches that sampler up to floating-point reassociation (the weights sum
|
|
18
|
+
* to 1 and renormalization divides by it).
|
|
19
|
+
*
|
|
20
|
+
* @param {Float32Array} data Cell-centered scalar field.
|
|
21
|
+
* @param {Uint8Array} solid Cell-centered solid flags.
|
|
22
|
+
* @param {number} res_x
|
|
23
|
+
* @param {number} res_y
|
|
24
|
+
* @param {number} res_z
|
|
25
|
+
* @param {number} x
|
|
26
|
+
* @param {number} y
|
|
27
|
+
* @param {number} z
|
|
28
|
+
* @param {number} fallback Returned when no fluid tap is available.
|
|
29
|
+
* @return {number}
|
|
30
|
+
*/
|
|
31
|
+
export function v3_grid_sample_scalar_masked(data, solid, res_x, res_y, res_z, x, y, z, fallback) {
|
|
32
|
+
const x_max = res_x - 1;
|
|
33
|
+
const y_max = res_y - 1;
|
|
34
|
+
const z_max = res_z - 1;
|
|
35
|
+
|
|
36
|
+
const x_clamped = clamp(x, 0, x_max);
|
|
37
|
+
const y_clamped = clamp(y, 0, y_max);
|
|
38
|
+
const z_clamped = clamp(z, 0, z_max);
|
|
39
|
+
|
|
40
|
+
const x0 = x_clamped | 0;
|
|
41
|
+
const y0 = y_clamped | 0;
|
|
42
|
+
const z0 = z_clamped | 0;
|
|
43
|
+
|
|
44
|
+
const f_x = x_clamped - x0;
|
|
45
|
+
const f_y = y_clamped - y0;
|
|
46
|
+
const f_z = z_clamped - z0;
|
|
47
|
+
|
|
48
|
+
const x1 = x_clamped === x0 ? x0 : x0 + 1;
|
|
49
|
+
const y1 = y_clamped === y0 ? y0 : y0 + 1;
|
|
50
|
+
const z1 = z_clamped === z0 ? z0 : z0 + 1;
|
|
51
|
+
|
|
52
|
+
const slice = res_x * res_y;
|
|
53
|
+
const z0_off = z0 * slice;
|
|
54
|
+
const z1_off = z1 * slice;
|
|
55
|
+
const y0_off = y0 * res_x;
|
|
56
|
+
const y1_off = y1 * res_x;
|
|
57
|
+
|
|
58
|
+
const c000 = z0_off + y0_off + x0;
|
|
59
|
+
const c100 = z0_off + y0_off + x1;
|
|
60
|
+
const c010 = z0_off + y1_off + x0;
|
|
61
|
+
const c110 = z0_off + y1_off + x1;
|
|
62
|
+
const c001 = z1_off + y0_off + x0;
|
|
63
|
+
const c101 = z1_off + y0_off + x1;
|
|
64
|
+
const c011 = z1_off + y1_off + x0;
|
|
65
|
+
const c111 = z1_off + y1_off + x1;
|
|
66
|
+
|
|
67
|
+
const gx = 1 - f_x;
|
|
68
|
+
const gy = 1 - f_y;
|
|
69
|
+
const gz = 1 - f_z;
|
|
70
|
+
|
|
71
|
+
// Per-tap trilinear weight, zeroed when the tap cell is solid.
|
|
72
|
+
const w000 = solid[c000] !== 0 ? 0 : gx * gy * gz;
|
|
73
|
+
const w100 = solid[c100] !== 0 ? 0 : f_x * gy * gz;
|
|
74
|
+
const w010 = solid[c010] !== 0 ? 0 : gx * f_y * gz;
|
|
75
|
+
const w110 = solid[c110] !== 0 ? 0 : f_x * f_y * gz;
|
|
76
|
+
const w001 = solid[c001] !== 0 ? 0 : gx * gy * f_z;
|
|
77
|
+
const w101 = solid[c101] !== 0 ? 0 : f_x * gy * f_z;
|
|
78
|
+
const w011 = solid[c011] !== 0 ? 0 : gx * f_y * f_z;
|
|
79
|
+
const w111 = solid[c111] !== 0 ? 0 : f_x * f_y * f_z;
|
|
80
|
+
|
|
81
|
+
const total = w000 + w100 + w010 + w110 + w001 + w101 + w011 + w111;
|
|
82
|
+
if (total <= 0) {
|
|
83
|
+
return fallback;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const sum = w000 * data[c000] + w100 * data[c100]
|
|
87
|
+
+ w010 * data[c010] + w110 * data[c110]
|
|
88
|
+
+ w001 * data[c001] + w101 * data[c101]
|
|
89
|
+
+ w011 * data[c011] + w111 * data[c111];
|
|
90
|
+
|
|
91
|
+
return sum / total;
|
|
92
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unconditionally stable MacCormack advection of a MAC (face-centered)
|
|
3
|
+
* velocity field — the staggered counterpart of
|
|
4
|
+
* {@link v3_grid_advect_maccormack_velocity}. Per component lattice:
|
|
5
|
+
*
|
|
6
|
+
* 1. forward: û = SL(src, back-trace by carrier·dt)
|
|
7
|
+
* 2. backward: ū = SL(û, trace by +carrier·dt)
|
|
8
|
+
* 3. correct: u* = û + ½·(src − ū)
|
|
9
|
+
* 4. limit: clamp to min/max of the 8 src faces around the back-traced
|
|
10
|
+
* position (Selle et al. 2008 monotone limiter; pinned faces
|
|
11
|
+
* participate — they hold valid boundary values)
|
|
12
|
+
*
|
|
13
|
+
* Solid handling matches {@link v3_mac_advect_sl_velocity}: PINNED faces pass
|
|
14
|
+
* their source value through (the projection owns them as boundary
|
|
15
|
+
* conditions), and every trace — backward in both passes, forward in the
|
|
16
|
+
* corrector — is CLIPPED against solid cells so momentum is never sampled
|
|
17
|
+
* from inside a wall or through one.
|
|
18
|
+
*
|
|
19
|
+
* The cross-component carrier reconstruction uses the exact fixed 4-tap MAC
|
|
20
|
+
* averages (face positions are grid-aligned in the cross axes — see the SL
|
|
21
|
+
* kernel), recomputed per pass; the carrier does not change between passes
|
|
22
|
+
* so both passes see identical trace vectors.
|
|
23
|
+
*
|
|
24
|
+
* Aliasing: per component, `out`, `src` and `fwd` must be distinct, and
|
|
25
|
+
* outputs must not alias any carrier lattice (cross-component carrier
|
|
26
|
+
* sampling — see {@link v3_mac_advect_sl_velocity}).
|
|
27
|
+
*
|
|
28
|
+
* @param {Float32Array[]} outputs `[out_u, out_v, out_w]`. Mutated.
|
|
29
|
+
* @param {Float32Array[]} sources `[src_u, src_v, src_w]`. Read.
|
|
30
|
+
* @param {Float32Array[]} carrier `[car_u, car_v, car_w]`.
|
|
31
|
+
* @param {Float32Array[]} forward_scratch `[fwd_u, fwd_v, fwd_w]`. Mutated.
|
|
32
|
+
* @param {number} res_x
|
|
33
|
+
* @param {number} res_y
|
|
34
|
+
* @param {number} res_z
|
|
35
|
+
* @param {number} time_delta
|
|
36
|
+
* @param {Uint8Array} face_solid_x
|
|
37
|
+
* @param {Uint8Array} face_solid_y
|
|
38
|
+
* @param {Uint8Array} face_solid_z
|
|
39
|
+
* @param {Uint8Array} solid Cell-centered solid flags (trace clipping).
|
|
40
|
+
*/
|
|
41
|
+
export function v3_mac_advect_maccormack_velocity(outputs: Float32Array[], sources: Float32Array[], carrier: Float32Array[], forward_scratch: 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;
|
|
42
|
+
//# sourceMappingURL=v3_mac_advect_maccormack_velocity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v3_mac_advect_maccormack_velocity.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/fluid/solver/v3_mac_advect_maccormack_velocity.js"],"names":[],"mappings":"AAiCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,2DAbW,YAAY,EAAE,WACd,YAAY,EAAE,WACd,YAAY,EAAE,mBACd,YAAY,EAAE,SACd,MAAM,SACN,MAAM,SACN,MAAM,cACN,MAAM,gBACN,UAAU,gBACV,UAAU,gBACV,UAAU,SACV,UAAU,QA0MpB"}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { assert } from "../../../../core/assert.js";
|
|
2
|
+
import { clamp } from "../../../../core/math/clamp.js";
|
|
3
|
+
import { scs3d_sample_linear } from "../../../graphics/texture/3d/scs3d_sample_linear.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
|
+
* Unconditionally stable MacCormack advection of a MAC (face-centered)
|
|
36
|
+
* velocity field — the staggered counterpart of
|
|
37
|
+
* {@link v3_grid_advect_maccormack_velocity}. Per component lattice:
|
|
38
|
+
*
|
|
39
|
+
* 1. forward: û = SL(src, back-trace by carrier·dt)
|
|
40
|
+
* 2. backward: ū = SL(û, trace by +carrier·dt)
|
|
41
|
+
* 3. correct: u* = û + ½·(src − ū)
|
|
42
|
+
* 4. limit: clamp to min/max of the 8 src faces around the back-traced
|
|
43
|
+
* position (Selle et al. 2008 monotone limiter; pinned faces
|
|
44
|
+
* participate — they hold valid boundary values)
|
|
45
|
+
*
|
|
46
|
+
* Solid handling matches {@link v3_mac_advect_sl_velocity}: PINNED faces pass
|
|
47
|
+
* their source value through (the projection owns them as boundary
|
|
48
|
+
* conditions), and every trace — backward in both passes, forward in the
|
|
49
|
+
* corrector — is CLIPPED against solid cells so momentum is never sampled
|
|
50
|
+
* from inside a wall or through one.
|
|
51
|
+
*
|
|
52
|
+
* The cross-component carrier reconstruction uses the exact fixed 4-tap MAC
|
|
53
|
+
* averages (face positions are grid-aligned in the cross axes — see the SL
|
|
54
|
+
* kernel), recomputed per pass; the carrier does not change between passes
|
|
55
|
+
* so both passes see identical trace vectors.
|
|
56
|
+
*
|
|
57
|
+
* Aliasing: per component, `out`, `src` and `fwd` must be distinct, and
|
|
58
|
+
* outputs must not alias any carrier lattice (cross-component carrier
|
|
59
|
+
* sampling — see {@link v3_mac_advect_sl_velocity}).
|
|
60
|
+
*
|
|
61
|
+
* @param {Float32Array[]} outputs `[out_u, out_v, out_w]`. Mutated.
|
|
62
|
+
* @param {Float32Array[]} sources `[src_u, src_v, src_w]`. Read.
|
|
63
|
+
* @param {Float32Array[]} carrier `[car_u, car_v, car_w]`.
|
|
64
|
+
* @param {Float32Array[]} forward_scratch `[fwd_u, fwd_v, fwd_w]`. Mutated.
|
|
65
|
+
* @param {number} res_x
|
|
66
|
+
* @param {number} res_y
|
|
67
|
+
* @param {number} res_z
|
|
68
|
+
* @param {number} time_delta
|
|
69
|
+
* @param {Uint8Array} face_solid_x
|
|
70
|
+
* @param {Uint8Array} face_solid_y
|
|
71
|
+
* @param {Uint8Array} face_solid_z
|
|
72
|
+
* @param {Uint8Array} solid Cell-centered solid flags (trace clipping).
|
|
73
|
+
*/
|
|
74
|
+
export function v3_mac_advect_maccormack_velocity(outputs, sources, carrier, forward_scratch, res_x, res_y, res_z, time_delta, face_solid_x, face_solid_y, face_solid_z, solid) {
|
|
75
|
+
assert.equal(outputs.length, 3, "outputs is a 3-component array");
|
|
76
|
+
assert.equal(sources.length, 3, "sources is a 3-component array");
|
|
77
|
+
assert.equal(carrier.length, 3, "carrier is a 3-component array");
|
|
78
|
+
assert.equal(forward_scratch.length, 3, "forward_scratch is a 3-component array");
|
|
79
|
+
|
|
80
|
+
const out_u = outputs[0], out_v = outputs[1], out_w = outputs[2];
|
|
81
|
+
const src_u = sources[0], src_v = sources[1], src_w = sources[2];
|
|
82
|
+
const car_u = carrier[0], car_v = carrier[1], car_w = carrier[2];
|
|
83
|
+
const fwd_u = forward_scratch[0], fwd_v = forward_scratch[1], fwd_w = forward_scratch[2];
|
|
84
|
+
|
|
85
|
+
assert.notEqual(out_u, src_u, "out_u must not alias src_u");
|
|
86
|
+
assert.notEqual(out_u, fwd_u, "out_u must not alias fwd_u");
|
|
87
|
+
assert.notEqual(src_u, fwd_u, "src_u must not alias fwd_u");
|
|
88
|
+
|
|
89
|
+
const sx = res_x + 1;
|
|
90
|
+
const sy = res_y + 1;
|
|
91
|
+
const sz = res_z + 1;
|
|
92
|
+
const cell_slice = res_x * res_y;
|
|
93
|
+
const u_slice = sx * res_y;
|
|
94
|
+
const v_slice = res_x * sy;
|
|
95
|
+
const last_cx = res_x - 1;
|
|
96
|
+
const last_cy = res_y - 1;
|
|
97
|
+
const last_cz = res_z - 1;
|
|
98
|
+
const dt = time_delta;
|
|
99
|
+
|
|
100
|
+
// ─── u (x-faces): face (x,y,z) sits at (x − 0.5, y, z) ──────────────────
|
|
101
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
102
|
+
for (let z = 0; z < res_z; z++) {
|
|
103
|
+
const f_z = z * u_slice;
|
|
104
|
+
const v_z = z * v_slice;
|
|
105
|
+
const w_z = z * cell_slice;
|
|
106
|
+
for (let y = 0; y < res_y; y++) {
|
|
107
|
+
const f_y = f_z + y * sx;
|
|
108
|
+
const v_lo = v_z + y * res_x;
|
|
109
|
+
const v_hi = v_lo + res_x;
|
|
110
|
+
const w_lo = w_z + y * res_x;
|
|
111
|
+
const w_hi = w_lo + cell_slice;
|
|
112
|
+
for (let x = 0; x <= res_x; x++) {
|
|
113
|
+
const f = f_y + x;
|
|
114
|
+
if (face_solid_x[f] !== 0) {
|
|
115
|
+
if (pass === 0) fwd_u[f] = src_u[f];
|
|
116
|
+
else out_u[f] = src_u[f];
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const xa = x > 0 ? x - 1 : 0;
|
|
120
|
+
const xb = x < res_x ? x : last_cx;
|
|
121
|
+
const cu = car_u[f];
|
|
122
|
+
const cv = 0.25 * (car_v[v_lo + xa] + car_v[v_lo + xb] + car_v[v_hi + xa] + car_v[v_hi + xb]);
|
|
123
|
+
const cw = 0.25 * (car_w[w_lo + xa] + car_w[w_lo + xb] + car_w[w_hi + xa] + car_w[w_hi + xb]);
|
|
124
|
+
const px = x - 0.5;
|
|
125
|
+
|
|
126
|
+
// Back-trace end (shared by pass A and the limiter).
|
|
127
|
+
let bx = px - cu * dt;
|
|
128
|
+
let by = y - cv * dt;
|
|
129
|
+
let bz = z - cw * dt;
|
|
130
|
+
if (end_in_solid(solid, res_x, res_y, res_z, bx, by, bz)) {
|
|
131
|
+
v3_mac_clip_trace(scratch_clip, solid, res_x, res_y, res_z, px, y, z, bx, by, bz);
|
|
132
|
+
bx = scratch_clip[0]; by = scratch_clip[1]; bz = scratch_clip[2];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (pass === 0) {
|
|
136
|
+
fwd_u[f] = scs3d_sample_linear(src_u, sx, res_y, res_z, bx + 0.5, by, bz);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Forward probe through the forward result.
|
|
141
|
+
let qx = px + cu * dt;
|
|
142
|
+
let qy = y + cv * dt;
|
|
143
|
+
let qz = z + cw * dt;
|
|
144
|
+
if (end_in_solid(solid, res_x, res_y, res_z, qx, qy, qz)) {
|
|
145
|
+
v3_mac_clip_trace(scratch_clip, solid, res_x, res_y, res_z, px, y, z, qx, qy, qz);
|
|
146
|
+
qx = scratch_clip[0]; qy = scratch_clip[1]; qz = scratch_clip[2];
|
|
147
|
+
}
|
|
148
|
+
const backward = scs3d_sample_linear(fwd_u, sx, res_y, res_z, qx + 0.5, qy, qz);
|
|
149
|
+
const corrected = fwd_u[f] + 0.5 * (src_u[f] - backward);
|
|
150
|
+
|
|
151
|
+
out_u[f] = limit8(corrected, src_u,
|
|
152
|
+
bx + 0.5, by, bz,
|
|
153
|
+
sx, res_y, res_z, sx, u_slice);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ─── v (y-faces): face (x,y,z) sits at (x, y − 0.5, z) ──────────────────
|
|
160
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
161
|
+
for (let z = 0; z < res_z; z++) {
|
|
162
|
+
const f_z = z * v_slice;
|
|
163
|
+
const u_z = z * u_slice;
|
|
164
|
+
const w_z = z * cell_slice;
|
|
165
|
+
for (let y = 0; y <= res_y; y++) {
|
|
166
|
+
const f_y = f_z + y * res_x;
|
|
167
|
+
const ya = y > 0 ? y - 1 : 0;
|
|
168
|
+
const yb = y < res_y ? y : last_cy;
|
|
169
|
+
const u_lo = u_z + ya * sx;
|
|
170
|
+
const u_hi = u_z + yb * sx;
|
|
171
|
+
const w_lo = w_z + ya * res_x;
|
|
172
|
+
const w_hi = w_z + yb * res_x;
|
|
173
|
+
for (let x = 0; x < res_x; x++) {
|
|
174
|
+
const f = f_y + x;
|
|
175
|
+
if (face_solid_y[f] !== 0) {
|
|
176
|
+
if (pass === 0) fwd_v[f] = src_v[f];
|
|
177
|
+
else out_v[f] = src_v[f];
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
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]);
|
|
181
|
+
const cv = car_v[f];
|
|
182
|
+
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]);
|
|
183
|
+
const py = y - 0.5;
|
|
184
|
+
|
|
185
|
+
let bx = x - cu * dt;
|
|
186
|
+
let by = py - cv * dt;
|
|
187
|
+
let bz = z - cw * dt;
|
|
188
|
+
if (end_in_solid(solid, res_x, res_y, res_z, bx, by, bz)) {
|
|
189
|
+
v3_mac_clip_trace(scratch_clip, solid, res_x, res_y, res_z, x, py, z, bx, by, bz);
|
|
190
|
+
bx = scratch_clip[0]; by = scratch_clip[1]; bz = scratch_clip[2];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (pass === 0) {
|
|
194
|
+
fwd_v[f] = scs3d_sample_linear(src_v, res_x, sy, res_z, bx, by + 0.5, bz);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let qx = x + cu * dt;
|
|
199
|
+
let qy = py + cv * dt;
|
|
200
|
+
let qz = z + cw * dt;
|
|
201
|
+
if (end_in_solid(solid, res_x, res_y, res_z, qx, qy, qz)) {
|
|
202
|
+
v3_mac_clip_trace(scratch_clip, solid, res_x, res_y, res_z, x, py, z, qx, qy, qz);
|
|
203
|
+
qx = scratch_clip[0]; qy = scratch_clip[1]; qz = scratch_clip[2];
|
|
204
|
+
}
|
|
205
|
+
const backward = scs3d_sample_linear(fwd_v, res_x, sy, res_z, qx, qy + 0.5, qz);
|
|
206
|
+
const corrected = fwd_v[f] + 0.5 * (src_v[f] - backward);
|
|
207
|
+
|
|
208
|
+
out_v[f] = limit8(corrected, src_v,
|
|
209
|
+
bx, by + 0.5, bz,
|
|
210
|
+
res_x, sy, res_z, res_x, v_slice);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ─── w (z-faces): face (x,y,z) sits at (x, y, z − 0.5) ──────────────────
|
|
217
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
218
|
+
for (let z = 0; z <= res_z; z++) {
|
|
219
|
+
const f_z = z * cell_slice;
|
|
220
|
+
const za = z > 0 ? z - 1 : 0;
|
|
221
|
+
const zb = z < res_z ? z : last_cz;
|
|
222
|
+
const u_lo = za * u_slice;
|
|
223
|
+
const u_hi = zb * u_slice;
|
|
224
|
+
const v_lo = za * v_slice;
|
|
225
|
+
const v_hi = zb * v_slice;
|
|
226
|
+
for (let y = 0; y < res_y; y++) {
|
|
227
|
+
const f_y = f_z + y * res_x;
|
|
228
|
+
const u_row_lo = u_lo + y * sx;
|
|
229
|
+
const u_row_hi = u_hi + y * sx;
|
|
230
|
+
const v_row_lo = v_lo + y * res_x;
|
|
231
|
+
const v_row_hi = v_hi + y * res_x;
|
|
232
|
+
for (let x = 0; x < res_x; x++) {
|
|
233
|
+
const f = f_y + x;
|
|
234
|
+
if (face_solid_z[f] !== 0) {
|
|
235
|
+
if (pass === 0) fwd_w[f] = src_w[f];
|
|
236
|
+
else out_w[f] = src_w[f];
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
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]);
|
|
240
|
+
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]);
|
|
241
|
+
const cw = car_w[f];
|
|
242
|
+
const pz = z - 0.5;
|
|
243
|
+
|
|
244
|
+
let bx = x - cu * dt;
|
|
245
|
+
let by = y - cv * dt;
|
|
246
|
+
let bz = pz - cw * dt;
|
|
247
|
+
if (end_in_solid(solid, res_x, res_y, res_z, bx, by, bz)) {
|
|
248
|
+
v3_mac_clip_trace(scratch_clip, solid, res_x, res_y, res_z, x, y, pz, bx, by, bz);
|
|
249
|
+
bx = scratch_clip[0]; by = scratch_clip[1]; bz = scratch_clip[2];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (pass === 0) {
|
|
253
|
+
fwd_w[f] = scs3d_sample_linear(src_w, res_x, res_y, sz, bx, by, bz + 0.5);
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
let qx = x + cu * dt;
|
|
258
|
+
let qy = y + cv * dt;
|
|
259
|
+
let qz = pz + cw * dt;
|
|
260
|
+
if (end_in_solid(solid, res_x, res_y, res_z, qx, qy, qz)) {
|
|
261
|
+
v3_mac_clip_trace(scratch_clip, solid, res_x, res_y, res_z, x, y, pz, qx, qy, qz);
|
|
262
|
+
qx = scratch_clip[0]; qy = scratch_clip[1]; qz = scratch_clip[2];
|
|
263
|
+
}
|
|
264
|
+
const backward = scs3d_sample_linear(fwd_w, res_x, res_y, sz, qx, qy, qz + 0.5);
|
|
265
|
+
const corrected = fwd_w[f] + 0.5 * (src_w[f] - backward);
|
|
266
|
+
|
|
267
|
+
out_w[f] = limit8(corrected, src_w,
|
|
268
|
+
bx, by, bz + 0.5,
|
|
269
|
+
res_x, res_y, sz, res_x, cell_slice);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Clamp `value` to the min/max of the 8 lattice taps around the fractional
|
|
278
|
+
* position `(lx, ly, lz)` in the lattice's own index space (clamped to its
|
|
279
|
+
* bounds — the same corner set the trilinear sampler interpolates from).
|
|
280
|
+
*
|
|
281
|
+
* @param {number} value
|
|
282
|
+
* @param {Float32Array} data
|
|
283
|
+
* @param {number} lx
|
|
284
|
+
* @param {number} ly
|
|
285
|
+
* @param {number} lz
|
|
286
|
+
* @param {number} dim_x
|
|
287
|
+
* @param {number} dim_y
|
|
288
|
+
* @param {number} dim_z
|
|
289
|
+
* @param {number} row lattice row stride
|
|
290
|
+
* @param {number} slice lattice slice stride
|
|
291
|
+
* @return {number}
|
|
292
|
+
*/
|
|
293
|
+
function limit8(value, data, lx, ly, lz, dim_x, dim_y, dim_z, row, slice) {
|
|
294
|
+
const bx = clamp(lx, 0, dim_x - 1);
|
|
295
|
+
const by = clamp(ly, 0, dim_y - 1);
|
|
296
|
+
const bz = clamp(lz, 0, dim_z - 1);
|
|
297
|
+
const x0 = bx | 0;
|
|
298
|
+
const y0 = by | 0;
|
|
299
|
+
const z0 = bz | 0;
|
|
300
|
+
const x1 = bx === x0 ? x0 : x0 + 1;
|
|
301
|
+
const y1 = by === y0 ? y0 : y0 + 1;
|
|
302
|
+
const z1 = bz === z0 ? z0 : z0 + 1;
|
|
303
|
+
|
|
304
|
+
const z0_off = z0 * slice;
|
|
305
|
+
const z1_off = z1 * slice;
|
|
306
|
+
const y0_off = y0 * row;
|
|
307
|
+
const y1_off = y1 * row;
|
|
308
|
+
|
|
309
|
+
let mn = data[z0_off + y0_off + x0];
|
|
310
|
+
let mx = mn;
|
|
311
|
+
let v = data[z0_off + y0_off + x1]; if (v < mn) mn = v; else if (v > mx) mx = v;
|
|
312
|
+
v = data[z0_off + y1_off + x0]; if (v < mn) mn = v; else if (v > mx) mx = v;
|
|
313
|
+
v = data[z0_off + y1_off + x1]; if (v < mn) mn = v; else if (v > mx) mx = v;
|
|
314
|
+
v = data[z1_off + y0_off + x0]; if (v < mn) mn = v; else if (v > mx) mx = v;
|
|
315
|
+
v = data[z1_off + y0_off + x1]; if (v < mn) mn = v; else if (v > mx) mx = v;
|
|
316
|
+
v = data[z1_off + y1_off + x0]; if (v < mn) mn = v; else if (v > mx) mx = v;
|
|
317
|
+
v = data[z1_off + y1_off + x1]; if (v < mn) mn = v; else if (v > mx) mx = v;
|
|
318
|
+
return value < mn ? mn : (value > mx ? mx : value);
|
|
319
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semi-Lagrangian advection of a cell-centered scalar by a MAC velocity
|
|
3
|
+
* field, solid-aware on both ends of the trace:
|
|
4
|
+
*
|
|
5
|
+
* - the back-trace segment is CLIPPED against solid cells
|
|
6
|
+
* ({@link v3_mac_clip_trace}) so a trace can neither sample inside a wall
|
|
7
|
+
* nor tunnel through a thin one into a disconnected pocket;
|
|
8
|
+
* - the sample at the (clipped) source position uses solid-MASKED
|
|
9
|
+
* interpolation ({@link v3_grid_sample_scalar_masked}) so taps inside
|
|
10
|
+
* walls — frozen values from whenever the cell solidified — contribute
|
|
11
|
+
* nothing. Without this, walls slowly leak their frozen contents into
|
|
12
|
+
* passing fluid and absorb dye from it (the stage-5 scalar-mass defect).
|
|
13
|
+
*
|
|
14
|
+
* The carrier velocity at each cell center is the average of the cell's two
|
|
15
|
+
* faces per axis (exact on the staggered layout). Solid destination cells
|
|
16
|
+
* pass their value through unchanged.
|
|
17
|
+
*
|
|
18
|
+
* @param {Float32Array} output Cell-centered destination. Mutated.
|
|
19
|
+
* @param {Float32Array} input Cell-centered source. Must not alias output.
|
|
20
|
+
* @param {Float32Array} vel_u x-face velocities, (res_x+1)·res_y·res_z.
|
|
21
|
+
* @param {Float32Array} vel_v y-face velocities.
|
|
22
|
+
* @param {Float32Array} vel_w z-face velocities.
|
|
23
|
+
* @param {number} res_x
|
|
24
|
+
* @param {number} res_y
|
|
25
|
+
* @param {number} res_z
|
|
26
|
+
* @param {number} time_delta
|
|
27
|
+
* @param {Uint8Array} solid
|
|
28
|
+
*/
|
|
29
|
+
export function v3_mac_advect_sl_scalar(output: Float32Array, input: Float32Array, vel_u: Float32Array, vel_v: Float32Array, vel_w: Float32Array, res_x: number, res_y: number, res_z: number, time_delta: number, solid: Uint8Array): void;
|
|
30
|
+
/**
|
|
31
|
+
* Unconditionally stable MacCormack advection of a cell-centered scalar by a
|
|
32
|
+
* MAC velocity field — forward + backward semi-Lagrangian passes, error
|
|
33
|
+
* correction, and Selle's monotone min-max limiter. Solid-aware throughout:
|
|
34
|
+
* both passes clip their traces and sample solid-masked (see
|
|
35
|
+
* {@link v3_mac_advect_sl_scalar}), and the limiter takes its extrema over
|
|
36
|
+
* the FLUID corners around the back-traced position only — when none are
|
|
37
|
+
* fluid the corrected value is discarded in favour of the first-order
|
|
38
|
+
* forward result.
|
|
39
|
+
*
|
|
40
|
+
* @param {Float32Array} output Destination. Mutated.
|
|
41
|
+
* @param {Float32Array} input Source. NOT mutated.
|
|
42
|
+
* @param {Float32Array} forward_scratch Working buffer for the forward pass.
|
|
43
|
+
* @param {Float32Array} vel_u
|
|
44
|
+
* @param {Float32Array} vel_v
|
|
45
|
+
* @param {Float32Array} vel_w
|
|
46
|
+
* @param {number} res_x
|
|
47
|
+
* @param {number} res_y
|
|
48
|
+
* @param {number} res_z
|
|
49
|
+
* @param {number} time_delta
|
|
50
|
+
* @param {Uint8Array} solid
|
|
51
|
+
*/
|
|
52
|
+
export function v3_mac_advect_maccormack_scalar(output: Float32Array, input: Float32Array, forward_scratch: Float32Array, vel_u: Float32Array, vel_v: Float32Array, vel_w: Float32Array, res_x: number, res_y: number, res_z: number, time_delta: number, solid: Uint8Array): void;
|
|
53
|
+
//# sourceMappingURL=v3_mac_advect_scalar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v3_mac_advect_scalar.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/fluid/solver/v3_mac_advect_scalar.js"],"names":[],"mappings":"AAiCA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,gDAXW,YAAY,SACZ,YAAY,SACZ,YAAY,SACZ,YAAY,SACZ,YAAY,SACZ,MAAM,SACN,MAAM,SACN,MAAM,cACN,MAAM,SACN,UAAU,QA4CpB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wDAZW,YAAY,SACZ,YAAY,mBACZ,YAAY,SACZ,YAAY,SACZ,YAAY,SACZ,YAAY,SACZ,MAAM,SACN,MAAM,SACN,MAAM,cACN,MAAM,SACN,UAAU,QA8GpB"}
|