* Brush: remember the last selected brush across restarts (split from #2516)
The persistence half of #2516 on its own, so it can merge and be maintained
apart from the brush-outline half. The tool's brush (settings, tip
references, and the name of the preset it was picked from) is saved once the
pointer is up and restored at launch: options bar, painting cursor and
Brushes panel highlight all start where the session left off.
Review fix from #2516 -- no per-frame work:
- `ToolState::brush_rev` is bumped by every brush/preset setter
(`tools.setBrush`, preset save/delete/rename/setCurrent/deleteGroup,
`brush.defineFromSelection`, `brush.presets.importAbr`, the store's tip
re-linking, and the shell's tool-switch/reset paths); `remember_brush`
serialises only when it moved since `brush_saved_rev`, so an unchanged
brush costs a counter compare per frame, not a clone and a JSON dump of
the whole brush (sampled tip previews included).
- Restoring counts as saved: the launch value is never rewritten.
Bitmaps stay out of the preferences document: remembered tips and patterns
are references (a loaded stored tile drops its full bitmap), and a sampled
bitmap too big to embed is skipped, keeping the previous value so a preview
never enters the prefs file. A malformed remembered brush falls back to the
default.
docs/scorecard.md regenerated with `cargo xtask scorecard` (34 of 154).
Tests: engine 1267 lib (5 new: revision gating, rejected changes, picked
preset, restore round trip, no embedded bitmaps), ui-egui 1982 lib; clippy,
layers, wasm green.
* rustfmt the StoredTile line and regenerate the scorecard after the main merge
* engine tests: destructive Levels keep the signed float overshoot at 32 bits (#2592)
* Scorecard: regenerate after merging upstream/main
* Export As: compare batch paths without the Windows separator
* font fix ?
* fix(ui): only Filter menu dialogs get the in-dialog preview window
#3877 drew the preview window in every schema dialog with a Preview
checkbox, including Image > Rotation > Arbitrary. Photoshop's Rotate
Canvas and adjustment dialogs have no preview window, and the taller
dialog covered the canvas, which broke
arbitrary_rotation_preview::size_changing_rotation_preview_is_visible_and_preserves_the_panned_center
on main. The window and its reserved height now apply to filter.*
commands only.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* cargo fmt --check max_width = 160 fix
---------
Co-authored-by: JJ <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Add the brush-outline half of #2516 on its own, so review and merge stay
small; the last-brush persistence half follows separately.
- paint: new `outline` module traces the tip shape as closed rings in
radius units -- a computed ellipse for round tips (rotated/flattened),
marching squares over the bitmap for sampled/stored tips, at the full
tip or the 50 % alpha contour. Traced rings stay finite and bounded for
hostile sizes; tested for the half-coverage contour, blank tips and
hostile input.
- ui-egui: new `brush_cursor` picks the traced outline when the tip has a
shape and falls back to the plain circle otherwise; `tool_cursor` gained
the `Outline` shape (bitmap rasterisation by segment splatting, painter
fallback on non-Windows).
- The Normal Brush Tip circle now goes through `half_coverage_radius`
(#2744) so outline and circle agree on the real 50 % contour.
Review fixes from #2516:
- Merged main; docs/scorecard.md is main's version and was verified with
`cargo xtask scorecard --check` (regeneration changes nothing).
- Dropped the scratch benchmark `crates/paint/examples/tmp_outline_bench.rs`
and the two `#[ignore = "temp bench"]` tests in tool_cursor/tests.rs.
Co-authored-by: JJ <[email protected]>
* update brush button
* ui brush duplicated highlight fix
* formatting fix + locale
* i18n: translate "Update the current brush with these settings" in remaining complete catalogs
* Tip Shape picks the tip only; set_brush records the preset once accepted
- A click in the Brush Tip Shape grid copies the tip fields only (main's
Photoshop behaviour: the dynamics sections stay); the picked preset is
marked current with brush.presets.setCurrent instead of a whole-preset
tools.setBrush, so identity-based selection still tracks it.
- tools.setBrush sets current_preset only after the brush merged and
validated, so a failed call leaves no state behind; a reset still wins
over a preset in the same call. Tested.
- brush.presets.save / brush.defineFromSelection append at the end again
(insert-after-the-copied-brush had no Photoshop reference).
* Brush presets: forget the picked preset when the tool's brush swaps; no indexing in brush.presets.update
Each painting tool keeps its own brush (#218), so Update Brush could overwrite the
preset picked with one tool with another tool's brush.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: JJ <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
* Lasso inside liquify mode , with hotkeys L, alt , ctrl+i, ctrl+h for visibility
* Liquify Freeze Lasso: scanline fill, tests, one translated tooltip, theme colours
- apply_lasso fills node rows with an even-odd scanline pass (O(rows x edges) plus the nodes
written) instead of a point-in-polygon test per node; non-finite points are skipped and huge
or off-canvas polygons clamp to the field.
- LassoMask joins LiquifyTool::ALL, so the strip draws its button in the loop; its tooltip is
one translated string (all catalogs); the outline uses theme tokens.
- A closed lasso clears Redo (#435).
- filter.liquify params doc lists lassoMask.
- Tests: concave polygon vs. a reference even-odd test at several cell sizes, thaw, degenerate
and hostile polygons, replay; the UI lasso records a stroke, Alt thaws, undo/redo.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: JJ <[email protected]>
Co-authored-by: Brandon Thomas <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>