This commit is contained in:
Felix Klement committed 2026-09-18 16:04:05 +02:00
commit 4cda1ca41b
72 files changed
+8169

No files matched your search

+107
View File
@@ -0,0 +1,107 @@
---
/**
* Grid objects: wireframe solids drawn as technical line drawings.
* Single stroke in currentColor, even width, no fills. The geometry is computed
* at build time so the markup stays small and the stroke stays exact.
* Kinds: sphere (a model seen from every side), cube (a built structure),
* plane (one element lifted out for inspection), wave (a signal over time).
*/
interface Props { kind: 'sphere' | 'cube' | 'plane' | 'wave'; class?: string }
const { kind, class: className } = Astro.props;
const S = 400;
const f = (n: number) => Math.round(n * 10) / 10;
const poly = (pts: [number, number][]) => pts.map(([x, y]) => `${f(x)},${f(y)}`).join(' ');
/* Isometric projection of a 3D point (x right, y up, z towards the viewer's left). */
const ISO = { cx: S / 2, cy: S / 2, c30: Math.cos(Math.PI / 6), s30: Math.sin(Math.PI / 6) };
const iso = (x: number, y: number, z: number, cy = ISO.cy): [number, number] =>
[ISO.cx + (x - z) * ISO.c30, cy + (x + z) * ISO.s30 - y];
const lines: string[] = [];
if (kind === 'sphere') {
const R = 150, tilt = (22 * Math.PI) / 180, N = 48;
const p3 = (lat: number, lon: number): [number, number] => {
const x = R * Math.cos(lat) * Math.sin(lon);
const y = R * Math.sin(lat);
const z = R * Math.cos(lat) * Math.cos(lon);
const y2 = y * Math.cos(tilt) - z * Math.sin(tilt);
return [ISO.cx + x, ISO.cy - y2];
};
// Parallels every 20 degrees, meridians every 22.5 degrees (8 great circles).
for (let d = -80; d <= 80; d += 20) {
const lat = (d * Math.PI) / 180;
const pts: [number, number][] = [];
for (let i = 0; i <= N; i++) pts.push(p3(lat, (i / N) * 2 * Math.PI));
lines.push(poly(pts));
}
for (let m = 0; m < 8; m++) {
const lon = (m * Math.PI) / 8;
const pts: [number, number][] = [];
for (let i = 0; i <= N; i++) pts.push(p3((i / N) * 2 * Math.PI - Math.PI, lon));
lines.push(poly(pts));
}
lines.push(`circle:${R}`);
}
if (kind === 'cube') {
const n = 4, u = 46, L = n * u, cy = ISO.cy + L * 0.5;
for (let i = 0; i <= n; i++) {
const t = i * u;
// top face (y = L)
lines.push(poly([iso(t, L, 0, cy), iso(t, L, L, cy)]));
lines.push(poly([iso(0, L, t, cy), iso(L, L, t, cy)]));
// front-right face (z = 0): x by y
lines.push(poly([iso(t, 0, 0, cy), iso(t, L, 0, cy)]));
lines.push(poly([iso(0, t, 0, cy), iso(L, t, 0, cy)]));
// front-left face (x = 0): z by y
lines.push(poly([iso(0, 0, t, cy), iso(0, L, t, cy)]));
lines.push(poly([iso(0, t, 0, cy), iso(0, t, L, cy)]));
}
}
if (kind === 'plane') {
const n = 6, u = 34, L = n * u, cy = ISO.cy + 20, h = 96, tx = 2, tz = 2;
for (let i = 0; i <= n; i++) {
const t = i * u;
lines.push(poly([iso(t, 0, 0, cy), iso(t, 0, L, cy)]));
lines.push(poly([iso(0, 0, t, cy), iso(L, 0, t, cy)]));
}
const c: [number, number][] = [[tx, tz], [tx + 1, tz], [tx + 1, tz + 1], [tx, tz + 1]];
const top = c.map(([x, z]) => iso(x * u, h, z * u, cy));
lines.push(poly([...top, top[0]]));
c.forEach(([x, z], i) => lines.push(poly([iso(x * u, 0, z * u, cy), top[i]])));
}
if (kind === 'wave') {
const nx = 12, nz = 8, u = 26, cy = ISO.cy + 10, A = 26;
const yAt = (x: number, z: number) => A * Math.sin((x / nx) * Math.PI * 2 - 0.6) * Math.cos((z / nz) * Math.PI * 0.9);
const P = (x: number, z: number) => iso((x - nx / 2) * u, yAt(x, z), (z - nz / 2) * u, cy);
for (let z = 0; z <= nz; z++) {
const pts: [number, number][] = [];
for (let x = 0; x <= nx; x++) pts.push(P(x, z));
lines.push(poly(pts));
}
for (let x = 0; x <= nx; x++) {
const pts: [number, number][] = [];
for (let z = 0; z <= nz; z++) pts.push(P(x, z));
lines.push(poly(pts));
}
}
---
<svg class:list={['grid-object', className]} viewBox={`0 0 ${S} ${S}`} aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg">
<g fill="none" stroke="currentColor" stroke-width="1.25" stroke-linejoin="round" stroke-linecap="round" vector-effect="non-scaling-stroke">
{lines.map((l) => l.startsWith('circle:')
? <circle cx={S / 2} cy={S / 2} r={Number(l.slice(7))} />
: <polyline points={l} />)}
</g>
</svg>
<style>
.grid-object {
width: 100%;
height: auto;
color: var(--ink);
}
</style>