From 6e1ef2f0d8c37b83c9f6543f14aee8c5150394d6 Mon Sep 17 00:00:00 2001 From: Raunak Bhagat Date: Thu, 14 Sep 2023 00:19:44 -0700 Subject: [PATCH] Add declarative way of making trees; add tests --- common/mod.rs | 111 +++++++++++++++++++++ src/lib.rs | 36 ++++--- src/solver/mod.rs | 7 +- tests/test_solver.rs | 227 ++++++++++++++++++++++++++----------------- 4 files changed, 269 insertions(+), 112 deletions(-) create mode 100644 common/mod.rs diff --git a/common/mod.rs b/common/mod.rs new file mode 100644 index 0000000..eedae51 --- /dev/null +++ b/common/mod.rs @@ -0,0 +1,111 @@ +#[macro_export] +macro_rules! node { + ($value:expr, [$($child:expr),*$(,)?]) => {{ + use common::DeclarativeNode; + + DeclarativeNode { + value: $value, + children: vec![$($child),*], + } + }}; + + ($value:expr$(,)?) => {{ + use common::DeclarativeNode; + + DeclarativeNode { + value: $value, + children: vec![], + } + }}; +} + +use indexmap::IndexSet; +use stretchbox::{Constraint, ConstraintKey, Frame, Solver}; + +pub use node; + +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct DeclarativeNode { + pub value: V, + pub children: Vec, +} + +pub fn make_solver(declarative_node: Option<&DeclarativeNode>) -> Option { + fn insert_root_node( + mut solver: Solver, + declarative_node: &DeclarativeNode, + ) -> Option { + solver + .insert_root(declarative_node.value) + .map(|root_constraint_key| { + insert_nodes(solver, &declarative_node.children, root_constraint_key) + }) + } + + fn insert_nodes( + mut solver: Solver, + declarative_nodes: &Vec>, + parent_constraint_key: ConstraintKey, + ) -> Solver { + let mut to_visit_keys_and_nodes = vec![(parent_constraint_key, declarative_nodes)]; + + while let Some((parent_constraint_key, declarative_nodes)) = to_visit_keys_and_nodes.pop() { + for declarative_node in declarative_nodes { + let constraint_key = solver + .insert(declarative_node.value, parent_constraint_key) + .unwrap(); + to_visit_keys_and_nodes.push((constraint_key, &declarative_node.children)); + } + } + + solver + } + + declarative_node.map_or_else( + || Some(Solver::default()), + |declarative_node| { + let solver = Solver::default(); + insert_root_node(solver, declarative_node) + }, + ) +} + +pub fn make_frame_tree(solver: &Solver) -> Option> { + fn get_frame(solver: &Solver, constraint_key: ConstraintKey) -> DeclarativeNode { + let frame = solver.get_frame(constraint_key).unwrap(); + let child_constraint_keys = solver.get(constraint_key).unwrap().child_keys; + let children = get_frames(solver, child_constraint_keys); + + DeclarativeNode { + value: frame, + children, + } + } + + fn get_frames( + solver: &Solver, + constraint_keys: &IndexSet, + ) -> Vec> { + let mut frames = vec![]; + + for &constraint_key in constraint_keys { + let frame = get_frame(solver, constraint_key); + frames.push(frame); + } + + frames + } + + solver + .root_constraint_key() + .and_then(|root_constraint_key| { + let is_dirty = solver.is_dirty(); + + if is_dirty { + None + } else { + let frame_tree = get_frame(solver, root_constraint_key); + Some(frame_tree) + } + }) +} diff --git a/src/lib.rs b/src/lib.rs index 46f39b5..0902386 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,7 @@ impl Solver { // Insertion/removal methods: - pub fn insert_root(&mut self, constraint: Constraint) -> ConstraintKey { + pub fn insert_root(&mut self, constraint: Constraint) -> Option { self.insert_root_with_capacity(constraint, 0) } @@ -55,12 +55,14 @@ impl Solver { &mut self, constraint: Constraint, capacity: usize, - ) -> ConstraintKey { - let root_key = self - .constraint_tree - .insert_root_with_capacity(constraint, capacity); - self.is_dirty = true; - root_key + ) -> Option { + matches!(constraint.fill_x, Fill::Scale(..)).then(|| { + let root_key = self + .constraint_tree + .insert_root_with_capacity(constraint, capacity); + self.is_dirty = true; + root_key + }) } pub fn insert( @@ -155,31 +157,27 @@ impl Solver { // Solve method: - pub fn solve(&mut self, length_x: f64) -> bool { + pub fn solve(&mut self, length_x: f64) { let is_dirty = self.is_dirty; let is_empty = self.constraint_tree.is_empty(); match (is_dirty, is_empty) { - (true, true) => { - self.is_dirty = false; - true - } + (true, true) => self.is_dirty = false, (true, false) => { let length_x = length_x.max(0.); - let did_solve = solve( + + solve( &self.constraint_tree, &mut self.frame_tree, &mut self.key_map, length_x, ); - if did_solve { - self.is_dirty = false; - }; - did_solve + + self.is_dirty = false; } - (false, _) => true, + (false, _) => (), } } } @@ -208,7 +206,7 @@ pub struct Padding { pub right: f64, } -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Default, Debug, Clone, Copy, PartialEq)] pub struct Frame { pub offset_x: f64, pub length_x: f64, diff --git a/src/solver/mod.rs b/src/solver/mod.rs index f716f64..3736789 100644 --- a/src/solver/mod.rs +++ b/src/solver/mod.rs @@ -13,11 +13,10 @@ pub(super) fn solve( frame_tree: &mut Tree, key_map: &mut BTreeMap, length_x: f64, -) -> bool { +) { let (root_constraint_key, root_constraint_node) = constraint_tree.root_key_value().unwrap(); match root_constraint_node.value.fill_x { - Fill::Exact(..) | Fill::Minimize => false, Fill::Scale(scale) => { let length_x = match scale { 0 => 0., @@ -43,9 +42,9 @@ pub(super) fn solve( root_frame_key, root_content_frame, ); - - true } + + Fill::Exact(..) | Fill::Minimize => unreachable!(), } } diff --git a/tests/test_solver.rs b/tests/test_solver.rs index dea77e7..ed2a979 100644 --- a/tests/test_solver.rs +++ b/tests/test_solver.rs @@ -1,92 +1,141 @@ -use stretchbox::{Constraint, Fill, Frame, Padding, Solver}; +#[path = "../common/mod.rs"] +mod common; + +use common::{make_frame_tree, make_solver}; +use stretchbox::{Constraint, Fill, Frame}; + +#[test] +fn test_solver_with_empty_tree() { + let mut solver = make_solver(None).unwrap(); + + solver.solve(10.); + + let actual_frame_tree = make_frame_tree(&solver); + let expected_frame_tree = None; + + assert_eq!(actual_frame_tree, expected_frame_tree); +} #[test] -fn test_solver() { - let mut solver = Solver::default(); - - assert!(!solver.is_dirty()); - - let root_constraint_key = solver.insert_root(Constraint { - fill_x: Fill::Scale(1), - padding: Padding { - left: 1., - right: 1., - }, - }); - let child_constraint_key_1 = solver - .insert( - Constraint { - fill_x: Fill::Scale(1), - padding: Padding { - left: 100., - right: 100., - }, - }, - root_constraint_key, - ) - .unwrap(); - let child_constraint_key_2 = solver - .insert( - Constraint { - fill_x: Fill::Exact(10.), - padding: Padding { - left: 100., - right: 100., - }, - }, - root_constraint_key, - ) - .unwrap(); - let child_constraint_key_3 = solver - .insert( - Constraint { - fill_x: Fill::Minimize, - padding: Padding { - left: 100., - right: 100., - }, - }, - root_constraint_key, - ) - .unwrap(); - - assert!(solver.is_dirty()); - - let did_solve = solver.solve(12.); - assert!(did_solve); - assert!(!solver.is_dirty()); - - let root_frame = solver.get_frame(root_constraint_key).unwrap(); - let child_frame_1 = solver.get_frame(child_constraint_key_1).unwrap(); - let child_frame_2 = solver.get_frame(child_constraint_key_2).unwrap(); - let child_frame_3 = solver.get_frame(child_constraint_key_3).unwrap(); - - assert_eq!( - root_frame, - Frame { - offset_x: 0., - length_x: 12. - } - ); - assert_eq!( - child_frame_1, - Frame { - offset_x: 1., - length_x: 0. - } - ); - assert_eq!( - child_frame_2, - Frame { - offset_x: 1., - length_x: 10. - } - ); - assert_eq!( - child_frame_3, - Frame { - offset_x: 11., - length_x: 0. - } - ); +fn test_solver_with_zero_length() { + let mut solver = make_solver(Some(&node! { Constraint::default() })).unwrap(); + + solver.solve(0.); + + let actual_frame_tree = make_frame_tree(&solver); + let expected_frame_tree = Some(node! { Frame::default() }); + + assert_eq!(actual_frame_tree, expected_frame_tree); } + +#[test] +fn test_solver_with_nonzero_length() { + let mut solver = make_solver(Some(&node! { Constraint::default() })).unwrap(); + + solver.solve(100.); + + let actual_frame_tree = make_frame_tree(&solver); + let expected_frame_tree = Some(node! { Frame { length_x: 100., ..Default::default() } }); + + assert_eq!(actual_frame_tree, expected_frame_tree); +} + +#[test] +fn test_solver_with_invalid_root_constraint() { + let solver = make_solver(Some( + &node! { Constraint { fill_x: Fill::Minimize, ..Default::default() } }, + )); + + assert!(solver.is_none()); +} + +// #[test] +// fn test_solver() { +// let mut solver = Solver::default(); + +// assert!(!solver.is_dirty()); + +// let root_constraint_key = solver.insert_root(Constraint { +// fill_x: Fill::Scale(1), +// padding: Padding { +// left: 1., +// right: 1., +// }, +// }); +// let child_constraint_key_1 = solver +// .insert( +// Constraint { +// fill_x: Fill::Scale(1), +// padding: Padding { +// left: 100., +// right: 100., +// }, +// }, +// root_constraint_key, +// ) +// .unwrap(); +// let child_constraint_key_2 = solver +// .insert( +// Constraint { +// fill_x: Fill::Exact(10.), +// padding: Padding { +// left: 100., +// right: 100., +// }, +// }, +// root_constraint_key, +// ) +// .unwrap(); +// let child_constraint_key_3 = solver +// .insert( +// Constraint { +// fill_x: Fill::Minimize, +// padding: Padding { +// left: 100., +// right: 100., +// }, +// }, +// root_constraint_key, +// ) +// .unwrap(); + +// assert!(solver.is_dirty()); + +// let did_solve = solver.solve(12.); +// assert!(did_solve); +// assert!(!solver.is_dirty()); + +// let root_frame = solver.get_frame(root_constraint_key).unwrap(); +// let child_frame_1 = solver.get_frame(child_constraint_key_1).unwrap(); +// let child_frame_2 = solver.get_frame(child_constraint_key_2).unwrap(); +// let child_frame_3 = solver.get_frame(child_constraint_key_3).unwrap(); + +// assert_eq!( +// root_frame, +// Frame { +// offset_x: 0., +// length_x: 12. +// } +// ); +// assert_eq!( +// child_frame_1, +// Frame { +// offset_x: 1., +// length_x: 0. +// } +// ); +// assert_eq!( +// child_frame_2, +// Frame { +// offset_x: 1., +// length_x: 10. +// } +// ); +// assert_eq!( +// child_frame_3, +// Frame { +// offset_x: 11., +// length_x: 0. +// } +// ); +// }