Files
photocraft/scorecard/tools.toml
T
Nicolas FalesyandClaude Opus 5.5 7a0e60c104 test(engine): add Color Range fuzziness 0 exact-match test (TOOL-214-3) (#3581)
* test(engine): add Color Range fuzziness 0 exact-match test (#214)

Adds an explicit test for Sampled Colors at Fuzziness 0 (point sampling
and hex colors) asserting that exact matches are fully selected and
adjacent shades are rejected. Updates scorecard TOOL-214-3 to done.

* docs: regenerate scorecard (TOOL-214-3 done)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017WeVmjnnPPKdxVTpr2Vjtw

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-14 01:59:13 -04:00

308 lines
18 KiB
TOML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Scorecard checklist: tools (#213 painting and retouching, #214 selection, transform, drawing).
# An item is done when it has a command, a UI and tests.
# Statuses must be true on main. Verified against 4b53f62 on 2026-10-05.
area = "tools"
title = "Tools"
order = 3
[[item]]
id = "TOOL-213-1"
target = "Pencil tool: aliased 1 px painting, square pixel-aligned cursor"
status = "done"
issue = 213
note = "Tool::Pencil (B group): live aliased strokes on the pixel grid, square cursor, Auto Erase; pencil_tests.rs"
[[item]]
id = "TOOL-213-2"
target = "Mixer Brush tool on the toolbar"
status = "done"
issue = 213
note = "Tool::MixerBrush in the Brush (B) flyout; Wet/Load/Mix/Flow/Sample All Layers options, command/control dispatch and selection-clipping tests"
[[item]]
id = "TOOL-213-3"
target = "Patch tool with a live preview of the patched area"
status = "done"
issue = 213
note = "paint.patch (retouch_cmds/patch.rs, source/destination) + Tool::Patch (J group: lasso, then drag the selection); the drag shows the heal live (patch_preview.rs, coarse colour fit: 1000x1000 in 52 ms)"
[[item]]
id = "TOOL-213-4"
target = "Content-Aware Move tool reusing inpaint completion"
status = "partial"
issue = 213
note = "paint.contentAwareMove (retouch_cmds/content_aware_move.rs: move/extend, Structure 1-7, Color 0-10, Sample All Layers, Transform On Drop's scaleX/scaleY/rotate; background job) + Tool::ContentAwareMove (J group: lasso, then drag; Transform On Drop puts a scale/rotate box on the dropped patch, cam_transform.rs: Enter commits as one step, Esc cancels). cam_preview.rs: once the pointer or the box rests, the result shows (a cancellable background job on a smaller sampling window, small selections only, #4385). Missing: a progressive preview for big selections"
[[item]]
id = "TOOL-213-5"
target = "Painting a type/shape/smart/fill layer offers to rasterize, then continues the stroke"
status = "done"
issue = 213
note = "rasterize_prompt.rs: OK rasterizes then paints (two history states), Cancel does nothing; drivable via ui.dialog.confirm"
[[item]]
id = "TOOL-213-6"
target = "Fill dialog: contents, blend, opacity, preserve transparency; remembered; Shift+Backspace"
status = "done"
issue = 213
note = "fill_ui.rs dialog over edit.fill (fill_cmds.rs): all contents, mode, opacity, preserve; remembered in prefs; Shift+F5 / Shift+Backspace"
[[item]]
id = "TOOL-213-7"
target = "Live stroke preview for every brush-based retouch tool"
status = "partial"
issue = 213
note = "Live (canvas.rs strokes_live): Brush, Pencil, Eraser, Blur, Sharpen, Smudge, Dodge, Burn, Sponge, Clone Stamp, Healing Brush, History Brush (#1026, #1040); the release commits the live stroke's pixels (P32). Not yet live: Spot Healing Brush, Mixer Brush, Background Eraser, Pattern Stamp"
[[item]]
id = "TOOL-213-8"
target = "Clone Source: W/H link, drawn overlay (opacity/blend/clip/auto-hide), Alt target cursor"
status = "partial"
issue = 213
note = "Clone Source overlay drawn with opacity, inversion, circular clipping, auto-hide and the Normal/Darken/Lighten/Difference blend against the image under the brush (canvas.rs draw_clone_preview, clone_source::overlay_blend); a shaped tip (sampled bitmap, rotation, roundness) clips the overlay to its outline (brush_cursor::tip_mask, #4372); W/H link (cloneSource.set linked, panel chain button, #3298)"
[[item]]
id = "TOOL-213-9"
target = "Eyedropper comparison ring (new colour above, current below) while sampling"
status = "done"
issue = 213
note = "eyedropper_ring (canvas.rs): the sampled colour on the upper arc, the foreground below, drawn for the Eyedropper and Alt-sampling while the button is held; the Precise-cursor preference keeps the crosshair"
[[item]]
id = "TOOL-213-10"
target = "Ctrl+Alt drag resizes brush size/hardness with an on-canvas readout"
status = "done"
issue = 213
note = "brush_resize.rs (#297, #370): Alt+right-drag, or Control+Option-drag on macOS (Control+Alt everywhere), sets diameter horizontally and hardness vertically, circle plus Diameter/Hardness readout, one coalesced tools.setBrush, no paint or history; 9 tests there"
[[item]]
id = "TOOL-213-11"
target = "Editable pressure curve, global and per control"
status = "partial"
issue = 213
note = "Global: Preferences › Tools › Pressure Curve (prefs tools.pressureCurve; monotone cubic, engine prefs::PressureCurve; applied to every pen sample in ui-egui stylus.rs before any brush; editor pressure_curve_ui.rs with Linear/Soft/Hard presets). Missing: per-control curves (each brush dynamic's own response to pressure, tilt, rotation)"
[[item]]
id = "TOOL-213-12"
target = "Import an image file as a brush tip"
status = "missing"
issue = 213
note = "Only brush.defineFromSelection and brush.presets.importAbr"
[[item]]
id = "TOOL-213-13"
target = "Brush regression tests: no seams, hard edge, zero-scatter defaults, ABR never black"
status = "done"
issue = 213
note = "Seam, hard-edge and zero-scatter tests in brush_tests.rs; ABR tip colour test in abr_map/tests.rs"
[[item]]
id = "TOOL-213-14"
target = "Tablet: no OS arrow cursor over the canvas on macOS, Windows and Linux"
status = "partial"
issue = 213
note = "Brush tools set CursorIcon::None (canvas.rs:1259-1275); not verified per OS"
[[item]]
id = "TOOL-213-18"
target = "Pattern Stamp tool: sibling of Clone Stamp in the S flyout"
status = "done"
issue = 213
note = "Tool::PatternStamp in the S group; paint.patternStamp stamps the Patterns-panel tile with Aligned and Impressionist (an approximation: it jitters each dab's tile phase, not Photoshop's smeared impressionist look); engine tests cover depths, seams, hostile input"
[[item]]
id = "TOOL-214-1"
target = "Select and Mask workspace: view modes, Refine Edge brush, 6 outputs"
status = "partial"
issue = 214
note = "select.refineEdge dialog with a live preview in Photoshop's view modes: Onion Skin, Marching Ants, Overlay, On Black, On White, Black & White, On Layers, per-view Opacity, Show Edge (J), Show Original (P), F cycles, X shows the image (select_mask_view.rs, #1754); Refine Edge Brush (R, size, Expand Detection Area / Restore Original Edge, Alt swaps) paints select.refineEdge refineStrokes, estimated by colour-line matting (matting/brush.rs, #2972); outputs Selection, Layer Mask, New Layer, New Layer with Mask, New Document, New Document with Mask (#2998); no workspace layout"
[[item]]
id = "TOOL-214-2"
target = "Marquee: Shift square, Alt from centre, live W x H readout"
status = "done"
issue = 214
note = "canvas.rs marquee_corners: Shift square or circle, Alt from the centre, live W x H readout beside the cursor (marquee_readout); marquee_tests.rs"
[[item]]
id = "TOOL-214-3"
target = "Color Range test: Sampled Colors at Fuzziness 0 selects the exact match only"
status = "done"
issue = 214
note = "color_range_fuzziness_zero_selects_exact_match_only in selection_cmds.rs asserts exact match is selected and 1-level-off pixels are rejected at fuzziness 0; tonal presets covered in color_range_presets"
[[item]]
id = "TOOL-214-4"
target = "Move tool: box-select layers, Auto-Select hover outline, double-click fit, drag to a tab"
status = "partial"
issue = 214
note = "Auto-Select hover outline (Preferences › Tools › Show Highlight on Rollover, #2834) and box-select layers from an empty spot (move_box.rs, layer.selectInRect, #2844) done; double-click fit and drag to a tab open"
[[item]]
id = "TOOL-214-5"
target = "Right-click menu during Free Transform (scale, rotate, skew, warp, flip, rotate 90/180)"
status = "done"
issue = 214
note = "canvas_tool_menu.rs TRANSFORM_MENU: modes, Content-Aware Scale / Puppet Warp greyed, Rotate 180 / 90 CW / 90 CCW and Flip turn the open box (transform_tool::intercept, #2832)"
[[item]]
id = "TOOL-214-6"
target = "Puppet Warp mesh follows the object outline (constrained triangulation)"
status = "done"
issue = 214
note = "algo/puppet.rs build_mesh: traced outline of the opaque region grown by Expansion, triangulated with a Density-spaced lattice (conforming Delaunay); separate shapes get separate meshes (#3105)"
[[item]]
id = "TOOL-214-7"
target = "Content-Aware Scale through the Free Transform box with a live proxy preview"
status = "done"
issue = 214
note = "Edit > Content-Aware Scale opens a scale-only Free Transform box (cas_transform.rs) with Amount, Protect and Protect Skin Tones in the options bar; edit.contentAwareScale carves a 384 px copy in the background for the live preview, and Enter runs it with the box's size and x/y as one step (#3096)"
[[item]]
id = "TOOL-214-8"
target = "A measured ruler angle prefills Image Rotation > Arbitrary (0.01 deg)"
status = "done"
issue = 214
note = "analysis_ui.rs: image.rotation.arbitrary opens seeded with the ruler angle and the direction that straightens it; arbitrary_rotation_starts_at_the_ruler_angle checks 5.7106 deg within 1e-3 and cw/ccw for three measured lines"
[[item]]
id = "TOOL-214-9"
target = "Crop: W x H x Resolution with units, saved crop presets, Straighten in the options bar"
status = "done"
issue = 214
note = "Ratio dropdown and fields, Straighten (TOOL-214-21); W x H x Resolution with units, built-in size presets and Front Image (crop_size.rs, image.crop targetWidth/targetHeight/resolution, #2443); New / Delete Crop Preset save and delete crop tool presets (crop_presets.rs, tool.presets.* with tool crop, persisted with the preferences)"
[[item]]
id = "TOOL-214-10"
target = "Pen: Alt breaks a handle, Shift 45 deg, Ctrl direct-selects, Backspace, close-drag handle"
status = "partial"
issue = 214
note = "Ctrl/Cmd = Direct Selection and Alt = Convert Point on existing paths (direct_select.rs, #790); Backspace/Delete retracts the last point while drawing (#1466); Alt during a drag breaks the handle pair, Alt-drag turns the last anchor's outgoing handle alone, and dragging on the first anchor closes with that anchor's handles (vector_ui.rs, #3306); no Shift 45 deg"
[[item]]
id = "TOOL-214-11"
target = "Stroke Path dialog: pick any brush-based tool, Simulate Pressure"
status = "done"
issue = 214
note = "path.stroke tool: Brush, Pencil, Eraser and every other brush-based tool (Background Eraser, Clone/Pattern Stamp, Healing/Spot Healing Brush, History Brush, Blur, Sharpen, Smudge, Dodge, Burn, Sponge, Color Replacement, Mixer Brush: their paint.* command along the path, one step) plus simulatePressure (taper both ends); Stroke Path dialog from the Pen menu, the Paths panel menu and Alt-click on its Stroke button (#2978)"
[[item]]
id = "TOOL-214-12"
target = "Shapes: on-canvas corner radius handles, shape feather, selection to shape with tolerance"
status = "partial"
issue = 214
note = "select.convertToShape with tolerance (vector_cmds.rs:1256); radius as a field only"
[[item]]
id = "TOOL-214-13"
target = "Mask Properties: Density and Feather controls"
status = "partial"
issue = 214
note = "Vector-mask density/feather in the engine (vector_cmds.rs:948); no UI controls"
[[item]]
id = "TOOL-214-14"
target = "Vanishing Point window: draw and edit planes, paste, clone in perspective"
status = "partial"
issue = 214
note = "filter.vanishingPoint (vp_cmds.rs:15); on-canvas plane editor (vp_ui.rs: Create/Edit Plane, edge nodes, valid/doubtful/invalid grid colours, Grid Size, clipboard pasted onto the plane on OK); clone stamp, marquee and tear-off planes in the window still missing"
[[item]]
id = "TOOL-214-15"
target = "Content-Aware Fill workspace: paint the sampling area, preview, rotate/scale/mirror, new layer"
status = "partial"
issue = 214
note = "Engine sampling auto, rect, custom (edit_menu_cmds.rs:324); generic dialog"
[[item]]
id = "TOOL-214-16"
target = "Liquify: redo (Cmd+Shift+Z)"
status = "done"
issue = 214
note = "liquify_ui.rs supports bounded redo through Cmd+Shift+Z and the control channel; new strokes clear redo"
[[item]]
id = "TOOL-213-17"
target = "Red Eye tool: click a pupil to neutralize the red reflection"
status = "done"
issue = 213
note = "paint.redEye (redeye_cmds.rs) + Tool::RedEye in the J flyout; Pupil Size and Darken Amount; catchlights kept; a miss is an error"
[[item]]
id = "TOOL-214-17"
target = "Rotate View tool: drag to turn the view, Reset View, Match Rotation copies the angle"
status = "done"
issue = 214
note = "Tool::RotateView in the Hand flyout; view.rotateView / view.resetView; Match Rotation copies views[].rotation; enhanced_controls.rotate_view_with_trackpad is read"
[[item]]
id = "TOOL-1649-1"
target = "Eyedropper Sample Size (Point, 3×3 … 101×101 Average), Sample layers, Show Sampling Ring"
status = "done"
issue = 1649
note = "document.sampleColor (sample_cmds.rs): average of the square clipped to the canvas, in the document's model and depth; eyedropper_ui.rs options bar; shared with Alt-click, dialog eyedroppers and the Info panel; ui.set eyedropperSampleSize/eyedropperSample/eyedropperRing. Color Sampler tool is separate (#1046, sampler_cmds.rs)"
[[item]]
id = "TOOL-214-20"
target = "Crop overlay options: Rule of Thirds, Grid, Diagonal, Triangle, Golden Ratio, Golden Spiral; Auto / Always / Never Show Overlay; O cycles the overlay, Shift+O its orientation"
status = "done"
issue = 1919
note = "crop_overlay.rs: unit-square guides shared by upright and turned frames (canvas.rs), options-bar menu, O / Shift+O ahead of the tool keys in shortcuts.rs; ToolOptions crop_overlay / crop_overlay_show / crop_overlay_orientation, ui.set cropOverlay / cropOverlayShow / cropOverlayOrientation"
[[item]]
id = "TOOL-214-18"
target = "Crop: drag outside the frame to rotate it (Shift 15 deg steps, angle readout); Enter rotates and crops in one step"
status = "done"
issue = 1792
note = "image.crop angle param (image_cmds.rs: Image Rotation > Arbitrary + crop, one undo step); crop_ui.rs turns, moves and resizes the frame along its axes (Classic Mode; default mode turns the image instead, TOOL-214-23); UiState.crop_angle"
[[item]]
id = "TOOL-214-19"
target = "Crop: arrow keys nudge the crop box (Shift 10 px), X swaps portrait/landscape, W x H readout while drawing or resizing"
status = "done"
issue = 1919
note = "crop_ui.rs keys/nudge/swap_orientation (ahead of the selection nudge and tools.swapColors in shortcuts.rs; X also swaps the options-bar ratio) and sizing (canvas.rs draws it with the marquee readout); turned frames included"
[[item]]
id = "TOOL-214-21"
target = "Crop: Straighten button in the options bar and Cmd/Ctrl-drag draw a line; the frame turns so the line ends up level (or plumb when nearer vertical), with an angle readout"
status = "done"
issue = 1919
note = "crop_straighten.rs: one-shot options-bar button, Cmd/Ctrl held is a temporary Straighten (ui.pointer command flag); the Ruler's straighten_angle convention; the frame is fitted to the largest of its proportions inside the image (Classic Mode); a pending edit committed by Enter"
[[item]]
id = "TOOL-214-22"
target = "Crop gear menu: Show Cropped Area (H), Enable Crop Shield with Match Canvas / custom colour, opacity and Auto Adjust Opacity"
status = "done"
issue = 1919
note = "crop_shield.rs: fill() resolves the shield (canvas.rs paints it around upright and turned frames), gear popup in the options bar, H ahead of the Hand key in shortcuts.rs, Custom… opens the Color Picker; ToolOptions crop_shield, ui.set cropShield. Use Classic Mode / Auto Center Preview: TOOL-214-23"
[[item]]
id = "TOOL-214-23"
target = "Crop: Photoshop's default mode (the image turns and moves behind an upright box, Auto Center Preview keeps a resized box's centre in place and centres a drawn one) and Use Classic Mode (P) in the gear menu"
status = "done"
issue = 1919
note = "crop_mode.rs: the crop model (crop_rect + crop_angle) and image.crop are shared by both modes; default mode sets the view's rotation to -crop_angle while the tool shows a frame (picking the tool, in either mode, turns a Rotate View turn upright about the view centre, not restored after Enter/Esc or by P; Classic Mode keeps the camera upright with the frame drawn turned; as Photoshop 25.1), pivoting so the box stays put, moves view and frame together for drags inside and arrow keys, keeps a resized box's centre in place live (Auto Center Preview; a drawn box is centred on release, a moved one never), readout = the image's turn; gear menu order and Classic Mode greying as Photoshop 25.1; CropShield classic_mode / auto_center_preview (ui.set cropShield), P ahead of the Pen key"
[[item]]
id = "TOOL-1053-1"
target = "Shape stroke controls: solid/dashed/dotted, custom dash/gap/offset, caps, joins, alignment, miter limit and opacity"
status = "done"
issue = 1053
note = "shape_stroke_ui.rs: shared staged editor, renderer samples, cached Properties canvas preview; existing shape.create/edit/info; serde tool defaults, ui.set shapeStroke; pcraft and vstk style round trips. Gradient fill UI and saved stroke libraries remain open; Line retains filled-bar geometry"
[[item]]
id = "TOOL-213-19"
target = "Remove tool: paint over an object (or circle it) and it is filled from its surroundings"
status = "partial"
issue = 213
note = "paint.remove (retouch_cmds/remove.rs, background job) + Tool::Remove in the J flyout; loops close over what they enclose; fill by non-local patch completion with texture features, onion-peel start, best-patch copy and gradient-domain seam hiding (algo/nonlocal.rs, Newson et al. IPOL 2017); sparse holes in tiles (algo/remove.rs). remove_quality on 20 holes in public-domain photos: the fill keeps 0.89 of the texture it replaces (0.54 with Content-Aware Fill's completion), edge gradient 1.03 of the original's; remove_bench 24 MP: 100 px ring 120 ms, 1000 px ring 2.5 s, 3000 px line 1.7 s. Options bar: Sample All Layers, Create a new layer (the fill on a new layer above, `newLayer`), Remove after each stroke off (strokes collect as a tint and ↩ applies them as one `strokes` job, Esc drops them). Missing: generative AI mode, live preview of the result"
[[item]]
id = "TOOL-1045-1"
target = "Color Replacement tool on the toolbar"
status = "done"
issue = 1045
note = "Tool::ColorReplacement in the Brush (B) flyout after the Mixer Brush; Mode/Sampling/Limits/Tolerance/Anti-alias options drive paint.colorReplacement with the foreground colour; live preview (LiveDab with the stroke's finishing dab), one History step; color_replacement_tests.rs"