2026-08-26 21:30:54 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* core-drawing – Built-in V2-Plugin (Task A3 Pilot).
|
|
|
|
|
|
*
|
|
|
|
|
|
* Werkzeuge: line (Migrations-Pilot aus dem Legacy-Switch).
|
|
|
|
|
|
* Elemente werden über CADDocument committed → kollaborativ korrektes Undo.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Sitzungszustand (Startpunkt der aktuellen Linie) lebt als Modul-Member,
|
|
|
|
|
|
* NICHT in options: Options sind geteilter UI-Store-Zustand.
|
|
|
|
|
|
*/
|
|
|
|
|
|
import type { PluginV2, ToolExtensionV2, FullToolContext } from '../../types';
|
|
|
|
|
|
import type { CADElement } from '../../../types/cad.types';
|
|
|
|
|
|
import type { Pt } from '../../../tools/modification/geometry';
|
|
|
|
|
|
|
|
|
|
|
|
const uid = (prefix: string): string =>
|
|
|
|
|
|
`${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 9)}`;
|
|
|
|
|
|
|
|
|
|
|
|
/** Startpunkt der laufenden Linie (nur innerhalb einer down→up-Sitzung gesetzt). */
|
|
|
|
|
|
let lineStart: Pt | null = null;
|
2026-08-26 23:14:26 +02:00
|
|
|
|
/** Startpunkt des laufenden Rechtecks (A4.2). */
|
|
|
|
|
|
let rectStart: Pt | null = null;
|
2026-08-26 21:30:54 +02:00
|
|
|
|
|
|
|
|
|
|
function buildLine(start: Pt, end: Pt, ctx: FullToolContext): CADElement {
|
|
|
|
|
|
const stroke = (ctx.options.strokeColor as string | undefined) ?? '#e0e0f0';
|
|
|
|
|
|
const layerId = (ctx.options.layerId as string | undefined) ?? 'layer-0';
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: uid('line'),
|
|
|
|
|
|
type: 'line',
|
|
|
|
|
|
layerId,
|
|
|
|
|
|
x: (start.x + end.x) / 2,
|
|
|
|
|
|
y: (start.y + end.y) / 2,
|
|
|
|
|
|
width: Math.abs(end.x - start.x),
|
|
|
|
|
|
height: Math.abs(end.y - start.y),
|
|
|
|
|
|
properties: { x1: start.x, y1: start.y, x2: end.x, y2: end.y, stroke, strokeWidth: 1 },
|
|
|
|
|
|
} as unknown as CADElement;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-26 23:14:26 +02:00
|
|
|
|
function buildRect(cornerA: Pt, cornerB: Pt, ctx: FullToolContext): CADElement {
|
|
|
|
|
|
const stroke = (ctx.options.strokeColor as string | undefined) ?? '#e0e0f0';
|
|
|
|
|
|
const layerId = (ctx.options.layerId as string | undefined) ?? 'layer-0';
|
|
|
|
|
|
const minX = Math.min(cornerA.x, cornerB.x);
|
|
|
|
|
|
const minY = Math.min(cornerA.y, cornerB.y);
|
|
|
|
|
|
const w = Math.abs(cornerB.x - cornerA.x);
|
|
|
|
|
|
const h = Math.abs(cornerB.y - cornerA.y);
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: uid('rect'),
|
|
|
|
|
|
type: 'rect',
|
|
|
|
|
|
layerId,
|
|
|
|
|
|
x: minX + w / 2, // BBox-Zentrum (Legacy-Konvention)
|
|
|
|
|
|
y: minY + h / 2,
|
|
|
|
|
|
width: w,
|
|
|
|
|
|
height: h,
|
|
|
|
|
|
properties: { stroke, strokeWidth: 1 },
|
|
|
|
|
|
} as unknown as CADElement;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-26 21:30:54 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* line-Werkzeug: down setzt Startpunkt, move zeigt Live-Vorschau,
|
|
|
|
|
|
* up committet via doc.transact (1 Linie = 1 Undo-Schritt).
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const lineTool: ToolExtensionV2 = {
|
|
|
|
|
|
manifest: {
|
|
|
|
|
|
id: 'line',
|
|
|
|
|
|
label: 'Linie',
|
|
|
|
|
|
icon: '\u2571',
|
|
|
|
|
|
ribbonTab: 'canvas',
|
|
|
|
|
|
tags: ['basic'],
|
|
|
|
|
|
description: 'Zeichnet eine Linie (Klick Start, Klick Ende)',
|
|
|
|
|
|
},
|
|
|
|
|
|
optionsSchema: [
|
|
|
|
|
|
{ key: 'strokeColor', label: 'Farbe', type: 'color' },
|
|
|
|
|
|
],
|
|
|
|
|
|
handlers: {
|
|
|
|
|
|
down(e, ctx) {
|
|
|
|
|
|
lineStart = e.world;
|
|
|
|
|
|
ctx.setStatus(`Linie ab (${lineStart.x.toFixed(0)}, ${lineStart.y.toFixed(0)}) – Ende wählen`);
|
|
|
|
|
|
},
|
|
|
|
|
|
move(e, ctx) {
|
|
|
|
|
|
if (!lineStart) return;
|
|
|
|
|
|
ctx.setPreview?.(buildLine(lineStart, e.world, ctx));
|
|
|
|
|
|
},
|
|
|
|
|
|
up(e, ctx) {
|
|
|
|
|
|
const c = ctx as FullToolContext;
|
|
|
|
|
|
if (!lineStart || !c.doc) {
|
|
|
|
|
|
lineStart = null;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const el = buildLine(lineStart, e.world, c);
|
|
|
|
|
|
c.doc.transact(() => {
|
|
|
|
|
|
// addElement öffnet inneres transact — Yjs verschachtelt sie → EIN Undo-Schritt
|
|
|
|
|
|
c.doc!.addElement(el);
|
|
|
|
|
|
});
|
|
|
|
|
|
lineStart = null;
|
|
|
|
|
|
ctx.setPreview?.(null);
|
|
|
|
|
|
ctx.setStatus(`Linie erstellt (${el.id})`);
|
|
|
|
|
|
},
|
|
|
|
|
|
cancel(ctx) {
|
|
|
|
|
|
lineStart = null;
|
|
|
|
|
|
ctx.setPreview?.(null);
|
|
|
|
|
|
ctx.setStatus('Linie abgebrochen');
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-08-26 23:14:26 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* rect-Werkzeug (A4.2): down setzt Ecke, move zeigt BBox-Vorschau,
|
|
|
|
|
|
* up committet via doc.transact. Portiert aus Legacy case 'rect'
|
|
|
|
|
|
* (BBox-Konvention: x/y = Zentrum, width/height absolut).
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const rectTool: ToolExtensionV2 = {
|
|
|
|
|
|
manifest: {
|
|
|
|
|
|
id: 'rect',
|
|
|
|
|
|
label: 'Rechteck',
|
|
|
|
|
|
icon: '\u25ad',
|
|
|
|
|
|
ribbonTab: 'canvas',
|
|
|
|
|
|
tags: ['basic'],
|
|
|
|
|
|
description: 'Zieht ein Rechteck auf (Klick Ecke, Klick Gegenecke)',
|
|
|
|
|
|
},
|
|
|
|
|
|
optionsSchema: [
|
|
|
|
|
|
{ key: 'strokeColor', label: 'Farbe', type: 'color' },
|
|
|
|
|
|
],
|
|
|
|
|
|
handlers: {
|
|
|
|
|
|
down(e, ctx) {
|
|
|
|
|
|
rectStart = e.world;
|
|
|
|
|
|
ctx.setStatus(`Rechteck ab (${rectStart.x.toFixed(0)}, ${rectStart.y.toFixed(0)}) – Gegenecke wählen`);
|
|
|
|
|
|
},
|
|
|
|
|
|
move(e, ctx) {
|
|
|
|
|
|
if (!rectStart) return;
|
|
|
|
|
|
ctx.setPreview?.(buildRect(rectStart, e.world, ctx));
|
|
|
|
|
|
},
|
|
|
|
|
|
up(e, ctx) {
|
|
|
|
|
|
const c = ctx as FullToolContext;
|
|
|
|
|
|
if (!rectStart || !c.doc) {
|
|
|
|
|
|
rectStart = null;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const el = buildRect(rectStart, e.world, c);
|
|
|
|
|
|
c.doc.transact(() => {
|
|
|
|
|
|
c.doc!.addElement(el);
|
|
|
|
|
|
});
|
|
|
|
|
|
rectStart = null;
|
|
|
|
|
|
ctx.setPreview?.(null);
|
|
|
|
|
|
ctx.setStatus(`Rechteck erstellt (${el.id})`);
|
|
|
|
|
|
},
|
|
|
|
|
|
cancel(ctx) {
|
|
|
|
|
|
rectStart = null;
|
|
|
|
|
|
ctx.setPreview?.(null);
|
|
|
|
|
|
ctx.setStatus('Rechteck abgebrochen');
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/** V2-Plugin: Kern-Zeichenwerkzeuge (Pilot: line; A4.2: rect). */
|
2026-08-26 21:30:54 +02:00
|
|
|
|
export const coreDrawingPlugin: PluginV2 = {
|
|
|
|
|
|
manifest: {
|
|
|
|
|
|
id: 'core-drawing',
|
|
|
|
|
|
name: 'Zeichnen (Kern)',
|
2026-08-26 23:14:26 +02:00
|
|
|
|
version: '1.1.0',
|
2026-08-26 21:30:54 +02:00
|
|
|
|
author: 'web-cad team',
|
2026-08-26 23:14:26 +02:00
|
|
|
|
description: 'Grundlegende Zeichenwerkzeuge (V2: Linie, Rechteck).',
|
2026-08-26 21:30:54 +02:00
|
|
|
|
category: 'tools',
|
|
|
|
|
|
enabledByDefault: true,
|
|
|
|
|
|
},
|
2026-08-26 23:14:26 +02:00
|
|
|
|
tools: [lineTool, rectTool],
|
2026-08-26 21:30:54 +02:00
|
|
|
|
};
|