use crate::transform::Transformer;
pub struct ApproxTransformer<T: Transformer> {
inner: T,
max_error: f64,
}
impl<T: Transformer> ApproxTransformer<T> {
pub fn new(inner: T, max_error: f64) -> Self {
Self { inner, max_error }
}
pub fn inner(&self) -> &T {
&self.inner
}
fn approx_entry(
&self,
dst_to_src: bool,
x: &mut [f64],
y: &mut [f64],
success: &mut [bool],
start: usize,
len: usize,
) {
if len <= 5 {
let ok = self.inner.transform(
dst_to_src,
&mut x[start..start + len],
&mut y[start..start + len],
);
success[start..start + len].copy_from_slice(&ok);
return;
}
let end = start + len - 1;
let n_middle = (len - 1) / 2;
let mid = start + n_middle;
if y[start] != y[end] || y[start] != y[mid]
|| x[start] == x[end] || x[start] == x[mid]
{
let ok = self.inner.transform(
dst_to_src,
&mut x[start..start + len],
&mut y[start..start + len],
);
success[start..start + len].copy_from_slice(&ok);
return;
}
let mut tx = [x[start], x[mid], x[end]];
let mut ty = [y[start], y[mid], y[end]];
let ok3 = self.inner.transform(dst_to_src, &mut tx, &mut ty);
if !ok3[0] || !ok3[1] || !ok3[2] {
let ok = self.inner.transform(
dst_to_src,
&mut x[start..start + len],
&mut y[start..start + len],
);
success[start..start + len].copy_from_slice(&ok);
return;
}
self.approx_internal(dst_to_src, x, y, success, start, len, &tx, &ty);
}
fn approx_internal(
&self,
dst_to_src: bool,
x: &mut [f64],
y: &mut [f64],
success: &mut [bool],
start: usize,
len: usize,
sme_x: &[f64; 3],
sme_y: &[f64; 3],
) {
let n_middle = (len - 1) / 2;
let mid = start + n_middle;
let end = start + len - 1;
let x_span = x[end] - x[start];
let df_delta_x = (sme_x[2] - sme_x[0]) / x_span;
let df_delta_y = (sme_y[2] - sme_y[0]) / x_span;
let mid_dist = x[mid] - x[start];
let error =
(sme_x[0] + df_delta_x * mid_dist - sme_x[1]).abs() +
(sme_y[0] + df_delta_y * mid_dist - sme_y[1]).abs();
if error > self.max_error {
let left_len = n_middle; let right_len = len - n_middle;
let left_mid = start + (n_middle - 1) / 2;
let right_mid = mid + (right_len - 1) / 2;
let use_base_left = left_len <= 5
|| y[start] != y[start + left_len - 1]
|| y[start] != y[left_mid]
|| x[start] == x[start + left_len - 1]
|| x[start] == x[left_mid];
let use_base_right = right_len <= 5
|| y[mid] != y[end]
|| y[mid] != y[right_mid]
|| x[mid] == x[end]
|| x[mid] == x[right_mid];
let mut mx = [x[left_mid], x[start + left_len - 1], x[right_mid]];
let mut my = [y[left_mid], y[start + left_len - 1], y[right_mid]];
let mid_ok = if !use_base_left && !use_base_right {
let ok = self.inner.transform(dst_to_src, &mut mx, &mut my);
ok[0] && ok[1] && ok[2]
} else if !use_base_left {
let ok = self.inner.transform(dst_to_src, &mut mx[..2], &mut my[..2]);
ok[0] && ok[1]
} else if !use_base_right {
let ok = self.inner.transform(dst_to_src, &mut mx[2..], &mut my[2..]);
ok[0]
} else {
true };
if !mid_ok {
if left_len > 2 {
let ok = self.inner.transform(
dst_to_src,
&mut x[start + 1..start + left_len],
&mut y[start + 1..start + left_len],
);
for i in 0..left_len - 1 {
success[start + 1 + i] = ok[i];
}
}
if right_len > 2 {
let ok = self.inner.transform(
dst_to_src,
&mut x[mid + 1..end],
&mut y[mid + 1..end],
);
for i in 0..(right_len - 2) {
success[mid + 1 + i] = ok[i];
}
}
x[start] = sme_x[0]; y[start] = sme_y[0]; success[start] = true;
x[mid] = sme_x[1]; y[mid] = sme_y[1]; success[mid] = true;
x[end] = sme_x[2]; y[end] = sme_y[2]; success[end] = true;
return;
}
if !use_base_left {
let left_sme_x = [sme_x[0], mx[0], mx[1]];
let left_sme_y = [sme_y[0], my[0], my[1]];
self.approx_internal(
dst_to_src, x, y, success, start, left_len,
&left_sme_x, &left_sme_y,
);
} else {
if left_len > 2 {
let ok = self.inner.transform(
dst_to_src,
&mut x[start + 1..start + left_len],
&mut y[start + 1..start + left_len],
);
for i in 0..left_len - 1 {
success[start + 1 + i] = ok[i];
}
}
x[start] = sme_x[0];
y[start] = sme_y[0];
success[start] = true;
}
if !use_base_right {
let right_sme_x = [sme_x[1], mx[2], sme_x[2]];
let right_sme_y = [sme_y[1], my[2], sme_y[2]];
self.approx_internal(
dst_to_src, x, y, success, mid, right_len,
&right_sme_x, &right_sme_y,
);
} else {
if right_len > 2 {
let ok = self.inner.transform(
dst_to_src,
&mut x[mid + 1..end],
&mut y[mid + 1..end],
);
for i in 0..(right_len - 2) {
success[mid + 1 + i] = ok[i];
}
}
x[mid] = sme_x[1]; y[mid] = sme_y[1]; success[mid] = true;
x[end] = sme_x[2]; y[end] = sme_y[2]; success[end] = true;
}
} else {
for i in (0..len).rev() {
let df_dist = x[start + i] - x[start];
x[start + i] = sme_x[0] + df_delta_x * df_dist;
y[start + i] = sme_y[0] + df_delta_y * df_dist;
success[start + i] = true;
}
}
}
}
impl<T: Transformer> Transformer for ApproxTransformer<T> {
fn transform(
&self,
dst_to_src: bool,
x: &mut [f64],
y: &mut [f64],
) -> Vec<bool> {
let n = x.len();
if self.max_error <= 0.0 {
return self.inner.transform(dst_to_src, x, y);
}
let mut success = vec![false; n];
self.approx_entry(dst_to_src, x, y, &mut success, 0, n);
success
}
}