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) <noreply@anthropic.com>
This commit is contained in:
@@ -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).
|
||||
|
||||
37
DESIGN.md
37
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
|
||||
|
||||
|
||||
39
PROGRESS.md
39
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
|
||||
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user