Skip to content

Commit fb28283

Browse files
committed
Random color map
1 parent f1365fa commit fb28283

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed

src/pages/notes/random-color.tsx

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import { useRef, useEffect } from "react";
2+
3+
function randInt(min: number, max: number) {
4+
return Math.floor(Math.random() * (max - min) + min);
5+
}
6+
7+
function shuffle<T>(arr: T[]) {
8+
for (let i = arr.length - 1; i >= 0; i--) {
9+
const j = randInt(0, i + 1);
10+
const temp = arr[i]!;
11+
arr[i] = arr[j]!;
12+
arr[j] = temp;
13+
}
14+
}
15+
16+
type Coord3 = { x: number; y: number; z: number };
17+
type Coord2 = { x: number; y: number };
18+
19+
function mapCoord2(
20+
coord: Coord2,
21+
fn: (x: number, k: "x" | "y") => number,
22+
): Coord2 {
23+
return { x: fn(coord.x, "x"), y: fn(coord.y, "y") };
24+
}
25+
26+
function mapCoord3(
27+
coord: Coord3,
28+
fn: (x: number, k: "x" | "y" | "z") => number,
29+
): Coord3 {
30+
return { x: fn(coord.x, "x"), y: fn(coord.y, "y"), z: fn(coord.z, "z") };
31+
}
32+
33+
const dirs2D: Coord2[] = [
34+
{ x: -1, y: 0 },
35+
{ x: 0, y: -1 },
36+
{ x: 0, y: 1 },
37+
{ x: 1, y: 0 },
38+
];
39+
40+
const dirs3D: Coord3[] = [
41+
{ x: -1, y: 0, z: 0 },
42+
{ x: 0, y: -1, z: 0 },
43+
{ x: 0, y: 0, z: -1 },
44+
{ x: 1, y: 0, z: 0 },
45+
{ x: 0, y: 1, z: 0 },
46+
{ x: 0, y: 0, z: 1 },
47+
];
48+
49+
function* traverseRGB(width: number, type: "BFS" | "DFS") {
50+
const stk: Coord3[] = [];
51+
stk.push({
52+
x: randInt(0, width),
53+
y: randInt(0, width),
54+
z: randInt(0, width),
55+
});
56+
const vis = Array.from({ length: width }, () =>
57+
Array.from({ length: width }, () =>
58+
Array.from({ length: width }, () => false),
59+
),
60+
);
61+
while (stk.length > 0) {
62+
const p = stk[type === "BFS" ? "shift" : "pop"]()!;
63+
if (!vis[p.x]![p.y]![p.z]) {
64+
yield mapCoord3(p, (x) => x / width);
65+
vis[p.x]![p.y]![p.z] = true;
66+
}
67+
shuffle(dirs3D);
68+
for (const dir of dirs3D) {
69+
const pp = mapCoord3(p, (x, k) => x + dir[k]);
70+
if (
71+
Object.values(pp).every((x) => x >= 0 && x < width) &&
72+
!vis[pp.x]![pp.y]![pp.z]
73+
)
74+
stk.push(pp);
75+
}
76+
}
77+
}
78+
79+
function* traversePixel(width: number, type: "BFS" | "DFS") {
80+
const stk: Coord2[] = [];
81+
stk.push({
82+
x: randInt(0, width),
83+
y: randInt(0, width),
84+
});
85+
const vis = Array.from({ length: width }, () =>
86+
Array.from({ length: width }, () => false),
87+
);
88+
while (stk.length > 0) {
89+
const p = stk[type === "BFS" ? "shift" : "pop"]()!;
90+
if (!vis[p.x]![p.y]) {
91+
yield p;
92+
vis[p.x]![p.y] = true;
93+
}
94+
shuffle(dirs2D);
95+
for (const dir of dirs2D) {
96+
const pp = mapCoord2(p, (x, k) => x + dir[k]);
97+
if (
98+
Object.values(pp).every((x) => x >= 0 && x < width) &&
99+
!vis[pp.x]![pp.y]
100+
)
101+
stk.push(pp);
102+
}
103+
}
104+
}
105+
106+
const rgbWidth = 256 / 4;
107+
const pictureWidth = Math.floor(Math.sqrt(rgbWidth ** 3));
108+
const canvasWidth = 800;
109+
const scale = canvasWidth / pictureWidth;
110+
111+
export default function RandomColor(): JSX.Element {
112+
const canvasRef = useRef<HTMLCanvasElement>(null);
113+
const rafRef = useRef<number>();
114+
useEffect(() => {
115+
const ctx = canvasRef.current!.getContext("2d")!;
116+
const iter = traversePixel(pictureWidth, "DFS");
117+
const rgbPath = traverseRGB(rgbWidth, "DFS");
118+
rafRef.current = requestAnimationFrame(function draw() {
119+
for (let i = 0; i < 200; i++) {
120+
const nextPixel = iter.next();
121+
const nextRGB = rgbPath.next();
122+
if (nextPixel.done || nextRGB.done) return;
123+
const p = nextPixel.value;
124+
const [r, g, b] = Object.values(nextRGB.value).map((x) =>
125+
Math.floor(x * 255),
126+
);
127+
ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;
128+
ctx.fillRect(p.x * scale, p.y * scale, scale, scale);
129+
}
130+
rafRef.current = requestAnimationFrame(draw);
131+
});
132+
return () => cancelAnimationFrame(rafRef.current!);
133+
}, []);
134+
return <canvas ref={canvasRef} width={canvasWidth} height={canvasWidth} />;
135+
}
136+
137+
export const meta = {
138+
title: "Random Color",
139+
description: "Generates a map of random RGB colors",
140+
};

0 commit comments

Comments
 (0)