task(E1): seating-row/block drag interaction with live preview and options
This commit is contained in:
@@ -1,19 +1,31 @@
|
||||
/**
|
||||
* event-tools – Built-in V2-Plugin (Task A4.9).
|
||||
* event-tools – Built-in V2-Plugin (Tasks A4.9 + E1).
|
||||
*
|
||||
* Fachwerkzeuge für Veranstaltungsplanung: chair, seating-row,
|
||||
* seating-block, table, stage — alle Einzelklick-Platzierer über
|
||||
* den (durch BUG-1-Fix korrigierten) SeatingService.
|
||||
* Fachwerkzeuge für Veranstaltungsplanung: chair (Einzelnklick),
|
||||
* seating-row + seating-block (Drag-Interaktion mit Live-Preview
|
||||
* und Options-Schema), table + stage (Einzelnklick).
|
||||
*
|
||||
* Jede Platzierung = doc.transact → 1 Undo-Schritt (Multi-Element-Sets
|
||||
* wie seating-block erscheinen/verschwinden gemeinsam).
|
||||
*
|
||||
* SeatingService ist durch BUG-1-Fix deterministisch (rowIds unique).
|
||||
*/
|
||||
import type { PluginV2, ToolExtensionV2, FullToolContext } from '../../types';
|
||||
import type { CADElement } from '../../../types/cad.types';
|
||||
import type { Pt } from '../../../tools/modification/geometry';
|
||||
import { SeatingService } from '../../../services/seatingService';
|
||||
|
||||
const seating = new SeatingService();
|
||||
|
||||
// ─── Drag-Sitzungs-State für row/block ─────────────────────
|
||||
let dragStart: Pt | null = null;
|
||||
|
||||
function optsOf(ctx: FullToolContext): { layerId: string } {
|
||||
return {
|
||||
layerId: (ctx.options.layerId as string | undefined) ?? 'layer-0',
|
||||
};
|
||||
}
|
||||
|
||||
/** Erzeugt ein Einzelklick-Platzierer-Werkzeug über einen placer. */
|
||||
function makePlaceTool(
|
||||
id: string,
|
||||
@@ -36,7 +48,7 @@ function makePlaceTool(
|
||||
down(e, ctx) {
|
||||
const c = ctx as FullToolContext;
|
||||
if (!c.doc) return;
|
||||
const layerId = (c.options.layerId as string | undefined) ?? 'layer-0';
|
||||
const { layerId } = optsOf(ctx);
|
||||
const placed = placer(e.world.x, e.world.y, layerId);
|
||||
const els = Array.isArray(placed) ? placed : [placed];
|
||||
c.doc.transact(() => {
|
||||
@@ -48,31 +60,142 @@ function makePlaceTool(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* seating-row (Task E1): Klick1 = Start, Drag = Länge/Vorschau,
|
||||
* up = Commit. Anzahl Stühle wird aus Distanz berechnet (spacing aus Options).
|
||||
*/
|
||||
export const seatingRowToolDrag: ToolExtensionV2 = {
|
||||
manifest: {
|
||||
id: 'seating-row',
|
||||
label: 'Stuhldreihe',
|
||||
icon: '\u2500\u2570\u2500',
|
||||
ribbonTab: 'insert',
|
||||
tags: ['basic'],
|
||||
description: 'Reihe: Start klicken, Ende ziehen. Stuhlzahl aus Länge.',
|
||||
},
|
||||
optionsSchema: [
|
||||
{ key: 'spacing', label: 'Abstand (cm)', type: 'number', min: 20, max: 200 },
|
||||
],
|
||||
handlers: {
|
||||
down(e, ctx) {
|
||||
dragStart = e.world;
|
||||
ctx.setStatus('Reihe: Endpunkt ziehen');
|
||||
},
|
||||
move(e, ctx) {
|
||||
if (!dragStart) return;
|
||||
const c = ctx as FullToolContext;
|
||||
const { layerId } = optsOf(c);
|
||||
const spacing = (c.options.spacing as number | undefined) ?? 50;
|
||||
const length = Math.hypot(e.world.x - dragStart.x, e.world.y - dragStart.y);
|
||||
const count = Math.max(2, Math.floor(length / spacing));
|
||||
const preview = seating.createSeatingRow(dragStart.x, dragStart.y, layerId, { count, spacing });
|
||||
c.setPreview?.(preview[0] ?? null); // Legacy-Vorschau zeigt ersten Stuhl als Referenz
|
||||
ctx.setStatus(`Reihe: ${count} Stühle (${length.toFixed(0)}mm)`);
|
||||
},
|
||||
up(e, ctx) {
|
||||
const c = ctx as FullToolContext;
|
||||
if (!dragStart || !c.doc) { dragStart = null; return; }
|
||||
const { layerId } = optsOf(c);
|
||||
const spacing = (c.options.spacing as number | undefined) ?? 50;
|
||||
const length = Math.hypot(e.world.x - dragStart.x, e.world.y - dragStart.y);
|
||||
const count = Math.max(2, Math.floor(length / spacing));
|
||||
const els = seating.createSeatingRow(dragStart.x, dragStart.y, layerId, { count, spacing });
|
||||
c.doc.transact(() => {
|
||||
for (const el of els) c.doc!.addElement(el);
|
||||
});
|
||||
ctx.setStatus(`Reihe: ${count} Stühle platziert`);
|
||||
dragStart = null;
|
||||
c.setPreview?.(null);
|
||||
},
|
||||
cancel(ctx) {
|
||||
dragStart = null;
|
||||
ctx.setPreview?.(null);
|
||||
ctx.setStatus('Reihe abgebrochen');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* seating-block (Task E1): Klick1 = Start-Ecke, Drag = Block-Dimension,
|
||||
* up = Commit. rows/cols aus Distanz + spacing berechnet.
|
||||
*/
|
||||
export const seatingBlockToolDrag: ToolExtensionV2 = {
|
||||
manifest: {
|
||||
id: 'seating-block',
|
||||
label: 'Stuhlblock',
|
||||
icon: '\u2565',
|
||||
ribbonTab: 'insert',
|
||||
tags: ['basic'],
|
||||
description: 'Block: Start-Ecke klicken, Gegenecke ziehen.',
|
||||
},
|
||||
optionsSchema: [
|
||||
{ key: 'colSpacing', label: 'Sitzabstand X (cm)', type: 'number', min: 30, max: 200 },
|
||||
{ key: 'rowSpacing', label: 'Reihenabstand Y (cm)', type: 'number', min: 30, max: 200 },
|
||||
],
|
||||
handlers: {
|
||||
down(e, ctx) {
|
||||
dragStart = e.world;
|
||||
ctx.setStatus('Block: Gegenecke ziehen');
|
||||
},
|
||||
move(e, ctx) {
|
||||
if (!dragStart) return;
|
||||
const c = ctx as FullToolContext;
|
||||
const { layerId } = optsOf(c);
|
||||
const colSpacing = (c.options.colSpacing as number | undefined) ?? 50;
|
||||
const rowSpacing = (c.options.rowSpacing as number | undefined) ?? 50;
|
||||
const w = Math.abs(e.world.x - dragStart.x);
|
||||
const h = Math.abs(e.world.y - dragStart.y);
|
||||
const cols = Math.max(2, Math.floor(w / colSpacing));
|
||||
const rows = Math.max(2, Math.floor(h / rowSpacing));
|
||||
ctx.setStatus(`Block: ${rows} Reihen × ${cols} Stühle = ${rows * cols} Sitzplätze`);
|
||||
},
|
||||
up(e, ctx) {
|
||||
const c = ctx as FullToolContext;
|
||||
if (!dragStart || !c.doc) { dragStart = null; return; }
|
||||
const { layerId } = optsOf(c);
|
||||
const colSpacing = (c.options.colSpacing as number | undefined) ?? 50;
|
||||
const rowSpacing = (c.options.rowSpacing as number | undefined) ?? 50;
|
||||
const w = Math.abs(e.world.x - dragStart.x);
|
||||
const h = Math.abs(e.world.y - dragStart.y);
|
||||
const cols = Math.max(2, Math.floor(w / colSpacing));
|
||||
const rows = Math.max(2, Math.floor(h / rowSpacing));
|
||||
const centerX = (dragStart.x + e.world.x) / 2;
|
||||
const centerY = (dragStart.y + e.world.y) / 2;
|
||||
const els = seating.createSeatingBlock(centerX, centerY, layerId, { rows, cols, colSpacing, rowSpacing });
|
||||
c.doc.transact(() => {
|
||||
for (const el of els) c.doc!.addElement(el);
|
||||
});
|
||||
ctx.setStatus(`Block: ${rows}×${cols} = ${els.length} Stühle platziert`);
|
||||
dragStart = null;
|
||||
c.setPreview?.(null);
|
||||
},
|
||||
cancel(ctx) {
|
||||
dragStart = null;
|
||||
ctx.setPreview?.(null);
|
||||
ctx.setStatus('Block abgebrochen');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const chairTool = makePlaceTool('chair', 'Stuhl', '\u2570',
|
||||
(x, y, layerId) => seating.createChair(x, y, layerId), 'Stuhl');
|
||||
|
||||
export const seatingRowTool = makePlaceTool('seating-row', 'Stuhldreihe', '\u2500\u2570\u2500',
|
||||
(x, y, layerId) => seating.createSeatingRow(x, y, layerId), 'Reihe');
|
||||
|
||||
export const seatingBlockTool = makePlaceTool('seating-block', 'Stuhlblock', '\u2565',
|
||||
(x, y, layerId) => seating.createSeatingBlock(x, y, layerId), 'Block');
|
||||
|
||||
export const tableTool = makePlaceTool('table', 'Tisch', '\u25cb',
|
||||
(x, y, layerId) => seating.createTable(x, y, layerId), 'Tisch');
|
||||
|
||||
export const stageTool = makePlaceTool('stage', 'Bühne', '\u25ad',
|
||||
(x, y, layerId) => seating.createStage(x, y, layerId), 'Bühne');
|
||||
|
||||
/** V2-Plugin: Event-Domain (Task A4.9). */
|
||||
/** V2-Plugin: Event-Domain (Tasks A4.9 + E1). */
|
||||
export const eventToolsV2Plugin: PluginV2 = {
|
||||
manifest: {
|
||||
id: 'event-tools',
|
||||
name: 'Event-Bestuhlung',
|
||||
version: '2.0.0',
|
||||
version: '2.1.0',
|
||||
author: 'web-cad team',
|
||||
description: 'Stühle, Reihen, Blöcke, Tische, Bühne (V2).',
|
||||
description: 'Stühle, Reihen (Drag), Blöcke (Drag), Tische, Bühne (V2).',
|
||||
category: 'elements',
|
||||
enabledByDefault: true,
|
||||
},
|
||||
tools: [chairTool, seatingRowTool, seatingBlockTool, tableTool, stageTool],
|
||||
tools: [chairTool, seatingRowToolDrag, seatingBlockToolDrag, tableTool, stageTool],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user