Files
web-cad/frontend/src/plugins/builtin/eventTools.ts
T

222 lines
7.4 KiB
TypeScript
Raw Normal View History

/**
* Event-Tools Plugin Built-in example plugin
* Adds custom element types: stage-curtain, spotlight, barrier
* Adds command: EVENT_SEATING (generates seating rows)
*/
import type { Plugin, PluginContext, ElementTypeExtension, CommandExtension } from '../types';
import type { CADElement } from '../../types/cad.types';
function uid(prefix: string): string {
return `${prefix}-${Date.now()}-${Math.floor(Math.random() * 10000)}`;
}
// ─── Element Type: Stage Curtain ────────────────────────
const stageCurtain: ElementTypeExtension = {
typeName: 'stage-curtain',
displayName: 'Bühnenvorhang',
defaultWidth: 6,
defaultHeight: 0.3,
defaultProperties: {
fill: '#8B0000',
stroke: '#5C0000',
strokeWidth: 2,
curtainStyle: 'pleated',
},
render(ctx, element, scale) {
const { x, y, width, height, properties } = element;
ctx.save();
ctx.fillStyle = (properties.fill as string) || '#8B0000';
ctx.strokeStyle = (properties.stroke as string) || '#5C0000';
ctx.lineWidth = (properties.strokeWidth as number) || 2;
// Draw pleated curtain
const pleats = Math.max(6, Math.floor(width / 0.5));
const pleatWidth = width / pleats;
ctx.beginPath();
ctx.moveTo(x, y);
for (let i = 0; i <= pleats; i++) {
const px = x + i * pleatWidth;
const py = y + (i % 2 === 0 ? 0 : height * 0.15);
ctx.lineTo(px, py);
}
ctx.lineTo(x + width, y + height);
ctx.lineTo(x, y + height);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.restore();
return true;
},
hitTest(element, hx, hy, tolerance) {
const { x, y, width, height } = element;
return hx >= x - tolerance && hx <= x + width + tolerance &&
hy >= y - tolerance && hy <= y + height + tolerance;
},
propertyFields: [
{ key: 'fill', label: 'Farbe', type: 'color' },
{ key: 'curtainStyle', label: 'Stil', type: 'select', options: [
{ value: 'pleated', label: 'Gefaltet' },
{ value: 'flat', label: 'Glatt' },
]},
],
};
// ─── Element Type: Spotlight ────────────────────────────
const spotlight: ElementTypeExtension = {
typeName: 'spotlight',
displayName: 'Scheinwerfer',
defaultWidth: 2,
defaultHeight: 2,
defaultProperties: {
fill: 'rgba(255, 220, 100, 0.3)',
stroke: '#FFD700',
strokeWidth: 1.5,
beamAngle: 45,
},
render(ctx, element, scale) {
const { x, y, width, height, properties } = element;
ctx.save();
const cx = x + width / 2;
const cy = y + height / 2;
const radius = Math.max(width, height) / 2;
// Draw beam cone
const beamAngle = ((properties.beamAngle as number) || 45) * Math.PI / 180;
const gradient = ctx.createRadialGradient(cx, cy, 0, cx, cy, radius);
gradient.addColorStop(0, 'rgba(255, 220, 100, 0.5)');
gradient.addColorStop(1, 'rgba(255, 220, 100, 0.05)');
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.arc(cx, cy, radius, -Math.PI / 2 - beamAngle / 2, -Math.PI / 2 + beamAngle / 2);
ctx.closePath();
ctx.fill();
// Draw fixture circle
ctx.fillStyle = '#333';
ctx.strokeStyle = (properties.stroke as string) || '#FFD700';
ctx.lineWidth = (properties.strokeWidth as number) || 1.5;
ctx.beginPath();
ctx.arc(cx, cy, 0.15 * scale, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
ctx.restore();
return true;
},
hitTest(element, hx, hy, tolerance) {
const cx = element.x + element.width / 2;
const cy = element.y + element.height / 2;
const radius = Math.max(element.width, element.height) / 2 + tolerance;
const dx = hx - cx;
const dy = hy - cy;
return dx * dx + dy * dy <= radius * radius;
},
propertyFields: [
{ key: 'beamAngle', label: 'Strahlwinkel°', type: 'number', min: 10, max: 180, step: 5 },
{ key: 'stroke', label: 'Farbe', type: 'color' },
],
};
// ─── Element Type: Barrier ──────────────────────────────
const barrier: ElementTypeExtension = {
typeName: 'barrier',
displayName: 'Absperrung',
defaultWidth: 3,
defaultHeight: 0.1,
defaultProperties: {
fill: '#FFA500',
stroke: '#CC8400',
strokeWidth: 1.5,
pattern: 'striped',
},
render(ctx, element, scale) {
const { x, y, width, height, properties } = element;
ctx.save();
ctx.fillStyle = (properties.fill as string) || '#FFA500';
ctx.strokeStyle = (properties.stroke as string) || '#CC8400';
ctx.lineWidth = (properties.strokeWidth as number) || 1.5;
// Draw striped barrier
const stripeWidth = 0.3;
const stripes = Math.floor(width / stripeWidth);
for (let i = 0; i < stripes; i++) {
ctx.fillStyle = i % 2 === 0 ? '#FFA500' : '#000000';
ctx.fillRect(x + i * stripeWidth, y, stripeWidth, height);
}
ctx.strokeStyle = (properties.stroke as string) || '#CC8400';
ctx.strokeRect(x, y, width, height);
ctx.restore();
return true;
},
hitTest(element, hx, hy, tolerance) {
const { x, y, width, height } = element;
return hx >= x - tolerance && hx <= x + width + tolerance &&
hy >= y - tolerance && hy <= y + height + tolerance;
},
propertyFields: [
{ key: 'fill', label: 'Farbe', type: 'color' },
{ key: 'pattern', label: 'Muster', type: 'select', options: [
{ value: 'striped', label: 'Gestreift' },
{ value: 'solid', label: 'Einfarbig' },
]},
],
};
// ─── Command: EVENT_SEATING ─────────────────────────────
const eventSeatingCommand: CommandExtension = {
name: 'EVENT_SEATING',
description: 'Erzeugt Bestuhlung in Reihen',
usage: 'EVENT_SEATING <rows> <cols> [gap] [rowGap]',
execute(args, context) {
const rows = parseInt(args[0] || '5', 10);
const cols = parseInt(args[1] || '10', 10);
const gap = parseFloat(args[2] || '0.6');
const rowGap = parseFloat(args[3] || '1.0');
const layerId = context.getActiveLayerId();
const chairWidth = 0.5;
const chairHeight = 0.5;
const startX = 2;
const startY = 2;
let count = 0;
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const el: CADElement = {
id: uid('chair'),
type: 'chair',
layerId,
x: startX + c * (chairWidth + gap),
y: startY + r * (chairHeight + rowGap),
width: chairWidth,
height: chairHeight,
properties: { fill: '#4A90D9', stroke: '#2A70B9', strokeWidth: 1, rotation: 0 },
};
context.addElement(el);
count++;
}
}
context.showToast(`${count} Stühle in ${rows} Reihen erstellt`, 'success');
},
};
// ─── Plugin Definition ──────────────────────────────────
export const eventToolsPlugin: Plugin = {
manifest: {
id: 'event-tools',
name: 'Event-Tools',
version: '1.0.0',
author: 'Web CAD Team',
description: 'Erweitert Web CAD um Bühnenvorhänge, Scheinwerfer, Absperrungen und Bestuhlungs-Befehle.',
category: 'elements',
enabledByDefault: true,
},
elementTypes: [stageCurtain, spotlight, barrier],
commands: [eventSeatingCommand],
onInit(context) {
context.log('Event-Tools Plugin initialisiert');
},
onActivate(context) {
context.log('Event-Tools Plugin aktiviert');
},
};