task(A4.2): migrate rect tool to v2 plugin
This commit is contained in:
@@ -16,6 +16,8 @@ const uid = (prefix: string): string =>
|
||||
|
||||
/** Startpunkt der laufenden Linie (nur innerhalb einer down→up-Sitzung gesetzt). */
|
||||
let lineStart: Pt | null = null;
|
||||
/** Startpunkt des laufenden Rechtecks (A4.2). */
|
||||
let rectStart: Pt | null = null;
|
||||
|
||||
function buildLine(start: Pt, end: Pt, ctx: FullToolContext): CADElement {
|
||||
const stroke = (ctx.options.strokeColor as string | undefined) ?? '#e0e0f0';
|
||||
@@ -32,6 +34,25 @@ function buildLine(start: Pt, end: Pt, ctx: FullToolContext): CADElement {
|
||||
} as unknown as CADElement;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* line-Werkzeug: down setzt Startpunkt, move zeigt Live-Vorschau,
|
||||
* up committet via doc.transact (1 Linie = 1 Undo-Schritt).
|
||||
@@ -80,16 +101,64 @@ export const lineTool: ToolExtensionV2 = {
|
||||
},
|
||||
};
|
||||
|
||||
/** V2-Plugin: Kern-Zeichenwerkzeuge (Pilot: nur line). */
|
||||
/**
|
||||
* 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). */
|
||||
export const coreDrawingPlugin: PluginV2 = {
|
||||
manifest: {
|
||||
id: 'core-drawing',
|
||||
name: 'Zeichnen (Kern)',
|
||||
version: '1.0.0',
|
||||
version: '1.1.0',
|
||||
author: 'web-cad team',
|
||||
description: 'Grundlegende Zeichenwerkzeuge (V2-Pilot: Linie).',
|
||||
description: 'Grundlegende Zeichenwerkzeuge (V2: Linie, Rechteck).',
|
||||
category: 'tools',
|
||||
enabledByDefault: true,
|
||||
},
|
||||
tools: [lineTool],
|
||||
tools: [lineTool, rectTool],
|
||||
};
|
||||
|
||||
@@ -80,6 +80,46 @@ describe('A3 pilot: line tool', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('A4.2 pilot: rect tool', () => {
|
||||
it('erzeugt Rechteck mit BBox-Konvention; ein undo entfernt es', () => {
|
||||
const { ydoc } = createInMemoryDoc();
|
||||
const doc = new CADDocument(ydoc);
|
||||
const ctx = makeCtx({ doc });
|
||||
const rect = coreDrawingPlugin.tools?.find((t) => t.manifest.id === 'rect');
|
||||
expect(rect).toBeDefined();
|
||||
|
||||
rect!.handlers.down!(pe(10, 10), ctx);
|
||||
rect!.handlers.move!(pe(110, 60), ctx);
|
||||
expect(ctx.previews.length).toBe(1); // Live-Vorschau
|
||||
rect!.handlers.up!(pe(110, 60), ctx);
|
||||
|
||||
const all = doc.getAllElements();
|
||||
expect(all.length).toBe(1);
|
||||
expect(all[0].type).toBe('rect');
|
||||
// Legacy-BBox-Konvention: Zentrum zwischen beiden Ecken
|
||||
expect(all[0].x).toBe(60);
|
||||
expect(all[0].y).toBe(35);
|
||||
expect(all[0].width).toBe(100);
|
||||
expect(all[0].height).toBe(50);
|
||||
|
||||
expect(doc.undo()).toBe(true);
|
||||
expect(doc.getAllElements().length).toBe(0);
|
||||
});
|
||||
|
||||
it('cancel verwirft das laufende Rechteck', () => {
|
||||
const { ydoc } = createInMemoryDoc();
|
||||
const doc = new CADDocument(ydoc);
|
||||
const ctx = makeCtx({ doc });
|
||||
const rect = coreDrawingPlugin.tools!.find((t) => t.manifest.id === 'rect')!;
|
||||
|
||||
rect.handlers.down!(pe(5, 5), ctx);
|
||||
rect.handlers.cancel!(ctx);
|
||||
rect.handlers.up!(pe(50, 50), ctx);
|
||||
|
||||
expect(doc.getAllElements().length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('A3 pilot: select tool', () => {
|
||||
function makeBridge(elements: CADElement[]) {
|
||||
let ids: string[] = [];
|
||||
|
||||
Reference in New Issue
Block a user