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:
parent
04d3f7d0e8
commit
23d86503b5
|
|
@ -9,7 +9,7 @@ const BLOCKS = [" ", "\u2591", "\u2592", "\u2593", "\u2588"];
|
||||||
|
|
||||||
// Color palette: density maps through cyan -> green -> yellow -> white
|
// Color palette: density maps through cyan -> green -> yellow -> white
|
||||||
function densityToColor(d: number): string {
|
function densityToColor(d: number): string {
|
||||||
if (d < 0.01) return "";
|
if (d < 0.005) return "";
|
||||||
|
|
||||||
const clamped = Math.min(d, 1);
|
const clamped = Math.min(d, 1);
|
||||||
|
|
||||||
|
|
@ -38,9 +38,11 @@ function densityToColor(d: number): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
function densityToBlock(d: number): string {
|
function densityToBlock(d: number): string {
|
||||||
if (d < 0.01) return " ";
|
if (d < 0.005) return " ";
|
||||||
const idx = Math.min(Math.floor(d * BLOCKS.length), BLOCKS.length - 1);
|
if (d < 0.03) return BLOCKS[1]; // ░
|
||||||
return BLOCKS[idx];
|
if (d < 0.1) return BLOCKS[2]; // ▒
|
||||||
|
if (d < 0.3) return BLOCKS[3]; // ▓
|
||||||
|
return BLOCKS[4]; // █
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RenderTarget {
|
export interface RenderTarget {
|
||||||
|
|
|
||||||
|
|
@ -8,19 +8,12 @@ const DESKTOP_GRID = { w: 80, h: 40 };
|
||||||
const MOBILE_GRID = { w: 40, h: 25 };
|
const MOBILE_GRID = { w: 40, h: 25 };
|
||||||
const MOBILE_BREAKPOINT = 768;
|
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 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 grid = isMobile ? MOBILE_GRID : DESKTOP_GRID;
|
||||||
|
|
||||||
const { toggleFaucet, clear } = useFluidSim(preRef, {
|
const { toggleFaucet, clear } = useFluidSim(preRef, {
|
||||||
|
|
@ -33,30 +26,6 @@ export default function FluidSimulation() {
|
||||||
viscosity: 0.000001,
|
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 (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
{/* The simulation canvas */}
|
{/* The simulation canvas */}
|
||||||
|
|
@ -108,3 +77,44 @@ export default function FluidSimulation() {
|
||||||
</div>
|
</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} />;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,14 +76,14 @@ export function useFluidSim(
|
||||||
const time = Date.now() * 0.001;
|
const time = Date.now() * 0.001;
|
||||||
const ax = Math.floor(gridWidth * 0.2);
|
const ax = Math.floor(gridWidth * 0.2);
|
||||||
const ay = Math.floor(gridHeight * 0.8);
|
const ay = Math.floor(gridHeight * 0.8);
|
||||||
addSource(state, ax, ay, 0.05, 2);
|
addSource(state, ax, ay, 0.2, 3);
|
||||||
addVelocity(
|
addVelocity(
|
||||||
state,
|
state,
|
||||||
ax,
|
ax,
|
||||||
ay,
|
ay,
|
||||||
Math.sin(time) * 0.5,
|
Math.sin(time) * 1.0,
|
||||||
-0.5 + Math.cos(time * 0.7) * 0.3,
|
-1.0 + Math.cos(time * 0.7) * 0.5,
|
||||||
2
|
3
|
||||||
);
|
);
|
||||||
|
|
||||||
stepFluid(state, dt, diffusion, viscosity, iterations);
|
stepFluid(state, dt, diffusion, viscosity, iterations);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue