fix(experiment): score occupied perimeter cells including one-column cycles
This commit is contained in:
@@ -54,23 +54,49 @@ fn components(arcs: &[BTreeSet<usize>], work: &mut usize) -> Result<Vec<Vec<usiz
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// Clockwise perimeter slots. Restrict to near-square physical envelopes when
|
||||
// possible; minimizing perimeter area without this restriction makes a strip.
|
||||
fn perimeter_slots(n: usize, rows: usize, cols: usize) -> Vec<(usize, usize)> {
|
||||
let mut slots = Vec::new();
|
||||
if rows == 1 {
|
||||
slots.extend((0..cols).map(|col| (col, 0)));
|
||||
} else if cols == 1 {
|
||||
slots.extend((0..rows).map(|row| (0, row)));
|
||||
} else {
|
||||
slots.extend((0..cols).map(|col| (col, 0)));
|
||||
slots.extend((1..rows).map(|row| (cols - 1, row)));
|
||||
slots.extend((0..cols - 1).rev().map(|col| (col, rows - 1)));
|
||||
slots.extend((1..rows - 1).rev().map(|row| (0, row)));
|
||||
}
|
||||
slots.truncate(n);
|
||||
slots
|
||||
}
|
||||
|
||||
// Score occupied maximum-member cells, excluding unused perimeter slots.
|
||||
// Heterogeneous members may occupy less than this nominal cell envelope.
|
||||
fn perimeter(
|
||||
n: usize,
|
||||
cell: Size,
|
||||
gap: Size,
|
||||
work: &mut usize,
|
||||
) -> Result<(Vec<Position>, usize, usize), ModelError> {
|
||||
let mut best: Option<((bool, f32, f32, usize, usize), usize, usize)> = None;
|
||||
for rows in 2..=n.max(2) {
|
||||
for cols in 2..=n.max(2) {
|
||||
type GridScore = (bool, f32, f32, usize, usize);
|
||||
let mut best: Option<(GridScore, usize, usize)> = None;
|
||||
for rows in 1..=n {
|
||||
for cols in 1..=n {
|
||||
charge(work, 1)?;
|
||||
if 2 * rows + 2 * cols - 4 < n {
|
||||
let capacity = if rows == 1 || cols == 1 {
|
||||
rows * cols
|
||||
} else {
|
||||
2 * rows + 2 * cols - 4
|
||||
};
|
||||
if capacity < n {
|
||||
continue;
|
||||
}
|
||||
let width = cols as f32 * cell.width + (cols - 1) as f32 * gap.width;
|
||||
let height = rows as f32 * cell.height + (rows - 1) as f32 * gap.height;
|
||||
charge(work, capacity)?;
|
||||
let slots = perimeter_slots(n, rows, cols);
|
||||
let max_x = slots.iter().map(|p| p.0).max().unwrap();
|
||||
let max_y = slots.iter().map(|p| p.1).max().unwrap();
|
||||
let width = (max_x + 1) as f32 * cell.width + max_x as f32 * gap.width;
|
||||
let height = (max_y + 1) as f32 * cell.height + max_y as f32 * gap.height;
|
||||
let aspect = (width / height).max(height / width);
|
||||
let key = (
|
||||
aspect > 2.0,
|
||||
@@ -89,19 +115,7 @@ fn perimeter(
|
||||
}
|
||||
}
|
||||
let (_, rows, cols) = best.ok_or_else(|| error("no perimeter grid"))?;
|
||||
let mut slots = Vec::new();
|
||||
for col in 0..cols {
|
||||
slots.push((col, 0));
|
||||
}
|
||||
for row in 1..rows {
|
||||
slots.push((cols - 1, row));
|
||||
}
|
||||
for col in (0..cols - 1).rev() {
|
||||
slots.push((col, rows - 1));
|
||||
}
|
||||
for row in (1..rows - 1).rev() {
|
||||
slots.push((0, row));
|
||||
}
|
||||
let slots = perimeter_slots(n, rows, cols);
|
||||
Ok((
|
||||
slots
|
||||
.into_iter()
|
||||
@@ -548,4 +562,27 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_node_perimeter_scores_occupied_cells_instead_of_an_unused_row() {
|
||||
let (positions, rows, cols) = perimeter(
|
||||
2,
|
||||
Size {
|
||||
width: 224.0,
|
||||
height: 160.0,
|
||||
},
|
||||
Size {
|
||||
width: 250.0,
|
||||
height: 56.0,
|
||||
},
|
||||
&mut 0,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!((rows, cols), (2, 1));
|
||||
assert_eq!(
|
||||
positions,
|
||||
vec![Position { x: 0.0, y: 0.0 }, Position { x: 0.0, y: 216.0 }]
|
||||
);
|
||||
assert!(224.0_f32 / 376.0 > 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user