feat(iced): parametrize shape previews with user tool settings

This commit is contained in:
2026-07-16 12:16:12 +03:00
parent 2dc376a54b
commit 849e3728fe
2 changed files with 63 additions and 11 deletions
@@ -68,6 +68,12 @@ struct OverlayProgram {
pressure: f32,
/// Active tool (drives vector shape preview geometry).
active_tool: hcie_engine_api::Tool,
/// Number of points for star shape preview.
star_points: u32,
/// Number of sides for polygon shape preview.
polygon_sides: u32,
/// Corner radius for rectangle shape preview (rounded corners when > 0).
rect_radius: f32,
/// Gradient drag preview endpoints in canvas-space.
gradient_drag: Option<((f32, f32), (f32, f32))>,
/// Extracted edges for marching ants rendering.
@@ -706,14 +712,15 @@ impl canvas::Program<Message> for OverlayProgram {
frame.stroke(&line_path, stroke_style);
}
hcie_engine_api::Tool::VectorStar => {
// 5-point star preview (ignores settings points for the preview).
// Star preview using user's configured point count.
let cx = origin_x + ((x0 + x1) / 2.0) * self.zoom;
let cy = origin_y + ((y0 + y1) / 2.0) * self.zoom;
let r_out = ((x1 - x0).abs() / 2.0).max(1.0) * self.zoom;
let r_in = r_out * 0.5;
let points = self.star_points.max(3);
let star_path = Path::new(|b| {
for i in 0..10 {
let t = (i as f32) * std::f32::consts::PI / 5.0 - std::f32::consts::FRAC_PI_2;
for i in 0..(points * 2) {
let t = (i as f32) * std::f32::consts::PI / points as f32 - std::f32::consts::FRAC_PI_2;
let r = if i % 2 == 0 { r_out } else { r_in };
let px = cx + r * t.cos();
let py = cy + r * t.sin();
@@ -728,13 +735,14 @@ impl canvas::Program<Message> for OverlayProgram {
frame.stroke(&star_path, stroke_style);
}
hcie_engine_api::Tool::VectorPolygon => {
// 6-sided polygon preview.
// Polygon preview using user's configured side count.
let cx = origin_x + ((x0 + x1) / 2.0) * self.zoom;
let cy = origin_y + ((y0 + y1) / 2.0) * self.zoom;
let r = ((x1 - x0).abs() / 2.0).max(1.0) * self.zoom;
let sides = self.polygon_sides.max(3);
let poly_path = Path::new(|b| {
for i in 0..6 {
let t = (i as f32) * std::f32::consts::TAU / 6.0 - std::f32::consts::FRAC_PI_2;
for i in 0..sides {
let t = (i as f32) * std::f32::consts::TAU / sides as f32 - std::f32::consts::FRAC_PI_2;
let px = cx + r * t.cos();
let py = cy + r * t.sin();
if i == 0 {
@@ -1063,14 +1071,48 @@ impl canvas::Program<Message> for OverlayProgram {
_ => {
// Rectangular shapes (Rect, Arrow, Rhombus, etc.) fall
// back to a bounding-box outline for the preview.
// For VectorRect, show rounded corners when rect_radius > 0.
let v_x = origin_x + x0.min(x1) * self.zoom;
let v_y = origin_y + y0.min(y1) * self.zoom;
let v_w = (x1 - x0).abs() * self.zoom;
let v_h = (y1 - y0).abs() * self.zoom;
let v_path = Path::rectangle(
Point::new(v_x, v_y),
Size::new(v_w, v_h),
);
let v_path = if self.active_tool == hcie_engine_api::Tool::VectorRect && self.rect_radius > 0.0 {
let max_r = (v_w.min(v_h) / 2.0).min(self.rect_radius * self.zoom);
let k = max_r * 0.5523; // cubic approximation of quarter circle
Path::new(|b| {
b.move_to(Point::new(v_x + max_r, v_y));
b.line_to(Point::new(v_x + v_w - max_r, v_y));
b.bezier_curve_to(
Point::new(v_x + v_w - max_r + k, v_y),
Point::new(v_x + v_w, v_y + max_r - k),
Point::new(v_x + v_w, v_y + max_r),
);
b.line_to(Point::new(v_x + v_w, v_y + v_h - max_r));
b.bezier_curve_to(
Point::new(v_x + v_w, v_y + v_h - max_r + k),
Point::new(v_x + v_w - max_r + k, v_y + v_h),
Point::new(v_x + v_w - max_r, v_y + v_h),
);
b.line_to(Point::new(v_x + max_r, v_y + v_h));
b.bezier_curve_to(
Point::new(v_x + max_r - k, v_y + v_h),
Point::new(v_x, v_y + v_h - max_r + k),
Point::new(v_x, v_y + v_h - max_r),
);
b.line_to(Point::new(v_x, v_y + max_r));
b.bezier_curve_to(
Point::new(v_x, v_y + max_r - k),
Point::new(v_x + max_r - k, v_y),
Point::new(v_x + max_r, v_y),
);
b.close();
})
} else {
Path::rectangle(
Point::new(v_x, v_y),
Size::new(v_w, v_h),
)
};
frame.stroke(&v_path, stroke_style);
}
}
@@ -1221,11 +1263,17 @@ impl canvas::Program<Message> for OverlayProgram {
/// * `tool_state` — current tool state (for status bar info)
/// * `marching_ants_offset` — animation offset for marching ants (0.0..1.0)
/// * `pressure` — current pen pressure in [0.0, 1.0] for HUD display
/// * `star_points` — number of points for star shape preview
/// * `polygon_sides` — number of sides for polygon shape preview
/// * `rect_radius` — corner radius for rectangle shape preview
pub fn view<'a>(
doc: &'a crate::app::IcedDocument,
tool_state: &'a crate::app::ToolState,
marching_ants_offset: f32,
pressure: f32,
star_points: u32,
polygon_sides: u32,
rect_radius: f32,
) -> Element<'a, Message> {
let engine_w = doc.engine.canvas_width();
let engine_h = doc.engine.canvas_height();
@@ -1280,6 +1328,9 @@ pub fn view<'a>(
marching_ants_offset,
pressure,
active_tool: tool_state.active_tool,
star_points,
polygon_sides,
rect_radius,
gradient_drag: doc.gradient_drag,
marching_ants_edges: doc.marching_ants_edges.clone(),
selected_vector_bounds: sel_vec_bounds,
@@ -41,7 +41,8 @@ pub fn dock_view<'a>(
let canvas = {
let doc = &app.documents[app.active_doc];
let pressure = app.tablet_state.lock().map(|s| s.current_pressure()).unwrap_or(1.0);
crate::canvas::view(doc, &app.tool_state, app.marching_ants_offset, pressure)
let ts = &app.settings.tool_settings;
crate::canvas::view(doc, &app.tool_state, app.marching_ants_offset, pressure, ts.vector_points, ts.vector_sides, ts.vector_radius)
};
column![doc_tab_bar, canvas]
.width(Length::Fill)