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
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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} />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue