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 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 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 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 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 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 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 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}