feat: auto shape completion#1798
Open
andrinwinzap wants to merge 2 commits into
Open
Conversation
Add OneNote-style automatic shape recognition: when enabled, drawn brush strokes that closely resemble a geometric shape are replaced with the clean shape upon finishing the stroke. Recognized are lines, circles, ellipses, rectangles (also rotated) and closed polygons with up to six corners. Recognition works by resampling the stroke to equal arc-length spacing, classifying it as open or closed via the endpoint gap, and then either fitting a line to the chord, fitting an ellipse through the second moments of the outline, or detecting corners from the distribution of turn angles along the loop and validating the resulting polygon against the stroke. The feature is off by default and toggled in the brush config popover.
Instead of attempting to recognize a shape every time the pen is lifted, recognition is now triggered like in OneNote: by holding the pen still at the end of a drawn stroke. Lifting the pen normally always keeps the drawn ink. While drawing, the brush tracks an anchor position and restarts a one-off timeout task whenever the pen moves out of a small radius around it. When the pen rests for the hold duration (700ms), the task sends an engine task which attempts the recognition and replaces the drawn stroke with the recognized shape while the pen is still down. Subsequent events are swallowed until the pen is lifted.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automatic shape completion for the brush pen
Adds opt-in, OneNote-style shape recognition to the brush pen. When enabled, holding
the pen still at the end of a stroke replaces the drawn ink with the clean geometric
shape it resembles.
fixes #120
Motivation
Drawing exact shapes freehand is difficult, and switching to the shaper pen for a
single rectangle interrupts the flow of writing. Letting the brush recognize shapes
on demand keeps the user in one tool. Recognition is deliberately not run on every
pen-up: doing so makes the brush unpredictable, since any stroke that happens to look
like a line or a loop would be silently rewritten. Requiring an explicit hold makes
the replacement something the user asks for rather than something that happens to them.
Changes
rnote-compose: newshaperecognitionmodule.recognize_shape(&PenPath) -> Option<Shape>resamples the stroke to equal arc-lengthspacing, classifies it as open or closed from the ratio of the endpoint gap to the
perimeter, and then dispatches:
Linealong the loop; low-corner strokes are fitted as an
Ellipsethrough the secondmoments of the outline, and high-corner strokes have their corners extracted and
are validated as a
PolygonRectangle,snapping to axis-alignment when it is within a few degrees
Recognized shapes are lines, circles, ellipses, rectangles (including rotated ones),
and closed polygons of up to six corners. Every fit is validated against the original
stroke by residual/deviation thresholds, so strokes that do not clearly resemble a
shape are left as ink. The module is dependency-free and covered by unit tests for
each shape class, plus rejection tests for scribbles and degenerate strokes.
rnote-engine: hold-to-trigger in the brush pen.While drawing, the brush tracks an anchor position and restarts a one-off timeout task
whenever the pen leaves a small radius around it. When the pen rests for the hold
duration, the task emits a new
EngineTask::BrushRecognizeShape, which attemptsrecognition and swaps the in-progress stroke for the recognized shape while the pen is
still down. Subsequent motion is swallowed until pen-up, so the shape does not get
re-drawn over.
Two constants govern the interaction, both in
brush.rs:HOLD_DURATION = 700ms— long enough not to fire during the natural pause at theend of a stroke, short enough not to feel like waiting.
HOLD_RADIUS_SURFACE = 6.0(surface coords) — tolerates hand tremor and penjitter while resting, without swallowing slow deliberate movement.
Both are hardcoded rather than exposed in the UI, to avoid adding a preference for a
value most users will not tune. Happy to surface them if you would prefer.
rnote-ui: a "Shape Recognition" group in the brush page with a singleRecognize Shapesswitch. The feature is off by default, so existing behaviour isunchanged unless the user opts in. The setting is stored as
BrushConfig::shape_recognition_enabled;BrushConfigalready carries#[serde(default)], so configs written by older versions continue to load.Recognized shapes are drawn with the brush's current colour and width. Textured strokes
fall back to a smooth style with a constant pressure curve, since shapes cannot be
composed with the textured style.
Screenshots / screen capture
Screencast_20260709_163543.webm
Testing
cargo fmt --checkis clean andcargo clippy --workspace --all-targetsreports nowarnings.
cargo test --workspacepasses, including 8 new unit tests inrnote-compose::shaperecognitioncovering line, circle, flat ellipse, axis-alignedrectangle, rotated rectangle and triangle recognition, plus rejection of scribbles
and strokes below the minimum size.
AI disclosure
This contribution was developed with the assistance of LLM/GenAI tools.