mod block;
mod compute;
mod engine;
mod flex;
mod grid;
mod node;
mod position;
pub mod responsive;
mod tree;
pub use engine::{LayoutEngine, LayoutError, LayoutResult};
pub use responsive::{
breakpoints, container_max_width, container_min_width, max_width, min_width,
responsive as responsive_value, responsive_layout, Breakpoint, Breakpoints, ContainerQuery,
MediaQuery, ResponsiveContainer, ResponsiveLayout, ResponsiveValue,
};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Rect {
pub x: u16,
pub y: u16,
pub width: u16,
pub height: u16,
}
impl Rect {
pub fn new(x: u16, y: u16, width: u16, height: u16) -> Self {
Self {
x,
y,
width,
height,
}
}
pub fn contains(&self, x: u16, y: u16) -> bool {
x >= self.x && x < self.right() && y >= self.y && y < self.bottom()
}
pub fn right(&self) -> u16 {
self.x.saturating_add(self.width)
}
pub fn bottom(&self) -> u16 {
self.y.saturating_add(self.height)
}
pub fn intersects(&self, other: &Rect) -> bool {
self.x < other.right()
&& self.right() > other.x
&& self.y < other.bottom()
&& self.bottom() > other.y
}
pub fn intersection(&self, other: &Rect) -> Option<Rect> {
if !self.intersects(other) {
return None;
}
let x = self.x.max(other.x);
let y = self.y.max(other.y);
let right = self.right().min(other.right());
let bottom = self.bottom().min(other.bottom());
Some(Rect {
x,
y,
width: right - x,
height: bottom - y,
})
}
pub fn union(&self, other: &Rect) -> Rect {
let x = self.x.min(other.x);
let y = self.y.min(other.y);
let right = self.right().max(other.right());
let bottom = self.bottom().max(other.bottom());
Rect {
x,
y,
width: right.saturating_sub(x),
height: bottom.saturating_sub(y),
}
}
}
pub fn merge_rects(rects: &[Rect]) -> Vec<Rect> {
if rects.is_empty() {
return Vec::new();
}
let mut merged = Vec::new();
let mut remaining: Vec<Rect> = rects.to_vec();
while !remaining.is_empty() {
let mut current = remaining.remove(0);
let mut changed = true;
while changed {
changed = false;
let mut i = 0;
while i < remaining.len() {
if current.intersects(&remaining[i]) {
current = current.union(&remaining[i]);
remaining.remove(i);
changed = true;
} else {
i += 1;
}
}
}
merged.push(current);
}
merged
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rect_new() {
let rect = Rect::new(10, 20, 30, 40);
assert_eq!(rect.x, 10);
assert_eq!(rect.y, 20);
assert_eq!(rect.width, 30);
assert_eq!(rect.height, 40);
}
#[test]
fn test_rect_default() {
let rect = Rect::default();
assert_eq!(rect.x, 0);
assert_eq!(rect.y, 0);
assert_eq!(rect.width, 0);
assert_eq!(rect.height, 0);
}
#[test]
fn test_rect_contains_inside() {
let rect = Rect::new(10, 10, 50, 50);
assert!(rect.contains(20, 20));
assert!(rect.contains(10, 10));
}
#[test]
fn test_rect_contains_outside() {
let rect = Rect::new(10, 10, 50, 50);
assert!(!rect.contains(5, 20));
assert!(!rect.contains(20, 5));
assert!(!rect.contains(70, 20));
assert!(!rect.contains(20, 70));
}
#[test]
fn test_rect_contains_on_edges() {
let rect = Rect::new(10, 10, 50, 50);
assert!(!rect.contains(60, 30));
assert!(!rect.contains(30, 60));
}
#[test]
fn test_rect_right() {
let rect = Rect::new(10, 20, 30, 40);
assert_eq!(rect.right(), 40);
}
#[test]
fn test_rect_bottom() {
let rect = Rect::new(10, 20, 30, 40);
assert_eq!(rect.bottom(), 60);
}
#[test]
fn test_rect_intersects_true() {
let rect1 = Rect::new(0, 0, 50, 50);
let rect2 = Rect::new(25, 25, 50, 50);
assert!(rect1.intersects(&rect2));
assert!(rect2.intersects(&rect1));
}
#[test]
fn test_rect_intersects_false() {
let rect1 = Rect::new(0, 0, 50, 50);
let rect2 = Rect::new(60, 60, 50, 50);
assert!(!rect1.intersects(&rect2));
assert!(!rect2.intersects(&rect1));
}
#[test]
fn test_rect_intersects_adjacent() {
let rect1 = Rect::new(0, 0, 50, 50);
let rect2 = Rect::new(50, 0, 50, 50);
assert!(!rect1.intersects(&rect2));
}
#[test]
fn test_rect_intersection_some() {
let rect1 = Rect::new(0, 0, 50, 50);
let rect2 = Rect::new(25, 25, 50, 50);
let result = rect1.intersection(&rect2);
assert!(result.is_some());
let intersection = result.unwrap();
assert_eq!(intersection.x, 25);
assert_eq!(intersection.y, 25);
assert_eq!(intersection.width, 25);
assert_eq!(intersection.height, 25);
}
#[test]
fn test_rect_intersection_none() {
let rect1 = Rect::new(0, 0, 50, 50);
let rect2 = Rect::new(60, 60, 50, 50);
let result = rect1.intersection(&rect2);
assert!(result.is_none());
}
#[test]
fn test_rect_union() {
let rect1 = Rect::new(0, 0, 50, 50);
let rect2 = Rect::new(25, 25, 50, 50);
let result = rect1.union(&rect2);
assert_eq!(result.x, 0);
assert_eq!(result.y, 0);
assert_eq!(result.width, 75);
assert_eq!(result.height, 75);
}
#[test]
fn test_rect_union_non_overlapping() {
let rect1 = Rect::new(0, 0, 10, 10);
let rect2 = Rect::new(100, 100, 10, 10);
let result = rect1.union(&rect2);
assert_eq!(result.x, 0);
assert_eq!(result.y, 0);
assert_eq!(result.width, 110);
assert_eq!(result.height, 110);
}
#[test]
fn test_merge_rects_empty() {
let result = merge_rects(&[]);
assert!(result.is_empty());
}
#[test]
fn test_merge_rects_single() {
let rects = vec![Rect::new(0, 0, 10, 10)];
let result = merge_rects(&rects);
assert_eq!(result.len(), 1);
}
#[test]
fn test_merge_rects_overlapping() {
let rects = vec![Rect::new(0, 0, 50, 50), Rect::new(25, 25, 50, 50)];
let result = merge_rects(&rects);
assert_eq!(result.len(), 1);
assert_eq!(result[0].width, 75);
assert_eq!(result[0].height, 75);
}
#[test]
fn test_merge_rects_non_overlapping() {
let rects = vec![Rect::new(0, 0, 10, 10), Rect::new(100, 100, 10, 10)];
let result = merge_rects(&rects);
assert_eq!(result.len(), 2);
}
#[test]
fn test_merge_rects_chain() {
let rects = vec![
Rect::new(0, 0, 20, 20),
Rect::new(15, 0, 20, 20),
Rect::new(30, 0, 20, 20),
];
let result = merge_rects(&rects);
assert_eq!(result.len(), 1);
assert_eq!(result[0].width, 50);
}
}