use approximate partitioning for binary search

This commit is contained in:
SpaceCat-Chan 2022-12-06 07:38:01 +01:00 committed by Lofty
parent 0bdadde9c0
commit 3d2635f379

View File

@ -124,37 +124,23 @@ pub fn find_partition_point(
let mut x_diff = (x_finish - x_start) / 4;
let mut y_diff = (y_finish - y_start) / 4;
let mut ne;
let mut se;
let mut sw;
let mut nw;
while x_diff != 0 {
(ne, se, sw, nw) = partition(
ctx,
nets,
arcs,
pips,
x,
y,
(x_start, x_finish),
(y_start, y_finish),
);
let north = ne.len() + nw.len();
let south = se.len() + sw.len();
let (ne, se, sw, nw) = approximate_partition_results(arcs, (x, y));
let north = ne + nw;
let south = se + sw;
let nets = (north + south) as f64;
let ne_dist = f64::abs(((ne.len() as f64) / nets) - 0.25);
let se_dist = f64::abs(((se.len() as f64) / nets) - 0.25);
let sw_dist = f64::abs(((sw.len() as f64) / nets) - 0.25);
let nw_dist = f64::abs(((nw.len() as f64) / nets) - 0.25);
let ne_dist = f64::abs(((ne as f64) / nets) - 0.25);
let se_dist = f64::abs(((se as f64) / nets) - 0.25);
let sw_dist = f64::abs(((sw as f64) / nets) - 0.25);
let nw_dist = f64::abs(((nw as f64) / nets) - 0.25);
let distortion = 100.0 * (ne_dist + se_dist + sw_dist + nw_dist);
// Stop early if Good Enough.
if distortion <= 5.0 {
return (x, y, ne, se, sw, nw);
break;
}
x += match north.cmp(&south) {
@ -163,8 +149,8 @@ pub fn find_partition_point(
std::cmp::Ordering::Greater => -x_diff,
};
let east = ne.len() + se.len();
let west = nw.len() + sw.len();
let east = ne + se;
let west = nw + sw;
y += match east.cmp(&west) {
std::cmp::Ordering::Less => y_diff,
std::cmp::Ordering::Equal => 0,
@ -175,7 +161,7 @@ pub fn find_partition_point(
y_diff >>= 1;
}
(ne, se, sw, nw) = partition(
let (ne, se, sw, nw) = partition(
ctx,
nets,
arcs,
@ -203,6 +189,123 @@ pub fn find_partition_point(
(x, y, ne, se, sw, nw)
}
fn approximate_partition_results(
arcs: &[Arc],
partition_point: (i32, i32),
) -> (usize, usize, usize, usize) {
let mut count_ne = 0;
let mut count_se = 0;
let mut count_sw = 0;
let mut count_nw = 0;
for arc in arcs {
// TODO(SpaceCat~Chan): stop being lazy and merge Loc and Coord already
let source_is_north = arc.get_source_loc().x < partition_point.0;
let source_is_east = arc.get_source_loc().y < partition_point.1;
let sink_is_north = arc.get_sink_loc().x < partition_point.0;
let sink_is_east = arc.get_sink_loc().y < partition_point.1;
if source_is_north == sink_is_north && source_is_east == sink_is_east {
match (source_is_north, source_is_east) {
(true, true) => count_ne += 1,
(false, true) => count_se += 1,
(false, false) => count_sw += 1,
(true, false) => count_nw += 1,
}
} else if source_is_north != sink_is_north && source_is_east == sink_is_east {
if source_is_east {
count_ne += 1;
count_se += 1;
} else {
count_nw += 1;
count_sw += 1;
}
} else if source_is_north == sink_is_north {
if source_is_north {
count_ne += 1;
count_nw += 1;
} else {
count_se += 1;
count_sw += 1;
}
} else {
// all of this calculation is not be needed and an approximation would be good enough
// but i can't be bothered (yes this is all copy-pasted from the actual partitioner)
let mut middle_horiz = (
partition_point.0,
split_line_over_x(
(arc.get_source_loc(), arc.get_sink_loc()),
partition_point.0,
),
);
let mut middle_vert = (
split_line_over_y(
(arc.get_source_loc(), arc.get_sink_loc()),
partition_point.1,
),
partition_point.1,
);
// need to avoid the partition point
if middle_horiz.1 == partition_point.1 || middle_vert.0 == partition_point.0 {
if source_is_east != sink_is_north {
middle_horiz.1 = partition_point.1 + 1;
middle_vert.0 = partition_point.0 - 1;
} else {
middle_horiz.1 = partition_point.1 + 1;
middle_vert.0 = partition_point.0 + 1;
}
}
let horiz_happens_first = (middle_horiz.1 < partition_point.1) == source_is_east;
// note: if you invert all the bools it adds to the same things, not sure how make less redundant
match (source_is_north, source_is_east, horiz_happens_first) {
(true, true, true) => {
count_ne += 1;
count_se += 1;
count_sw += 1;
}
(true, false, true) => {
count_nw += 1;
count_se += 1;
count_sw += 1;
}
(false, true, true) => {
count_ne += 1;
count_nw += 1;
count_se += 1;
}
(false, false, true) => {
count_ne += 1;
count_nw += 1;
count_sw += 1;
}
(true, true, false) => {
count_ne += 1;
count_nw += 1;
count_sw += 1;
}
(true, false, false) => {
count_ne += 1;
count_nw += 1;
count_se += 1;
}
(false, true, false) => {
count_nw += 1;
count_se += 1;
count_sw += 1;
}
(false, false, false) => {
count_ne += 1;
count_se += 1;
count_sw += 1;
}
}
}
}
(count_ne, count_se, count_sw, count_nw)
}
/// finds the y location a line would be split at if you split it at a certain x location
///
/// the function assumes the line goes on forever in both directions, and it truncates the actual coordinate