3
3
import { PerlinNoise2D } from './PerlinNoise' ;
4
4
5
5
export interface IFluidSimState {
6
- canvas : HTMLCanvasElement ;
6
+ running : boolean ;
7
7
sim : IFluidSim ;
8
- targetDefs : ICanvasTargetDef [ ] ;
8
+ canvasTemp : HTMLCanvasElement ;
9
9
}
10
10
11
11
export interface ICanvasTargetDef {
@@ -16,6 +16,7 @@ export interface ICanvasTargetDef {
16
16
export interface IFluidSim {
17
17
width : number ;
18
18
height : number ;
19
+ numPressureIterations : number ;
19
20
cells : Float32Array ; // 2d array of floats, with values for (temperature, density, velocityX, velocityY)
20
21
21
22
cells2 : Float32Array ;
@@ -29,13 +30,15 @@ export interface IFluidSim {
29
30
cellSize : number ;
30
31
31
32
aggregates : ISimAggregates | null ;
33
+
34
+ iterCount : number ;
32
35
}
33
36
34
- export function initFluidSimState ( canvas : HTMLCanvasElement ) : IFluidSimState {
37
+ export function initFluidSimState ( ) : IFluidSimState {
35
38
return {
36
- canvas : canvas ,
37
39
sim : initFluidSim ( 64 , 64 ) ,
38
- targetDefs : [ ] ,
40
+ running : false ,
41
+ canvasTemp : document . createElement ( 'canvas' ) ,
39
42
} ;
40
43
}
41
44
@@ -49,6 +52,7 @@ export function initFluidSim(w: number, h: number): IFluidSim {
49
52
let sim : IFluidSim = {
50
53
width,
51
54
height,
55
+ numPressureIterations : 1 ,
52
56
cells : new Float32Array ( width * height * 4 ) ,
53
57
cells2 : new Float32Array ( width * height * 4 ) ,
54
58
cells3 : new Float32Array ( width * height * 4 ) ,
@@ -58,13 +62,15 @@ export function initFluidSim(w: number, h: number): IFluidSim {
58
62
divergence1 : new Float32Array ( width * height ) ,
59
63
cellSize,
60
64
aggregates : null ,
65
+ iterCount : 0 ,
61
66
} ;
62
67
63
68
let scaleX = 4 / width ;
64
69
let scaleY = 4 / height ;
65
70
let perlin = new PerlinNoise2D ( 4 ) ;
66
71
let perlinVelX = new PerlinNoise2D ( 7 ) ;
67
72
let perlinVelY = new PerlinNoise2D ( 8 ) ;
73
+ let maxVelX = 0 ;
68
74
for ( let y = 0 ; y < height ; y ++ ) {
69
75
for ( let x = 0 ; x < width ; x ++ ) {
70
76
sim . cells [ ( y * width + x ) * 4 + 0 ] = perlin . octaveNoise ( ( x + 10 ) * scaleX , ( y + 10 ) * scaleY , 5 , 0.8 ) ; // temperature
@@ -78,15 +84,18 @@ export function initFluidSim(w: number, h: number): IFluidSim {
78
84
// units: m/s
79
85
// a rectangular region of velocity, in the center of the screen, pointing up and to the right a bit
80
86
if ( x > width * 4 / 10 && x < width * 6 / 10 && y > height * 4 / 10 && y < height * 6 / 10 ) {
81
- velX = 1.5 / 100 ; // 1.5 m/s
82
- velY = 1.5 / 100 ;
87
+ velX = 4 / 100 ; // 0.4 cm/s
88
+ velY = 4 / 100 ;
89
+ maxVelX = Math . max ( maxVelX , velX ) ;
83
90
}
84
91
85
92
sim . cells [ ( y * width + x ) * 4 + 2 ] = velX ;
86
93
sim . cells [ ( y * width + x ) * 4 + 3 ] = velY ;
87
94
}
88
95
}
89
96
97
+ console . log ( `maxVelX: ${ maxVelX } , cellSize: ${ cellSize } , maxVelX / cellSize: ${ maxVelX / cellSize } ` ) ;
98
+
90
99
fixBoundaries ( sim ) ;
91
100
computeAggregateValues ( sim ) ;
92
101
@@ -147,7 +156,9 @@ export function stepFluidSim(sim: IFluidSim, dtMs: number) {
147
156
148
157
// advection
149
158
150
- if ( false ) {
159
+ if ( sim . iterCount > 0 ) {
160
+ let maxDx = 0 ;
161
+ let maxVx = 0 ;
151
162
for ( let y = 0 ; y < sim . height ; y ++ ) {
152
163
for ( let x = 0 ; x < sim . width ; x ++ ) {
153
164
let vX = getCell ( sim . cells , y , x , 2 ) ;
@@ -157,6 +168,9 @@ export function stepFluidSim(sim: IFluidSim, dtMs: number) {
157
168
let x0 = x - vX * dt / sim . cellSize ;
158
169
let y0 = y - vY * dt / sim . cellSize ;
159
170
171
+ maxDx = Math . max ( maxDx , Math . abs ( x0 - x ) ) ;
172
+ maxVx = Math . max ( maxVx , Math . abs ( vX ) ) ;
173
+
160
174
// get all the state variables at that point
161
175
let tempSrc = getCellBilinearClamped ( sim . cells , y0 , x0 , 0 ) ;
162
176
let densitySrc = getCellBilinearClamped ( sim . cells , y0 , x0 , 1 ) ;
@@ -169,6 +183,8 @@ export function stepFluidSim(sim: IFluidSim, dtMs: number) {
169
183
setCell ( sim . cells2 , y , x , 3 , velYSrc ) ;
170
184
}
171
185
}
186
+
187
+ // console.log(`maxDx: ${maxDx * 1000/20} (cells/s), maxVx: ${maxVx * 100} (cm/s)`);
172
188
} else {
173
189
sim . cells2 . set ( sim . cells ) ;
174
190
}
@@ -229,8 +245,8 @@ export function stepFluidSim(sim: IFluidSim, dtMs: number) {
229
245
// (we've already got our tentative velocity field in arrFrom, and don't have things like gravity to worry about)
230
246
231
247
// solve pressure-poisson equation
232
- let pressureAlpha = - sim . cellSize * sim . cellSize / ( 4 * dt ) ;
233
- let pressureBeta = 1 ;
248
+ // let pressureAlpha = sim.cellSize * sim.cellSize;
249
+ // let pressureBeta = 1;
234
250
235
251
let pressureFrom = sim . pressure0 ;
236
252
let pressureTo = sim . pressure1 ;
@@ -276,7 +292,7 @@ export function stepFluidSim(sim: IFluidSim, dtMs: number) {
276
292
let pressureRight = getCell0Clamped ( pressureFrom , y , x + 1 ) ;
277
293
278
294
let pressureLaplace = pressureLeft + pressureRight + pressureUp + pressureDown ;
279
- let pressureDst = ( divergence + pressureAlpha * pressureLaplace ) / pressureBeta ;
295
+ let pressureDst = ( pressureLaplace - divergence * sim . cellSize * sim . cellSize ) / 4 ;
280
296
setCell0 ( pressureTo , y , x , pressureDst ) ;
281
297
}
282
298
}
@@ -304,9 +320,12 @@ export function stepFluidSim(sim: IFluidSim, dtMs: number) {
304
320
pressureFrom . fill ( 0 ) ;
305
321
306
322
calcDivergence ( arrFrom , sim . divergence0 ) ;
323
+ sim . divergence1 . set ( sim . divergence0 ) ;
324
+
325
+ let pressureIterCount = sim . iterCount === 0 ? 1024 : 200 ;
307
326
308
327
// iteratively calculate the pressure field
309
- for ( let iter = 0 ; iter < 1000 ; iter ++ ) {
328
+ for ( let iter = 0 ; iter < pressureIterCount ; iter ++ ) {
310
329
calcPressureField ( sim . divergence0 , pressureFrom , pressureTo ) ;
311
330
312
331
// swap arrays
@@ -315,16 +334,19 @@ export function stepFluidSim(sim: IFluidSim, dtMs: number) {
315
334
pressureTo = tmp ;
316
335
317
336
// check for convergence
318
- calcDivergenceFreeVelocityField ( arrFrom , arrTo , pressureFrom ) ;
319
- calcDivergence ( arrTo , sim . divergence1 ) ;
320
- logDivergenceStats ( sim . divergence1 ) ;
337
+ // logDivergenceStats(sim.divergence1);
321
338
}
339
+ calcDivergenceFreeVelocityField ( arrFrom , arrTo , pressureFrom ) ;
340
+ calcDivergence ( arrTo , sim . divergence1 ) ;
322
341
323
342
// now we have the pressure field in pressureFrom
324
343
// use it to calculate the final velocity field, which will be stored in arrFrom (which currently contains the tentative velocity field)
325
344
calcDivergenceFreeVelocityField ( arrFrom , arrFrom , pressureFrom ) ;
326
345
}
327
346
347
+ sim . pressure0 = pressureFrom ;
348
+ sim . pressure1 = pressureTo ;
349
+
328
350
// woo, now we have the complete set of state variables for the next frame in arrFrom
329
351
// so shuffle them around, ensuring arrFrom is stored to the main array (the others are just temporary)
330
352
sim . cells = arrFrom ;
@@ -334,6 +356,8 @@ export function stepFluidSim(sim: IFluidSim, dtMs: number) {
334
356
fixBoundaries ( sim ) ;
335
357
computeAggregateValues ( sim ) ;
336
358
359
+ sim . iterCount += 1 ;
360
+
337
361
console . log ( "step took " + ( performance . now ( ) - startTime ) + "ms" ) ;
338
362
}
339
363
@@ -399,7 +423,7 @@ function computeAggregateValues(sim: IFluidSim): ISimAggregates {
399
423
400
424
sim . aggregates = aggs ;
401
425
402
- console . log ( `aggregates: mass=${ aggs . totalMass . toFixed ( 2 ) } , p_x=${ aggs . totalMomentumX . toFixed ( 2 ) } , p_y=${ aggs . totalMomentumY . toFixed ( 2 ) } , E_k=${ aggs . totalKineticEnergy . toFixed ( 2 ) } , T=${ aggs . averageTemperature . toFixed ( 2 ) } ` ) ;
426
+ // console.log(`aggregates: mass=${aggs.totalMass.toFixed(2)}, p_x=${aggs.totalMomentumX.toFixed(2)}, p_y=${aggs.totalMomentumY.toFixed(2)}, E_k=${aggs.totalKineticEnergy.toFixed(2)}, T=${aggs.averageTemperature.toFixed(2)}`);
403
427
404
428
return aggs ;
405
429
}
0 commit comments