Organize the lighting project and ship the combined XCHC show.

Group workspaces, generators, stage map, and tests into clear folders, and add the unified cans+movers show plus a simple shared/individual RGBW control board.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-08 00:59:36 +12:00
co-authored by Cursor
parent c92094cc26
commit 5eb623d6af
17 changed files with 7457 additions and 1195 deletions
+279
View File
@@ -0,0 +1,279 @@
import {
Card,
CardBody,
CardHeader,
H1,
H2,
Stack,
Table,
Text,
useHostTheme,
} from "cursor/canvas";
/** Y=0 = row of 4; +Y toward audience. Side cans on walls at ±3.5 m. */
const WALL = 3.5;
const FIXTURES: {
key: string;
label: string;
kind: "can" | "mover";
x: number;
y: number;
}[] = [
{ key: "c3", label: "#3", kind: "can", x: -1.8, y: 0.0 },
{ key: "c4", label: "#4", kind: "can", x: -0.6, y: 0.0 },
{ key: "c5", label: "#5", kind: "can", x: 0.6, y: 0.0 },
{ key: "c6", label: "#6", kind: "can", x: 1.8, y: 0.0 },
{ key: "c2", label: "#2", kind: "can", x: -WALL, y: 1.2 },
{ key: "c7", label: "#7", kind: "can", x: WALL, y: 1.2 },
{ key: "m1", label: "M1", kind: "mover", x: -0.3, y: 1.1 },
{ key: "m2", label: "M2", kind: "mover", x: 0.3, y: 1.1 },
{ key: "c1", label: "#1", kind: "can", x: -WALL, y: 2.4 },
{ key: "c8", label: "#8", kind: "can", x: WALL, y: 2.4 },
];
const BUILDING_W = 7;
const DEPTH = 2.4;
export default function StageMap() {
const theme = useHostTheme();
const pad = 0.55;
const x0 = -BUILDING_W / 2 - pad;
const x1 = BUILDING_W / 2 + pad;
const y0 = -0.45;
const y1 = DEPTH + 0.55;
const vbW = x1 - x0;
const vbH = y1 - y0;
const px = 88;
const svgW = vbW * px;
const svgH = vbH * px;
const sx = (x: number) => ((x - x0) / vbW) * svgW;
const sy = (y: number) => ((y1 - y) / vbH) * svgH;
const dim = theme.text.tertiary;
const ink = theme.text.primary;
const accent = theme.accent.primary;
const canFill = theme.fill.tertiary;
const stroke = theme.stroke.primary;
const onAccent = theme.text.onAccent;
function DimH(
xA: number,
xB: number,
y: number,
label: string,
side: "above" | "below" = "above",
) {
const ya = sy(y);
const x1p = sx(Math.min(xA, xB));
const x2p = sx(Math.max(xA, xB));
const mid = (x1p + x2p) / 2;
const ty = side === "above" ? ya - 8 : ya + 14;
return (
<g stroke={dim} fill={dim} strokeWidth={1}>
<line x1={x1p} y1={ya} x2={x2p} y2={ya} />
<line x1={x1p} y1={ya - 4} x2={x1p} y2={ya + 4} />
<line x1={x2p} y1={ya - 4} x2={x2p} y2={ya + 4} />
<text x={mid} y={ty} textAnchor="middle" fontSize={11} fill={dim} stroke="none">
{label}
</text>
</g>
);
}
function DimV(
yA: number,
yB: number,
x: number,
label: string,
side: "left" | "right" = "right",
) {
const xa = sx(x);
const y1p = sy(Math.max(yA, yB));
const y2p = sy(Math.min(yA, yB));
const mid = (y1p + y2p) / 2;
const tx = side === "right" ? xa + 8 : xa - 8;
return (
<g stroke={dim} fill={dim} strokeWidth={1}>
<line x1={xa} y1={y1p} x2={xa} y2={y2p} />
<line x1={xa - 4} y1={y1p} x2={xa + 4} y2={y1p} />
<line x1={xa - 4} y1={y2p} x2={xa + 4} y2={y2p} />
<text
x={tx}
y={mid + 4}
textAnchor={side === "right" ? "start" : "end"}
fontSize={11}
fill={dim}
stroke="none"
>
{label}
</text>
</g>
);
}
return (
<Stack gap={20} style={{ padding: 20, maxWidth: 960 }}>
<Stack gap={6}>
<H1>Stage map</H1>
<Text tone="secondary">
Building {BUILDING_W} m wide · walls at ±{WALL} m · Y = 0 at row of 4 ·
+Y toward audience
</Text>
</Stack>
<Card>
<CardHeader>Plan (audience at bottom)</CardHeader>
<CardBody>
<svg
width="100%"
viewBox={`0 0 ${svgW} ${svgH}`}
style={{ display: "block", maxWidth: svgW, background: theme.bg.editor }}
>
<rect
x={sx(-BUILDING_W / 2)}
y={sy(DEPTH)}
width={BUILDING_W * px}
height={DEPTH * px}
fill="none"
stroke={stroke}
strokeWidth={2}
/>
{/* Wall labels */}
<text
x={sx(-WALL) + 4}
y={sy(1.8)}
fontSize={10}
fill={dim}
stroke="none"
transform={`rotate(-90 ${sx(-WALL) + 4} ${sy(1.8)})`}
>
3.5 m from centre
</text>
<text
x={sx(WALL) - 4}
y={sy(1.8)}
fontSize={10}
fill={dim}
stroke="none"
textAnchor="end"
transform={`rotate(90 ${sx(WALL) - 4} ${sy(1.8)})`}
>
3.5 m from centre
</text>
{DimH(0, -WALL, 1.8, "3.5 m", "above")}
{DimH(0, WALL, 1.8, "3.5 m", "below")}
<line
x1={sx(0)}
y1={sy(-0.15)}
x2={sx(0)}
y2={sy(DEPTH + 0.2)}
stroke={dim}
strokeWidth={1}
strokeDasharray="3 5"
/>
<text x={sx(0) + 6} y={sy(-0.05)} fontSize={10} fill={dim} stroke="none">
centre
</text>
<text
x={sx(0)}
y={sy(DEPTH + 0.38)}
textAnchor="middle"
fontSize={12}
fill={ink}
fontWeight={600}
stroke="none"
>
AUDIENCE
</text>
<text
x={sx(0)}
y={sy(-0.28)}
textAnchor="middle"
fontSize={11}
fill={dim}
stroke="none"
>
row of 4
</text>
{DimH(-3.5, 3.5, DEPTH + 0.32, "7.0 m building", "below")}
{DimH(0, 0.6, 0, "0.6", "above")}
{DimH(0, 1.8, 0, "1.8", "above")}
{DimH(0, -0.6, 0, "0.6", "below")}
{DimH(0, -1.8, 0, "1.8", "below")}
{DimH(0, 0.3, 1.1, "0.3", "above")}
{DimV(0, 1.1, 2.4, "1.1 m movers")}
{DimV(0, 1.2, 2.4, "1.2 m")}
{DimV(0, 2.4, -2.4, "2.4 m", "left")}
{FIXTURES.map((f) => {
const cx = sx(f.x);
const cy = sy(f.y);
const r = f.kind === "mover" ? 14 : 12;
return (
<g key={f.key}>
{f.kind === "mover" ? (
<rect
x={cx - r}
y={cy - r}
width={r * 2}
height={r * 2}
rx={3}
fill={accent}
stroke={stroke}
strokeWidth={1}
/>
) : (
<circle
cx={cx}
cy={cy}
r={r}
fill={canFill}
stroke={stroke}
strokeWidth={1.5}
/>
)}
<text
x={cx}
y={cy + 4}
textAnchor="middle"
fontSize={11}
fontWeight={600}
fill={f.kind === "mover" ? onAccent : ink}
stroke="none"
>
{f.label}
</text>
</g>
);
})}
</svg>
</CardBody>
</Card>
<Stack gap={12}>
<H2>Coordinates</H2>
<Table
headers={["Fixture", "Type", "X (m)", "Y (m)", "Notes"]}
rows={[
["#3", "Can", "−1.8", "0.0", "Row of 4"],
["#4", "Can", "−0.6", "0.0", "Row of 4"],
["#5", "Can", "+0.6", "0.0", "Row of 4"],
["#6", "Can", "+1.8", "0.0", "Row of 4"],
["#2", "Can", "−3.5", "1.2", "Side · 3.5 m from centre · 1.2 m in front of the 4"],
["#7", "Can", "+3.5", "1.2", "Side · 3.5 m from centre · 1.2 m in front of the 4"],
["M1", "Mover", "−0.3", "1.1", "0.3 m off centre · 1.1 m in front of the 4"],
["M2", "Mover", "+0.3", "1.1", "0.3 m off centre · 1.1 m in front of the 4"],
["#1", "Can", "−3.5", "2.4", "Side · 3.5 m from centre · front left · 2.4 m in front"],
["#8", "Can", "+3.5", "2.4", "Side · 3.5 m from centre · front right · 2.4 m in front"],
]}
/>
<Text tone="secondary" size="small">
Source: integrate_u_patterns.py LAYOUT_M
</Text>
</Stack>
</Stack>
);
}