Files
photos/frontend/src/gallery/layout.rs
2023-02-23 18:28:12 +01:00

97 lines
2.7 KiB
Rust

use pathfinding::prelude::dijkstra;
use weblog::console_log;
pub struct Rect {
pub width: u32,
pub height: u32,
}
pub fn get_common_height(row: &[Rect], container_width: u32, margin: u32) -> f32 {
debug_assert!(container_width > (row.len() as u32) * (margin * 2));
let row_width: u32 = container_width - (row.len() as u32) * (margin * 2);
let total_aspect_ratio: f32 = row
.iter()
.map(|p| (p.width as f32) / (p.height as f32))
.sum();
row_width as f32 / total_aspect_ratio
}
pub fn cost(
photos: &[Rect],
i: usize,
j: usize,
width: u32,
target_height: u32,
margin: u32,
) -> u32 {
let common_height = get_common_height(&photos[i..j], width, margin);
(common_height - target_height as f32).powi(2) as u32
}
pub fn make_successors(
target_height: u32,
container_width: u32,
photos: &Vec<Rect>,
node_search_limit: usize,
margin: u32,
) -> Vec<Vec<(usize, u32)>> {
let mut results = vec![Vec::new(); photos.len()];
(0..photos.len()).for_each(|start| {
for j in start + 1..photos.len() + 1 {
if j - start > node_search_limit {
break;
}
results[start].push((
j,
cost(photos, start, j, container_width, target_height, margin),
));
}
});
results
}
pub fn calc_node_search_limit(target_row_height: u32, container_width: u32) -> usize {
let row_aspect_ratio = container_width as f32 / target_row_height as f32;
(row_aspect_ratio / 1.5) as usize + 8
}
pub fn compute_row_layout(
container_width: u32,
target_height: u32,
margin: u32,
photos: &Vec<Rect>,
) -> Option<Vec<Rect>> {
if photos.is_empty() {
return None;
}
let node_search_limit = calc_node_search_limit(target_height, container_width);
let successors = make_successors(
target_height,
container_width,
photos,
node_search_limit,
margin,
);
let path = dijkstra(&0, |p| successors[*p].clone(), |p| *p == photos.len());
let (path, _cost) = if let Some(p) = path {
p
} else {
(Vec::new(), 0)
};
let mut dimensions: Vec<Rect> = Vec::with_capacity(photos.len());
for i in 1..path.len() {
let row = &photos[path[i - 1]..path[i]];
let height = get_common_height(row, container_width, margin) as u32;
(path[i - 1]..path[i]).for_each(|j| {
let ratio = photos[j].width as f32 / photos[j].height as f32;
dimensions.push(Rect {
width: (height as f32 * ratio) as u32,
height,
});
});
}
Some(dimensions)
}