From 558b5a24cea02878583546052838dacd551d2466 Mon Sep 17 00:00:00 2001 From: Zixuan Chen Date: Fri, 3 Jun 2022 11:49:19 +0800 Subject: [PATCH] fix: use float as coord & fix mirror bug --- rust/Cargo.lock | 83 +++++++++++++++++++ rust/crates/tidy-tree/Cargo.toml | 1 + rust/crates/tidy-tree/src/geometry.rs | 81 +++++++++--------- .../tidy-tree/src/layout/basic_layout.rs | 66 ++++++++------- rust/crates/tidy-tree/src/node.rs | 24 +++--- .../crates/tidy-tree/tests/aesthetic_rules.rs | 19 +++-- rust/crates/tidy-tree/tests/layout_test.rs | 14 ++-- 7 files changed, 191 insertions(+), 97 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 8f4f8b6..c6de382 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "bumpalo" version = "3.10.0" @@ -46,6 +52,82 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fbc387afefefd5e9e39493299f3069e14a140dd34dc19b4c1c1a8fddb6a790" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -115,6 +197,7 @@ dependencies = [ name = "tidy-tree" version = "0.1.0" dependencies = [ + "num", "rand", ] diff --git a/rust/crates/tidy-tree/Cargo.toml b/rust/crates/tidy-tree/Cargo.toml index f8c53b8..3f75285 100644 --- a/rust/crates/tidy-tree/Cargo.toml +++ b/rust/crates/tidy-tree/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +num = "0.4.0" [dev-dependencies] rand = "0.8.5" diff --git a/rust/crates/tidy-tree/src/geometry.rs b/rust/crates/tidy-tree/src/geometry.rs index ac69002..0697646 100644 --- a/rust/crates/tidy-tree/src/geometry.rs +++ b/rust/crates/tidy-tree/src/geometry.rs @@ -1,9 +1,12 @@ +use num::Float; use std::cmp::{max, min}; -#[derive(PartialEq, Eq, Debug)] +pub type Coord = f64; + +#[derive(PartialEq, Debug)] pub struct Point { - pub x: isize, - pub y: isize, + pub x: Coord, + pub y: Coord, } #[derive(PartialEq, Eq, Debug)] @@ -17,9 +20,9 @@ impl Point { pub fn orientation(p: &Point, q: &Point, r: &Point) -> Orientation { let val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); - if val == 0 { + if val.abs() < 1e-7 { Orientation::Colinear - } else if val > 0 { + } else if val > 0. { Orientation::ClockWise } else { Orientation::CounterClockWise @@ -27,7 +30,7 @@ impl Point { } } -#[derive(PartialEq, Eq, Debug)] +#[derive(PartialEq, Debug)] pub struct Line { pub from: Point, pub to: Point, @@ -38,10 +41,10 @@ impl Line { let from = &self.from; let to = &self.to; - point.x >= min(from.x, to.x) - && point.x <= max(from.x, to.x) - && point.y >= min(from.y, to.y) - && point.y <= max(from.y, to.y) + point.x >= Float::min(from.x, to.x) + && point.x <= Float::max(from.x, to.x) + && point.y >= Float::min(from.y, to.y) + && point.y <= Float::max(from.y, to.y) } pub fn intersect(&self, other: &Self) -> bool { @@ -82,72 +85,72 @@ mod test { use super::*; #[test] fn orient() { - let a = Point { x: 0, y: 0 }; - let b = Point { x: 1, y: 0 }; - let c = Point { x: 0, y: 1 }; + let a = Point { x: 0., y: 0. }; + let b = Point { x: 1., y: 0. }; + let c = Point { x: 0., y: 1. }; assert_eq!( Point::orientation(&a, &b, &c), Orientation::CounterClockWise ); - let a = Point { x: 0, y: 0 }; - let b = Point { x: 0, y: 1 }; - let c = Point { x: 1, y: 0 }; + let a = Point { x: 0., y: 0. }; + let b = Point { x: 0., y: 1. }; + let c = Point { x: 1., y: 0. }; assert_eq!(Point::orientation(&a, &b, &c), Orientation::ClockWise); - let a = Point { x: 0, y: 0 }; - let b = Point { x: 1, y: 1 }; - let c = Point { x: 4, y: 4 }; + let a = Point { x: 0., y: 0. }; + let b = Point { x: 1., y: 1. }; + let c = Point { x: 4., y: 4. }; assert_eq!(Point::orientation(&a, &b, &c), Orientation::Colinear); } #[test] fn intersect() { let a = Line { - from: Point { x: 0, y: 0 }, - to: Point { x: 1, y: 0 }, + from: Point { x: 0., y: 0. }, + to: Point { x: 1., y: 0. }, }; let b = Line { - from: Point { x: 1, y: 1 }, - to: Point { x: 1, y: -1 }, + from: Point { x: 1., y: 1. }, + to: Point { x: 1., y: -1. }, }; assert!(a.intersect(&b)); let a = Line { - from: Point { x: 0, y: 0 }, - to: Point { x: 1, y: 0 }, + from: Point { x: 0., y: 0. }, + to: Point { x: 1., y: 0. }, }; let b = Line { - from: Point { x: 2, y: 1 }, - to: Point { x: 1, y: -1 }, + from: Point { x: 2., y: 1. }, + to: Point { x: 1., y: -1. }, }; assert!(!a.intersect(&b)); let a = Line { - from: Point { x: 0, y: 0 }, - to: Point { x: 1, y: 1 }, + from: Point { x: 0., y: 0. }, + to: Point { x: 1., y: 1. }, }; let b = Line { - from: Point { x: 0, y: 1 }, - to: Point { x: 1, y: 0 }, + from: Point { x: 0., y: 1. }, + to: Point { x: 1., y: 0. }, }; assert!(a.intersect(&b)); let a = Line { - from: Point { x: 0, y: 0 }, - to: Point { x: 1, y: 1 }, + from: Point { x: 0., y: 0. }, + to: Point { x: 1., y: 1. }, }; let b = Line { - from: Point { x: 1, y: 1 }, - to: Point { x: 2, y: 2 }, + from: Point { x: 1., y: 1. }, + to: Point { x: 2., y: 2. }, }; assert!(a.intersect(&b)); let a = Line { - from: Point { x: 0, y: 0 }, - to: Point { x: 1, y: 1 }, + from: Point { x: 0., y: 0. }, + to: Point { x: 1., y: 1. }, }; let b = Line { - from: Point { x: 2, y: 2 }, - to: Point { x: 3, y: 3 }, + from: Point { x: 2., y: 2. }, + to: Point { x: 3., y: 3. }, }; assert!(!a.intersect(&b)); } diff --git a/rust/crates/tidy-tree/src/layout/basic_layout.rs b/rust/crates/tidy-tree/src/layout/basic_layout.rs index f867a94..4649602 100644 --- a/rust/crates/tidy-tree/src/layout/basic_layout.rs +++ b/rust/crates/tidy-tree/src/layout/basic_layout.rs @@ -1,13 +1,15 @@ +use num::Float; + use super::Layout; -use crate::node::Node; +use crate::{geometry::Coord, node::Node}; use std::cmp::{max, min}; /// Relative position /// /// Relative position illustration pub struct BasicLayout { - pub parent_child_margin: isize, - pub peer_margin: isize, + pub parent_child_margin: Coord, + pub peer_margin: Coord, } /// Relative position @@ -15,19 +17,19 @@ pub struct BasicLayout { /// Relative position illustration #[derive(Debug, Clone)] pub struct BoundingBox { - pub total_width: isize, - pub total_height: isize, - pub relative_x: isize, - pub relative_y: isize, + pub total_width: Coord, + pub total_height: Coord, + pub relative_x: Coord, + pub relative_y: Coord, } impl Default for BoundingBox { fn default() -> Self { Self { - total_height: 0, - total_width: 0, - relative_x: 0, - relative_y: 0, + total_height: 0., + total_width: 0., + relative_x: 0., + relative_y: 0., } } } @@ -36,11 +38,11 @@ impl Layout for BasicLayout { type Meta = BoundingBox; fn layout(&mut self, root: &mut Node) { root.post_order_traversal_mut(|node| { - self.update_children_meta(node); + self.update_meta(node); }); root.pre_order_traversal_mut(|node| { - if let Some(mut parent) = node.parent { - let parent = unsafe { parent.as_mut() }; + if let Some(parent) = node.parent { + let parent = unsafe { parent.as_ref() }; node.x = parent.x + node.meta.relative_x; node.y = parent.y + node.meta.relative_y; } @@ -57,29 +59,29 @@ impl Layout for BasicLayout { } impl BasicLayout { - fn update_children_meta(&mut self, node: &mut Node) { + fn update_meta(&mut self, node: &mut Node) { node.meta = BoundingBox { total_height: node.height, total_width: node.width, - relative_x: 0, - relative_y: 0, + relative_x: 0., + relative_y: 0., }; let children: *mut _ = &mut node.children; let children = unsafe { &mut *children }; - let n = children.len() as isize; - if n > 0 { - let mut total_width: isize = 0; + let n = children.len() as Coord; + if n > 0. { + let mut total_width: Coord = 0.; for child in children.iter() { total_width += child.meta.total_width; } - total_width += (n - 1) * self.peer_margin; - let mut relative_x = -total_width / 2; - let mut max_height = 0; + total_width += (n - 1.) * self.peer_margin; + let mut relative_x = -total_width / 2.; + let mut max_height = 0.; for child in children.iter_mut() { child.meta.relative_y = node.height + self.parent_child_margin; - child.meta.relative_x = relative_x + child.meta.total_width / 2; + child.meta.relative_x = relative_x + child.meta.total_width / 2.; relative_x += child.meta.total_width + self.peer_margin; - max_height = max(child.meta.total_height, max_height); + max_height = Float::max(child.meta.total_height, max_height); } node.meta.total_width = total_width; @@ -95,15 +97,15 @@ mod basic_layout_test { #[test] fn easy_test_0() { - let mut root = Node::::new(10, 10); - root.append_child(Node::new(10, 10)); - let mut second = Node::new(10, 10); - second.append_child(Node::new(10, 10)); + let mut root = Node::::new(10., 10.); + root.append_child(Node::new(10., 10.)); + let mut second = Node::new(10., 10.); + second.append_child(Node::new(10., 10.)); root.append_child(second); - root.append_child(Node::new(10, 10)); + root.append_child(Node::new(10., 10.)); let mut layout = BasicLayout { - parent_child_margin: 10, - peer_margin: 5, + parent_child_margin: 10., + peer_margin: 5., }; layout.layout(&mut root); println!("{:#?}", root); diff --git a/rust/crates/tidy-tree/src/node.rs b/rust/crates/tidy-tree/src/node.rs index 620bb04..5dbc140 100644 --- a/rust/crates/tidy-tree/src/node.rs +++ b/rust/crates/tidy-tree/src/node.rs @@ -1,11 +1,13 @@ use std::ptr::NonNull; +use crate::geometry::Coord; + #[derive(Debug, Clone)] pub struct Node { - pub width: isize, - pub height: isize, - pub x: isize, - pub y: isize, + pub width: Coord, + pub height: Coord, + pub x: Coord, + pub y: Coord, pub meta: Meta, pub parent: Option>>, /// Children need boxing to get a stable addr in the heap @@ -15,10 +17,10 @@ pub struct Node { impl Default for Node { fn default() -> Self { Self { - width: 0, - height: 0, - x: 0, - y: 0, + width: 0., + height: 0., + x: 0., + y: 0., children: vec![], parent: None, meta: Default::default(), @@ -27,13 +29,13 @@ impl Default for Node { } impl Node { - pub fn new(width: isize, height: isize) -> Self { + pub fn new(width: Coord, height: Coord) -> Self { Node { width, height, meta: Default::default(), - x: 0, - y: 0, + x: 0., + y: 0., children: vec![], parent: None, } diff --git a/rust/crates/tidy-tree/tests/aesthetic_rules.rs b/rust/crates/tidy-tree/tests/aesthetic_rules.rs index be43274..0b55339 100644 --- a/rust/crates/tidy-tree/tests/aesthetic_rules.rs +++ b/rust/crates/tidy-tree/tests/aesthetic_rules.rs @@ -74,18 +74,16 @@ pub fn assert_no_crossed_lines(root: &Node) { pub fn assert_symmetric(root: &Node, layout: &mut dyn Layout) { let mut mirrored = mirror(root); layout.layout(&mut mirrored); - let mut point_origin: Vec = vec![]; - let mut point_mirrored: Vec = vec![]; + let mut point_origin: Vec = vec![]; + let mut point_mirrored: Vec = vec![]; root.pre_order_traversal(|node| { if let Some(parent) = node.parent { - let parent = unsafe { parent.as_ref() }; - point_origin.push(node.x - parent.x); + point_origin.push(node.x); } }); pre_order_traversal_rev(&mirrored, |node| { if let Some(parent) = node.parent { - let parent = unsafe { parent.as_ref() }; - point_mirrored.push(-node.x + parent.x); + point_mirrored.push(node.x); } }); // println!("{:#?}", root); @@ -94,10 +92,10 @@ pub fn assert_symmetric(root: &Node, layout: &mut dyn Layou assert_eq!(point_origin.len(), point_mirrored.len()); for i in 0..point_origin.len() { assert!( - (point_origin[i] - point_mirrored[i]).abs() <= 1, + (point_origin[i] + point_mirrored[i]).abs() <= 1e-6, "{} != {}", point_origin[i], - point_mirrored[i] + -point_mirrored[i] ) } @@ -123,6 +121,11 @@ fn mirror(root: &Node) -> Node { for i in 0..n / 2 { node.children.swap(i, n - i - 1); } + + let node_ptr = node.into(); + for child in node.children.iter_mut() { + child.parent = Some(node_ptr); + } }); root } diff --git a/rust/crates/tidy-tree/tests/layout_test.rs b/rust/crates/tidy-tree/tests/layout_test.rs index ef42ccd..1b126fd 100644 --- a/rust/crates/tidy-tree/tests/layout_test.rs +++ b/rust/crates/tidy-tree/tests/layout_test.rs @@ -2,7 +2,7 @@ use std::{fmt::Debug, ptr::NonNull}; mod aesthetic_rules; use rand::prelude::*; -use tidy_tree::{BasicLayout, Layout, Node}; +use tidy_tree::{geometry::Coord, BasicLayout, Layout, Node}; pub fn test_layout(layout: &mut dyn Layout) { let mut rng = StdRng::seed_from_u64(101); @@ -31,10 +31,10 @@ pub fn gen_tree(rng: &mut StdRng, num: usize) -> Node { fn gen_node(rng: &mut StdRng) -> Node { Node { - width: 10, - height: rng.gen_range(1..10), - x: 0, - y: 0, + width: rng.gen_range(1..10) as Coord, + height: rng.gen_range(1..10) as Coord, + x: 0., + y: 0., meta: Default::default(), parent: None, children: vec![], @@ -44,8 +44,8 @@ fn gen_node(rng: &mut StdRng) -> Node { #[test] fn test_basic_layout() { let mut layout = BasicLayout { - parent_child_margin: 10, - peer_margin: 10, + parent_child_margin: 10., + peer_margin: 10., }; test_layout(&mut layout); }