# Phase 4: Bearbeitungs-Tools (Modification Tools) ## Requirements - F-CAD-02: Verschieben (MOVE), Kopieren (COPY/CO), Rotieren (ROTATE/RO), Skalieren (SCALE/SC), Spiegeln (MIRROR/MI), Trimmen (TRIM), Verlängern (EXTEND), Abrunden (FILLET), Versatz (OFFSET) - F-CAD-07: Auswahl-Methoden (already in SelectionEngine, ensure modification tools use it) - F-CAD-08: Gruppierung — Elemente zu Gruppen zusammenfassen, verschachteln, speichern ## Current State - `src/interaction/index.ts`: `handleModifyDown()` is a stub (lines 411-421) — only checks if selection exists, sets phase to 'modifying', pushes point. No actual modification logic. - `onElementsModified` callback exists but is never called. - App.tsx has `handleElementsDeleted` but no `handleElementsModified`. - No tool classes exist in `src/tools/`. ## Architecture Decision Instead of separate tool class files (BAUPLAN suggests 10+ files), implement modification logic directly in InteractionEngine with a `handleModifyMove(pt)` method that handles all modify tools via switch. This is simpler, matches the existing pattern (handleDrawDown handles all draw tools), and avoids over-engineering. Create a `src/tools/modification/` directory with pure geometry utility functions that InteractionEngine calls. ## Tasks ### Task 1: Create geometry utility functions File: `src/tools/modification/geometry.ts` Pure functions for element transformation: ```typescript export function moveElement(el: CADElement, dx: number, dy: number): CADElement export function rotateElement(el: CADElement, cx: number, cy: number, angle: number): CADElement export function scaleElement(el: CADElement, cx: number, cy: number, sx: number, sy: number): CADElement export function mirrorElement(el: CADElement, x1: number, y1: number, x2: number, y2: number): CADElement export function offsetElement(el: CADElement, distance: number): CADElement export function trimElement(el: CADElement, boundary: CADElement): CADElement | null export function extendElement(el: CADElement, boundary: CADElement): CADElement | null export function filletElements(el1: CADElement, el2: CADElement, radius: number): [CADElement, CADElement] | null ``` Each function returns a NEW CADElement (immutable). For lines: update x1/y1/x2/y2 in properties. For circles: update x/y/radius. For polylines: update points array. For text: update x/y. Apply rotation to properties.rotation. ### Task 2: Implement modification logic in InteractionEngine File: `src/interaction/index.ts` Replace the stub `handleModifyDown` with real multi-step logic: **Move/Copy (2 clicks):** 1. First click: set base point (state.points[0]) 2. Mouse move: update preview with moved elements 3. Second click: confirm — call onElementsModified with moved elements (Move) or onElementCreated for copies (Copy) 4. Reset to idle **Rotate (2 clicks):** 1. First click: set pivot point 2. Mouse move: preview rotation (angle = atan2(mouse - pivot)) 3. Second click: confirm with current angle **Scale (2 clicks):** 1. First click: set base point 2. Mouse move: preview scale (factor = dist(mouse, base) / dist(firstElement, base)) 3. Second click: confirm **Mirror (2 clicks):** 1. First click: first point of mirror axis 2. Mouse move: preview mirror 3. Second click: second point of mirror axis, confirm **Trim/Extend (2 clicks):** 1. First click: select boundary element (hitTest) 2. Second click: select element to trim/extend (hitTest), apply, confirm **Fillet (3 clicks):** 1. First click: select first element 2. Second click: select second element 3. Third click or Enter: set radius (use distance between elements or prompt) **Offset (2 clicks):** 1. First click: select element to offset 2. Second click: direction + distance = offset Add `updateModifyPreview(pt)` method similar to `updatePreview(pt)` but for modifications. Add `confirmModify()` method that calls `onElementsModified` with modified elements. For Copy: call `onElementCreated` for each copied element instead of `onElementsModified`. ### Task 3: Wire onElementsModified in App.tsx File: `src/App.tsx` - Add `handleElementsModified` callback: push current elements to undoStack, replace modified elements by ID, clear redoStack - Pass `onElementsModified` to CanvasArea - CanvasArea wires it to `interaction.setCallbacks({ onElementsModified: ... })` ### Task 4: Add GroupTool File: `src/tools/modification/GroupTool.ts` Simple group management: ```typescript export interface ElementGroup { id: string; name: string; elementIds: string[]; parentGroupId: string | null; } export class GroupManager { private groups: Map = new Map(); createGroup(elementIds: string[], name?: string): ElementGroup ungroup(groupId: string): void getGroup(id: string): ElementGroup | undefined getGroups(): ElementGroup[] getGroupedElements(): Set // all element IDs in any group moveGroup(groupId: string, dx: number, dy: number): string[] // returns modified element IDs } ``` Wire in App.tsx: add `groups` state, add command 'G' or 'GROUP' to create group from selection. ### Task 5: Update command shortcuts File: `src/App.tsx` Add to handleCommand: - `M` or `MOVE` → setTool('move') - `CO` or `COPY` → setTool('copy') - `RO` or `ROTATE` → setTool('rotate') - `SC` or `SCALE` → setTool('scale') - `MI` or `MIRROR` → setTool('mirror') - `TR` or `TRIM` → setTool('trim') - `EX` or `EXTEND` → setTool('extend') - `F` or `FILLET` → setTool('fillet') - `O` or `OFFSET` → setTool('offset') - `E` or `ERASE` → setTool('delete') - `G` or `GROUP` → create group from selection - `UNG` or `UNGROUP` → ungroup selection Note: Some of these already exist from Phase 3. Check and avoid duplicates. ### Task 6: Update CanvasAreaProps File: `src/types/ui.types.ts` Add `onElementsModified` to CanvasAreaProps: ```typescript onElementsModified: (els: CADElement[]) => void; ``` ## Constraints - TypeScript functional components, no inline styles (except where mockup uses style) - No styled-components, no Tailwind - `npx tsc --noEmit` must pass with 0 errors - `npx vite build` must succeed - Minimal changes — extend existing files, don't rewrite - Keep all existing CSS class names - Pure geometry functions in separate file, not in InteractionEngine ## File Summary 1. `src/tools/modification/geometry.ts` — NEW: pure geometry transform functions 2. `src/tools/modification/GroupTool.ts` — NEW: GroupManager class 3. `src/interaction/index.ts` — PATCH: implement handleModifyDown, updateModifyPreview, confirmModify 4. `src/App.tsx` — PATCH: handleElementsModified, groups state, command shortcuts 5. `src/types/ui.types.ts` — PATCH: add onElementsModified to CanvasAreaProps 6. `src/components/CanvasArea.tsx` — PATCH: wire onElementsModified callback ## Test Criteria 1. `npx tsc --noEmit` — 0 errors 2. `npx vite build` — success