lb_rs/model/svg/
buffer.rs

1use std::collections::HashMap;
2use std::fmt::Write;
3
4use bezier_rs::{Bezier, Subpath};
5use glam::{DAffine2, DMat2, DVec2};
6use indexmap::IndexMap;
7use serde::{Deserialize, Serialize};
8use usvg::{
9    fontdb::Database,
10    tiny_skia_path::{PathSegment, Point},
11    Options, Transform,
12};
13use usvg::{Color, Paint};
14use uuid::Uuid;
15
16use super::{
17    diff::DiffState,
18    element::{Element, ManipulatorGroupId, Path},
19};
20use super::{
21    element::{DynamicColor, Stroke, WeakImage, WeakImages, WeakPathPressures},
22    WeakTransform,
23};
24
25const ZOOM_G_ID: &str = "lb_master_transform";
26const WEAK_IMAGE_G_ID: &str = "lb_images";
27const WEAK_PATH_PRESSURES_G_ID: &str = "lb_path_pressures";
28const WEAK_VIEWPORT_SETTINGS_G_ID: &str = "lb_viewport_settings";
29
30#[derive(Default, Clone)]
31pub struct Buffer {
32    pub elements: IndexMap<Uuid, Element>,
33    pub weak_images: WeakImages,
34    pub weak_path_pressures: WeakPathPressures,
35    pub weak_viewport_settings: WeakViewportSettings,
36    pub master_transform_changed: bool,
37    pub id_map: HashMap<Uuid, String>,
38}
39
40#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
41pub struct WeakRect {
42    pub min: (f32, f32),
43    pub max: (f32, f32),
44}
45
46#[derive(Clone, Default, Copy, Serialize, Deserialize)]
47pub struct WeakViewportSettings {
48    /// the drawable rect in the master-transformed plane
49    pub bounded_rect: Option<WeakRect>,
50    pub master_transform: WeakTransform,
51    pub viewport_transform: Option<WeakTransform>,
52    pub left_locked: bool,
53    pub right_locked: bool,
54    pub bottom_locked: bool,
55    pub top_locked: bool,
56}
57
58impl Buffer {
59    pub fn new(content: &str) -> Self {
60        let mut elements = IndexMap::default();
61        let mut id_map = HashMap::default();
62        let mut weak_images = WeakImages::default();
63        let mut weak_path_pressures = WeakPathPressures::default();
64        let mut weak_viewport_settings = WeakViewportSettings::default();
65
66        let maybe_tree = usvg::Tree::from_str(content, &Options::default(), &Database::default());
67
68        if let Err(err) = maybe_tree {
69            println!("{:#?}", err);
70        } else {
71            let utree = maybe_tree.unwrap();
72
73            utree.root().children().iter().for_each(|u_el| {
74                parse_child(
75                    u_el,
76                    &mut elements,
77                    &mut weak_viewport_settings,
78                    &mut id_map,
79                    &mut weak_images,
80                    &mut weak_path_pressures,
81                )
82            });
83        }
84
85        Self {
86            elements,
87            id_map,
88            weak_images,
89            weak_viewport_settings,
90            weak_path_pressures,
91            master_transform_changed: false,
92        }
93    }
94
95    pub fn reload(
96        local_elements: &mut IndexMap<Uuid, Element>, local_weak_images: &mut WeakImages,
97        local_weak_pressures: &mut WeakPathPressures,
98        local_viewport_settings: &mut WeakViewportSettings, base_buffer: &Self,
99        remote_buffer: &Self,
100    ) {
101        // todo: convert weak images
102        for (id, base_img) in base_buffer.weak_images.iter() {
103            if let Some(remote_img) = remote_buffer.weak_images.get(id) {
104                if remote_img != base_img {
105                    local_weak_images.insert(*id, *remote_img);
106                }
107            } else {
108                // this was deleted remotly
109                local_weak_images.remove(id);
110                local_elements.shift_remove(id);
111            }
112        }
113
114        for (id, remote_img) in remote_buffer.weak_images.iter() {
115            if !base_buffer.weak_images.contains_key(id) {
116                local_weak_images.insert(*id, *remote_img);
117            }
118        }
119
120        let local_master_transform = Transform::from(local_viewport_settings.master_transform);
121
122        base_buffer
123            .elements
124            .iter()
125            .filter_map(|(id, el)| if let Element::Path(p) = el { Some((id, p)) } else { None })
126            .for_each(|(id, base_path)| {
127                if let Some(Element::Path(remote_path)) = remote_buffer.elements.get(id) {
128                    if remote_path != base_path {
129                        // this element was changed remotly
130                        let mut transformed_path = remote_path.clone();
131                        transformed_path.diff_state.transformed = Some(local_master_transform);
132                        transformed_path.diff_state.transformed = None;
133                        transformed_path
134                            .data
135                            .apply_transform(u_transform_to_bezier(&local_master_transform));
136                        transformed_path.diff_state.data_changed = true;
137
138                        local_elements.insert(*id, Element::Path(transformed_path.clone()));
139
140                        if let Some(remote_pressure) = remote_buffer.weak_path_pressures.get(id) {
141                            local_weak_pressures.insert(*id, remote_pressure.clone());
142                        }
143                    }
144                } else {
145                    // this was deletd remotly
146                    local_elements.shift_remove(id);
147                    local_weak_pressures.remove(id);
148                }
149            });
150
151        remote_buffer
152            .elements
153            .iter()
154            .filter_map(|(id, el)| if let Element::Path(p) = el { Some((id, p)) } else { None })
155            .enumerate()
156            .for_each(|(i, (id, remote_el))| {
157                if !base_buffer.elements.contains_key(id) {
158                    let mut transformed_path = remote_el.clone();
159                    transformed_path.diff_state.transformed = Some(local_master_transform);
160                    transformed_path
161                        .data
162                        .apply_transform(u_transform_to_bezier(&local_master_transform));
163
164                    transformed_path.diff_state.data_changed = true;
165                    transformed_path.diff_state.transformed = None;
166
167                    local_elements.insert_before(i, *id, Element::Path(transformed_path));
168                    if let Some(base_pressure) = base_buffer.weak_path_pressures.get(id) {
169                        local_weak_pressures.insert(*id, base_pressure.clone());
170                    }
171                }
172            });
173    }
174
175    pub fn insert(&mut self, id: Uuid, mut el: Element) {
176        match el {
177            Element::Path(ref mut path) => {
178                path.diff_state.data_changed = true;
179                path.diff_state.transformed = None
180            }
181            Element::Image(ref mut image) => {
182                image.diff_state.data_changed = true;
183                image.diff_state.transformed = None
184            }
185            _ => {}
186        }
187        self.elements.insert_before(0, id, el);
188    }
189
190    pub fn hard_remove(&mut self, id: Uuid) {
191        self.elements.shift_remove(&id);
192    }
193
194    /// soft remove that marks the element as deleted but retains it in memeory
195    pub fn remove(&mut self, id: Uuid) {
196        if let Some(el) = self.elements.get_mut(&id) {
197            match el {
198                Element::Path(ref mut path) => {
199                    path.deleted = true;
200                    path.diff_state.delete_changed = true;
201                }
202                Element::Image(ref mut image) => {
203                    image.deleted = true;
204                    image.diff_state.delete_changed = true;
205                }
206                _ => {}
207            }
208        }
209    }
210    pub fn serialize(&self) -> String {
211        serialize_inner(
212            &self.id_map,
213            &self.elements,
214            &self.weak_viewport_settings,
215            &self.weak_images,
216            &self.weak_path_pressures,
217        )
218    }
219}
220
221pub fn serialize_inner(
222    id_map: &HashMap<Uuid, String>, elements: &IndexMap<Uuid, Element>,
223    weak_viewport_settings: &WeakViewportSettings, buffer_weak_images: &WeakImages,
224    weak_pressures: &WeakPathPressures,
225) -> String {
226    let mut root = r#"<svg xmlns="http://www.w3.org/2000/svg">"#.into();
227    let mut weak_images = WeakImages::default();
228    let master_transform = Transform::from(weak_viewport_settings.master_transform);
229
230    for (index, el) in elements.iter().enumerate() {
231        match el.1 {
232            Element::Path(p) => {
233                if p.deleted {
234                    continue;
235                }
236                let mut curv_attrs = " ".to_string();
237                // if it's empty then the curve will not be converted to string via bezier_rs
238                if let Some(stroke) = p.stroke {
239                    curv_attrs = format!(
240                        "stroke-width='{}' stroke='rgba({},{},{},{})' fill='none' id='{}' transform='{}'",
241                        stroke.width,
242                        stroke.color.light.red,
243                        stroke.color.light.green,
244                        stroke.color.light.blue,
245                        stroke.opacity,
246                        id_map.get(el.0).unwrap_or(&el.0.to_string()),
247                        to_svg_transform(p.transform)
248                    );
249                }
250
251                let mut data = p.data.clone();
252                data.apply_transform(u_transform_to_bezier(
253                    &master_transform.invert().unwrap_or_default(),
254                ));
255
256                if data.len() > 1 {
257                    data.to_svg(&mut root, curv_attrs, "".into(), "".into(), "".into())
258                }
259            }
260            Element::Image(img) => {
261                if img.deleted {
262                    continue;
263                }
264
265                let mut weak_image: WeakImage = img.into_weak(index);
266
267                weak_image.transform(master_transform.invert().unwrap_or_default());
268
269                weak_images.insert(*el.0, weak_image);
270            }
271            Element::Text(_) => {}
272        }
273    }
274
275    let zoom_level = format!(
276        r#"<g id="{}" transform="matrix({} {} {} {} {} {})"></g>"#,
277        ZOOM_G_ID,
278        master_transform.sx,
279        master_transform.kx,
280        master_transform.ky,
281        master_transform.sy,
282        master_transform.tx,
283        master_transform.ty
284    );
285
286    weak_images.extend(buffer_weak_images.iter());
287
288    if !weak_images.is_empty() {
289        let binary_data = bincode::serialize(&weak_images).expect("Failed to serialize");
290        let base64_data = base64::encode(&binary_data);
291
292        let _ =
293            write!(&mut root, "<g id=\"{}\"> <g id=\"{}\"></g></g>", WEAK_IMAGE_G_ID, base64_data);
294    }
295
296    if !weak_pressures.is_empty() {
297        let binary_data = bincode::serialize(&weak_pressures).expect("Failed to serialize");
298        let base64_data = base64::encode(&binary_data);
299
300        let _ = write!(
301            &mut root,
302            "<g id=\"{}\"> <g id=\"{}\"></g></g>",
303            WEAK_PATH_PRESSURES_G_ID, base64_data
304        );
305    }
306
307    let binary_data = bincode::serialize(&weak_viewport_settings).expect("Failed to serialize");
308    let base64_data = base64::encode(&binary_data);
309
310    let _ = write!(
311        &mut root,
312        "<g id=\"{}\"> <g id=\"{}\"></g></g>",
313        WEAK_VIEWPORT_SETTINGS_G_ID, base64_data
314    );
315
316    let _ = write!(&mut root, "{} </svg>", zoom_level);
317    root
318}
319
320pub fn parse_child(
321    u_el: &usvg::Node, elements: &mut IndexMap<Uuid, Element>,
322    weak_viewport_settings: &mut WeakViewportSettings, id_map: &mut HashMap<Uuid, String>,
323    weak_images: &mut WeakImages, weak_path_pressures: &mut WeakPathPressures,
324) {
325    match &u_el {
326        usvg::Node::Group(group) => {
327            if group.id().eq(WEAK_IMAGE_G_ID) {
328                if let Some(usvg::Node::Group(weak_images_g)) = group.children().first() {
329                    let base64 = base64::decode(weak_images_g.id().as_bytes())
330                        .expect("Failed to decode base64");
331
332                    let decoded: WeakImages = bincode::deserialize(&base64).unwrap_or_default();
333                    *weak_images = decoded;
334                }
335            } else if group.id().eq(WEAK_PATH_PRESSURES_G_ID) {
336                if let Some(usvg::Node::Group(weak_pressures_g)) = group.children().first() {
337                    let base64 = base64::decode(weak_pressures_g.id().as_bytes())
338                        .expect("Failed to decode base64");
339
340                    let decoded: WeakPathPressures =
341                        bincode::deserialize(&base64).unwrap_or_default();
342                    *weak_path_pressures = decoded;
343                }
344            } else if group.id().eq(WEAK_VIEWPORT_SETTINGS_G_ID) {
345                if let Some(usvg::Node::Group(weak_viewport_settings_g)) = group.children().first()
346                {
347                    let base64 = base64::decode(weak_viewport_settings_g.id().as_bytes())
348                        .expect("Failed to decode base64");
349
350                    let decoded: WeakViewportSettings =
351                        bincode::deserialize(&base64).unwrap_or_default();
352                    *weak_viewport_settings = decoded;
353                }
354            } else {
355                group.children().iter().for_each(|u_el| {
356                    parse_child(
357                        u_el,
358                        elements,
359                        weak_viewport_settings,
360                        id_map,
361                        weak_images,
362                        weak_path_pressures,
363                    )
364                });
365            }
366        }
367
368        usvg::Node::Image(_) => {}
369        usvg::Node::Path(path) => {
370            let diff_state = DiffState { data_changed: true, ..Default::default() };
371
372            let id = get_internal_id(path.id(), id_map);
373
374            let stroke = if let Some(s) = path.stroke() {
375                if let Paint::Color(color) = *s.paint() {
376                    let maybe_dynamic_color = get_dyn_color(color);
377
378                    Some(Stroke {
379                        color: maybe_dynamic_color,
380                        opacity: s.opacity().get(),
381                        width: s.width().get(),
382                    })
383                } else {
384                    None
385                }
386            } else {
387                None
388            };
389            let mut data = usvg_d_to_subpath(path);
390
391            data.apply_transform(u_transform_to_bezier(
392                &path.abs_transform().invert().unwrap_or_default(),
393            ));
394
395            elements.insert(
396                id,
397                Element::Path(Path {
398                    data,
399                    visibility: path.visibility(),
400                    fill: path.fill().cloned(),
401                    stroke,
402                    transform: path.abs_transform(),
403                    diff_state,
404                    deleted: false,
405                    opacity: 1.0,
406                }),
407            );
408        }
409        _ => {}
410    }
411}
412
413pub fn get_dyn_color(color: Color) -> DynamicColor {
414    let canvas_colors = get_canvas_colors();
415
416    let maybe_dynamic_color = if let Some(dynamic_color) = canvas_colors
417        .iter()
418        .find(|c| c.light.eq(&color) || c.dark.eq(&color))
419    {
420        *dynamic_color
421    } else {
422        DynamicColor { light: color, dark: color }
423    };
424    maybe_dynamic_color
425}
426
427fn get_internal_id(svg_id: &str, id_map: &mut HashMap<Uuid, String>) -> Uuid {
428    let id: Uuid = svg_id.parse().unwrap_or(Uuid::new_v4());
429
430    if id_map.insert(id, svg_id.to_owned()).is_some() {
431        warn!(id = svg_id, "found elements  with duplicate id");
432    }
433    id
434}
435
436pub fn get_canvas_colors() -> Vec<DynamicColor> {
437    let mut highlighter_colors = get_highlighter_colors();
438    highlighter_colors.append(&mut get_pen_colors());
439
440    highlighter_colors
441}
442
443pub fn get_highlighter_colors() -> Vec<DynamicColor> {
444    let yellow =
445        DynamicColor { light: Color::new_rgb(244, 250, 65), dark: Color::new_rgb(244, 250, 65) };
446    let blue =
447        DynamicColor { light: Color::new_rgb(65, 194, 250), dark: Color::new_rgb(65, 194, 250) };
448    let pink =
449        DynamicColor { light: Color::new_rgb(254, 110, 175), dark: Color::new_rgb(254, 110, 175) };
450    vec![yellow, blue, pink]
451}
452
453pub fn get_pen_colors() -> Vec<DynamicColor> {
454    let red =
455        DynamicColor { light: Color::new_rgb(218, 21, 21), dark: Color::new_rgb(174, 33, 33) };
456    let orange =
457        DynamicColor { light: Color::new_rgb(255, 149, 0), dark: Color::new_rgb(255, 159, 10) };
458    let yellow =
459        DynamicColor { light: Color::new_rgb(255, 204, 0), dark: Color::new_rgb(255, 214, 10) };
460    let green =
461        DynamicColor { light: Color::new_rgb(42, 136, 49), dark: Color::new_rgb(56, 176, 65) };
462    let teal =
463        DynamicColor { light: Color::new_rgb(0, 128, 128), dark: Color::new_rgb(0, 147, 147) };
464    let cyan =
465        DynamicColor { light: Color::new_rgb(85, 190, 240), dark: Color::new_rgb(90, 200, 245) };
466    let blue =
467        DynamicColor { light: Color::new_rgb(62, 130, 230), dark: Color::new_rgb(54, 116, 207) };
468    let indigo =
469        DynamicColor { light: Color::new_rgb(75, 0, 130), dark: Color::new_rgb(64, 0, 110) };
470    let purple =
471        DynamicColor { light: Color::new_rgb(128, 0, 128), dark: Color::new_rgb(147, 0, 147) };
472    let magenta =
473        DynamicColor { light: Color::new_rgb(175, 82, 222), dark: Color::new_rgb(191, 90, 242) };
474    let pink =
475        DynamicColor { light: Color::new_rgb(255, 105, 180), dark: Color::new_rgb(255, 120, 190) };
476    let brown =
477        DynamicColor { light: Color::new_rgb(139, 69, 19), dark: Color::new_rgb(101, 53, 17) };
478
479    let fg = DynamicColor { light: Color::black(), dark: Color::white() };
480
481    vec![fg, red, orange, yellow, green, teal, cyan, blue, indigo, purple, brown, magenta, pink]
482}
483
484fn usvg_d_to_subpath(path: &usvg::Path) -> Subpath<ManipulatorGroupId> {
485    let mut prev = Point::default();
486    let mut subpath: Subpath<ManipulatorGroupId> = Subpath::new(vec![], false);
487    for segment in path.data().segments() {
488        match segment {
489            PathSegment::MoveTo(p) => {
490                prev = p;
491            }
492            PathSegment::CubicTo(p1, p2, p3) => {
493                let bez = Bezier::from_cubic_coordinates(
494                    prev.x.into(),
495                    prev.y.into(),
496                    p1.x.into(),
497                    p1.y.into(),
498                    p2.x.into(),
499                    p2.y.into(),
500                    p3.x.into(),
501                    p3.y.into(),
502                );
503                subpath.append_bezier(&bez, bezier_rs::AppendType::IgnoreStart);
504                prev = p3;
505            }
506            PathSegment::LineTo(p) => {
507                let bez = Bezier::from_linear_coordinates(
508                    prev.x.into(),
509                    prev.y.into(),
510                    p.x.into(),
511                    p.y.into(),
512                );
513                subpath.append_bezier(&bez, bezier_rs::AppendType::IgnoreStart);
514                prev = p;
515            }
516            _ => {}
517        }
518    }
519
520    let t = path.abs_transform();
521
522    subpath.apply_transform(DAffine2 {
523        matrix2: DMat2 {
524            x_axis: DVec2 { x: t.sx.into(), y: t.ky.into() },
525            y_axis: DVec2 { x: t.kx.into(), y: t.sy.into() },
526        },
527        translation: DVec2 { x: t.tx.into(), y: t.ty.into() },
528    });
529
530    subpath
531}
532
533pub fn u_transform_to_bezier(src: &Transform) -> DAffine2 {
534    glam::DAffine2 {
535        matrix2: DMat2 {
536            x_axis: DVec2 { x: src.sx.into(), y: src.ky.into() },
537            y_axis: DVec2 { x: src.kx.into(), y: src.sy.into() },
538        },
539        translation: glam::DVec2 { x: src.tx.into(), y: src.ty.into() },
540    }
541}
542
543fn to_svg_transform(transform: Transform) -> String {
544    format!(
545        "matrix({} {} {} {} {} {})",
546        transform.sx, transform.ky, transform.kx, transform.sy, transform.tx, transform.ty
547    )
548}