task(A4.10): migrate trim/extend/fillet to v2 + real CAD extend fallback

This commit is contained in:
Agent Zero
2026-08-27 01:21:44 +02:00
parent ebafcf0c65
commit 19f838fbf8
3 changed files with 271 additions and 7 deletions
+97 -1
View File
@@ -482,7 +482,103 @@ describe('A4.6 pilot: move/copy tools', () => {
expect(selIds2).toEqual(['auto']);
move.handlers.down!(pe(30, 30), ctx); // Zielklick (Δ=+20)
expect(doc.getElement('auto')!.x).toBe(30); // 10+Δ20
expect(doc.undo()).toBe(true);
expect(doc.getAllElements().length).toBe(1);
});
});
describe('A4.10 pilot: geometry tools', () => {
function makeLineEl(id: string): CADElement {
return {
id,
type: 'line',
layerId: 'l',
x: 100,
y: 0,
width: 200,
height: 0,
properties: { x1: 0, y1: 0, x2: 200, y2: 0 },
} as unknown as CADElement;
}
function makeCircleEl(id: string): CADElement {
return {
id,
type: 'circle',
layerId: 'l',
x: 100,
y: 0,
width: 80,
height: 80,
properties: { radius: 40 },
} as unknown as CADElement;
}
it('trim: Kante+Ziel kürzt die Linie am Schnittpunkt; undo stellt her', () => {
const { ydoc } = createInMemoryDoc();
const doc = new CADDocument(ydoc);
const boundary = makeCircleEl('bnd');
const target = makeLineEl('tgt');
doc.addElement(boundary);
doc.addElement(target);
// Sequenzieller Brücken-HitTest: 1. Klick → boundary, 2. Klick → target
const pickQueue = [boundary, target];
let pickIdx = 0;
const bridge = {
getIds: () => [] as string[],
setIds: () => {},
hitTest: () => pickQueue[Math.min(pickIdx++, pickQueue.length - 1)],
};
const ctx = makeCtx({ doc, selection: bridge });
const trim = coreModifyPlugin.tools!.find((t) => t.manifest.id === 'trim')!;
trim.handlers.down!(pe(140, 0), ctx); // Kante
trim.handlers.down!(pe(180, 0), ctx); // Ziel-Linie rechts vom Kreis
const trimmed = doc.getElement('tgt')!;
expect(trimmed.properties.x2).toBeLessThan(200); // gekürzt
// Legacy-Semantik: das NÄHERE Ende wird auf den ERSTEN Schnittpunkt
// gesetzt (findIntersection wählt den nächsten zu p1 → x=60):
expect(Math.abs(trimmed.properties.x2 as number)).toBeCloseTo(60); // Schnitt bei x=140
expect(doc.undo()).toBe(true);
expect(doc.getElement('tgt')!.properties.x2).toBe(200);
});
it('extend: Linie wird zur Kante hin verlängert', () => {
const { ydoc } = createInMemoryDoc();
const doc = new CADDocument(ydoc);
// kurze Linie (10→50); Kreis (100,0,r=40) hat Schnitte bei x=60/140
// → Extend muss das nahe Ende x2 auf 60 strecken
const boundary = makeCircleEl('bnd');
const shortLine = {
id: 'sl', type: 'line', layerId: 'l', x: 30, y: 0,
width: 40, height: 0,
properties: { x1: 10, y1: 0, x2: 50, y2: 0 },
} as unknown as CADElement;
doc.addElement(boundary);
doc.addElement(shortLine);
const pickQueue = [boundary, shortLine];
let pickIdx = 0;
const bridge = {
getIds: () => [] as string[],
setIds: () => {},
hitTest: () => pickQueue[Math.min(pickIdx++, pickQueue.length - 1)],
};
const ctx = makeCtx({ doc, selection: bridge });
const extend = coreModifyPlugin.tools!.find((t) => t.manifest.id === 'extend')!;
extend.handlers.down!(pe(100, 40), ctx);
extend.handlers.down!(pe(45, 0), ctx);
const ext = doc.getElement('sl')!;
// Extend richtet das nahe Ende exakt auf den Schnittpunkt aus:
expect(ext.properties.x2).toBeCloseTo(60);
expect(doc.undo()).toBe(true);
expect(doc.getElement('sl')!.properties.x2).toBe(50); // Original-Länge
});
});