use smallvec::SmallVec;
use std::fmt;
use std::iter::Sum;
use crate::geom::Size;
#[allow(unused)]
use kas::draw::SizeHandle;
#[derive(Copy, Clone, Debug, Default)]
pub struct Margins {
pub horiz: (u16, u16),
pub vert: (u16, u16),
}
impl Margins {
pub const ZERO: Margins = Margins::uniform(0);
#[inline]
pub const fn uniform(size: u16) -> Self {
Margins::hv_uniform(size, size)
}
#[inline]
pub const fn hv(horiz: (u16, u16), vert: (u16, u16)) -> Self {
Margins { horiz, vert }
}
#[inline]
pub const fn hv_uniform(h: u16, v: u16) -> Self {
Margins {
horiz: (h, h),
vert: (v, v),
}
}
pub fn pad(self, size: Size) -> Size {
let w = size.0 + (self.horiz.0 + self.horiz.1) as u32;
let h = size.1 + (self.vert.0 + self.vert.1) as u32;
Size(w, h)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum StretchPolicy {
Fixed,
Filler,
LowUtility,
HighUtility,
Maximise,
}
impl Default for StretchPolicy {
fn default() -> Self {
StretchPolicy::Fixed
}
}
#[derive(Copy, Clone, Default, PartialEq, Eq)]
pub struct SizeRules {
a: u32,
b: u32,
m: (u16, u16),
stretch: StretchPolicy,
}
impl fmt::Debug for SizeRules {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"SizeRules {{ a: {}, b: {}, m: ({}, {}), stretch: {:?} }}",
self.a, self.b, self.m.0, self.m.1, self.stretch
)
}
}
impl SizeRules {
pub const EMPTY: Self = SizeRules::empty(StretchPolicy::Fixed);
#[inline]
pub const fn empty(stretch: StretchPolicy) -> Self {
SizeRules {
a: 0,
b: 0,
m: (0, 0),
stretch,
}
}
#[inline]
pub fn fixed(size: u32, margins: (u16, u16)) -> Self {
SizeRules {
a: size,
b: size,
m: margins,
stretch: StretchPolicy::Fixed,
}
}
#[inline]
pub fn extract_fixed(vertical: bool, size: Size, margin: Margins) -> Self {
if !vertical {
SizeRules {
a: size.0,
b: size.0,
m: margin.horiz,
stretch: StretchPolicy::Fixed,
}
} else {
SizeRules {
a: size.1,
b: size.1,
m: margin.vert,
stretch: StretchPolicy::Fixed,
}
}
}
#[inline]
pub fn new(min: u32, ideal: u32, margins: (u16, u16), stretch: StretchPolicy) -> Self {
SizeRules {
a: min,
b: ideal.max(min),
m: margins,
stretch,
}
}
#[inline]
pub fn min_size(self) -> u32 {
self.a
}
#[inline]
pub fn ideal_size(self) -> u32 {
self.b
}
#[inline]
pub fn margins(self) -> (u16, u16) {
self.m
}
pub fn include_margins(&mut self, margins: (u16, u16)) {
self.m.0 = self.m.0.max(margins.0);
self.m.1 = self.m.1.max(margins.1);
}
#[inline]
pub fn max(self, rhs: Self) -> SizeRules {
SizeRules {
a: self.a.max(rhs.a),
b: self.b.max(rhs.b),
m: (self.m.0.max(rhs.m.0), self.m.1.max(rhs.m.1)),
stretch: self.stretch.max(rhs.stretch),
}
}
#[inline]
pub fn max_with(&mut self, rhs: Self) {
*self = self.max(rhs);
}
pub fn append(&mut self, rhs: SizeRules) {
let c = self.m.1.max(rhs.m.0) as u32;
self.a += rhs.a + c;
self.b += rhs.b + c;
self.m.1 = rhs.m.1;
self.stretch = self.stretch.max(rhs.stretch);
}
#[inline]
pub fn appended(self, rhs: SizeRules) -> Self {
let c = self.m.1.max(rhs.m.0) as u32;
SizeRules {
a: self.a + rhs.a + c,
b: self.b + rhs.b + c,
m: (self.m.0, rhs.m.1),
stretch: self.stretch.max(rhs.stretch),
}
}
pub fn surrounded_by(self, frame: SizeRules, internal_margins: bool) -> Self {
let (c, m) = if internal_margins {
((self.m.0 + self.m.1) as u32, frame.m)
} else {
(0, (self.m.0.max(frame.m.0), self.m.1.max(frame.m.1)))
};
SizeRules {
a: self.a + frame.a + c,
b: self.b + frame.b + c,
m,
stretch: self.stretch.max(frame.stretch),
}
}
pub fn sum(range: &[SizeRules]) -> SizeRules {
range.iter().sum()
}
pub fn min_sum(range: &[SizeRules]) -> SizeRules {
if range.is_empty() {
return SizeRules::EMPTY;
}
let mut rules = range[0];
for r in &range[1..] {
rules.a += rules.m.1.max(r.m.0) as u32 + r.a;
}
rules.b = rules.a;
rules.m.1 = range[range.len() - 1].m.1;
rules
}
pub fn sub_add(&mut self, x: Self, y: Self) {
self.a = self.a + y.a - x.a;
self.b = self.b + y.b - x.b;
self.m.1 = y.m.1;
self.stretch = self.stretch.max(y.stretch);
}
#[inline]
pub fn reduce_min_to(&mut self, min: u32) {
self.a = self.a.min(min);
}
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[inline]
pub fn solve_seq_total(out: &mut [u32], rules: &[Self], target: u32) {
let len = rules.len() - 1;
let total = rules[len];
let rules = &rules[0..len];
debug_assert_eq!(
SizeRules::sum(rules),
total,
"solve_seq_total: invalid input (missing configure or invalid usage?)"
);
Self::solve_seq_(out, rules, total, target);
}
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
pub fn solve_seq(out: &mut [u32], rules: &[Self], target: u32) {
let total = SizeRules::sum(rules);
Self::solve_seq_(out, rules, total, target);
}
fn solve_seq_(out: &mut [u32], rules: &[Self], total: Self, target: u32) {
type Targets = SmallVec<[u32; 16]>;
#[allow(non_snake_case)]
let N = out.len();
assert_eq!(rules.len(), N);
if N == 0 {
return;
}
if target > total.a {
out[0] = out[0].max(rules[0].a);
let mut margin_sum = 0;
let mut sum = out[0];
let mut dist_under_b = rules[0].b.saturating_sub(out[0]);
let mut dist_over_b = out[0].saturating_sub(rules[0].b);
for i in 1..N {
out[i] = out[i].max(rules[i].a);
margin_sum += (rules[i - 1].m.1).max(rules[i].m.0) as u32;
sum += out[i];
dist_under_b += rules[i].b.saturating_sub(out[i]);
dist_over_b += out[i].saturating_sub(rules[i].b);
}
let target = target - margin_sum;
if sum == target {
return;
} else if sum < target {
fn increase_targets<F: Fn(usize) -> u32>(
out: &mut [u32],
targets: &mut Targets,
base: F,
mut avail: u32,
) {
let mut any_removed = true;
while any_removed {
any_removed = false;
let count = targets.len() as u32;
let ceil = (avail + count - 1) / count; let mut t = 0;
while t < targets.len() {
let i = targets[t] as usize;
if out[i] >= base(i) + ceil {
avail -= out[i] - base(i);
targets.remove(t);
any_removed = true;
continue;
}
t += 1;
}
if targets.is_empty() {
return;
}
}
let count = targets.len() as u32;
let per_elt = avail / count;
let extra = (avail - per_elt * count) as usize;
assert!(extra < targets.len());
for t in 0..extra {
let i = targets[t] as usize;
out[i] = base(i) + per_elt + 1;
}
for t in extra..targets.len() {
let i = targets[t] as usize;
out[i] = base(i) + per_elt;
}
}
if target - sum >= dist_under_b {
sum = 0;
let highest_stretch = total.stretch;
let mut targets = Targets::new();
let mut over = 0;
for i in 0..N {
out[i] = out[i].max(rules[i].b);
sum += out[i];
if rules[i].stretch == highest_stretch {
over += out[i] - rules[i].b;
targets.push(i as u32);
}
}
let avail = target - sum + over;
increase_targets(out, &mut targets, |i| rules[i].b, avail);
debug_assert_eq!(target, (0..N).fold(0, |x, i| x + out[i]));
} else {
let mut targets = Targets::new();
let mut over = 0;
for i in 0..N {
if out[i] < rules[i].b {
over += out[i] - rules[i].a;
targets.push(i as u32);
}
}
let avail = target - sum + over;
increase_targets(out, &mut targets, |i| rules[i].a, avail);
debug_assert_eq!(target, (0..N).fold(0, |x, i| x + out[i]));
}
} else {
fn reduce_targets<F: Fn(usize) -> u32>(
out: &mut [u32],
targets: &mut Targets,
base: F,
mut avail: u32,
) {
let mut any_removed = true;
while any_removed {
any_removed = false;
let floor = avail / targets.len() as u32;
let mut t = 0;
while t < targets.len() {
let i = targets[t] as usize;
if out[i] <= base(i) + floor {
avail -= out[i] - base(i);
targets.remove(t);
any_removed = true;
continue;
}
t += 1;
}
}
let floor = avail / targets.len() as u32;
let extra = avail as usize - floor as usize * targets.len();
assert!(extra < targets.len());
for t in 0..extra {
let i = targets[t] as usize;
out[i] = base(i) + floor + 1;
}
for t in extra..targets.len() {
let i = targets[t] as usize;
out[i] = base(i) + floor;
}
}
if dist_over_b > sum - target {
const MAX_POLICY: usize = StretchPolicy::Maximise as usize + 1;
let mut dists = [0; MAX_POLICY];
for i in 0..N {
dists[rules[i].stretch as usize] += out[i].saturating_sub(rules[i].b);
}
let mut accum = 0;
let mut highest_affected = 0;
for i in 0..MAX_POLICY {
highest_affected = i;
dists[i] += accum;
accum = dists[i];
if accum >= sum - target {
break;
}
}
let mut avail = 0;
let mut targets = Targets::new();
for i in 0..N {
let stretch = rules[i].stretch as usize;
if out[i] > rules[i].b {
if stretch < highest_affected {
sum -= out[i] - rules[i].b;
out[i] = rules[i].b;
} else if stretch == highest_affected {
avail += out[i] - rules[i].b;
targets.push(i as u32);
}
}
}
if sum > target {
avail = avail + target - sum;
reduce_targets(out, &mut targets, |i| rules[i].b, avail);
}
debug_assert_eq!(target, (0..N).fold(0, |x, i| x + out[i]));
} else {
let mut targets = Targets::new();
sum = 0;
for i in 0..N {
out[i] = out[i].min(rules[i].b);
sum += out[i];
if out[i] > rules[i].a {
targets.push(i as u32);
}
}
if sum > target {
let avail = target + margin_sum - total.a;
reduce_targets(out, &mut targets, |i| rules[i].a, avail);
}
debug_assert_eq!(target, (0..N).fold(0, |x, i| x + out[i]));
}
}
} else {
let mut excess = total.a - target;
let mut largest = 0;
let mut num_equal = 0;
let mut next_largest = 0;
for n in 0..N {
let a = rules[n].a;
out[n] = a;
if a == largest {
num_equal += 1;
} else if a > largest {
next_largest = largest;
largest = a;
num_equal = 1;
} else if a > next_largest {
next_largest = a;
}
}
while excess > 0 {
let step = (excess / num_equal).min(largest - next_largest);
if step == 0 {
for n in 0..N {
if out[n] == largest {
out[n] -= 1;
if excess == 0 {
break;
}
excess -= 1;
}
}
break;
}
let thresh = next_largest;
let mut num_add = 0;
next_largest = 0;
for n in 0..N {
let a = out[n];
if a == largest {
out[n] = a - step;
} else if a == thresh {
num_add += 1;
} else if a > next_largest {
next_largest = a;
}
}
excess -= step * num_equal;
largest -= step;
num_equal += num_add;
}
}
}
pub fn distribute_span_over(self, rules: &mut [Self]) {
let len = rules.len();
assert!(len > 0);
let len1 = len - 1;
let sum: SizeRules = rules.iter().sum();
rules[0].m.0 = rules[0].m.0.max(self.m.0);
rules[len1].m.1 = rules[len1].m.1.max(self.m.1);
let excess_a = self.a.saturating_sub(sum.a);
let excess_b = self.b.saturating_sub(sum.b);
if excess_a == 0 && excess_b == 0 {
return;
}
let highest_stretch = sum.stretch;
let count = (0..len)
.filter(|i| rules[*i].stretch == highest_stretch)
.count() as u32;
let a_per_elt = excess_a / count;
let b_per_elt = excess_b / count;
let mut extra_a = excess_a - count * a_per_elt;
let mut extra_b = excess_b - count * b_per_elt;
for i in 0..len {
if rules[i].stretch == highest_stretch {
rules[i].a += a_per_elt;
rules[i].b += b_per_elt;
if extra_a > 0 {
rules[i].a += 1;
extra_a -= 1;
}
if extra_b > 0 {
rules[i].b += 1;
extra_b -= 1;
}
if highest_stretch < self.stretch {
rules[i].stretch = self.stretch;
}
}
}
}
}
impl Sum for SizeRules {
fn sum<I: Iterator<Item = Self>>(mut iter: I) -> Self {
if let Some(first) = iter.next() {
iter.fold(first, |x, y| x.appended(y))
} else {
SizeRules::EMPTY
}
}
}
impl<'a> Sum<&'a Self> for SizeRules {
fn sum<I: Iterator<Item = &'a Self>>(mut iter: I) -> Self {
if let Some(first) = iter.next() {
iter.fold(*first, |x, y| x.appended(*y))
} else {
SizeRules::EMPTY
}
}
}