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, PartialEq)]
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 Image {
74    pub fn into_weak(&self, z_index: usize) -> WeakImage {
75        WeakImage {
76            href: self.href,
77            transform: WeakTransform::from(self.transform),
78            opacity: self.opacity,
79            width: self.view_box.width(),
80            height: self.view_box.height(),
81            x: self.view_box.x(),
82            y: self.view_box.y(),
83            z_index,
84        }
85    }
86}
87
88#[derive(Serialize, Deserialize, PartialEq, Debug, Default, Clone)]
89pub struct WeakImages(HashMap<Uuid, WeakImage>);
90
91#[derive(Serialize, Deserialize, PartialEq, Debug, Default, Clone)]
92pub struct WeakPathPressures(HashMap<Uuid, Vec<f32>>);
93
94impl Deref for WeakPathPressures {
95    type Target = HashMap<Uuid, Vec<f32>>;
96
97    fn deref(&self) -> &Self::Target {
98        &self.0
99    }
100}
101
102impl DerefMut for WeakPathPressures {
103    fn deref_mut(&mut self) -> &mut Self::Target {
104        &mut self.0
105    }
106}
107
108impl Deref for WeakImages {
109    type Target = HashMap<Uuid, WeakImage>;
110
111    fn deref(&self) -> &Self::Target {
112        &self.0
113    }
114}
115
116impl DerefMut for WeakImages {
117    fn deref_mut(&mut self) -> &mut Self::Target {
118        &mut self.0
119    }
120}
121
122/// image that only contains a ref to the data but not the data itself.
123#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
124pub struct WeakImage {
125    pub href: Uuid,
126    pub transform: WeakTransform,
127    pub opacity: f32,
128    pub width: f32,
129    pub height: f32,
130    pub x: f32,
131    pub y: f32,
132    pub z_index: usize,
133}
134
135impl PartialEq for WeakImage {
136    fn eq(&self, other: &Self) -> bool {
137        self.href == other.href
138            && self.transform == other.transform
139            && self.opacity == other.opacity
140            && self.z_index == other.z_index
141    }
142}
143
144impl WeakImage {
145    pub fn transform(&mut self, transform: Transform) {
146        if transform.is_identity() {
147            return;
148        }
149        if let Some(view_box) = NonZeroRect::from_xywh(self.x, self.y, self.width, self.height) {
150            if let Some(ts_view_box) = view_box.transform(transform) {
151                self.x = ts_view_box.x();
152                self.y = ts_view_box.y();
153                self.width = ts_view_box.width();
154                self.height = ts_view_box.height();
155            }
156        }
157    }
158}
159
160#[derive(Serialize, Deserialize, PartialEq, Debug, Clone, Copy)]
161pub struct WeakTransform {
162    pub sx: f32,
163    pub kx: f32,
164    pub ky: f32,
165    pub sy: f32,
166    pub tx: f32,
167    pub ty: f32,
168}
169
170impl Default for WeakTransform {
171    fn default() -> Self {
172        Self { sx: 1.0, kx: 0.0, ky: 0.0, sy: 1.0, tx: 0.0, ty: 0.0 }
173    }
174}
175
176impl From<WeakTransform> for usvg::Transform {
177    fn from(wt: WeakTransform) -> Self {
178        usvg::Transform { sx: wt.sx, kx: wt.kx, ky: wt.ky, sy: wt.sy, tx: wt.tx, ty: wt.ty }
179    }
180}
181
182impl From<usvg::Transform> for WeakTransform {
183    fn from(t: usvg::Transform) -> Self {
184        WeakTransform { sx: t.sx, kx: t.kx, ky: t.ky, sy: t.sy, tx: t.tx, ty: t.ty }
185    }
186}
187
188impl Identifier for ManipulatorGroupId {
189    fn new() -> Self {
190        ManipulatorGroupId
191    }
192}
193#[derive(Clone, PartialEq, Eq, Hash, Debug)]
194pub struct ManipulatorGroupId;
195
196impl Element {
197    pub fn opacity_changed(&self) -> bool {
198        match self {
199            Element::Path(p) => p.diff_state.opacity_changed,
200            Element::Image(i) => i.diff_state.opacity_changed,
201            Element::Text(_) => todo!(),
202        }
203    }
204    pub fn delete_changed(&self) -> bool {
205        match self {
206            Element::Path(p) => p.diff_state.delete_changed,
207            Element::Image(i) => i.diff_state.delete_changed,
208            Element::Text(_) => todo!(),
209        }
210    }
211    pub fn data_changed(&self) -> bool {
212        match self {
213            Element::Path(p) => p.diff_state.data_changed,
214            Element::Image(i) => i.diff_state.data_changed,
215            Element::Text(_) => todo!(),
216        }
217    }
218    pub fn deleted(&self) -> bool {
219        match self {
220            Element::Path(p) => p.deleted,
221            Element::Image(i) => i.deleted,
222            Element::Text(_) => todo!(),
223        }
224    }
225    pub fn transformed(&self) -> Option<Transform> {
226        match self {
227            Element::Path(p) => p.diff_state.transformed,
228            Element::Image(i) => i.diff_state.transformed,
229            Element::Text(_) => todo!(),
230        }
231    }
232    pub fn transform(&mut self, transform: Transform) {
233        match self {
234            Element::Path(path) => {
235                path.diff_state.transformed = Some(transform);
236                path.transform = path.transform.post_concat(transform);
237                path.data.apply_transform(u_transform_to_bezier(&transform));
238            }
239            Element::Image(img) => {
240                img.diff_state.transformed = Some(transform);
241                img.transform = img.transform.post_concat(transform);
242                if let Some(new_vbox) = img.view_box.transform(transform) {
243                    img.view_box = new_vbox;
244                }
245            }
246            Element::Text(_) => todo!(),
247        }
248    }
249
250    pub fn get_transform(&self) -> Transform {
251        match self {
252            Element::Path(path) => path.transform,
253            Element::Image(img) => img.transform,
254            Element::Text(_) => todo!(),
255        }
256    }
257
258    pub fn opacity(&self) -> f32 {
259        match self {
260            Element::Path(path) => path.opacity,
261            Element::Image(image) => image.opacity,
262            Element::Text(_) => todo!(),
263        }
264    }
265}