task(E2): arc-row tool for curved seating around stage

This commit is contained in:
Agent Zero
2026-08-27 22:49:09 +02:00
parent 85105d3f01
commit 633cf3d647
2 changed files with 97 additions and 1 deletions
@@ -186,6 +186,61 @@ export const tableTool = makePlaceTool('table', 'Tisch', '\u25cb',
export const stageTool = makePlaceTool('stage', 'Bühne', '\u25ad', export const stageTool = makePlaceTool('stage', 'Bühne', '\u25ad',
(x, y, layerId) => seating.createStage(x, y, layerId), 'Bühne'); (x, y, layerId) => seating.createStage(x, y, layerId), 'Bühne');
// ─────────────────────────────────────────────────────────────
// Arc-Row (Task E2): Reihen entlang Kreisbogen um Bühnenpunkt.
// Klick 1: Zentrum (Bühne). Drag: Radius + Startwinkel.
// Klick 2: Endwinkel → Commit.
// Phasen: 0=Zentrum setzen, 1=Radius+Start, 2=Endwinkel → commit
// ─────────────────────────────────────────────────────────────
let arcCenter: Pt | null = null;
let arcStartPt: Pt | null = null;
let arcPhase = 0;
export const arcRowTool: ToolExtensionV2 = {
manifest: {
id: 'arc-row',
label: 'Arc-Row',
icon: '\u2312',
ribbonTab: 'insert',
tags: ['basic'],
description: 'Bogenreihe: Zentrum, Startpunkt, Endpunkt klicken',
},
optionsSchema: [
{ key: 'arcSpacing', label: 'Abstand (cm)', type: 'number', min: 20, max: 200 },
],
handlers: {
down(e, ctx) {
const c = ctx as FullToolContext;
const { layerId } = optsOf(c);
const spacing = (c.options.arcSpacing as number | undefined) ?? 50;
if (arcPhase === 0) {
arcCenter = e.world;
arcPhase = 1;
ctx.setStatus('Arc-Row: Radius ziehen (Startpunkt)');
} else if (arcPhase === 1 && arcCenter) {
arcStartPt = e.world;
arcPhase = 2;
ctx.setStatus('Arc-Row: Endwinkel klicken');
} else if (arcPhase === 2 && arcCenter && arcStartPt && c.doc) {
const els = seating.createArcRow(arcCenter.x, arcCenter.y, Math.hypot(arcStartPt.x - arcCenter.x, arcStartPt.y - arcCenter.y),
(Math.atan2(arcStartPt.y - arcCenter.y, arcStartPt.x - arcCenter.x) * 180) / Math.PI,
(Math.atan2(e.world.y - arcCenter.y, e.world.x - arcCenter.x) * 180) / Math.PI,
layerId, spacing);
c.doc.transact(() => {
for (const el of els) c.doc!.addElement(el);
});
ctx.setStatus(`Arc-Row: ${els.length} Stühle platziert`);
arcPhase = 0; arcCenter = null; arcStartPt = null;
}
},
cancel(ctx) {
arcPhase = 0; arcCenter = null; arcStartPt = null;
ctx.setPreview?.(null);
ctx.setStatus('Arc-Row abgebrochen');
},
},
};
/** V2-Plugin: Event-Domain (Tasks A4.9 + E1). */ /** V2-Plugin: Event-Domain (Tasks A4.9 + E1). */
export const eventToolsV2Plugin: PluginV2 = { export const eventToolsV2Plugin: PluginV2 = {
manifest: { manifest: {
@@ -197,5 +252,5 @@ export const eventToolsV2Plugin: PluginV2 = {
category: 'elements', category: 'elements',
enabledByDefault: true, enabledByDefault: true,
}, },
tools: [chairTool, seatingRowToolDrag, seatingBlockToolDrag, tableTool, stageTool], tools: [chairTool, seatingRowToolDrag, seatingBlockToolDrag, tableTool, stageTool, arcRowTool],
}; };
+41
View File
@@ -280,6 +280,47 @@ export class SeatingService {
return elements; return elements;
} }
/** Create an arc row (curved seating around a center point) — Task E2 */
createArcRow(
centerX: number,
centerY: number,
radius: number,
startAngleDeg: number,
endAngleDeg: number,
layerId: string,
spacing = 50,
): CADElement[] {
const arcLen = Math.abs(endAngleDeg - startAngleDeg) * Math.PI / 180 * radius;
const count = Math.max(2, Math.floor(arcLen / spacing));
const rowId = `arcrow_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
const elements: CADElement[] = [];
for (let i = 0; i <= count; i++) {
const angle = startAngleDeg + (endAngleDeg - startAngleDeg) * (i / count);
const rad = angle * Math.PI / 180;
const px = centerX + radius * Math.cos(rad);
const py = centerY + radius * Math.sin(rad);
elements.push({
id: this.generateId(),
type: 'chair',
layerId,
x: px,
y: py,
width: 45,
height: 45,
properties: {
rotation: angle + 90,
fill: '#4a90d9',
backrestColor: '#3a7ac9',
outlineColor: '#2a5a99',
seatType: 'standard',
rowIndex: i,
rowId,
},
});
}
return elements;
}
/** Create a table element */ /** Create a table element */
createTable(x: number, y: number, layerId: string, config: Partial<TableConfig> = {}): CADElement { createTable(x: number, y: number, layerId: string, config: Partial<TableConfig> = {}): CADElement {
const cfg = { ...DEFAULT_TABLE, ...config }; const cfg = { ...DEFAULT_TABLE, ...config };