| 311 | } |
| 312 | |
| 313 | void FlowFieldFloat::flowAdvect(float dt) { |
| 314 | int w = (int)getWidth(); |
| 315 | int h = (int)getHeight(); |
| 316 | float halfLife = fl::max(mParams.persistence, 0.001f); |
| 317 | float fade = powf(0.5f, dt / halfLife); |
| 318 | float shift = mParams.flow_shift; |
| 319 | |
| 320 | // Pass 1: horizontal row shift. |
| 321 | for (int y = 0; y < h; y++) { |
| 322 | float sh = mYProf[y] * shift; |
| 323 | for (int x = 0; x < w; x++) { |
| 324 | float sx = fmodPos((float)x - sh, (float)w); |
| 325 | int ix0 = (int)floorf(sx) % w; |
| 326 | int ix1 = (ix0 + 1) % w; |
| 327 | float f = sx - floorf(sx); |
| 328 | float inv = 1.0f - f; |
| 329 | int src0 = idx(y, ix0); |
| 330 | int src1 = idx(y, ix1); |
| 331 | int dst = idx(y, x); |
| 332 | mTR[dst] = mR[src0] * inv + mR[src1] * f; |
| 333 | mTG[dst] = mG[src0] * inv + mG[src1] * f; |
| 334 | mTB[dst] = mB[src0] * inv + mB[src1] * f; |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | // Pass 2: vertical column shift + fade. |
| 339 | for (int x = 0; x < w; x++) { |
| 340 | float sh = mXProf[x] * shift; |
| 341 | for (int y = 0; y < h; y++) { |
| 342 | float sy = fmodPos((float)y - sh, (float)h); |
| 343 | int iy0 = (int)floorf(sy) % h; |
| 344 | int iy1 = (iy0 + 1) % h; |
| 345 | float f = sy - floorf(sy); |
| 346 | float inv = 1.0f - f; |
| 347 | int src0 = idx(iy0, x); |
| 348 | int src1 = idx(iy1, x); |
| 349 | int dst = idx(y, x); |
| 350 | mR[dst] = (mTR[src0] * inv + mTR[src1] * f) * fade; |
| 351 | mG[dst] = (mTG[src0] * inv + mTG[src1] * f) * fade; |
| 352 | mB[dst] = (mTB[src0] * inv + mTB[src1] * f) * fade; |
| 353 | } |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | void FlowFieldFloat::drawFlowVectors(fl::span<CRGB> leds) { |
| 358 | int w = (int)getWidth(); |