use crate::basics::iround;
use crate::span_interpolator_linear::{SpanInterpolatorLinear, Transformer, SUBPIXEL_SCALE};
pub struct SpanSubdivAdaptor<T: Transformer> {
subdiv_shift: u32,
subdiv_size: u32,
subdiv_mask: u32,
interpolator: SpanInterpolatorLinear<T>,
src_x: i32,
src_y: f64,
pos: u32,
len: u32,
}
impl<T: Transformer> SpanSubdivAdaptor<T> {
pub fn new(interpolator: SpanInterpolatorLinear<T>) -> Self {
Self {
subdiv_shift: 4,
subdiv_size: 1 << 4,
subdiv_mask: (1 << 4) - 1,
interpolator,
src_x: 0,
src_y: 0.0,
pos: 0,
len: 0,
}
}
pub fn new_with_shift(interpolator: SpanInterpolatorLinear<T>, subdiv_shift: u32) -> Self {
Self {
subdiv_shift,
subdiv_size: 1 << subdiv_shift,
subdiv_mask: (1 << subdiv_shift) - 1,
interpolator,
src_x: 0,
src_y: 0.0,
pos: 0,
len: 0,
}
}
pub fn interpolator(&self) -> &SpanInterpolatorLinear<T> {
&self.interpolator
}
pub fn interpolator_mut(&mut self) -> &mut SpanInterpolatorLinear<T> {
&mut self.interpolator
}
pub fn transformer(&self) -> &T {
self.interpolator.transformer()
}
pub fn subdiv_shift(&self) -> u32 {
self.subdiv_shift
}
pub fn set_subdiv_shift(&mut self, shift: u32) {
self.subdiv_shift = shift;
self.subdiv_size = 1 << shift;
self.subdiv_mask = self.subdiv_size - 1;
}
pub fn begin(&mut self, x: f64, y: f64, len: u32) {
self.pos = 1;
self.src_x = iround(x * SUBPIXEL_SCALE as f64) + SUBPIXEL_SCALE;
self.src_y = y;
self.len = len;
let sub_len = if len > self.subdiv_size {
self.subdiv_size
} else {
len
};
self.interpolator.begin(x, y, sub_len);
}
#[inline]
pub fn next(&mut self) {
self.interpolator.next();
if self.pos >= self.subdiv_size {
let mut sub_len = self.len;
if sub_len > self.subdiv_size {
sub_len = self.subdiv_size;
}
self.interpolator.resynchronize(
self.src_x as f64 / SUBPIXEL_SCALE as f64 + sub_len as f64,
self.src_y,
sub_len,
);
self.pos = 0;
}
self.src_x += SUBPIXEL_SCALE;
self.pos += 1;
self.len -= 1;
}
#[inline]
pub fn coordinates(&self, x: &mut i32, y: &mut i32) {
self.interpolator.coordinates(x, y);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::trans_affine::TransAffine;
#[test]
fn test_identity_subdiv() {
let interp = SpanInterpolatorLinear::new(TransAffine::new());
let mut subdiv = SpanSubdivAdaptor::new(interp);
subdiv.begin(5.0, 10.0, 20);
let mut x = 0i32;
let mut y = 0i32;
subdiv.coordinates(&mut x, &mut y);
assert_eq!(x, 5 * 256);
assert_eq!(y, 10 * 256);
}
#[test]
fn test_next_advances() {
let interp = SpanInterpolatorLinear::new(TransAffine::new());
let mut subdiv = SpanSubdivAdaptor::new(interp);
subdiv.begin(0.0, 0.0, 10);
let mut x = 0i32;
let mut y = 0i32;
subdiv.coordinates(&mut x, &mut y);
assert_eq!(x, 0);
subdiv.next();
subdiv.coordinates(&mut x, &mut y);
assert_eq!(x, 256);
}
#[test]
fn test_subdiv_shift() {
let interp = SpanInterpolatorLinear::new(TransAffine::new());
let subdiv = SpanSubdivAdaptor::new_with_shift(interp, 3);
assert_eq!(subdiv.subdiv_shift(), 3);
}
#[test]
fn test_resynchronize_across_boundary() {
let interp = SpanInterpolatorLinear::new(TransAffine::new());
let mut subdiv = SpanSubdivAdaptor::new(interp);
subdiv.begin(0.0, 0.0, 32);
for _ in 0..16 {
subdiv.next();
}
let mut x = 0i32;
let mut y = 0i32;
subdiv.coordinates(&mut x, &mut y);
assert_eq!(x, 16 * 256);
}
#[test]
fn test_with_translation() {
let trans = TransAffine::new_translation(100.0, 200.0);
let interp = SpanInterpolatorLinear::new(trans);
let mut subdiv = SpanSubdivAdaptor::new(interp);
subdiv.begin(0.0, 0.0, 5);
let mut x = 0i32;
let mut y = 0i32;
subdiv.coordinates(&mut x, &mut y);
assert_eq!(x, 100 * 256);
assert_eq!(y, 200 * 256);
}
#[test]
fn test_set_subdiv_shift() {
let interp = SpanInterpolatorLinear::new(TransAffine::new());
let mut subdiv = SpanSubdivAdaptor::new(interp);
subdiv.set_subdiv_shift(6);
assert_eq!(subdiv.subdiv_shift(), 6);
}
}