lb_rs/model/svg/
element.rs

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