Fix fluid simulation not starting and invisible rendering

Two bugs: (1) useFluidSim's effect ran before the <pre> ref was available
because the mounted gate rendered a placeholder without the ref. Split into
FluidSimInner child component that only mounts after the DOM is ready.
(2) ASCII renderer thresholds were too high - density 0.01-0.2 produced
colored spaces (invisible). Use explicit thresholds aligned with the color
gradient and bump ambient source intensity for visible activity on load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
kbanc85 2026-02-15 05:44:37 -05:00
parent 04d3f7d0e8
commit 23d86503b5
3 changed files with 56 additions and 44 deletions

View File

@ -9,7 +9,7 @@ const BLOCKS = [" ", "\u2591", "\u2592", "\u2593", "\u2588"];
// Color palette: density maps through cyan -> green -> yellow -> white
function densityToColor(d: number): string {
if (d < 0.01) return "";
if (d < 0.005) return "";
const clamped = Math.min(d, 1);
@ -38,9 +38,11 @@ function densityToColor(d: number): string {
}
function densityToBlock(d: number): string {
if (d < 0.01) return " ";
const idx = Math.min(Math.floor(d * BLOCKS.length), BLOCKS.length - 1);
return BLOCKS[idx];
if (d < 0.005) return " ";
if (d < 0.03) return BLOCKS[1]; // ░
if (d < 0.1) return BLOCKS[2]; // ▒
if (d < 0.3) return BLOCKS[3]; // ▓
return BLOCKS[4]; // █
}
export interface RenderTarget {

View File

@ -8,19 +8,12 @@ const DESKTOP_GRID = { w: 80, h: 40 };
const MOBILE_GRID = { w: 40, h: 25 };
const MOBILE_BREAKPOINT = 768;
export default function FluidSimulation() {
/**
* Inner component that only mounts after the DOM is ready.
* This ensures useFluidSim's effect runs when preRef.current exists.
*/
function FluidSimInner({ isMobile }: { isMobile: boolean }) {
const preRef = useRef<HTMLPreElement>(null);
const [isMobile, setIsMobile] = useState(false);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
const check = () => setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
check();
window.addEventListener("resize", check);
return () => window.removeEventListener("resize", check);
}, []);
const grid = isMobile ? MOBILE_GRID : DESKTOP_GRID;
const { toggleFaucet, clear } = useFluidSim(preRef, {
@ -33,30 +26,6 @@ export default function FluidSimulation() {
viscosity: 0.000001,
});
if (!mounted) {
return (
<div className="w-full border border-border bg-background">
<pre
className="text-xs leading-none select-none overflow-hidden p-0 m-0"
style={{
fontSize: isMobile ? "10px" : "11px",
lineHeight: "1",
}}
aria-hidden="true"
>
{Array(grid.h)
.fill(null)
.map((_, j) => (
<span key={j}>
{" ".repeat(grid.w)}
{j < grid.h - 1 ? "\n" : ""}
</span>
))}
</pre>
</div>
);
}
return (
<div className="w-full">
{/* The simulation canvas */}
@ -108,3 +77,44 @@ export default function FluidSimulation() {
</div>
);
}
export default function FluidSimulation() {
const [isMobile, setIsMobile] = useState(false);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
const check = () => setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
check();
window.addEventListener("resize", check);
return () => window.removeEventListener("resize", check);
}, []);
const grid = isMobile ? MOBILE_GRID : DESKTOP_GRID;
if (!mounted) {
return (
<div className="w-full border border-border bg-background">
<pre
className="text-xs leading-none select-none overflow-hidden p-0 m-0"
style={{
fontSize: "11px",
lineHeight: "1",
}}
aria-hidden="true"
>
{Array(grid.h)
.fill(null)
.map((_, j) => (
<span key={j}>
{" ".repeat(grid.w)}
{j < grid.h - 1 ? "\n" : ""}
</span>
))}
</pre>
</div>
);
}
return <FluidSimInner isMobile={isMobile} />;
}

View File

@ -76,14 +76,14 @@ export function useFluidSim(
const time = Date.now() * 0.001;
const ax = Math.floor(gridWidth * 0.2);
const ay = Math.floor(gridHeight * 0.8);
addSource(state, ax, ay, 0.05, 2);
addSource(state, ax, ay, 0.2, 3);
addVelocity(
state,
ax,
ay,
Math.sin(time) * 0.5,
-0.5 + Math.cos(time * 0.7) * 0.3,
2
Math.sin(time) * 1.0,
-1.0 + Math.cos(time * 0.7) * 0.5,
3
);
stepFluid(state, dt, diffusion, viscosity, iterations);