Skip to main content

hwpforge_core/control/
shapes.rs

1//! Shape geometry and visual style primitives for drawing controls.
2//!
3//! Houses [`ShapePoint`], [`LineStyle`], [`ArrowStyle`], [`Fill`], and
4//! [`ShapeStyle`] — the building blocks shared by the shape variants of
5//! [`Control`](super::Control).
6
7use hwpforge_foundation::{
8    ArrowSize, ArrowType, Color, DropCapStyle, Flip, GradientType, ImageFillMode, PatternType,
9};
10use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12
13use crate::error::CoreError;
14
15/// A 2D point in raw HWPUNIT coordinates for shape geometry.
16///
17/// Uses `i32` (not `HwpUnit`) because shape geometry points are raw
18/// coordinate values within a bounding box, not document-level measurements.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
20pub struct ShapePoint {
21    /// X coordinate (HWPUNIT).
22    pub x: i32,
23    /// Y coordinate (HWPUNIT).
24    pub y: i32,
25}
26
27impl ShapePoint {
28    /// Creates a new shape point with the given coordinates.
29    ///
30    /// # Examples
31    ///
32    /// ```
33    /// use hwpforge_core::control::ShapePoint;
34    ///
35    /// let pt = ShapePoint::new(100, 200);
36    /// assert_eq!(pt.x, 100);
37    /// assert_eq!(pt.y, 200);
38    /// ```
39    pub fn new(x: i32, y: i32) -> Self {
40        Self { x, y }
41    }
42}
43
44/// Line drawing style for shapes.
45///
46/// Controls how the stroke of a shape is rendered (solid, dashed, etc.).
47/// Maps to HWPX `<hc:lineShape>` `dash` attribute values.
48///
49/// # Examples
50///
51/// ```
52/// use hwpforge_core::control::LineStyle;
53///
54/// let style = LineStyle::Dash;
55/// assert_eq!(style.to_string(), "DASH");
56/// assert_eq!("DOT".parse::<LineStyle>().unwrap(), LineStyle::Dot);
57/// ```
58#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
59#[non_exhaustive]
60pub enum LineStyle {
61    /// Continuous solid line (default).
62    #[default]
63    Solid,
64    /// Dashed line.
65    Dash,
66    /// Dotted line.
67    Dot,
68    /// Alternating dash and dot.
69    DashDot,
70    /// Alternating dash, dot, dot.
71    DashDotDot,
72    /// No visible line.
73    None,
74}
75
76impl std::fmt::Display for LineStyle {
77    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78        match self {
79            Self::Solid => f.write_str("SOLID"),
80            Self::Dash => f.write_str("DASH"),
81            Self::Dot => f.write_str("DOT"),
82            Self::DashDot => f.write_str("DASH_DOT"),
83            Self::DashDotDot => f.write_str("DASH_DOT_DOT"),
84            Self::None => f.write_str("NONE"),
85        }
86    }
87}
88
89impl std::str::FromStr for LineStyle {
90    type Err = CoreError;
91
92    fn from_str(s: &str) -> Result<Self, Self::Err> {
93        match s {
94            "SOLID" | "Solid" | "solid" => Ok(Self::Solid),
95            "DASH" | "Dash" | "dash" => Ok(Self::Dash),
96            "DOT" | "Dot" | "dot" => Ok(Self::Dot),
97            "DASH_DOT" | "DashDot" | "dash_dot" => Ok(Self::DashDot),
98            "DASH_DOT_DOT" | "DashDotDot" | "dash_dot_dot" => Ok(Self::DashDotDot),
99            "NONE" | "None" | "none" => Ok(Self::None),
100            _ => Err(CoreError::InvalidStructure {
101                context: "LineStyle".to_string(),
102                reason: format!(
103                    "unknown line style '{s}', valid: SOLID, DASH, DOT, DASH_DOT, DASH_DOT_DOT, NONE"
104                ),
105            }),
106        }
107    }
108}
109
110/// Arrowhead style for line endpoints.
111///
112/// # Examples
113///
114/// ```
115/// use hwpforge_core::control::ArrowStyle;
116/// use hwpforge_foundation::{ArrowType, ArrowSize};
117///
118/// let arrow = ArrowStyle {
119///     arrow_type: ArrowType::Normal,
120///     size: ArrowSize::Medium,
121///     filled: true,
122/// };
123/// ```
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
125pub struct ArrowStyle {
126    /// Shape of the arrowhead.
127    pub arrow_type: ArrowType,
128    /// Size of the arrowhead.
129    pub size: ArrowSize,
130    /// Whether the arrowhead is filled (true) or outlined (false).
131    pub filled: bool,
132}
133
134/// Fill specification for shapes.
135///
136/// Replaces simple `fill_color` for shapes that need gradient, pattern, or image fills.
137///
138/// # Examples
139///
140/// ```
141/// use hwpforge_core::control::Fill;
142/// use hwpforge_foundation::Color;
143///
144/// let solid = Fill::Solid { color: Color::from_rgb(255, 0, 0) };
145/// ```
146#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
147#[non_exhaustive]
148pub enum Fill {
149    /// Solid color fill.
150    Solid {
151        /// Fill color.
152        color: Color,
153    },
154    /// Gradient fill.
155    Gradient {
156        /// Gradient direction type.
157        gradient_type: GradientType,
158        /// Gradient angle in degrees.
159        angle: i32,
160        /// Color stops: (color, position 0-100).
161        colors: Vec<(Color, u32)>,
162    },
163    /// Hatch pattern fill.
164    Pattern {
165        /// Pattern type.
166        pattern_type: PatternType,
167        /// Foreground pattern color.
168        fg_color: Color,
169        /// Background color.
170        bg_color: Color,
171    },
172    /// Image fill.
173    Image {
174        /// Image binary data reference ID.
175        image_id: String,
176        /// Image fill mode (tile, stretch, etc.).
177        mode: ImageFillMode,
178    },
179}
180
181/// Visual style overrides for drawing shapes.
182///
183/// All fields are `Option`; `None` means "use the encoder's default"
184/// (typically black solid border, white fill, 0.12 mm stroke).
185///
186/// # Examples
187///
188/// ```
189/// use hwpforge_core::control::{ShapeStyle, LineStyle};
190/// use hwpforge_foundation::Color;
191///
192/// let style = ShapeStyle {
193///     line_color: Some(Color::from_rgb(255, 0, 0)),
194///     fill_color: Some(Color::from_rgb(0, 255, 0)),
195///     line_width: Some(100),
196///     line_style: Some(LineStyle::Dash),
197///     ..Default::default()
198/// };
199/// ```
200#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
201pub struct ShapeStyle {
202    /// Stroke/border color (e.g. `Color::from_rgb(255, 0, 0)` for red).
203    pub line_color: Option<Color>,
204    /// Fill color (e.g. `Color::from_rgb(0, 255, 0)` for green).
205    /// For advanced fills (gradient, pattern, image), use the `fill` field instead.
206    pub fill_color: Option<Color>,
207    /// Stroke width in HWPUNIT (33 ≈ 0.12mm, 100 ≈ 0.35mm).
208    pub line_width: Option<u32>,
209    /// Line drawing style (solid, dash, dot, etc.).
210    pub line_style: Option<LineStyle>,
211    /// Rotation angle in degrees (0-360). `None` means no rotation.
212    pub rotation: Option<f32>,
213    /// Flip/mirror state. `None` means no flip.
214    pub flip: Option<Flip>,
215    /// Arrowhead at the start of a line. Only meaningful for `Control::Line`.
216    pub head_arrow: Option<ArrowStyle>,
217    /// Arrowhead at the end of a line. Only meaningful for `Control::Line`.
218    pub tail_arrow: Option<ArrowStyle>,
219    /// Advanced fill (gradient, pattern, image). Overrides `fill_color` when present.
220    pub fill: Option<Fill>,
221    /// Drop cap style for the shape (HWPX `dropcapstyle` attribute).
222    /// Controls whether the shape participates in a drop-cap layout.
223    #[serde(default)]
224    pub drop_cap_style: DropCapStyle,
225}