From 85a210c001a223807339c48f6a033e3e93e7d0b6 Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Tue, 24 Mar 2026 15:13:25 -0700 Subject: [PATCH] Update docs for DC-1 testing, fix toolbar clipping - Updated DESIGN.md: rendering strategy (no backing bitmap, screen-space grid, viewport clamping), added line snap and box tool documentation - Updated PROGRESS.md: DC-1 testing results and fixes applied - Updated CLAUDE.md: key conventions for rendering - Fix toolbar overlap: use clipToBounds on canvas AndroidView to prevent it from drawing over the toolbar area Co-Authored-By: Claude Opus 4.6 (1M context) --- CLAUDE.md | 7 +++- DESIGN.md | 37 +++++++++++++----- PROGRESS.md | 39 ++++++++++++++----- .../engpad/ui/editor/EditorScreen.kt | 6 ++- 4 files changed, 66 insertions(+), 23 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index e413dfe..6a88b49 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -81,7 +81,10 @@ eng-pad/ - Stroke points are packed as little-endian float BLOBs: `[x0,y0,x1,y1,...]` - All coordinates are in canonical space (300 DPI). Screen transform via `Matrix`. -- Grid spacing: 60pt (~5 squares/inch). Grid is drawn on screen, excluded from exports. +- Grid drawn in **screen space** with pixel-snapped positions (not canonical space) for uniform squares. - Pen widths: 0.38mm = 4.49pt, 0.5mm = 5.91pt (at 300 DPI). -- Two pen sizes only, no pressure sensitivity. +- Anti-aliasing disabled on all paint objects for crisp e-ink rendering. +- No backing bitmap — strokes draw directly as paths. +- Viewport: page always fills screen, pan clamped to edges, dynamic min zoom. +- Line snap: hold pen still 1.5s to snap to straight line (60pt movement threshold). - Hardware palm rejection only (EMR digitizer). diff --git a/DESIGN.md b/DESIGN.md index 56da9a3..800958f 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -181,20 +181,39 @@ The editor toolbar controls the active mode: | Draw | Create strokes with the active pen size | | Erase | Touch a stroke to delete it (stroke-level eraser) | | Select | Draw a rectangle to select strokes, then move/copy/delete | +| Box | Drag corner-to-corner to draw rectangles | + +### Line Snap + +When drawing in pen mode, holding the stylus still for 1.5 seconds +activates line snap mode. The stroke is replaced with a straight line +from the original pen-down point to the current position. Moving the +pen continues the straight line. The snap timer is canceled if the pen +moves more than ~5mm (60pt at 300 DPI) from the origin before the +timer fires. This allows drawing straight lines for diagrams without +a separate ruler tool. ## Rendering Strategy ### On-Screen -1. **Completed strokes** are rendered to a backing `Bitmap`. This bitmap is - redrawn only when strokes change (add, delete, move). -2. **In-progress stroke** (pen is down) is drawn directly to the canvas on - each `onDraw` call, on top of the backing bitmap. -3. **Grid** is drawn as a separate pass — thin gray lines at 60pt intervals. -4. The view `Matrix` transforms everything from canonical to screen space. +1. **Completed strokes** are drawn directly as `Path` objects on each + `onDraw` call. No backing bitmap — direct path rendering avoids the + resolution loss that caused blurry strokes on e-ink displays. +2. **In-progress stroke** (pen is down) is drawn on top of completed strokes. +3. **Grid** is drawn in screen pixel space (not canonical space) with + pixel-snapped line positions. This ensures perfectly uniform square + spacing regardless of zoom level. Grid uses 1px screen-space lines. +4. **Strokes** are drawn in canonical space via the view `Matrix`. + Anti-aliasing is disabled on all paint objects for crisp e-ink rendering. -This approach means `onDraw` is fast for the common case (pen moving): -draw the cached bitmap + one active path + grid. +### Viewport + +The page always fills the entire visible canvas area: +- Minimum zoom is computed dynamically so the page never appears smaller + than the viewport in either dimension. +- Pan is clamped so page edges stay at or beyond viewport edges. +- Background is white to eliminate any visible non-page area. ### PDF Export @@ -220,6 +239,7 @@ Command pattern with a depth limit of 50: | `DeleteStrokeAction` | Delete stroke | Re-insert stroke | | `MoveStrokesAction` | Offset points | Offset back | | `DeleteMultipleAction`| Delete strokes | Re-insert strokes | +| `CopyStrokesAction` | Duplicate strokes | Delete copies | The `UndoManager` maintains two stacks. Performing a new action clears the redo stack. Each action also persists or deletes from Room as part of @@ -244,7 +264,6 @@ Stroke-level eraser (not pixel-level): 2. Hit test: check stroke bounding box first (fast reject), then check point-by-point distance (threshold: ~42 canonical points / ~3.5mm). 3. Hit strokes are deleted immediately (with undo support). -4. Backing bitmap is rebuilt after deletion. ## E-ink Considerations diff --git a/PROGRESS.md b/PROGRESS.md index 7e3499e..14c1e15 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -44,9 +44,10 @@ See PROJECT_PLAN.md for the full step list. ### Phase 3: Canvas — Basic Drawing (2026-03-24) - [x] 3.1–3.4: PadCanvasView — stylus input with historical points, Path/Paint - stroke rendering, backing bitmap at 1/4 resolution, 60pt grid, Matrix - coordinate transform (canonical ↔ screen) -- [x] 3.5: CanvasState — tool enum (PEN_FINE, PEN_MEDIUM, ERASER, SELECT), + stroke rendering, direct path drawing (no backing bitmap), 60pt grid + drawn in screen space with pixel-snapped positions, Matrix coordinate + transform (canonical ↔ screen) +- [x] 3.5: CanvasState — tool enum (PEN_FINE, PEN_MEDIUM, ERASER, SELECT, BOX), zoom/pan state, pen width constants (4.49pt, 5.91pt) - [x] 3.6: EditorViewModel — loads strokes from Room, saves on completion - [x] 3.7: EditorScreen + Toolbar — Compose wrapper with AndroidView, @@ -58,18 +59,19 @@ See PROJECT_PLAN.md for the full step list. ### Phase 4: Zoom and Pan (2026-03-24) -- [x] 4.1: ScaleGestureDetector with focal-point zoom (0.5×–4×) -- [x] 4.2: Finger drag for pan with multi-pointer tracking (handles finger - swaps during pinch gestures) +- [x] 4.1: ScaleGestureDetector with focal-point zoom (dynamic min–4×) +- [x] 4.2: Finger drag for pan with multi-pointer tracking, pan clamped + so page always fills viewport - [x] 4.3: Input routing by tool type already in place from Phase 3 - Zoom/pan state managed locally in PadCanvasView for responsiveness, synced to ViewModel on gesture end +- Minimum zoom computed dynamically from page/view dimensions ### Phase 5: Eraser (2026-03-24) - [x] 5.1–5.3: Stroke-level eraser with bounding box hit test (42pt radius), processes historical touch points for thorough erasing, deletes from - view + Room DB, rebuilds backing bitmap + view + Room DB ### Phase 6: Undo/Redo (2026-03-24) @@ -110,12 +112,29 @@ See PROJECT_PLAN.md for the full step list. - [x] 10.3: Empty states and delete confirmations already in place from Phase 2 - [ ] 10.4: Performance profiling — requires on-device testing +### On-Device Testing — Daylight DC-1 (2026-03-24) + +Tested on DC-1, identified and fixed rendering issues: + +- **Grid**: original grid drawn in canonical space had uneven spacing due to + sub-pixel positioning when scaled. Fixed by drawing grid in screen space + with pixel-snapped line positions. Now uniform squares. +- **Stroke quality**: original backing bitmap at 1/4 resolution caused blurry + strokes. Fixed by removing backing bitmap entirely and drawing paths directly. + Anti-aliasing disabled on all paint for crisp e-ink lines. +- **Viewport**: dark gray background was visible around page edges. Fixed by + clamping pan to page edges, computing dynamic min zoom, and white background. +- **Box tool**: works correctly on device. +- **PDF export**: correctly elides grid — confirmed on device. +- **Line snap**: increased threshold to 60pt (~5mm) and switched to max-distance + tracking to handle EMR stylus hand tremor. + ## Status All implementation phases complete. Remaining work: -- On-device testing on Supernote Manta and Daylight DC-1 -- Performance profiling (stroke rendering, page load times) -- Manual verification of PDF export fidelity +- Test on Supernote Manta +- Verify line snap behavior on device +- Performance profiling with heavily annotated pages ## Decisions & Deviations diff --git a/app/src/main/kotlin/net/metacircular/engpad/ui/editor/EditorScreen.kt b/app/src/main/kotlin/net/metacircular/engpad/ui/editor/EditorScreen.kt index 354b181..60145e8 100644 --- a/app/src/main/kotlin/net/metacircular/engpad/ui/editor/EditorScreen.kt +++ b/app/src/main/kotlin/net/metacircular/engpad/ui/editor/EditorScreen.kt @@ -3,6 +3,7 @@ package net.metacircular.engpad.ui.editor import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.ui.draw.clipToBounds import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -114,8 +115,9 @@ fun EditorScreen( AndroidView( factory = { canvasView }, modifier = Modifier - .fillMaxSize() - .weight(1f), + .fillMaxWidth() + .weight(1f) + .clipToBounds(), ) } }