Files
hcie-rust-v3.05/.kilo/plans/1784597466971-vector-state-management-fix.md
phantom e5d899d72d feat: add watercolor brush category with presets and splat renderer
- Implemented a new crate `hcie-watercolor-brushes` containing watercolor brush presets and a procedural splat preview renderer.
- Integrated watercolor brushes into the iced GUI's Brushes panel, adding a new category for watercolor brushes.
- Updated the `BrushCategory` enum to include `Watercolor` and modified the rendering logic to display watercolor presets.
- Added functionality to generate watercolor splat thumbnails using a noise-based algorithm.
- Ensured that existing brush styles and engine crates remain unmodified, adhering to project constraints.
2026-07-21 06:07:12 +03:00

7.7 KiB
Raw Permalink Blame History

Watercolor Brush Category — Implementation Plan

Goal

Add a new Watercolor brush category to the iced GUI's Brushes panel. Each brush has a watercolor-splat preview (not the standard sine-wave stroke). The brushes simulate realistic watercolor effects. Implemented as a new crate with zero modification to existing brush/engine crates.

Architecture Overview

Existing brush flow

  1. hcie-brush-engine defines BrushStyle enum (33 variants including Watercolor) and BrushPreset struct
  2. hcie-engine-api re-exports BrushPreset and provides draw_specialized_stroke → dispatches to draw_watercolor_brush for BrushStyle::Watercolor
  3. hcie-iced-gui/src/panels/brushes.rs defines BrushCategory enum, BRUSH_STYLES static array, and generate_brush_preview() for thumbnails
  4. When a preset is selected → Message::BrushPresetSelected(preset) → sets tool_state.brush_style, brush_size, brush_opacity, brush_hardness, brush_spacing, active_brush_preset

Key constraint

Engine crates (hcie-brush-engine, hcie-protocol, hcie-engine-api, hcie-draw) are locked — agents must not modify them. The new crate lives in the GUI layer and reuses the engine's existing BrushStyle::Watercolor rendering path.

New crate strategy

  • Define watercolor presets using existing BrushPreset (from hcie-engine-api) with style: BrushStyle::Watercolor
  • Provide a watercolor splat preview renderer that generates RGBA pixels for thumbnails
  • The brushes panel integrates the new crate's presets under a "Watercolor" category tab
  • Actual drawing uses the engine's existing watercolor brush rendering — no engine changes needed

Step 1: Create the new crate hcie-watercolor-brushes

Path: hcie-iced-app/crates/hcie-watercolor-brushes/

Cargo.toml

[package]
name = "hcie-watercolor-brushes"
version.workspace = true
edition.workspace = true

[dependencies]
hcie-engine-api = { path = "../../../hcie-engine-api" }

src/lib.rs — Presets + Preview Renderer

Exports:

  • pub fn watercolor_presets() -> Vec<BrushPreset> — returns 8 watercolor brush presets
  • pub fn render_watercolor_splat(style: BrushStyle, base_color: [u8; 3]) -> Vec<u8> — generates 64×64 RGBA watercolor splat preview

Presets (8 brushes)

ID Name Size Opacity Hardness Spacing Description
wc_wash Watercolor Wash 80 0.25 0.0 0.08 Large, transparent wash
wc_wet Wet Watercolor 60 0.35 0.0 0.06 Medium, wet-on-wet
wc_dry Dry Brush 30 0.70 0.6 0.12 Small, textured, high opacity
wc_glaze Glazing 50 0.15 0.0 0.10 Very transparent, layered
wc_splat Splatter 40 0.50 0.2 0.30 Scattered droplets
wc_bleed Color Bleed 70 0.30 0.0 0.07 Heavy diffusion edges
wc_grain Granulation 45 0.40 0.3 0.09 Visible paper grain texture
wc_bloom Bloom 90 0.20 0.0 0.05 Soft, blooming edges

All use BrushStyle::Watercolor as the engine style — the engine's existing draw_watercolor_brush handles the actual stroke rendering.

Watercolor Splat Preview Algorithm (64×64 RGBA)

The splat is generated procedurally using multi-octave value noise:

  1. Base shape: Radial gradient from center, alpha = (1 - dist/radius)^1.5
  2. Edge displacement: Displace the radius at each angle using 3-octave value noise → irregular organic boundary
  3. Multiple layers: 3-5 overlapping semi-transparent blobs at slightly offset positions
  4. Color variation: Shift hue slightly per layer (±10°) for color separation
  5. Granulation: High-frequency noise modulates alpha in the outer 40% of the radius
  6. Wet edge: Slightly darker/more saturated ring near the boundary (wet-edge accumulation effect)

The noise function is a simple hash-based value noise (no external deps):

fn value_noise(x: f32, y: f32) -> f32 {
    // hash-based pseudo-random, returns 0.0..1.0
}
fn fbm(x: f32, y: f32, octaves: u32) -> f32 {
    // fractal Brownian motion using value_noise
}

Step 2: Register the crate in the workspace

File: Cargo.toml (workspace root)

Add to [workspace] members:

"hcie-iced-app/crates/hcie-watercolor-brushes",

Step 3: Add hcie-watercolor-brushes dependency to hcie-iced-gui

File: hcie-iced-app/crates/hcie-iced-gui/Cargo.toml

Add:

hcie-watercolor-brushes = { path = "../hcie-watercolor-brushes" }

Step 4: Update the Brushes panel

File: hcie-iced-app/crates/hcie-iced-gui/src/panels/brushes.rs

4a. Add Watercolor variant to BrushCategory

pub enum BrushCategory {
    All,
    Drawing,
    Painting,
    Watercolor,  // ← NEW
    Effects,
    Custom,
    Imported,
}

4b. Update category_matches_style for the new variant

BrushCategory::Watercolor => style_category == BrushCategory::Watercolor,

4c. Add Watercolor tab to the category tabs array

(BrushCategory::Watercolor, "Watercolor", "icons/brush.svg"),

4d. Render watercolor presets when Watercolor category is active

When active_category == BrushCategory::Watercolor:

  • Call hcie_watercolor_brushes::watercolor_presets() to get the preset list
  • For each preset, call hcie_watercolor_brushes::render_watercolor_splat(...) for the thumbnail
  • Render as a grid (same 2-column layout as existing brushes)
  • Each cell sends Message::BrushPresetSelected(preset) on click

4e. Update view function signature

The function already accepts imported_presets: &[BrushPreset]. No signature change needed — the watercolor presets are fetched internally from the new crate.

4f. Update test catalog_contains_every_engine_brush_style

The test asserts BRUSH_STYLES.len() == 34. This doesn't change (watercolor presets are separate from BRUSH_STYLES). The test remains valid.


Step 5: Add "Watercolor" to the egui GUI category filter (optional, for parity)

File: hcie-egui-app/crates/hcie-gui-egui/src/app/tools/brushes_panel.rs

Add "Watercolor" to the categories array and update matches_style_category to map it to BrushStyle::Watercolor. This is a minimal change for parity — the watercolor presets themselves would need separate integration in the egui panel if desired.

Note: This step is optional for the initial implementation. The user's request focuses on the iced GUI.


Files Changed

File Change
hcie-iced-app/crates/hcie-watercolor-brushes/Cargo.toml New — crate manifest
hcie-iced-app/crates/hcie-watercolor-brushes/src/lib.rs New — presets + splat renderer
Cargo.toml (workspace root) Add member hcie-iced-app/crates/hcie-watercolor-brushes
hcie-iced-app/crates/hcie-iced-gui/Cargo.toml Add dependency hcie-watercolor-brushes
hcie-iced-app/crates/hcie-iced-gui/src/panels/brushes.rs Add Watercolor category, tab, and grid rendering

Files NOT modified: All engine crates (hcie-brush-engine, hcie-protocol, hcie-engine-api, hcie-draw), existing brush definitions, existing BRUSH_STYLES array.


Validation

  1. cargo check -p hcie-watercolor-brushes — new crate compiles
  2. cargo check -p hcie-iced-gui — GUI compiles with new dependency
  3. cargo test -p hcie-iced-gui — all existing tests pass
  4. cargo test -p hcie-watercolor-brushes — new crate tests pass
  5. cargo test --test feature_scorecard — scorecard passes
  6. Visual: launch the app, open Brushes panel, click "Watercolor" tab → 8 watercolor presets with splat previews appear
  7. Visual: select a watercolor brush → draw on canvas → realistic watercolor stroke rendered
  8. Regression: existing Drawing/Painting/Effects brushes unchanged