Conversation
…nvas Two things about the exclusion band shipped in marcinz606#1132 were wrong in use. A painted stroke was a search area: any connected mark the brush touched anywhere came back in full, so clipping the edge of a long hair released the whole hair. The band is now the cut. A mark crossing its rim keeps the repair on the side the brush missed, so half a hair can be released without the rest. The rim is feathered over the distance a manual heal already feathers its own, because the score is a ramp and cutting one at the brush edge prints the edge as a step. The hair mask is binary and takes the footprint unfeathered. _touched_components goes with the old semantics. The band's width was also unreachable while painting it. It borrows the heal brush's manual_dust_size, whose slider is hidden unless the Heal or Scratch tool is active, and an exclusion is painted with no tool active at all -- so the size was invisible, and Alt+M warned "not available" instead of moving it. The slider now shows whenever Optical Removal is on, which fixes the keyboard steps with it, and the canvas sizes the brush directly: Alt with the wheel, or a pinch while a brush is live. Alt is the modifier the keyboard already uses for this, so the plain wheel keeps zooming everywhere and no context loses a zoom route. The Alt branch sits downstream of the reverse-scroll preference, so one scroll direction means "more" for both. A pinch reports a fraction per event and is accumulated into whole pixels, or a slow one would never move the brush. Brush Size spans 2-64 px now, from one pair of constants the slider and the canvas clamp both read. A band has to cover an area of film, which 16 px at the heal reference could not. It is the same value all three brushes share, so the heal and scratch brushes reach it too. Armed to exclude on a right-click, the brush is drawn in the band's amber: nothing else draws it with no tool active, and a pinch that sizes an invisible brush is a pinch that does nothing visible. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`make format` collapses both on any branch that runs it, so they arrive as noise in the next unrelated diff. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Summary
Follow-up to #1132, which shipped the Optical Removal exclusion band. Two things about it were wrong in use.
The band released too much. A painted stroke was a search area: any connected mark the brush touched anywhere came back in full, so clipping the edge of a long hair released the whole hair. The band is now the cut — a mark crossing its rim keeps the repair on the side the brush missed, so half a hair can be released without the rest.
The band's width was unreachable while painting it. It borrows the heal brush's
manual_dust_size, whose slider is hidden unless the Heal or Scratch tool is active — and an exclusion is painted with no tool active at all. So the width was invisible, andAlt+Mwarned "not available" instead of moving it. The canvas now sizes the brush directly: Alt + wheel, or a pinch while a brush is live.Right-click behavior is untouched.
This changes a control that already shipped
Anyone who painted exclusions under #1132 gets a different result from the same stored strokes. The strokes stay valid; they release less. That is the point of the change, but it is a behavior change rather than an addition, so it is worth a deliberate second opinion before it lands.
How it works
drop_exclusionscuts toexclusion_cover's footprint instead of releasing whole connected components._touched_componentsgoes with the old semantics._MANUAL_RIM_PX, the distance a manual heal already feathers its own repairs with. The score is a ramp between_IR_SCORE_FLOORand 1.0, and cutting one at the brush edge prints the edge as a step. The hair mask is binary and takes the footprint unfeathered.HEAL_SIZE_MIN/HEAL_SIZE_MAX(2–64 px at the heal reference) are read by both the slider and the canvas clamp, so one range governs every route to the value. The old top end of 16 px could not cover an area of film.Alt+ wheel sits downstream of the reverse-scroll preference, so one scroll direction means "more" for both zoom and brush.wheel_notch_deltaalready normalizespixelDelta, so Alt + a trackpad two-finger scroll works like Alt + a wheel.slippy_mapalready does; dropping the fractions would leave a slow pinch unable to move the brush at all.Notes for review
Four judgment calls worth a second opinion:
dust_removealone would strip pinch-zoom out of ordinary editing, since Optical Removal is on for most normal work.MANUAL HEAL; moving it somewhere that serves both sections is a layout change I did not want to make unilaterally. This also fixesAlt+M/Alt+Shift+Msilently no-opping during exclusion, since those are gated on the same visibility.manual_size_inc/manual_size_decalready exist for this action; Alt+wheel and pinch are further input routes to it, not new actions. They are documented in the hand-written## Mousetable indocs/KEYBOARD.mdinstead. Happy to add registry entries if you would rather every input route be listed there.Test plan
make all— 6052 passed. One pre-existing failure,test_load_tooltip_follows_a_rebinding, which also fails on cleanmainon macOS (the tooltip renders⇧⌘Lwhere the test expects the literalCtrl+Shift+L); untouched by this branch and green on CI's Ubuntu.tests/test_canvas_brush_size.py(new, 12 tests): the plain wheel still zooms; Alt+wheel sizes and does not zoom, both directions; a reversed scroll preference reverses the brush too; the clamp holds at both ends; pinch sizes only in a brush context and zooms outside one; a trackpadpixelDeltascroll drives it like a wheel; the amber circle is drawn only where a right-click would paint.drop_exclusions' hair-mask branch gets its first coverage. Every existing call passedNone, and it is the branch where whole-component and covered-span differ most, a hair being long and thin.Docs updated in the same change: the "search area, not a cut" claim in
docs/USER_GUIDE.mdanddocs/PIPELINE.mdwas the old behavior stated outright, so both are rewritten; plus the Brush Size range and visibility, and two rows indocs/KEYBOARD.md's Mouse table.docs/CHANGELOG.mduntouched.The second commit is two lines
make formatcollapses on any branch that runs it, kept out of the feature diff so they do not read as part of it.🤖 Generated with Claude Code