use core::f32;
use colourblock;
use colourset::ColourSet;
use math::{Sym3x3, Vec3};
use {ColourWeights, Format};
use super::ColourFitImpl;
pub struct RangeFit<'a> {
colourset: &'a ColourSet,
format: Format,
weights: Vec3,
start: Vec3,
end: Vec3,
indices: [u8; 16],
best_error: f32,
best_compressed: [u8; 8],
}
impl<'a> RangeFit<'a> {
pub fn new(colourset: &'a ColourSet, format: Format, weights: ColourWeights) -> Self {
let mut fit = RangeFit {
colourset,
format,
weights: Vec3::new(weights[0], weights[1], weights[2]),
start: Vec3::new(0.0, 0.0, 0.0),
end: Vec3::new(0.0, 0.0, 0.0),
indices: [0u8; 16],
best_error: f32::MAX,
best_compressed: [0u8; 8],
};
let values = fit.colourset.points();
let weights = fit.colourset.weights();
let count = fit.colourset.count();
let covariance = Sym3x3::weighted_covariance(values, weights);
let principle = covariance.principle_component();
let mut start = Vec3::new(0.0, 0.0, 0.0);
let mut end = Vec3::new(0.0, 0.0, 0.0);
if count > 0 {
start = values[0];
end = start;
let mut min = start.dot(&principle);
let mut max = min;
for &val in values.iter().take(count).skip(1) {
let dot = val.dot(&principle);
if dot < min {
start = val;
min = dot;
} else if dot > max {
end = val;
max = dot;
}
}
}
let one = Vec3::new(1.0, 1.0, 1.0);
let zero = Vec3::new(0.0, 0.0, 0.0);
start = one.min(zero.max(start));
end = one.min(zero.max(end));
let grid = Vec3::new(31.0, 63.0, 31.0);
let gridrcp = Vec3::new(1.0 / 31.0, 1.0 / 63.0, 1.0 / 31.0);
let half = Vec3::new(0.5, 0.5, 0.5);
fit.start = (grid * start + half).truncate() * gridrcp;
fit.end = (grid * end + half).truncate() * gridrcp;
fit
}
fn compression_helper(&mut self, codes: &[Vec3]) -> bool {
let count = self.colourset.count();
let values = self.colourset.points();
let mut closest = [0u8; 16];
let mut error = 0f32;
for i in 0..count {
let mut dist = f32::MAX;
let mut idx = 0;
for (j, code) in codes.iter().enumerate() {
let d = (self.weights * (values[i] - code)).length2();
if d < dist {
dist = d;
idx = j;
}
}
closest[i] = idx as u8;
error += dist;
}
if error < self.best_error {
self.colourset.remap_indices(&closest, &mut self.indices);
self.best_error = error;
return true;
}
false
}
}
impl<'a> ColourFitImpl<'a> for RangeFit<'a> {
fn is_bc1(&self) -> bool {
self.format == Format::Bc1
}
fn is_transparent(&self) -> bool {
self.colourset.is_transparent()
}
fn best_compressed(&'a self) -> &'a [u8] {
&self.best_compressed
}
fn compress3(&mut self) {
let codes = [self.start, self.end, self.start * 0.5 + self.end * 0.5];
if self.compression_helper(&codes) {
colourblock::write3(
&self.start,
&self.end,
&self.indices,
&mut self.best_compressed,
);
}
}
fn compress4(&mut self) {
let codes = [
self.start,
self.end,
self.start * (2.0 / 3.0) + self.end * (1.0 / 3.0),
self.start * (1.0 / 3.0) + self.end * (2.0 / 3.0),
];
if self.compression_helper(&codes) {
colourblock::write4(
&self.start,
&self.end,
&self.indices,
&mut self.best_compressed,
);
}
}
}