Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
250 changes: 250 additions & 0 deletions src/aabb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
use crate::components::grid::core::item::GridItemData;

Check failure on line 1 in src/aabb.rs

View workflow job for this annotation

GitHub Actions / cargo clippy

failed to resolve: could not find `core` in `grid`

pub trait Aabb {
fn collides_with(&self, other: &Self) -> bool;

fn collides_with_optional(&self, other: Option<&Self>) -> bool {
other.map_or(false, |other| self.collides_with(other))
}
}

impl Aabb for GridItemData {
fn collides_with(&self, other: &Self) -> bool {
!(self.max_x() <= other.min_x()
|| self.min_x() >= other.max_x()
|| self.max_y() <= other.min_y()
|| self.min_y() >= other.max_y())
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::components::grid::core::{item::GridItemPosition, size::Size, span::Span};

Check failure on line 23 in src/aabb.rs

View workflow job for this annotation

GitHub Actions / cargo clippy

failed to resolve: could not find `core` in `grid`

/// Test AABB collision detection on the **x-axis**.
///
/// # Starting layout
/// ```
/// (1,1) -> (2,1)
/// ================|===============
/// item1 | item2
/// ================|===============
/// ```
/// # Item1 collides in item 2
///
/// ```
/// (1,1) -> (2,1) item1
/// ================|===============
/// | item1, item2
/// ================|===============
///```
///
/// # Item2 collides in item 1
///
/// ```
/// (1,1) <- (2,1)
/// ================|===============
/// item1, item2 |
/// ================|===============
/// ```
///
#[test]
fn test_aabb_collision_on_x_axis() {
let size = Size {
width: 100.0,
height: 100.0,
};
let span = Span {
col_span: 1,
row_span: 1,
};

let mut item1 = GridItemData {
id: 1,
position: GridItemPosition {
col_start: 1,
row_start: 1,
},
span,
size,
};

let mut item2 = GridItemData {
id: 2,
position: GridItemPosition {
col_start: 2,
row_start: 1,
},
span,
size,
};

assert!(
!item1.collides_with(&item2),
"Item1 should not collide with Item2"
);
assert!(
!item2.collides_with(&item1),
"Item2 should not collide with Item1"
);

item2.position.col_start = 1;

assert!(
item1.collides_with(&item2),
"Item1 should collide with Item2"
);
assert!(
item2.collides_with(&item1),
"Item2 should collide with Item1"
);

item2.position.col_start = 2;
item1.position.col_start = 2;

assert!(
item1.collides_with(&item2),
"Item1 should collide with Item2"
);
assert!(
item2.collides_with(&item1),
"Item2 should collide with Item1"
);
}

/// Test AABB collision detection on the **y-axis**.
///
/// # Starting layout
/// ```
/// (1,1)
/// ================
/// item1
/// ================
/// (1,2)
/// ================
/// item2
/// ================
/// ```
/// # Item1 collides in item 2
/// ```
/// (1,2)
/// ================
/// item1, item2
/// ================
/// ```
/// # Item2 collides in item 1
/// ```
/// (1,1)
/// ================
/// item1, item2
/// ================
/// ```
#[test]
fn test_aabb_collision_on_y_axis() {
let size = Size {
width: 100.0,
height: 100.0,
};
let span = Span {
col_span: 1,
row_span: 1,
};

let mut item1 = GridItemData {
id: 1,
position: GridItemPosition {
col_start: 1,
row_start: 1,
},
span,
size,
};

let mut item2 = GridItemData {
id: 2,
position: GridItemPosition {
col_start: 1,
row_start: 2,
},
span,
size,
};

assert!(
!item1.collides_with(&item2),
"Item1 should not collide with Item2"
);
assert!(
!item2.collides_with(&item1),
"Item2 should not collide with Item1"
);

item2.position.row_start = 1;

assert!(
item1.collides_with(&item2),
"Item1 should collide with Item2"
);
assert!(
item2.collides_with(&item1),
"Item2 should collide with Item1"
);

item2.position.row_start = 2;
item1.position.row_start = 2;

assert!(
item1.collides_with(&item2),
"Item1 should collide with Item2"
);
assert!(
item2.collides_with(&item1),
"Item2 should collide with Item1"
);
}

#[test]
fn test_aabb_collides_with_optional() {
let size = Size {
width: 100.0,
height: 100.0,
};
let span = Span {
col_span: 1,
row_span: 1,
};

let item1 = GridItemData {
id: 1,
position: GridItemPosition {
col_start: 1,
row_start: 1,
},
span,
size,
};

let item2 = GridItemData {
id: 2,
position: GridItemPosition {
col_start: 2,
row_start: 1,
},
span,
size,
};

// Test with Some(other)
assert!(
!item1.collides_with_optional(Some(&item2)),
"Item1 should not collide with Item2"
);

// Test with None
assert!(
!item1.collides_with_optional(None),
"Item1 should not collide with None"
);
}
}
59 changes: 59 additions & 0 deletions src/components/grid/core/layout.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use crate::components::grid::core::{item::GridItemData, size::Size};
use leptos::prelude::RwSignal;
use std::collections::HashMap;

#[derive(Clone, Debug, Default)]
pub struct Layout {
pub size: Size,
// TODO: see for detaching this global reactive state from the layout config
// And use signals in the config so we can listen on different events
pub items: HashMap<u32, RwSignal<GridItemData>>,
pub columns: u32,
pub cell_size: Size,
}

impl Layout {
pub fn add_item(&mut self, id: u32, data: RwSignal<GridItemData>) {
self.items.insert(id, data);
}

pub fn update_items_size(&mut self, (w_ratio, h_ratio): (f64, f64)) {
// we used to update the grid items size here, so we can prepare the
// collision detection and re-layout the items. But let's skip thos for
// now since we are not using that feature yet.
}
}

#[derive(Clone, Debug, Default)]
pub struct LayoutBuilder {
pub size: Size,
pub items: HashMap<u32, RwSignal<GridItemData>>,
pub columns: u32,
pub cell_size: Size,
}

impl LayoutBuilder {
pub fn columns(mut self, quantity: u32) -> Self {
self.columns = quantity;
self
}

pub fn size(mut self, width: f64, height: f64) -> Self {
self.size = Size { width, height };
self
}

pub fn cell_size(mut self, width: f64, height: f64) -> Self {
self.cell_size = Size { width, height };
self
}

pub fn build(self) -> Layout {
Layout {
size: self.size,
items: self.items,
columns: self.columns,
cell_size: self.cell_size,
}
}
}
5 changes: 4 additions & 1 deletion src/components/grid/core/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
pub mod aabb;
pub mod item;
pub mod layout;
pub mod size;
pub mod span;
5 changes: 5 additions & 0 deletions src/components/grid/grid_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,16 @@ pub fn GridItem(
height: cell_h,
} = layout.get_untracked().cell_size;
let item_data = GridItemData {
id,
position: GridItemPosition {
col_start,
row_start,
},
span: Span { row_span, col_span },
size: Size {
width: col_span as f64 * cell_w,
height: row_span as f64 * cell_h,
},
};
log!(
"col_span: {col_span} and cell_w: {cell_w}. Computed width: {}",
Expand Down
Loading
Loading