use crate::{Point, Rect};
use tiny_skia_path::Scalar;
pub const MAX_POINTS: usize = 4;
pub fn clip<'a>(
src: &[Point; 2],
clip: &Rect,
can_cull_to_the_right: bool,
points: &'a mut [Point; MAX_POINTS],
) -> &'a [Point] {
let (mut index0, mut index1) = if src[0].y < src[1].y { (0, 1) } else { (1, 0) };
if src[index1].y <= clip.top() {
return &[];
}
if src[index0].y >= clip.bottom() {
return &[];
}
let mut tmp = *src;
if src[index0].y < clip.top() {
tmp[index0] = Point::from_xy(sect_with_horizontal(src, clip.top()), clip.top());
debug_assert!(is_between_unsorted(tmp[index0].x, src[0].x, src[1].x));
}
if tmp[index1].y > clip.bottom() {
tmp[index1] = Point::from_xy(sect_with_horizontal(src, clip.bottom()), clip.bottom());
debug_assert!(is_between_unsorted(tmp[index1].x, src[0].x, src[1].x));
}
let mut result_storage = [Point::zero(); MAX_POINTS];
let mut line_count = 1;
let mut reverse;
if src[0].x < src[1].x {
index0 = 0;
index1 = 1;
reverse = false;
} else {
index0 = 1;
index1 = 0;
reverse = true;
}
let result: &[Point] = if tmp[index1].x <= clip.left() {
tmp[0].x = clip.left();
tmp[1].x = clip.left();
reverse = false;
&tmp
} else if tmp[index0].x >= clip.right() {
if can_cull_to_the_right {
return &[];
}
tmp[0].x = clip.right();
tmp[1].x = clip.right();
reverse = false;
&tmp
} else {
let mut offset = 0;
if tmp[index0].x < clip.left() {
result_storage[offset] = Point::from_xy(clip.left(), tmp[index0].y);
offset += 1;
result_storage[offset] =
Point::from_xy(clip.left(), sect_clamp_with_vertical(&tmp, clip.left()));
debug_assert!(is_between_unsorted(
result_storage[offset].y,
tmp[0].y,
tmp[1].y
));
} else {
result_storage[offset] = tmp[index0];
}
offset += 1;
if tmp[index1].x > clip.right() {
result_storage[offset] =
Point::from_xy(clip.right(), sect_clamp_with_vertical(&tmp, clip.right()));
debug_assert!(is_between_unsorted(
result_storage[offset].y,
tmp[0].y,
tmp[1].y
));
offset += 1;
result_storage[offset] = Point::from_xy(clip.right(), tmp[index1].y);
} else {
result_storage[offset] = tmp[index1];
}
line_count = offset;
&result_storage
};
if reverse {
for i in 0..=line_count {
points[line_count - i] = result[i];
}
} else {
let len = line_count + 1;
points[0..len].copy_from_slice(&result[0..len]);
}
&points[0..line_count + 1]
}
fn sect_with_horizontal(src: &[Point; 2], y: f32) -> f32 {
let dy = src[1].y - src[0].y;
if dy.is_nearly_zero() {
src[0].x.ave(src[1].x)
} else {
let x0 = f64::from(src[0].x);
let y0 = f64::from(src[0].y);
let x1 = f64::from(src[1].x);
let y1 = f64::from(src[1].y);
let result = x0 + (f64::from(y) - y0) * (x1 - x0) / (y1 - y0);
pin_unsorted_f64(result, x0, x1) as f32
}
}
fn is_between_unsorted(value: f32, limit0: f32, limit1: f32) -> bool {
if limit0 < limit1 {
limit0 <= value && value <= limit1
} else {
limit1 <= value && value <= limit0
}
}
fn sect_clamp_with_vertical(src: &[Point; 2], x: f32) -> f32 {
let y = sect_with_vertical(src, x);
pin_unsorted_f32(y, src[0].y, src[1].y)
}
fn sect_with_vertical(src: &[Point; 2], x: f32) -> f32 {
let dx = src[1].x - src[0].x;
if dx.is_nearly_zero() {
src[0].y.ave(src[1].y)
} else {
let x0 = f64::from(src[0].x);
let y0 = f64::from(src[0].y);
let x1 = f64::from(src[1].x);
let y1 = f64::from(src[1].y);
let result = y0 + (f64::from(x) - x0) * (y1 - y0) / (x1 - x0);
result as f32
}
}
fn pin_unsorted_f32(value: f32, mut limit0: f32, mut limit1: f32) -> f32 {
if limit1 < limit0 {
core::mem::swap(&mut limit0, &mut limit1);
}
debug_assert!(limit0 <= limit1);
if value < limit0 {
limit0
} else if value > limit1 {
limit1
} else {
value
}
}
fn pin_unsorted_f64(value: f64, mut limit0: f64, mut limit1: f64) -> f64 {
if limit1 < limit0 {
core::mem::swap(&mut limit0, &mut limit1);
}
debug_assert!(limit0 <= limit1);
if value < limit0 {
limit0
} else if value > limit1 {
limit1
} else {
value
}
}
pub fn intersect(src: &[Point; 2], clip: &Rect, dst: &mut [Point; 2]) -> bool {
let bounds = Rect::from_ltrb(
src[0].x.min(src[1].x),
src[0].y.min(src[1].y),
src[0].x.max(src[1].x),
src[0].y.max(src[1].y),
);
if let Some(bounds) = bounds {
if contains_no_empty_check(clip, &bounds) {
dst.copy_from_slice(src);
return true;
}
if nested_lt(bounds.right(), clip.left(), bounds.width())
|| nested_lt(clip.right(), bounds.left(), bounds.width())
|| nested_lt(bounds.bottom(), clip.top(), bounds.height())
|| nested_lt(clip.bottom(), bounds.top(), bounds.height())
{
return false;
}
}
let (index0, index1) = if src[0].y < src[1].y { (0, 1) } else { (1, 0) };
let mut tmp = src.clone();
if tmp[index0].y < clip.top() {
tmp[index0] = Point::from_xy(sect_with_horizontal(src, clip.top()), clip.top());
}
if tmp[index1].y > clip.bottom() {
tmp[index1] = Point::from_xy(sect_with_horizontal(src, clip.bottom()), clip.bottom());
}
let (index0, index1) = if tmp[0].x < tmp[1].x { (0, 1) } else { (1, 0) };
if tmp[index1].x <= clip.left() || tmp[index0].x >= clip.right() {
if tmp[0].x != tmp[1].x || tmp[0].x < clip.left() || tmp[0].x > clip.right() {
return false;
}
}
if tmp[index0].x < clip.left() {
tmp[index0] = Point::from_xy(clip.left(), sect_with_vertical(src, clip.left()));
}
if tmp[index1].x > clip.right() {
tmp[index1] = Point::from_xy(clip.right(), sect_with_vertical(src, clip.right()));
}
dst.copy_from_slice(&tmp);
true
}
fn nested_lt(a: f32, b: f32, dim: f32) -> bool {
a <= b && (a < b || dim > 0.0)
}
fn contains_no_empty_check(outer: &Rect, inner: &Rect) -> bool {
outer.left() <= inner.left()
&& outer.top() <= inner.top()
&& outer.right() >= inner.right()
&& outer.bottom() >= inner.bottom()
}