feat: add cyclic color and speed options for brush settings
mandatory-regression-gate / deterministic-tests (push) Waiting to run
mandatory-regression-gate / protected-performance-path (push) Waiting to run

This commit is contained in:
Your Name
2026-07-25 17:29:26 +03:00
parent 683dac06ec
commit 19a7e51813
6 changed files with 232 additions and 5 deletions
+39 -2
View File
@@ -587,6 +587,10 @@ pub struct ToolState {
pub brush_color_variant: bool,
/// Magnitude of the per-dab color variation (0.0..1.0).
pub brush_variant_amount: f32,
/// True when the brush color should cycle through the hue over stroke distance.
pub brush_cyclic_color: bool,
/// Speed of the hue cycle along the stroke (0.01..5.0).
pub brush_cyclic_speed: f32,
/// Timestamp of the last update() call for frame timing.
pub last_update_instant: std::time::Instant,
/// Imported brush presets from ABR/KPP files.
@@ -621,6 +625,8 @@ impl Default for ToolState {
brush_spacing: 1.0,
brush_color_variant: false,
brush_variant_amount: 0.0,
brush_cyclic_color: false,
brush_cyclic_speed: 0.5,
last_update_instant: std::time::Instant::now(),
imported_brushes: Vec::new(),
active_brush_preset: None,
@@ -778,6 +784,8 @@ pub enum Message {
BrushSpacingChanged(f32),
BrushColorVariantToggled(bool),
BrushVariantAmountChanged(f32),
BrushCyclicColorToggled(bool),
BrushCyclicSpeedChanged(f32),
BrushImportAbr,
BrushImportAbrFile(Result<Vec<hcie_engine_api::BrushPreset>, String>),
ResetToolDefaults,
@@ -1319,6 +1327,7 @@ fn build_brush_tip(state: &ToolState) -> BrushTip {
}
}
/// Converts an imported/default brush-engine preset tip into the protocol tip used by `Engine`.
///
/// **Arguments:** `source` is the preset-owned brush-engine tip. **Returns:** A protocol tip with
@@ -2564,7 +2573,7 @@ impl HcieIcedApp {
/// Apply brush parameters from tool state to the engine.
///
/// Called before starting a stroke to ensure the engine uses
/// the current brush size, opacity, hardness, and style.
/// the current brush size, opacity, hardness, style, and cyclic color.
fn apply_brush_params(&mut self) {
let mut tip = self
.tool_state
@@ -2577,6 +2586,15 @@ impl HcieIcedApp {
tip.opacity = self.tool_state.brush_opacity;
tip.hardness = self.tool_state.brush_hardness;
tip.spacing = self.tool_state.brush_spacing;
// Cyclic color is handled by engine state, not the tip, but preview rendering
// also reads the tip's time dynamics. Synchronize both here.
tip.time_enabled = self.tool_state.brush_cyclic_color;
tip.time_size_start = 1.0;
tip.time_size_end = 1.0;
tip.time_opacity_start = 1.0;
tip.time_opacity_end = 1.0;
tip.time_angle_start = 0.0;
tip.time_angle_end = 0.0;
match self.tool_state.active_tool {
Tool::Pen => {
tip.style = BrushStyle::Round;
@@ -2592,7 +2610,10 @@ impl HcieIcedApp {
}
_ => {}
}
self.documents[self.active_doc].engine.set_brush_tip(tip);
let engine = &mut self.documents[self.active_doc].engine;
engine.set_brush_tip(tip);
engine.set_cyclic_color(self.tool_state.brush_cyclic_color);
engine.set_cyclic_speed(self.tool_state.brush_cyclic_speed);
}
/// Push the current SVG editor model into the active vector shape so the
@@ -4955,6 +4976,22 @@ impl HcieIcedApp {
self.settings.update_from_tool_state(&self.tool_state);
let _ = self.settings.save();
}
Message::BrushCyclicColorToggled(enabled) => {
self.tool_state.brush_cyclic_color = enabled;
self.documents[self.active_doc]
.engine
.set_cyclic_color(enabled);
self.settings.update_from_tool_state(&self.tool_state);
let _ = self.settings.save();
}
Message::BrushCyclicSpeedChanged(speed) => {
self.tool_state.brush_cyclic_speed = speed;
self.documents[self.active_doc]
.engine
.set_cyclic_speed(speed);
self.settings.update_from_tool_state(&self.tool_state);
let _ = self.settings.save();
}
Message::SelectionToleranceChanged(tol) => {
self.settings.tool_settings.magic_wand_tolerance = tol as f32;
self.settings.tool_settings.flood_fill_tolerance = tol as f32;