6.7 KiB
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.onElementsModifiedcallback exists but is never called.- App.tsx has
handleElementsDeletedbut nohandleElementsModified. - 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:
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):
- First click: set base point (state.points[0])
- Mouse move: update preview with moved elements
- Second click: confirm — call onElementsModified with moved elements (Move) or onElementCreated for copies (Copy)
- Reset to idle
Rotate (2 clicks):
- First click: set pivot point
- Mouse move: preview rotation (angle = atan2(mouse - pivot))
- Second click: confirm with current angle
Scale (2 clicks):
- First click: set base point
- Mouse move: preview scale (factor = dist(mouse, base) / dist(firstElement, base))
- Second click: confirm
Mirror (2 clicks):
- First click: first point of mirror axis
- Mouse move: preview mirror
- Second click: second point of mirror axis, confirm
Trim/Extend (2 clicks):
- First click: select boundary element (hitTest)
- Second click: select element to trim/extend (hitTest), apply, confirm
Fillet (3 clicks):
- First click: select first element
- Second click: select second element
- Third click or Enter: set radius (use distance between elements or prompt)
Offset (2 clicks):
- First click: select element to offset
- 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
handleElementsModifiedcallback: push current elements to undoStack, replace modified elements by ID, clear redoStack - Pass
onElementsModifiedto CanvasArea - CanvasArea wires it to
interaction.setCallbacks({ onElementsModified: ... })
Task 4: Add GroupTool
File: src/tools/modification/GroupTool.ts
Simple group management:
export interface ElementGroup {
id: string;
name: string;
elementIds: string[];
parentGroupId: string | null;
}
export class GroupManager {
private groups: Map<string, ElementGroup> = new Map();
createGroup(elementIds: string[], name?: string): ElementGroup
ungroup(groupId: string): void
getGroup(id: string): ElementGroup | undefined
getGroups(): ElementGroup[]
getGroupedElements(): Set<string> // 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:
MorMOVE→ setTool('move')COorCOPY→ setTool('copy')ROorROTATE→ setTool('rotate')SCorSCALE→ setTool('scale')MIorMIRROR→ setTool('mirror')TRorTRIM→ setTool('trim')EXorEXTEND→ setTool('extend')ForFILLET→ setTool('fillet')OorOFFSET→ setTool('offset')EorERASE→ setTool('delete')GorGROUP→ create group from selectionUNGorUNGROUP→ 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:
onElementsModified: (els: CADElement[]) => void;
Constraints
- TypeScript functional components, no inline styles (except where mockup uses style)
- No styled-components, no Tailwind
npx tsc --noEmitmust pass with 0 errorsnpx vite buildmust 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
src/tools/modification/geometry.ts— NEW: pure geometry transform functionssrc/tools/modification/GroupTool.ts— NEW: GroupManager classsrc/interaction/index.ts— PATCH: implement handleModifyDown, updateModifyPreview, confirmModifysrc/App.tsx— PATCH: handleElementsModified, groups state, command shortcutssrc/types/ui.types.ts— PATCH: add onElementsModified to CanvasAreaPropssrc/components/CanvasArea.tsx— PATCH: wire onElementsModified callback
Test Criteria
npx tsc --noEmit— 0 errorsnpx vite build— success