Files
hcie-rust-v3.05/hcie-egui-app/crates/hcie-gui-egui/src/app/tools/shape_sync.rs
T
phantom 5632c916ee feat: introduce SVG-based complex vector shapes
- Added `SvgShape` variant to `VectorShape` in `hcie-protocol`, allowing for SVG rendering of complex shapes.
- Updated serialization to skip old shape variants for backward compatibility.
- Implemented SVG generation functions in `svg_templates.rs` for various shapes (arrow, star, rhombus, etc.).
- Created `svg_render.rs` to parse SVG strings and tessellate paths using `usvg`.
- Modified rendering logic in `hcie-vector` to handle `SvgShape` and integrate with existing shape rendering.
- Updated shape creation API in `hcie-engine-api` to support SVG shapes.
- Enhanced property editing and shape synchronization in both `hcie-egui-app` and `hcie-iced-app` to accommodate new SVG shapes.
- Added comprehensive documentation for new features and changes.
2026-07-19 02:44:59 +03:00

155 lines
5.4 KiB
Rust

#![allow(dead_code)]
//! Bidirectional vector shape ↔ tool state synchronization.
//!
//! Purpose: Keep `ToolState.tool_configs.vector` and the selected
//! VectorShape in the document engine in sync. When the user changes
//! tool properties (stroke, fill, color, opacity, etc.), push them
//! to the selected shape. When the user selects a different shape,
//! pull its properties back into the tool config.
//!
//! Logic & Workflow:
//! - `sync_shape_from_tool`: Reads ToolState vector config + colors,
//! writes all matching properties into the engine's selected shape.
//! - `sync_tool_from_shape`: Reads the engine's selected shape
//! properties, writes them back into ToolState vector config.
//!
//! Side Effects: Mutates both `AppDocument.engine` and `ToolState`.
use crate::app::{AppDocument, ToolState};
use hcie_engine_api::{Tool, VectorShape};
/// Push tool state properties (stroke, fill, color, opacity, hardness,
/// shape-specific fields) into the currently selected vector shape.
///
/// This is called when the user changes a tool property or picks a color
/// while a shape is selected in VectorSelect mode.
pub fn sync_shape_from_tool(doc: &mut AppDocument, state: &mut ToolState) {
if state.active_tool != Tool::VectorSelect {
return;
}
let shape_idx = match state.selected_vector_shape {
Some(i) => i,
None => return,
};
// Snapshot for undo (before first modification in this edit session)
if state.vector_shapes_snapshot.is_none() {
if let Some(shapes) = doc.engine.active_vector_shapes() {
state.vector_shapes_snapshot = Some(shapes);
}
}
let layer_id = doc.engine.active_layer_id();
let v = &state.tool_configs.vector;
// Stroke
doc.engine
.set_vector_shape_stroke(layer_id, shape_idx, v.stroke_size);
// Fill
if let Some(shapes) = doc.engine.active_vector_shapes() {
if let Some(shape) = shapes.get(shape_idx) {
if shape.fill_color().is_some() || shape.is_filled() != v.fill_shape {
doc.engine
.set_vector_shape_fill(layer_id, shape_idx, v.fill_shape);
}
}
}
// Opacity
doc.engine
.set_vector_shape_opacity(layer_id, shape_idx, v.opacity);
// Hardness
doc.engine
.set_vector_shape_hardness(layer_id, shape_idx, v.hardness);
// Primary color (stroke color)
doc.engine
.set_vector_shape_color(layer_id, shape_idx, state.primary_color);
// Fill color
if let Some(shapes) = doc.engine.active_vector_shapes() {
if let Some(shape) = shapes.get(shape_idx) {
if shape.fill_color().is_some() {
doc.engine
.set_vector_shape_fill_color(layer_id, shape_idx, state.secondary_color);
}
}
}
// Shape-specific properties
if let Some(shapes) = doc.engine.active_vector_shapes() {
if let Some(shape) = shapes.get(shape_idx) {
match shape {
VectorShape::Rect { .. } => {
// round_rect_radius is set directly via engine if needed
}
VectorShape::Star { .. } => {
// star_points / inner_radius via engine when available
}
VectorShape::Polygon { .. } => {
// polygon_sides via engine when available
}
VectorShape::SvgShape { .. } => {
// shape-specific params are baked into the SVG
}
_ => {}
}
}
}
}
/// Pull properties from the currently selected vector shape back into
/// `ToolState.tool_configs.vector` and color state.
///
/// Called when the user clicks/selects a shape in VectorSelect mode
/// so the properties panel reflects the shape's actual values.
pub fn sync_tool_from_shape(doc: &AppDocument, state: &mut ToolState) {
if state.active_tool != Tool::VectorSelect {
return;
}
let shape_idx = match state.selected_vector_shape {
Some(i) => i,
None => return,
};
if let Some(shapes) = doc.engine.active_vector_shapes() {
if let Some(shape) = shapes.get(shape_idx) {
let v = &mut state.tool_configs.vector;
v.stroke_size = shape.stroke();
v.opacity = shape.shape_opacity();
v.hardness = shape.hardness();
v.fill_shape = shape.is_filled();
match shape {
VectorShape::Rect { radius, .. } => {
v.round_rect_radius = *radius;
}
VectorShape::Star {
points,
inner_radius,
..
} => {
v.star_points = *points;
v.star_inner_radius = *inner_radius;
}
VectorShape::Polygon { sides, .. } => {
v.polygon_sides = *sides;
}
VectorShape::Line {
cap_start,
cap_end: _,
..
} => {
v.line_cap = *cap_start;
}
VectorShape::SvgShape { .. } => {
// params baked into SVG, nothing to sync
}
_ => {}
}
state.primary_color = shape.color();
if let Some(fc) = shape.fill_color() {
state.secondary_color = fc;
}
}
}
}