Files
qlcplus/stage/map.canvas.tsx
T
jimmyandCursor 5eb623d6af 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>
2026-09-08 00:59:36 +12:00

280 lines
8.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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>
);
}