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]>
This commit is contained in:
Nicolas FalesyandClaude Opus 5.5 authored and GitHub committed 2026-10-14 01:59:13 -04:00
1 parent 7e28900df3
commit 7a0e60c104
3 files changed
+43 -4

No files matched your search

+39
View File
@@ -1004,6 +1004,45 @@ mod tests {
assert!(lighter > redder + 0.15 && redder > 0.0, "lighter {lighter}, redder {redder}");
}
#[test]
fn color_range_fuzziness_zero_selects_exact_match_only() {
// Sampled Colors at Fuzziness 0 selects only the exact match (TOOL-214-3, #214).
let mut s = Session::new();
s.execute("file.new", json!({"width": 50, "height": 10})).unwrap();
s.edit("paint", |doc, _| {
let bg = doc.layers[0].surface_mut().unwrap();
let v = |c: u8| f32::from(c) / 255.0;
// Exact target color [200, 140, 120]
bg.fill_rect(Rect::new(0, 0, 10, 10), &[v(200), v(140), v(120), 1.0]);
// Off by 1 level in red [201, 140, 120]
bg.fill_rect(Rect::new(10, 0, 20, 10), &[v(201), v(140), v(120), 1.0]);
// Off by 1 level in green [200, 141, 120]
bg.fill_rect(Rect::new(20, 0, 30, 10), &[v(200), v(141), v(120), 1.0]);
// Off by 2 levels in blue [200, 140, 122] (crosses 8-bit Lab grid threshold)
bg.fill_rect(Rect::new(30, 0, 40, 10), &[v(200), v(140), v(122), 1.0]);
// Exact target color again at a different position
bg.fill_rect(Rect::new(40, 0, 50, 10), &[v(200), v(140), v(120), 1.0]);
Ok(())
})
.unwrap();
// Sampled via eyedropper point at [5, 5] with Fuzziness 0
s.execute("select.colorRange", json!({"points": [[5, 5]], "fuzziness": 0})).unwrap();
assert_eq!(coverage(&s, 5, 5), 1.0, "sampled pixel itself is fully selected");
assert_eq!(coverage(&s, 45, 5), 1.0, "identical color elsewhere on canvas is fully selected");
assert_eq!(coverage(&s, 15, 5), 0.0, "1 level off in red is unselected at fuzziness 0");
assert_eq!(coverage(&s, 25, 5), 0.0, "1 level off in green is unselected at fuzziness 0");
assert_eq!(coverage(&s, 35, 5), 0.0, "1 level off in blue is unselected at fuzziness 0");
// Sampled via hex color with Fuzziness 0
s.execute("select.colorRange", json!({"color": "#c88c78", "fuzziness": 0})).unwrap();
assert_eq!(coverage(&s, 5, 5), 1.0, "exact color match from hex is fully selected");
assert_eq!(coverage(&s, 45, 5), 1.0, "identical color elsewhere on canvas is fully selected");
assert_eq!(coverage(&s, 15, 5), 0.0, "1 level off in red is unselected for hex at fuzziness 0");
assert_eq!(coverage(&s, 25, 5), 0.0, "1 level off in green is unselected for hex at fuzziness 0");
assert_eq!(coverage(&s, 35, 5), 0.0, "1 level off in blue is unselected for hex at fuzziness 0");
}
/// Red squares (from `session`), a blue square, a black and a mid-grey strip on white.
fn session_colours() -> Session {
let mut s = session();
+2 -2
View File
@@ -8,7 +8,7 @@ Generated by `cargo xtask scorecard` from `perf/budgets.toml`, `perf/baseline.js
|---|---:|---:|---:|---|
| [Performance](#performance) | 3 | 22 | 22 | 56 scenarios: 3 within budget, 22 over budget or failing, 9 tracked only, 17 not run, 5 not measurable yet (baseline 2026-10-05) |
| [File compatibility](#file-compatibility) | 7 | 13 | 7 | io corpus: oracle ≥ 146/170, round trip ≥ 169/170; psd-tools corpus: oracle ≥ 236/309, round trip ≥ 308/309; photoshop oracles: oracle ≥ 145/256, round trip ≥ 258/256 |
| [Tools](#tools) | 28 | 14 | 1 | |
| [Tools](#tools) | 29 | 13 | 1 | |
| [Workspace and UI](#workspace-and-ui) | 13 | 6 | 4 | settings that do nothing: 26 of 157 (target 0) |
| [Type](#type) | 4 | 3 | 2 | |
| [Automation](#automation) | 5 | 2 | 4 | 631 of 631 menu items live (100.0%) |
@@ -147,7 +147,7 @@ Corpus floors (`crates/io/tests/corpus.rs`): the enforced minimums; the corpus t
| TOOL-213-18 | Pattern Stamp tool: sibling of Clone Stamp in the S flyout | done | [#213](https://github.com/storytold/photocraft/issues/213) | 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 |
| TOOL-214-1 | Select and Mask workspace: view modes, Refine Edge brush, 6 outputs | partial | [#214](https://github.com/storytold/photocraft/issues/214) | 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 |
| TOOL-214-2 | Marquee: Shift square, Alt from centre, live W x H readout | done | [#214](https://github.com/storytold/photocraft/issues/214) | 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 |
| TOOL-214-3 | Color Range test: Sampled Colors at Fuzziness 0 selects the exact match only | partial | [#214](https://github.com/storytold/photocraft/issues/214) | Fuzziness-0 tests cover midtones/shadows only (selection_cmds.rs:444-447) |
| TOOL-214-3 | Color Range test: Sampled Colors at Fuzziness 0 selects the exact match only | done | [#214](https://github.com/storytold/photocraft/issues/214) | 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 |
| TOOL-214-4 | Move tool: box-select layers, Auto-Select hover outline, double-click fit, drag to a tab | partial | [#214](https://github.com/storytold/photocraft/issues/214) | 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 |
| TOOL-214-5 | Right-click menu during Free Transform (scale, rotate, skew, warp, flip, rotate 90/180) | done | [#214](https://github.com/storytold/photocraft/issues/214) | 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) |
| TOOL-214-6 | Puppet Warp mesh follows the object outline (constrained triangulation) | done | [#214](https://github.com/storytold/photocraft/issues/214) | 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) |
+2 -2
View File
@@ -127,9 +127,9 @@ note = "canvas.rs marquee_corners: Shift square or circle, Alt from the centre,
[[item]]
id = "TOOL-214-3"
target = "Color Range test: Sampled Colors at Fuzziness 0 selects the exact match only"
status = "partial"
status = "done"
issue = 214
note = "Fuzziness-0 tests cover midtones/shadows only (selection_cmds.rs:444-447)"
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"