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, node_search_limit: usize, margin: u32, ) -> Vec> { 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, ) -> Option> { 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 = 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) }