Files

159 lines
6.7 KiB
Markdown
Raw Permalink Normal View History

# 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<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:
- `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