Differentiate between growing and shrinking segments

Also uses the new with_* naming scheme for segments
This commit is contained in:
Joscha 2023-02-20 16:14:24 +01:00
parent 607c11fea4
commit 417f33cc24

View file

@ -55,7 +55,8 @@ use super::{Either2, Either3, Either4, Either5, Either6, Either7};
struct Segment { struct Segment {
size: u16, size: u16,
weight: f32, weight: f32,
fixed: bool, growing: bool,
shrinking: bool,
} }
impl Segment { impl Segment {
@ -63,7 +64,8 @@ impl Segment {
Self { Self {
size: size.width, size: size.width,
weight: segment.weight, weight: segment.weight,
fixed: segment.fixed, growing: segment.growing,
shrinking: segment.shrinking,
} }
} }
@ -71,7 +73,8 @@ impl Segment {
Self { Self {
size: size.height, size: size.height,
weight: segment.weight, weight: segment.weight,
fixed: segment.fixed, growing: segment.growing,
shrinking: segment.shrinking,
} }
} }
} }
@ -88,31 +91,26 @@ fn total_weight(segments: &[&mut Segment]) -> f32 {
segments.iter().map(|s| s.weight).sum() segments.iter().map(|s| s.weight).sum()
} }
fn balance(segments: &mut [Segment], mut available: u16) { fn balance(segments: &mut [Segment], available: u16) {
let mut borrowed_segments = segments.iter_mut().collect::<Vec<_>>(); let segments = segments.iter_mut().collect::<Vec<_>>();
match total_size(&segments).cmp(&available) {
// Remove fixed segments Ordering::Less => grow(segments, available),
borrowed_segments.retain(|s| { Ordering::Greater => shrink(segments, available),
if !s.fixed {
return true;
}
available = available.saturating_sub(s.size);
false
});
if borrowed_segments.is_empty() || available == 0 {
return;
}
match total_size(&borrowed_segments).cmp(&available) {
Ordering::Less => grow(borrowed_segments, available),
Ordering::Greater => shrink(borrowed_segments, available),
Ordering::Equal => {} Ordering::Equal => {}
} }
} }
fn grow(mut segments: Vec<&mut Segment>, mut available: u16) { fn grow(mut segments: Vec<&mut Segment>, mut available: u16) {
assert!(available > total_size(&segments)); assert!(available >= total_size(&segments));
// Only grow segments that can be grown.
segments.retain(|s| {
if s.growing {
return true;
}
available = available.saturating_sub(s.size);
false
});
// Repeatedly remove all segments that do not need to grow, i. e. that are // Repeatedly remove all segments that do not need to grow, i. e. that are
// at least as large as their allotment. // at least as large as their allotment.
@ -139,18 +137,17 @@ fn grow(mut segments: Vec<&mut Segment>, mut available: u16) {
}); });
available -= removed; available -= removed;
// If all segments were at least as large as their allotments, we would
// be trying to shrink, not grow them. Hence, there must be at least one
// segment that is smaller than its allotment.
assert!(!segments.is_empty());
if removed == 0 { if removed == 0 {
break; // All remaining segments are smaller than their allotments break; // All remaining segments are smaller than their allotments
} }
} }
// Size each remaining segment according to its allotment.
let total_weight = segments.iter().map(|s| s.weight).sum::<f32>(); let total_weight = segments.iter().map(|s| s.weight).sum::<f32>();
if total_weight <= 0.0 {
return; // No more segments left
}
// Size each remaining segment according to its allotment.
let mut used = 0; let mut used = 0;
for segment in &mut segments { for segment in &mut segments {
let allotment = segment.weight / total_weight * available as f32; let allotment = segment.weight / total_weight * available as f32;
@ -170,7 +167,16 @@ fn grow(mut segments: Vec<&mut Segment>, mut available: u16) {
} }
fn shrink(mut segments: Vec<&mut Segment>, mut available: u16) { fn shrink(mut segments: Vec<&mut Segment>, mut available: u16) {
assert!(available < total_size(&segments)); assert!(available <= total_size(&segments));
// Only shrink segments that can be shrunk.
segments.retain(|s| {
if s.shrinking {
return true;
}
available = available.saturating_sub(s.size);
false
});
// Repeatedly remove all segments that do not need to shrink, i. e. that are // Repeatedly remove all segments that do not need to shrink, i. e. that are
// at least as small as their allotment. // at least as small as their allotment.
@ -203,18 +209,17 @@ fn shrink(mut segments: Vec<&mut Segment>, mut available: u16) {
}); });
available -= removed; available -= removed;
// If all segments were smaller or the same size as their allotments, we
// would be trying to grow, not shrink them. Hence, there must be at
// least one segment bigger than its allotment.
assert!(!segments.is_empty());
if removed == 0 { if removed == 0 {
break; // All segments want more than their weight allows. break; // All segments want more than their weight allows.
} }
} }
// Size each remaining segment according to its allotment.
let total_weight = segments.iter().map(|s| s.weight).sum::<f32>(); let total_weight = segments.iter().map(|s| s.weight).sum::<f32>();
if total_weight <= 0.0 {
return; // No more segments left
}
// Size each remaining segment according to its allotment.
let mut used = 0; let mut used = 0;
for segment in &mut segments { for segment in &mut segments {
let allotment = segment.weight / total_weight * available as f32; let allotment = segment.weight / total_weight * available as f32;
@ -234,9 +239,10 @@ fn shrink(mut segments: Vec<&mut Segment>, mut available: u16) {
} }
pub struct JoinSegment<I> { pub struct JoinSegment<I> {
inner: I, pub inner: I,
weight: f32, weight: f32,
fixed: bool, pub growing: bool,
pub shrinking: bool,
} }
impl<I> JoinSegment<I> { impl<I> JoinSegment<I> {
@ -244,20 +250,38 @@ impl<I> JoinSegment<I> {
Self { Self {
inner, inner,
weight: 1.0, weight: 1.0,
fixed: false, growing: true,
shrinking: true,
} }
} }
pub fn weight(mut self, weight: f32) -> Self { pub fn weight(&self) -> f32 {
self.weight
}
pub fn set_weight(&mut self, weight: f32) {
assert!(weight >= 0.0); assert!(weight >= 0.0);
self.weight = weight; self.weight = weight;
}
pub fn with_weight(mut self, weight: f32) -> Self {
self.set_weight(weight);
self self
} }
pub fn fixed(mut self, fixed: bool) -> Self { pub fn with_growing(mut self, enabled: bool) -> Self {
self.fixed = fixed; self.growing = enabled;
self self
} }
pub fn with_shrinking(mut self, enabled: bool) -> Self {
self.shrinking = enabled;
self
}
pub fn with_fixed(self, fixed: bool) -> Self {
self.with_growing(!fixed).with_shrinking(!fixed)
}
} }
pub struct JoinH<I> { pub struct JoinH<I> {
@ -556,7 +580,8 @@ macro_rules! mk_join {
JoinSegment { JoinSegment {
inner: $either::$constr($arg.inner), inner: $either::$constr($arg.inner),
weight: $arg.weight, weight: $arg.weight,
fixed: $arg.fixed, growing: $arg.growing,
shrinking: $arg.shrinking,
}, },
)+ ])) )+ ]))
} }