12 KiB
Brush Checkerboard Fix + AGENTS.md Update Plan
Context
- Project root:
/mnt/extra/00_PROJECTS/hcie-rust-v3.05 - Active GUI:
hcie-iced-app/(Iced + wgpu).hcie-egui-app/is legacy/abandoned. - The reported artifact is a regular grid/checkerboard inside brush strokes on white canvas, visible only with certain brushes.
AGENTS.mdis outdated: it still says "HCIE-Rust v4", lists egui as the active GUI, and omits the iced workspace.
Part 1: Brush Checkerboard Root Cause
The grid comes from deterministic, canvas-space pseudo-random sampling in hcie-brush-engine/src/lib.rs:
draw_grain_brush(..., woven: bool)uses:let hash = ((x.wrapping_mul(73_856_093) ^ y.wrapping_mul(19_349_663)) & 1023) as f32 / 1023.0; if texture < threshold { continue; }draw_crayon_brushuses the same constants and a hardcontinueskip:let noise = ((px * 73_856_093) ^ (py * 19_349_663)) & 1023; if grain < threshold { continue; }
Because the hash depends only on absolute pixel (x, y) and the same pixels are re-sampled on every overlapping dab, the skipped pixels form a rigid canvas-locked checkerboard.
Affected Brush Styles
| Style | Function | Mechanism |
|---|---|---|
Noise |
draw_grain_brush(..., woven=false) |
hash threshold skip |
Texture |
draw_grain_brush(..., woven=true) |
sin/cos woven threshold skip |
Pencil |
draw_pencil_brush → draw_grain_brush |
hash threshold skip |
Charcoal |
draw_charcoal_brush → draw_grain_brush |
hash threshold skip |
Sketch |
draw_sketch_brush → draw_grain_brush |
hash threshold skip |
Crayon |
draw_crayon_brush |
hash threshold skip |
Dry-media presets that map to these styles are also affected:
- Compressed Charcoal, Vine Charcoal, Charcoal Pencil
- Soft Pastel, Hard Pastel, Oil Pastel, Water-Soluble Pastel, PanPastel, Chalk Sanguine
- Graphite Pencils (HB/9B/Carpenter), Wax/Oil/Watercolor Pencils
Styles not affected: Round, HardRound, SoftRound, Square, Star, Spray, Pen, InkPen, Calligraphy, Oil, Airbrush, Watercolor, Marker, Glow, WetPaint, Leaf, Rock, Meadow, Clouds, Dirt, Tree, Bristle, Wood, Mixer, Blender, Bitmap.
Part 2: Proposed Fix
Decision
Apply both mechanisms together:
- Per-dab grain seed offset in
draw_grain_brush/draw_crayon_brushso consecutive dabs no longer hit the exact same canvas pixels. - Convert hard threshold skip to alpha modulation so "empty" pores become translucent rather than fully transparent, removing the rigid checkerboard.
Implementation Details
2.1 draw_grain_brush changes
Location: hcie-brush-engine/src/lib.rs:1350-1429
// Add inside the function, after radius/bbox calculation:
let seed_x = (cx.to_bits().wrapping_mul(73_856_093)
^ cy.to_bits().wrapping_mul(19_349_663)) as u32;
let seed_y = seed_x.wrapping_mul(668_265_263);
// Replace the hash computation with decorrelated coordinates:
let ox = x.wrapping_add(seed_x);
let oy = y.wrapping_add(seed_y);
let hash = ((ox.wrapping_mul(73_856_093) ^ oy.wrapping_mul(19_349_663)) & 1023) as f32 / 1023.0;
// For woven mode, shift the sin/cos arguments by the seed:
let texture = if woven {
0.55 + 0.45 * ((x as f32 * 0.42 + seed_x as f32 * 0.001).sin()
* (y as f32 * 0.31 + seed_y as f32 * 0.001).cos()).abs()
} else {
hash
};
// Replace `if texture < threshold { continue; }` with soft alpha modulation:
let grain_alpha = if texture < threshold {
(texture / threshold).clamp(0.0, 1.0)
} else {
0.55 + 0.45 * texture
};
// Use grain_alpha in the final alpha instead of the old `(0.55 + 0.45 * texture)` factor.
2.2 draw_crayon_brush changes
Location: hcie-brush-engine/src/lib.rs:2941-3026
Apply the same seed + soft alpha approach to the crayon noise loop:
let seed_x = (cx.to_bits().wrapping_mul(73_856_093)
^ cy.to_bits().wrapping_mul(19_349_663)) as u32;
let seed_y = seed_x.wrapping_mul(668_265_263);
// inside pixel loop:
let ox = px.wrapping_add(seed_x);
let oy = py.wrapping_add(seed_y);
let noise = (ox.wrapping_mul(73_856_093) ^ oy.wrapping_mul(19_349_663)) & 1023;
let grain = noise as f32 / 1023.0;
let threshold = 0.18 * (1.0 - pressure) + 0.08;
let grain_alpha = if grain < threshold {
(grain / threshold).clamp(0.0, 1.0)
} else {
0.35 + 0.65 * grain
};
// use grain_alpha instead of `(0.35 + 0.65 * grain)`.
2.3 Preset tuning (optional, verify visually)
If the new soft alpha makes dry-media strokes too dark or too dense, tune hcie-dry-media-brushes/src/lib.rs:
- Reduce
opacityslightly for charcoal/pastel presets. - Increase
spacinga little for coarse charcoal presets.
Do not add jitter as the primary fix; jitter alone does not break the canvas-space hash pattern.
Part 3: AGENTS.md Update
AGENTS.md currently describes the old egui-first architecture. Update it to match the actual project state.
3.1 Title & Mission
Change:
# HCIE-Rust v4 — AI-Aware Architecture
to:
# HCIE-Rust v3.05 — AI-Aware Architecture
Add a note:
> Note: `v3.05` is the in-repo identifier for what was previously labelled "v4" in older documentation and paths. The egui workspace is no longer maintained; the active native GUI is the Iced workspace.
3.2 Layer 6 GUI section
Replace the existing Layer 6 table with:
### Layer 6: GUI — open, agents work here
The active native GUI is the Iced workspace. The egui workspace exists for legacy reference but is not the current target.
| Crate / Project | Directory | Responsibility | Dependencies |
|-----------------|-----------|----------------|--------------|
| `hcie-iced-app` | `hcie-iced-app/` | Workspace root for the native Iced GUI. | `hcie-engine-api`, `hcie-iced-gui`, panels |
| `hcie-iced-gui` | `hcie-iced-app/crates/hcie-iced-gui/` | Main binary and application orchestration. | `hcie-engine-api`, panels |
| `iced-panel-adapter` | `hcie-iced-app/crates/iced-panel-adapter/` | Dynamic schema-to-widget UI binder. | `hcie-engine-api` |
| `iced-panel-ai-chat` | `hcie-iced-app/crates/iced-panel-ai-chat/` | AI chat panel. | `hcie-engine-api`, `hcie-ai` |
| `iced-panel-script` | `hcie-iced-app/crates/iced-panel-script/` | Scripting / automation panel. | `hcie-engine-api` |
| `hcie-dry-media-brushes` | `hcie-iced-app/crates/hcie-dry-media-brushes/` | Dry-media brush presets. | `hcie-engine-api` |
| `hcie-watercolor-brushes` | `hcie-iced-app/crates/hcie-watercolor-brushes/` | Watercolor brush presets + splat preview. | `hcie-engine-api` |
| `hcie-ink-brushes` | `hcie-iced-app/crates/hcie-ink-brushes/` | Ink brush presets. | `hcie-engine-api` |
| `hcie-paint-brushes` | `hcie-iced-app/crates/hcie-paint-brushes/` | Paint brush presets. | `hcie-engine-api` |
| `hcie-digital-brushes` | `hcie-iced-app/crates/hcie-digital-brushes/` | Digital/vector brush presets. | `hcie-engine-api` |
Keep the egui workspace mentioned in a short "Legacy / frozen" subsection so existing paths are not a surprise, but mark it clearly as not to be modified.
3.3 Build Order
Update Layer 6 line to:
Layer 6: hcie-iced-app / hcie-iced-gui
3.4 Performance Protection Notes
Replace egui-specific references with Iced equivalents:
hcie-egui-app/crates/hcie-gui-egui/src/app/mod.rs→hcie-iced-app/crates/hcie-iced-gui/src/app.rshcie-egui-app/crates/hcie-gui-egui/src/canvas/mod.rs→hcie-iced-app/crates/hcie-iced-gui/src/canvas/mod.rs- Keep all wgpu/Shader/RefCell dirty bounds/Nearest-Neighbor protections as-is (they already describe the Iced path).
3.5 Screenshot & Visual Verification
Replace the egui screenshot section with the Iced CLI and F12 mechanisms documented in hcie-iced-app/crates/hcie-iced-gui/src/cli.rs and screenshot.rs:
# Full viewport
cargo run -p hcie-iced-gui -- --screenshot output.png
# Named panel
cargo run -p hcie-iced-gui -- --screenshot-panel "Brushes" output.png
Available panel names: inspect src/screenshot.rs / src/dock/state.rs for the exact name list.
3.6 Open Files List
Update the "Open Files" section to the iced paths:
- `hcie-iced-app/crates/hcie-iced-gui/src/*.rs`
- `hcie-iced-app/crates/hcie-iced-gui/Cargo.toml`
- `hcie-iced-app/crates/iced-panel-adapter/src/*.rs`
- `hcie-iced-app/crates/iced-panel-ai-chat/src/*.rs`
- `hcie-iced-app/crates/iced-panel-script/src/*.rs`
- `hcie-iced-app/crates/hcie-dry-media-brushes/src/*.rs`
- `hcie-iced-app/crates/hcie-watercolor-brushes/src/*.rs`
- `hcie-iced-app/crates/hcie-ink-brushes/src/*.rs`
- `hcie-iced-app/crates/hcie-paint-brushes/src/*.rs`
- `hcie-iced-app/crates/hcie-digital-brushes/src/*.rs`
3.7 Important Notes
Update to:
- Never add an
eguidependency to any engine crate. - Never access
hcie-protocoldirectly from GUI code. - Never import a locked engine crate directly from GUI code; route everything through
hcie-engine-api. - The active GUI workspace is
hcie-iced-app/. Treathcie-egui-app/as read-only legacy reference.
Part 4: Validation Steps
4.1 Compile
cargo check -p hcie-brush-engine
cargo check -p hcie-engine-api
cargo check -p hcie-iced-gui
4.2 Unit / regression tests
cargo test -p hcie-engine-api --test visual_regression
cargo test -p hcie-engine-api --test performance_stroke_4k -- --nocapture
cargo test -p hcie-dry-media-brushes
4.3 Visual verification (Iced)
- Launch the Iced GUI.
- Select a dry-media preset (e.g., Soft Pastel, Charcoal Pencil).
- Draw several overlapping strokes on white canvas.
- Confirm the checkerboard is gone and a soft paper-grain texture remains.
- Compare with a round soft brush to ensure regular brushes are unchanged.
- Capture before/after screenshots:
cargo run -p hcie-iced-gui -- --screenshot after_brushes.png
4.4 Lock crate after edits
Because hcie-brush-engine is a locked engine crate, unlock before editing and lock after:
./unlock.sh hcie-brush-engine
# ... apply source edits ...
cargo test -p hcie-engine-api --test visual_regression
./lock.sh hcie-brush-engine
Risks & Rollback
- Risk: The alpha-modulation change may darken/lighten existing dry-media presets.
- Mitigation: Tune preset opacity/spacing after visual comparison.
- Risk:
visual_regressiongolden hashes may change because brush pixel output changes.- Mitigation: If the new output is visually correct, regenerate golden hashes with
HCIE_REGEN_GOLDENS=1 cargo test -p hcie-engine-api --test visual_regression -- --nocaptureand commit the updated hashes.
- Mitigation: If the new output is visually correct, regenerate golden hashes with
- Risk:
AGENTS.mdreferences to exact line numbers may drift.- Mitigation: Replace line-number citations with file paths or function names where possible.
Task Summary
- ✅ Unlock
hcie-brush-engine— file already writable (666), directory 555. - ✅ Edit
draw_grain_brush— per-dab seed offset + soft alpha modulation applied. - ✅ Edit
draw_crayon_brush— per-dab seed offset + soft alpha modulation applied. - ✅ Fix
generate_star_stamp— changed from 5-pointed star to 4-pointed plus-cross. - ✅ Enhance
draw_star_brush— added sparkle particle scatter around main dab. - ✅ Iced GUI: Renamed "Star" → "Star Sparkle", moved to Effects category.
- ✅ Iced GUI: Updated
media_preset_categoryto handle "Effects" category. - ✅ Egui GUI: Renamed "Stipple Dots" → "Star Sparkle" in styles grid.
- ✅ Added "Star Sparkle" preset to
hcie-dry-media-brushescrate (id:star_sparkle). - ✅ Updated preset count assertions (dry-media: 19→20, iced panel: 47→48).
- ✅
cargo checkpasses for brush-engine, dry-media-brushes, iced-gui. - ✅
cargo test -p hcie-dry-media-brushespasses. - ⬜ Build and run
visual_regression+performance_stroke_4ktests (needs full rebuild). - ⬜ Run Iced GUI, test affected presets, capture before/after screenshots.
- ⬜ Update
AGENTS.mdto reflect v3.05 naming, the Iced workspace, and current open/locked boundaries. - ⬜ Lock
hcie-brush-engineandhcie-egui-app.