summaryrefslogtreecommitdiffstats
path: root/packages/svgbob/src/buffer/fragment_buffer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'packages/svgbob/src/buffer/fragment_buffer.rs')
-rw-r--r--packages/svgbob/src/buffer/fragment_buffer.rs154
1 files changed, 154 insertions, 0 deletions
diff --git a/packages/svgbob/src/buffer/fragment_buffer.rs b/packages/svgbob/src/buffer/fragment_buffer.rs
new file mode 100644
index 0000000..a550b0f
--- /dev/null
+++ b/packages/svgbob/src/buffer/fragment_buffer.rs
@@ -0,0 +1,154 @@
+use crate::{buffer::Settings, Cell};
+pub use direction::Direction;
+pub use fragment::Fragment;
+pub use fragment_tree::FragmentTree;
+use itertools::Itertools;
+use std::{
+ collections::BTreeMap,
+ ops::{Deref, DerefMut},
+};
+
+pub mod direction;
+pub mod fragment;
+mod fragment_tree;
+
+/// Fragment buffer contains the drawing fragments for each cell
+/// Svg can be converted to fragment buffer
+/// then from the fragment we can match which characters is best suited for
+/// a particular set of fragment contained in a cell and then create a stringbuffer.
+/// The stringbuffer becomes the ascii diagrams
+/// SVG -> FragmentBuffer -> StringBuffer -> Ascii Diagrams
+///
+/// We can also create a reverse
+/// Ascii Diagrams -> String Buffer -> Fragment Buffer -> SVG
+///
+/// ```ignore
+/// 0 1 2 3 4 B C D
+/// 0┌─┬─┬─┬─┐ A┌─┬─┬─┬─┐E
+/// 1├─┼─┼─┼─┤ │ │ │ │ │
+/// 2├─┼─┼─┼─┤ F├─G─H─I─┤J
+/// 3├─┼─┼─┼─┤ │ │ │ │ │
+/// 4├─┼─┼─┼─┤ K├─L─M─N─┤O
+/// 5├─┼─┼─┼─┤ │ │ │ │ │
+/// 6├─┼─┼─┼─┤ P├─Q─R─S─┤T
+/// 7├─┼─┼─┼─┤ │ │ │ │ │
+/// 8└─┴─┴─┴─┘ U└─┴─┴─┴─┘Y
+/// ``` V W X
+#[derive(Debug)]
+pub struct FragmentBuffer(BTreeMap<Cell, Vec<Fragment>>);
+
+impl Deref for FragmentBuffer {
+ type Target = BTreeMap<Cell, Vec<Fragment>>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl DerefMut for FragmentBuffer {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+impl FragmentBuffer {
+ pub fn new() -> Self {
+ FragmentBuffer(BTreeMap::new())
+ }
+
+ /// dump for debugging purpose only
+ /// printling the fragments on this fragment buffer
+ pub fn dump(&self) -> String {
+ let mut buff = String::new();
+ for (cell, shapes) in self.iter() {
+ buff.push_str(&format!("\ncell: {} ", cell));
+ for shape in shapes {
+ buff.push_str(&format!("\n {}", shape));
+ }
+ }
+ buff
+ }
+
+ /// Add a single fragment to this cell
+ pub fn add_fragment_to_cell(&mut self, cell: Cell, fragment: Fragment) {
+ if let Some(existing) = self.get_mut(&cell) {
+ existing.push(fragment);
+ } else {
+ self.insert(cell, vec![fragment]);
+ }
+ self.sort_fragments_in_cell(cell);
+ }
+
+ /// sort the fragments content in this cell
+ fn sort_fragments_in_cell(&mut self, cell: Cell) {
+ if let Some(fragments) = &mut self.get_mut(&cell) {
+ (*fragments).sort();
+ }
+ }
+
+ fn bounds(&self) -> Option<(Cell, Cell)> {
+ let xlimits =
+ self.iter().map(|(cell, _)| cell.x).minmax().into_option();
+ let ylimits =
+ self.iter().map(|(cell, _)| cell.y).minmax().into_option();
+ match (xlimits, ylimits) {
+ (Some((min_x, max_x)), Some((min_y, max_y))) => {
+ Some((Cell::new(min_x, min_y), Cell::new(max_x, max_y)))
+ }
+ _ => None,
+ }
+ }
+
+ pub fn get_size(&self, settings: &Settings) -> (f32, f32) {
+ let (_top_left, bottom_right) =
+ self.bounds().unwrap_or((Cell::new(0, 0), Cell::new(0, 0)));
+ let w = settings.scale * (bottom_right.x + 2) as f32 * Cell::width();
+ let h = settings.scale * (bottom_right.y + 2) as f32 * Cell::height();
+ (w, h)
+ }
+
+ /// add multiple fragments to cell
+ pub fn add_fragments_to_cell(
+ &mut self,
+ cell: Cell,
+ fragments: Vec<Fragment>,
+ ) {
+ if let Some(existing) = self.get_mut(&cell) {
+ existing.extend(fragments);
+ } else {
+ self.insert(cell, fragments);
+ }
+ self.sort_fragments_in_cell(cell);
+ }
+
+ pub fn merge_fragments(&self, settings: &Settings) -> Vec<Fragment> {
+ let fragments = self.first_pass_merge(settings);
+ Fragment::merge_recursive(fragments, settings)
+ }
+
+ /// merge fragments that can be merged.
+ /// This is only merging the fragments that are in the same
+ /// cell
+ fn first_pass_merge(&self, settings: &Settings) -> Vec<Fragment> {
+ let mut merged: Vec<Fragment> = vec![];
+ for (cell, fragments) in self.iter() {
+ for frag in fragments.iter() {
+ //Note: The fragments are calculated with their absolute
+ // parameters and is derived from the cell position
+ let abs_frag = frag.absolute_position(*cell);
+ let had_merged = merged.iter_mut().rev().any(|mfrag| {
+ if let Some(new_merge) = mfrag.merge(&abs_frag, settings) {
+ *mfrag = new_merge;
+ true
+ } else {
+ false
+ }
+ });
+ if !had_merged {
+ merged.push(abs_frag);
+ }
+ }
+ }
+ merged
+ }
+}