lb_rs/model/svg/
element.rs

1use std::{
2    collections::HashMap,
3    ops::{Deref, DerefMut},
4};
5
6use bezier_rs::{Identifier, Subpath};
7use serde::{Deserialize, Serialize};
8
9use usvg::{self, Color, Fill, ImageKind, NonZeroRect, Text, Transform, Visibility};
10use uuid::Uuid;
11
12use super::{buffer::u_transform_to_bezier, diff::DiffState};
13
14#[derive(Clone)]
15pub enum Element {
16    Path(Path),
17    Image(Image),
18    Text(Text),
19}
20
21#[derive(Clone)]
22pub struct Path {
23    pub data: Subpath<ManipulatorGroupId>,
24    pub visibility: Visibility,
25    pub fill: Option<Fill>,
26    pub stroke: Option<Stroke>,
27    pub transform: Transform,
28    pub diff_state: DiffState,
29    pub deleted: bool,
30    pub opacity: f32,
31}
32
33#[derive(Clone, Copy)]
34pub struct Stroke {
35    pub color: DynamicColor,
36    pub opacity: f32,
37    pub width: f32,
38}
39
40#[derive(Clone, Copy)]
41pub struct DynamicColor {
42    pub light: usvg::Color,
43    pub dark: usvg::Color,
44}
45
46impl Default for DynamicColor {
47    fn default() -> Self {
48        Self { light: Color::black(), dark: Color::white() }
49    }
50}
51
52impl PartialEq for Path {
53    fn eq(&self, other: &Self) -> bool {
54        self.data.len() == other.data.len()
55            && self.visibility == other.visibility
56            && self.transform == other.transform
57            && self.deleted == other.deleted
58    }
59}
60
61#[derive(Clone)]
62pub struct Image {
63    pub data: ImageKind,
64    pub visibility: Visibility,
65    pub transform: Transform,
66    pub view_box: NonZeroRect,
67    pub opacity: f32,
68    pub href: Uuid,
69    pub diff_state: DiffState,
70    pub deleted: bool,
71}
72
73impl From<Transform> for WeakTransform {
74    fn from(value: Transform) -> Self {
75        WeakTransform {
76            sx: value.sx,
77            kx: value.kx,
78            ky: value.ky,
79            sy: value.sy,
80            tx: value.tx,
81            ty: value.ty,
82        }
83    }
84}
85
86impl Image {
87    pub fn into_weak(&self, z_index: usize) -> WeakImage {
88        WeakImage {
89            href: self.href,
90            transform: WeakTransform::from(self.transform),
91            opacity: self.opacity,
92            width: self.view_box.width(),
93            height: self.view_box.height(),
94            x: self.view_box.x(),
95            y: self.view_box.y(),
96            z_index,
97        }
98    }
99}
100
101#[derive(Serialize, Deserialize, PartialEq, Debug, Default, Clone)]
102pub struct WeakImages(HashMap<Uuid, WeakImage>);
103
104impl Deref for WeakImages {
105    type Target = HashMap<Uuid, WeakImage>;
106
107    fn deref(&self) -> &Self::Target {
108        &self.0
109    }
110}
111
112impl DerefMut for WeakImages {
113    fn deref_mut(&mut self) -> &mut Self::Target {
114        &mut self.0
115    }
116}
117
118/// image that only contains a ref to the data but not the data itself.
119#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
120pub struct WeakImage {
121    pub href: Uuid,
122    pub transform: WeakTransform,
123    pub opacity: f32,
124    pub width: f32,
125    pub height: f32,
126    pub x: f32,
127    pub y: f32,
128    pub z_index: usize,
129}
130
131impl PartialEq for WeakImage {
132    fn eq(&self, other: &Self) -> bool {
133        self.href == other.href
134            && self.transform == other.transform
135            && self.opacity == other.opacity
136            && self.z_index == other.z_index
137    }
138}
139
140impl WeakImage {
141    pub fn transform(&mut self, transform: Transform) {
142        if transform.is_identity() {
143            return;
144        }
145        if let Some(view_box) = NonZeroRect::from_xywh(self.x, self.y, self.width, self.height) {
146            if let Some(ts_view_box) = view_box.transform(transform) {
147                self.x = ts_view_box.x();
148                self.y = ts_view_box.y();
149                self.width = ts_view_box.width();
150                self.height = ts_view_box.height();
151            }
152        }
153    }
154}
155
156#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Copy)]
157pub struct WeakTransform {
158    pub sx: f32,
159    pub kx: f32,
160    pub ky: f32,
161    pub sy: f32,
162    pub tx: f32,
163    pub ty: f32,
164}
165
166impl Identifier for ManipulatorGroupId {
167    fn new() -> Self {
168        ManipulatorGroupId
169    }
170}
171#[derive(Clone, PartialEq, Eq, Hash, Debug)]
172pub struct ManipulatorGroupId;
173
174impl Element {
175    pub fn opacity_changed(&self) -> bool {
176        match self {
177            Element::Path(p) => p.diff_state.opacity_changed,
178            Element::Image(i) => i.diff_state.opacity_changed,
179            Element::Text(_) => todo!(),
180        }
181    }
182    pub fn delete_changed(&self) -> bool {
183        match self {
184            Element::Path(p) => p.diff_state.delete_changed,
185            Element::Image(i) => i.diff_state.delete_changed,
186            Element::Text(_) => todo!(),
187        }
188    }
189    pub fn data_changed(&self) -> bool {
190        match self {
191            Element::Path(p) => p.diff_state.data_changed,
192            Element::Image(i) => i.diff_state.data_changed,
193            Element::Text(_) => todo!(),
194        }
195    }
196    pub fn deleted(&self) -> bool {
197        match self {
198            Element::Path(p) => p.deleted,
199            Element::Image(i) => i.deleted,
200            Element::Text(_) => todo!(),
201        }
202    }
203    pub fn transformed(&self) -> Option<Transform> {
204        match self {
205            Element::Path(p) => p.diff_state.transformed,
206            Element::Image(i) => i.diff_state.transformed,
207            Element::Text(_) => todo!(),
208        }
209    }
210    pub fn transform(&mut self, transform: Transform) {
211        match self {
212            Element::Path(path) => {
213                path.diff_state.transformed = Some(transform);
214                path.transform = path.transform.post_concat(transform);
215                path.data.apply_transform(u_transform_to_bezier(&transform));
216            }
217            Element::Image(img) => {
218                img.diff_state.transformed = Some(transform);
219                img.transform = img.transform.post_concat(transform);
220                if let Some(new_vbox) = img.view_box.transform(transform) {
221                    img.view_box = new_vbox;
222                }
223            }
224            Element::Text(_) => todo!(),
225        }
226    }
227
228    pub fn get_transform(&self) -> Transform {
229        match self {
230            Element::Path(path) => path.transform,
231            Element::Image(img) => img.transform,
232            Element::Text(_) => todo!(),
233        }
234    }
235
236    pub fn opacity(&self) -> f32 {
237        match self {
238            Element::Path(path) => path.opacity,
239            Element::Image(image) => image.opacity,
240            Element::Text(_) => todo!(),
241        }
242    }
243}