Files
kniip/.cursor/plans/gebogen_lijnen_e6c44304.plan.md
T
2026-11-09 17:06:35 +01:00

51 lines
3.9 KiB
Markdown

---
name: Gebogen lijnen
overview: Lijnen en pijlen krijgen buigpunten waardoor een vloeiende kromme door alle punten loopt. Bij een geselecteerde lijn staat tussen elk paar punten een spook-greep; slepen daarvan maakt een nieuw buigpunt, dubbelklikken op een buigpunt verwijdert het.
todos:
- id: model
content: "LineAnnotation: Bends-lijst, Catmull-Rom-naar-Bezier helper, Render/Bounds/HitTest/Move/Clone aanpassen, pijlpunt langs raaklijn"
status: completed
- id: handles
content: "Grepen: buigpunten + spook-grepen, BeginHandleDrag voegt buigpunt in, RemoveHandle; IsGhostHandle/RemoveHandle in Annotation"
status: completed
- id: surface
content: "AnnotationSurface: spook-grepen anders tekenen, echte grepen voorrang bij HitHandle"
status: completed
- id: editor
content: "EditorWindow: dubbelklik op buigpunt verwijdert het (met undo), hint-tekst bijwerken"
status: completed
isProject: false
---
# Gebogen lijnen en pijlen
## Gedrag
- Selecteer een lijn of pijl: je ziet de begin- en eindgreep, de buigpunten en, halverwege elk stuk van de kromme, een kleine halfdoorzichtige spook-greep.
- Sleep je een spook-greep, dan wordt die een echt buigpunt en volgt de lijn de muis (zoals in je screenshot).
- Dubbelklik op een buigpunt verwijdert het. Beide acties kun je ongedaan maken.
- De kromme loopt precies door alle punten (Catmull-Rom, omgezet naar kubische Beziers). De pijlpunt volgt de richting van het laatste stuk.
## Wijzigingen
**[src/Kniip/Editor/Annotations/LineAnnotation.cs](src/Kniip/Editor/Annotations/LineAnnotation.cs)**
- Nieuw: `List<Point> Bends`, met `Start + Bends + End` als doorlooppunten.
- Een helper bouwt de Bezier-segmenten. Voor `Bounds`, `HitTest` en de posities van de spook-grepen gebruikt hij bemonsterde punten (ongeveer 16 per segment). Zonder buigpunten blijft alles gelijk aan nu.
- `Render`: een `StreamGeometry` met `BezierTo`. Bij een pijl wordt de richting bepaald door `End` min het laatste controlepunt. Het laatste segment eindigt op het basispunt van de pijlpunt.
- Grepen: index 0 is `Start` en 1 is `End`, zodat bestaande code blijft werken. Daarna volgen de buigpunten en dan de spook-grepen (het Bezier-punt bij t=0,5 van elk segment).
- `BeginHandleDrag`: bij een spook-greep wordt een buigpunt op de juiste plek in `Bends` ingevoegd en het actieve buigpunt onthouden. `MoveHandle` verplaatst daarna dat punt.
- Met Shift blijft `SnapAngle` werken voor begin en eind, maar dan ten opzichte van het naastliggende punt.
- `Move` verschuift ook alle buigpunten. `Clone` maakt een diepe kopie van `Bends`, nodig voor ongedaan maken (zelfde patroon als in `HighlightAnnotation`).
**[src/Kniip/Editor/Annotations/Annotation.cs](src/Kniip/Editor/Annotations/Annotation.cs)**
- `public virtual bool IsGhostHandle(int index) => false;`
- `public virtual bool RemoveHandle(int index) => false;` (LineAnnotation verwijdert hiermee een buigpunt)
**[src/Kniip/Editor/AnnotationSurface.cs](src/Kniip/Editor/AnnotationSurface.cs)**
- `DrawSelection` tekent spook-grepen als kleinere, halfdoorzichtige cirkels. `HitHandle` controleert eerst de echte grepen en daarna pas de spook-grepen, zodat een echte greep voorrang krijgt als ze overlappen.
**[src/Kniip/Editor/EditorWindow.xaml.cs](src/Kniip/Editor/EditorWindow.xaml.cs)** (rond regel 452)
- In `Surface_MouseLeftButtonDown`: als op een greep geklikt wordt met `e.ClickCount == 2` en `_selected.RemoveHandle(handle)` slaagt, dan volgen `Commit(_pending)` en `InvalidateVisual`, en stopt de verwerking daar. Anders start het bestaande sleepgedrag.
- Optioneel: in `UpdateHint` een korte tip toevoegen voor lijnen ("Sleep het middenpunt om te buigen, dubbelklik om een punt te verwijderen").
## Niet nodig
Exporteren gaat via `Render` in [src/Kniip/Editor/DocumentRenderer.cs](src/Kniip/Editor/DocumentRenderer.cs) en werkt dus automatisch. Ook het aanmaken van lijnen (`UpdateCreating`) blijft ongewijzigd: nieuwe lijnen zijn recht.