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