next
This commit is contained in:
1 parent
d97f541738
commit
0c2b4e7df2
16 files changed
+696
-179
No files matched your search
@@ -4,9 +4,11 @@
|
||||
* 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).
|
||||
* plane (one element lifted out for inspection), wave (a signal over time),
|
||||
* steps (a ranked row of columns), nested (a structure inside its own walls),
|
||||
* stack (layers kept one on top of the other).
|
||||
*/
|
||||
interface Props { kind: 'sphere' | 'cube' | 'plane' | 'wave'; class?: string }
|
||||
interface Props { kind: 'sphere' | 'cube' | 'plane' | 'wave' | 'steps' | 'nested' | 'stack'; class?: string }
|
||||
const { kind, class: className } = Astro.props;
|
||||
|
||||
const S = 400;
|
||||
@@ -15,8 +17,29 @@ const poly = (pts: [number, number][]) => pts.map(([x, y]) => `${f(x)},${f(y)}`)
|
||||
|
||||
/* 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 iso = (x: number, y: number, z: number, cy = ISO.cy, cx = ISO.cx): [number, number] =>
|
||||
[cx + (x - z) * ISO.c30, cy + (x + z) * ISO.s30 - y];
|
||||
|
||||
/* The twelve edges of a cuboid with its near-bottom corner at (x0, y0, z0). */
|
||||
function cuboid(x0: number, y0: number, z0: number, L: number, H: number, D: number, cy: number, cx = ISO.cx): string[] {
|
||||
const p = (x: number, y: number, z: number) => iso(x, y, z, cy, cx);
|
||||
const c = [
|
||||
p(x0, y0, z0), p(x0 + L, y0, z0), p(x0 + L, y0, z0 + D), p(x0, y0, z0 + D),
|
||||
p(x0, y0 + H, z0), p(x0 + L, y0 + H, z0), p(x0 + L, y0 + H, z0 + D), p(x0, y0 + H, z0 + D),
|
||||
];
|
||||
const e: [number, number][] = [[0, 1], [1, 2], [2, 3], [3, 0], [4, 5], [5, 6], [6, 7], [7, 4], [0, 4], [1, 5], [2, 6], [3, 7]];
|
||||
return e.map(([a, b]) => poly([c[a], c[b]]));
|
||||
}
|
||||
/* A square grid plane of n cells at height y. */
|
||||
function plane(n: number, u: number, y: number, cy: number, cx = ISO.cx): string[] {
|
||||
const L = n * u, out: string[] = [];
|
||||
for (let i = 0; i <= n; i++) {
|
||||
const t = i * u;
|
||||
out.push(poly([iso(t, y, 0, cy, cx), iso(t, y, L, cy, cx)]));
|
||||
out.push(poly([iso(0, y, t, cy, cx), iso(L, y, t, cy, cx)]));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const lines: string[] = [];
|
||||
|
||||
@@ -29,7 +52,6 @@ if (kind === 'sphere') {
|
||||
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][] = [];
|
||||
@@ -49,13 +71,10 @@ 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)]));
|
||||
}
|
||||
@@ -89,6 +108,40 @@ if (kind === 'wave') {
|
||||
lines.push(poly(pts));
|
||||
}
|
||||
}
|
||||
|
||||
if (kind === 'steps') {
|
||||
/* Five columns in a row, tallest first: a ranked list seen as a skyline. */
|
||||
const heights = [168, 128, 96, 68, 44], w = 40, gap = 14, depth = 40, cy = ISO.cy + 78;
|
||||
const total = heights.length * w + (heights.length - 1) * gap;
|
||||
heights.forEach((h, i) => {
|
||||
const x0 = -total / 2 + i * (w + gap);
|
||||
cuboid(x0, 0, -depth / 2, w, h, depth, cy).forEach((l) => lines.push(l));
|
||||
});
|
||||
const L = total + 48;
|
||||
lines.push(poly([iso(-L / 2, 0, -depth / 2 - 24, cy), iso(L / 2, 0, -depth / 2 - 24, cy)]));
|
||||
}
|
||||
|
||||
if (kind === 'nested') {
|
||||
/* A structure inside its own walls: a cube inside a cube, corners tied together. */
|
||||
const O = 200, I = 96, cy = ISO.cy + 84;
|
||||
cuboid(-O / 2, 0, -O / 2, O, O, O, cy).forEach((l) => lines.push(l));
|
||||
const off = (O - I) / 2;
|
||||
cuboid(-I / 2, off, -I / 2, I, I, I, cy).forEach((l) => lines.push(l));
|
||||
const outer = [[-O / 2, 0, -O / 2], [O / 2, 0, -O / 2], [O / 2, 0, O / 2], [-O / 2, 0, O / 2], [-O / 2, O, -O / 2], [O / 2, O, -O / 2], [O / 2, O, O / 2], [-O / 2, O, O / 2]];
|
||||
const inner = [[-I / 2, off, -I / 2], [I / 2, off, -I / 2], [I / 2, off, I / 2], [-I / 2, off, I / 2], [-I / 2, off + I, -I / 2], [I / 2, off + I, -I / 2], [I / 2, off + I, I / 2], [-I / 2, off + I, I / 2]];
|
||||
outer.forEach(([x, y, z], k) => lines.push(poly([iso(x, y, z, cy), iso(inner[k][0], inner[k][1], inner[k][2], cy)])));
|
||||
}
|
||||
|
||||
if (kind === 'stack') {
|
||||
/* Layers one on top of the other, each a record kept whole. */
|
||||
const n = 4, u = 38, L = n * u, gapY = 46, layers = 4, cy = ISO.cy + 60;
|
||||
for (let k = 0; k < layers; k++) plane(n, u, k * gapY, cy).forEach((l) => lines.push(l));
|
||||
const corners: [number, number][] = [[0, 0], [L, 0], [L, L], [0, L]];
|
||||
corners.forEach(([x, z]) => lines.push(poly([iso(x, 0, z, cy), iso(x, (layers - 1) * gapY, z, cy)])));
|
||||
// shift the whole drawing so the isometric footprint sits centred
|
||||
const dx = -((L - L) * ISO.c30) / 2;
|
||||
void dx;
|
||||
}
|
||||
---
|
||||
<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">
|
||||
|
||||
Reference in new issue
Block a user