diff --git a/README.md b/README.md new file mode 100644 index 0000000..c56757a --- /dev/null +++ b/README.md @@ -0,0 +1,165 @@ +# đŸ–‹ïž ink-notes + +Eine latenzoptimierte, hardwarebeschleunigte Inking- und Notizbuch-Anwendung fĂŒr Linux, entwickelt in **Rust** mit **wgpu** und **winit**. + +`ink-notes` kombiniert minimale Eingabelatenz beim Zeichnen mit Stift, Touchpad oder Maus mit einem flĂŒssigen stufenlosen Viewport, automatischer Geometrie- und Formerkennung, Objekttransformationen, Multi-Dokumenten-Verwaltung und Vektor-PDF-Export. + +--- + +## ✹ Features im Überblick + +### ⚡ Latenzoptimierte Inking-Pipeline +- **Hardware-Rendering:** Direct-to-Screen-Rendering mit `wgpu` (Vulkan / X11 / Wayland) unter Nutzung von `PresentMode::Mailbox` fĂŒr minimale Eingabeverzögerung. +- **1€-Filter (One-Euro-Filter):** Dynamischer Tiefpassfilter zur GlĂ€ttung von Zittern bei langsamen Bewegungen ohne spĂŒrbare Latenz bei schnellen SchwĂŒngen. +- **Centripetal Catmull-Rom Splines:** Weiche C1-stetige Kurveninterpolation ohne Überschwinger oder Schleifen. +- **Drucksensitive Dreiecksstreifen-Tessellierung:** Rechnerische Generierung von Triangle-Meshes mit winkelhalbierenden Normalenvektoren entlang des Pfads. +- **Werkzeuge:** + - **Stift (Pen):** Feine, variable StrichstĂ€rken (0.3 mm bis 1.2 mm) mit Notiz-Farben (Schwarz, Blau, Rot, GrĂŒn, Grau). + - **Textmarker (Highlighter):** Breite, halbtransparente Striche (4.0 mm bis 8.0 mm) mit Multiplikations-Blending. + - **Radierer (Eraser):** Segment- und abstandsbasierte Löschung einzelner Striche auf dem DIN-A4-Canvas. + +### 🔍 Stufenloser Zoom & Pan (Kamera) +- **Viewport-Transformation:** 2D-Projektionsmatrix im WGSL-Vertex-Shader. +- **Cursor-zentrierter Zoom:** Stufenloses Zoomen von **25 % bis 500 %** zentriert auf die Cursor-Position via Mausrad, Trackpad-Pinch oder TastenkĂŒrzel (`Strg +` / `Strg -` / `Strg 0`). +- **Pan:** Verschieben des Canvas per mittlerer Maustaste, `Leertaste + Drag` oder Trackpad-Pan. +- **Inverse Koordinatentransformation:** PrĂ€zise Umrechnung von Bildschirm- in DIN-A4-Dokumentkoordinaten (`screen_to_page`). + +### 📐 Geometrie- & Objekterkennung (Shape Recognition) +- **Erkannte Primitiven:** + - **Geraden (Line):** Sehnen- zu PfadlĂ€ngenverhĂ€ltnis > 0.92. + - **Rechtecke (Rectangle):** 4 orthogonale Ecken via Douglas-Peucker-Polygonzug (RDP). + - **Kreise & Ellipsen (Circle/Ellipse):** Geschlossene Pfade mit minimaler Varianz via Least-Squares Circle Fit. + - **Koordinatensysteme (Axes):** Zwei orthogonale Achsen (80°–100° gesnappt auf exakt 90°) mit optionalen Richtungspfeilen. + - **Dreiecke, Rhomben / Rauten & Oktaeder:** RegelmĂ€ĂŸige Polygone mit Eckpunkt-Analyse. +- **Interaktionsmodelle:** + - **Draw-and-Hold:** Verweilt der Stift am Ende eines Strichs fĂŒr > 400 ms regungslos (< 3 px), snappt die Skizze sofort automatisch in eine perfekte Vektorform ein. + - **Formen-Werkzeug (Shape Mode):** Direkte Erkennung und Ersetzung beim Loslassen. + +### đŸ–ïž Objektauswahl & Transformation (Lasso / Select) +- **Lasso & Klick-Auswahl:** Schnelles Markieren einzelner Striche oder Gruppen per Rahmen (`V`). +- **Echtzeit-Verschieben:** Freies Verschieben ausgewĂ€hlter Objekte ĂŒber die Seite. +- **Stufenlose Skalierung:** GrĂ¶ĂŸenĂ€nderung ĂŒber interaktive Eck-Griffe. +- **Drehung:** Stufenlose Rotation um das Objektzentrum ĂŒber den oberen Dreh-Griff oder exakte 90°-Schritte mit Taste `R`. +- **Multicolor-UmfĂ€rbung:** AusgewĂ€hlte Objekte direkt ĂŒber die Farbpalette umfĂ€rben. +- **Clipboard & Editieren:** Kopieren (`Strg + C`), EinfĂŒgen (`Strg + V`) und Löschen (`Entf` / `Backspace`). +- **Undo / Redo:** VollstĂ€ndige Transaktionshistorie (`Strg + Z` / `Strg + Y`) fĂŒr alle Aktionen inklusive Transformationen. + +### 📚 Multi-Dokumenten- & Notizbuch-Verwaltung +- **Dateisystem-Architektur:** Jedes Dokument wird in einem separaten Verzeichnis unter `~/.local/share/ink-notes/notebooks//` mit `manifest.json` und Einzelseiten (`page_.json`) gespeichert. +- **Dokumenten-Manager:** Dunkles Glassmorphism-Modal direkt in der Toolbar: + - Neues Notizbuch anlegen. + - Zwischen NotizbĂŒchern mit einem Klick wechseln. + - Dokumente direkt umbenennen (`✏`). + - Dokumente löschen (`🗑`). +- **Verlustfreier Wechsel:** Beim Dokumentenwechsel sichert ein synchroner Disk-Flush alle anstehenden Änderungen garantiert ab. + +### 📄 Seiten & HintergrĂŒnde +- **Virtuelles DIN-A4-Raster:** 2100 × 2970 Koordinateneinheiten (10 Einheiten = 1 mm). +- **Hintergrundmuster:** + - Blanko + - Kariert (5 mm Raster) + - Liniert (7.5 mm Zeilenabstand) + - Punktraster (5 mm Dot-Grid) +- **Seitenverwaltung:** Vor- und ZurĂŒckblĂ€ttern, neue Seiten anfĂŒgen, Seitensprung-MenĂŒ. + +### đŸ’Ÿ Autosave & Persistenz +- **Asynchroner Autosave-Controller:** Sichert Änderungen im Hintergrund-Thread nach 1.5 s Ruhezeit (Debounce), ohne das Zeichnen zu blockieren. +- **Atomares Schreiben:** Temp-Dateien mit atomarem Rename (`manifest.json.tmp` -> `manifest.json`) schĂŒtzen vor Datenkorruption bei AbstĂŒrzen. +- **Sicherer Shutdown:** `ESC` oder Schließen des Fensters fĂŒhrt vor Programmende einen vollstĂ€ndigen synchronen Flush aus. + +### đŸ–šïž Vektor-PDF-Export +- Exportiert das gesamte Dokument via `printpdf` nach `./export.pdf`. +- Striche und geometrische Formen werden als skalierbare Vektoren ausgegeben. + +--- + +## 🚀 Schnellstart + +### Voraussetzungen +- **Rust & Cargo:** Version 1.75 oder neuer (`rustup update stable`) +- **System-Bibliotheken (Linux):** + - Vulkan-Treiber (z. B. `mesa-vulkan-drivers` oder proprietĂ€re Treiber) + - X11 / XWayland Bibliotheken (`libx11-dev`, `libxcursor-dev`, `libxrandr-dev`, `libxi-dev`, `libxkbcommon-dev`) + +### Bauen & AusfĂŒhren + +```bash +# Repository klonen +git clone gitea@farmersdog.pansi.eu:harald/ink-notes.git +cd ink-notes + +# Kompilieren und starten via Starter-Skript +./run.sh +``` + +Alternativ direkt mit Cargo: + +```bash +cargo run --release +``` + +--- + +## ⌚ Tastatur- & Maus-Shortcuts + +| Aktion | TastenkĂŒrzel / Mausgeste | +| :--- | :--- | +| **Zeichnen** | Linke Maustaste / Stiftspitze | +| **Canvas verschieben (Pan)** | Mittlere Maustaste **oder** `Leertaste + Drag` | +| **Zoom zentriert auf Cursor** | Mausrad / Touchpad-Pinch | +| **Zoom zurĂŒcksetzen (100 %)** | `Strg + 0` | +| **Zoom In / Out** | `Strg + +` / `Strg + -` | +| **Auswahlwerkzeug (Lasso)** | `V` | +| **Auswahl um 90° drehen** | `R` | +| **Kopieren & EinfĂŒgen** | `Strg + C` / `Strg + V` | +| **Auswahl löschen** | `Entf` oder `Backspace` | +| **Undo / Redo** | `Strg + Z` / `Strg + Y` (oder `Strg + Shift + Z`) | +| **NĂ€chste Seite** | `Bild ab (PageDown)` oder `N` | +| **Vorherige Seite** | `Bild auf (PageUp)` | +| **Erste / Letzte Seite** | `Pos 1 (Home)` / `Ende (End)` | +| **PDF-Export** | `Strg + E` oder `Strg + S` | +| **Beenden (mit Disk-Flush)** | `Esc` oder `Q` | + +--- + +## đŸ—ïž Projekt- und Modul-Architektur + +```text +src/ +├── main.rs # Event-Loop (winit), Input-Routing, App-Orchestrierung +├── lib.rs # Bibliotheks-Exporte fĂŒr Tests und Komponenten +├── canvas.rs # wgpu Render-Pipeline, GPU-Buffer, Zeichenzustand +├── viewport.rs # Kamera-Pan, Zoom, 2D-Transformationsmatrizen +├── shader.wgsl # WGSL Vertex- & Fragment-Shader fĂŒr Inking-Meshes +├── filter.rs # One-Euro-Filter gegen Eingabe-Jitter +├── spline.rs # Centripetal Catmull-Rom & Dreiecksstreifen-Tessellierung +├── geometry.rs # RDP-Algorithmus, Least-Squares Circle Fit, Shape Recognition +├── selection.rs # Lasso-Auswahl, Bounding-Box, Griffe, Rotation & Skalierung +├── background.rs # Tessellierung fĂŒr Kariert, Liniert, Punktraster +├── document.rs # Notizbuch-Datenmodell, Seiten, DIN-A4-Koordinatensystem +├── history.rs # Undo/Redo-Transaktionsstapel (Add, Erase, Transform) +├── storage.rs # Atomares Laden/Speichern von Manifest & Seiten, Multi-Notebooks +├── autosave.rs # Debounced Hintergrund-Autosave mit MPSC-Kanal +├── export.rs # Skalierbarer Vektor-PDF-Export via printpdf +└── ui.rs # Floating Glassmorphism-Toolbar & Dokumenten-Manager (egui) +``` + +AusfĂŒhrliche Details zur Architektur und mathematischen Modellen findest du unter [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md). + +--- + +## đŸ§Ș Tests ausfĂŒhren + +Die gesamte Inking-Pipeline, Geometrieerkennung und Speicherlogik ist mit Unit-Tests abgesichert: + +```bash +cargo test +``` + +Alle 53 Unit-Tests prĂŒfen Berechnungen, Hit-Testing, Undo/Redo und Persistenz konsistent ab. + +--- + +## 📄 Lizenz + +ProprietĂ€res Notiz-Projekt fĂŒr Linux. Alle Rechte vorbehalten. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..219c09e --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,139 @@ +# Architektur & Technische Dokumentation von ink-notes + +Dieses Dokument beschreibt die interne Architektur, mathematische Modelle und Rendering-Pipelines von `ink-notes`. + +--- + +## 1. Die Inking-Pipeline + +``` +[Hardware Stylus / Touch / Maus] + │ + â–Œ + [winit Event-Loop] + │ (tx, ty, pressure, timestamp) + â–Œ + [Viewport Invertierung] (screen_to_page: Umrechnung in DIN-A4-Einheiten) + │ + â–Œ + [1€-Filter] (Jitter-Beseitigung bei langsamen Bewegungen) + │ + â–Œ + [Centripetal Catmull-Rom] (C1-stetige Spline-Interpolation, alpha = 0.5) + │ + â–Œ + [Mesh-Tessellierung] (Drucksensitive Dreiecksstreifen via Normalen) + │ + â–Œ + [wgpu GPU-Buffer] (Vertex- & Index-Buffer im VRAM) +``` + +### 1.1. 1€-Filter (One-Euro-Filter) +Stift- und Touch-Eingaben unterliegen bei niedrigen Geschwindigkeiten oft Mikrozittern (Jitter). Standard-Tiefpassfilter verursachen jedoch bei schnellen SchwĂŒngen eine störende Verzögerung (Latenz). + +Der 1€-Filter (`filter.rs`) löst dieses Dilemma mit einer adaptiven Grenzfrequenz $f_c$: +$$f_c = f_{c,\min} + \beta \cdot |\dot{x}|$$ +- Bei geringer Geschwindigkeit ($|\dot{x}| \approx 0$) dominiert $f_{c,\min}$, wodurch Zittern maximal geglĂ€ttet wird. +- Bei schnellen ZĂŒgen wĂ€chst $\beta \cdot |\dot{x}|$ an, wodurch der Filter durchlĂ€ssig wird und Latenz eliminiert. + +### 1.2. Centripetal Catmull-Rom Splines +Um Ecken zu vermeiden und eine organische SchriftfĂŒhrung zu erzielen, werden Abtastpunkte mit einem zentripetalen Catmull-Rom-Spline (`spline.rs`) interpoliert ($\alpha = 0.5$): +$$t_{i+1} = t_i + ||P_{i+1} - P_i||^\alpha$$ +Diese Parametrisierung garantiert: +- Keine SelbstĂŒberschneidungen bei scharfen Wendungen. +- Keine unerwĂŒnschten Cusp-Schleifen. +- Perfekte $C^1$-Stetigkeit an den StĂŒtzstellen. + +### 1.3. Drucksensitive Dreiecksstreifen (Tessellierung) +Aus den interpolierten Spline-Punkten mit Radius $r_i = f(\text{pressure}_i)$ wird ein Triangle-Strip erzeugt: +1. Richtungsvektor $T_i = P_{i+1} - P_i$. +2. Senkrechter Normalenvektor $N_i = (-T_{i,y}, T_{i,x}) / ||T_i||$. +3. An inneren StĂŒtzstellen werden Normalen benachbarter Segmente gemittelt (Winkelhalbierende). +4. Eckpunkte des Dreiecksstreifens: + $$V_{i,\text{left}} = P_i + N_i \cdot r_i, \quad V_{i,\text{right}} = P_i - N_i \cdot r_i$$ + +--- + +## 2. Rendering & Kamera-Pipeline (`wgpu`) + +### 2.1. Virtuelles Koordinatensystem +Die Anwendung rechnet intern in virtuellen DIN-A4-Koordinaten: +- Breite: **2100 Einheiten** (210 mm) +- Höhe: **2970 Einheiten** (297 mm) +- 1 mm entspricht exakt **10 Einheiten**. + +### 2.2. Viewport & Projektionsmatrix (`viewport.rs`) +Der Vertex-Shader transformiert DIN-A4-Koordinaten ĂŒber eine Orthogonal-Projektion kombiniert mit Pan und Zoom in Normalized Device Coordinates (NDC, $[-1, 1]$): + +```wgsl +struct CameraUniform { + transform: mat4x4, +}; +``` + +Die Projektionsmatrix $M$ wird berechnet aus: +1. Skalierung des DIN-A4-Papiers auf den Viewport unter BerĂŒcksichtigung des SeitenverhĂ€ltnisses. +2. Anwenden des Zoom-Faktors $z \in [0.25, 5.0]$. +3. Translation durch den Pan-Offset $(p_x, p_y)$. + +### 2.3. Cursor-zentrierter Zoom +Beim Zoomen mit dem Mausrad wird der neue Pan-Offset so korrigiert, dass der Punkt unter dem Cursor fixiert bleibt: +$$\vec{p}_{\text{neu}} = \vec{c} - (\vec{c} - \vec{p}_{\text{alt}}) \cdot \frac{z_{\text{neu}}}{z_{\text{alt}}}$$ + +### 2.4. Render-Passes +In jedem Frame werden zwei HauptpĂ€sse durchlaufen: +1. **Pass 1 (Canvas):** + - Hintergrund-Quad und Linien/Gitter (Instanced oder Batch Vertex-Buffer). + - Gespeicherte Striche und Geometrie-Meshes. + - Aktiver Live-Strich (Ephemeral Buffer). + - Auswahl-Overlay & Transformationsgriffe. +2. **Pass 2 (egui UI):** + - Floating Glassmorphism-Toolbar. + - Farb- und Werkzeug-Popovers. + - Modal-Dokumentenmanager. + +--- + +## 3. Geometrieerkennung & Shape-Fitting (`geometry.rs`) + +Die Erkennung erfolgt rein analytisch und deterministisch ohne schwere ML-Frameworks: + +### 3.1. Ramer-Douglas-Peucker (RDP) +Reduziert den Rohpfad auf signifikante Eckpunkte mit konfigurierbarem Toleranzabstand $\epsilon$. + +### 3.2. Erkennungsregeln: +- **Gerade:** VerhĂ€ltnis von SehnenlĂ€nge ($||P_{\text{end}} - P_{\text{start}}||$) zur Pfad-BogenlĂ€nge $> 0.92$. +- **Rechteck:** RDP liefert 4 dominante Ecken; Kantenwinkel liegen zwischen $75^\circ$ und $105^\circ$. +- **Kreis / Ellipse:** Pfad ist geschlossen ($||P_{\text{start}} - P_{\text{end}}|| < 0.1 \times \text{Umfang}$); Standardabweichung der Radien vom Mittelpunkt ist minimal ($< 0.12 \times \bar{r}$). +- **Koordinatensystem:** Zwei orthogonale Schenkel mit Schnittpunkt nahe dem Start/Ende; Winkel $80^\circ$ bis $100^\circ$ wird exakt auf $90^\circ$ eingerastet; positive Achsenenden erhalten Pfeilspitzen. +- **Dreiecke / Rhomben / Oktaeder:** Spezifische Eckpunkt- und Symmetrieanalysen der RDP-Kontur. + +### 3.3. Draw-and-Hold Erkennung +HĂ€lt der Anwender den Stift am Ende eines Strichs fĂŒr $> 400\text{ ms}$ innerhalb eines Radius von $< 3\text{ px}$ ruhig, wird die Form im fliegenden Wechsel als Vektorform visualisiert und beim Abheben des Stifts fixiert. + +--- + +## 4. Persistenz & Autosave-Architektur (`storage.rs`, `autosave.rs`) + +``` +[UI / Inking Event] + │ + â–Œ + [mark_page_dirty] + │ + â–Œ +[AutosaveController] ──(1.5s Debounce)──â–ș [Hintergrund-Thread (MPSC)] + │ + â–Œ + [Temp-Datei *.tmp schreiben] + │ + â–Œ + [Atomares std::fs::rename] + │ + â–Œ + [manifest.json / page_.json] +``` + +- **Keine UI-Blockierung:** Disk-I/O erfolgt vollstĂ€ndig in einem dedizierten Worker-Thread. +- **AtomaritĂ€t:** Daten werden stets zuerst in `.json.tmp` geschrieben und via `fs::rename` atomar ersetzt. +- **Verlustfreier Notizbuchwechsel:** Bei `switch_notebook_dir` wird synchron `flush()` aufgerufen, um alle ungespeicherten Daten des aktuellen Dokuments sicherzustellen, bevor der Pfad gewechselt wird. diff --git a/docs/FILE_FORMAT.md b/docs/FILE_FORMAT.md new file mode 100644 index 0000000..2082f15 --- /dev/null +++ b/docs/FILE_FORMAT.md @@ -0,0 +1,134 @@ +# Dateiformat & Speicherstruktur von ink-notes + +Dieses Dokument beschreibt die Speicherstruktur und die JSON-Dateiformate, mit denen `ink-notes` Dokumente auf dem Dateisystem ablegt. + +--- + +## 1. Verzeichnisstruktur + +NotizbĂŒcher werden standardmĂ€ĂŸig unter dem XDG-Datenpfad gespeichert: + +```text +~/.local/share/ink-notes/ +└── notebooks/ + ├── / + │ ├── manifest.json + │ ├── page_1.json + │ ├── page_2.json + │ └── ... + └── / + ├── manifest.json + └── page_1.json +``` + +--- + +## 2. Dokumenten-Manifest (`manifest.json`) + +Das Manifest enthĂ€lt Metadaten ĂŒber das gesamte Dokument sowie die Reihenfolge und IDs aller Seiten: + +```json +{ + "id": "notebook-a1b2c3d4", + "title": "Mathematik Vorlesung", + "version": 1, + "pages": [ + { + "id": 1, + "file_name": "page_1.json" + }, + { + "id": 2, + "file_name": "page_2.json" + } + ], + "current_page_index": 0, + "next_page_id": 3, + "next_stroke_id": 42, + "created_at": 1775460000, + "modified_at": 1775463600 +} +``` + +### Felder: +- `id`: Eindeutige ID des Notizbuchs (z. B. UUID oder `"default"`). +- `title`: Vom Nutzer vergebener Anzeigename des Notizbuchs. +- `version`: Format-Versionsnummer (aktuell `1`). +- `pages`: Geordnete Liste von Seitenreferenzen. +- `current_page_index`: Index der zuletzt aktiven Seite fĂŒr Session-Restore. +- `next_page_id`: Inkrementierender ID-ZĂ€hler fĂŒr neue Seiten. +- `next_stroke_id`: Globaler ID-ZĂ€hler fĂŒr neue Striche. +- `created_at` / `modified_at`: UNIX-Zeitstempel in Sekunden. + +--- + +## 3. Seitendatei (`page_.json`) + +Jede Seite wird in einer eigenen Datei gespeichert, um Lazy-Loading und schnelles inkrementelles Speichern zu ermöglichen. + +```json +{ + "id": 1, + "background": "Grid5mm", + "strokes": [ + { + "id": 1, + "color": [0.12, 0.12, 0.14, 1.0], + "width_mm": 0.5, + "samples": [ + { + "x": 350.2, + "y": 420.5, + "pressure": 0.65, + "timestamp": 12.345 + }, + { + "x": 352.1, + "y": 425.8, + "pressure": 0.70, + "timestamp": 12.355 + } + ], + "recognized_shape": null + }, + { + "id": 2, + "color": [0.15, 0.35, 0.85, 1.0], + "width_mm": 0.8, + "samples": [], + "recognized_shape": { + "Rectangle": { + "top_left": { "x": 500.0, "y": 600.0 }, + "width": 300.0, + "height": 200.0 + } + } + } + ] +} +``` + +### Felder: +- `id`: Eindeutige ID der Seite. +- `background`: Papiermuster (`Blank`, `Grid5mm`, `Lined`, `DotGrid`). +- `strokes`: Liste der auf der Seite gezeichneten Striche und Vektorformen. + - `id`: Eindeutige ID des Strichs. + - `color`: RGBA-Farbwerte im Bereich `[0.0, 1.0]`. + - `width_mm`: Basis-Strichbreite in Millimetern. + - `samples`: Rohabtastpunkte entlang des Pfads (`x`, `y` in DIN-A4-Einheiten, `pressure` in `[0.0, 1.0]`, `timestamp` in Sekunden). + - `recognized_shape`: Optionales Enum fĂŒr geometrische Vektorformen: + - `Line { start, end }` + - `Rectangle { top_left, width, height }` + - `Circle { center, radius }` + - `Axes { origin, x_end, y_end, has_arrows }` + - `Triangle { p1, p2, p3 }` + - `Rhombus { center, width, height }` + - `Octahedron { center, radius }` + +--- + +## 4. Atomares Schreibverfahren + +Um Datenverlust bei Stromausfall oder plötzlichem Prozessabbruch zu verhindern: +1. Daten werden in `.tmp` serialisiert und per `fsync` auf das Speichermedium geschrieben. +2. Mittels `std::fs::rename(".tmp", "")` wird die Zieldatei auf POSIX-Systemen atomar ersetzt.