use crate::r2;
use crate::s2::coords::{self, Face};
use crate::s2::edge_clipping;
use crate::s2::r2_edge_clipper::{R2Edge, R2EdgeClipper};
use crate::s2::{Cell, Point};
#[derive(Debug)]
pub struct UVEdgeClipper {
r2_clipper: R2EdgeClipper,
clip_face: Face,
last_face: Face,
face_uv_edge: R2Edge,
uv_error: f64,
missed_face: bool,
}
impl Default for UVEdgeClipper {
fn default() -> Self {
Self::new()
}
}
impl UVEdgeClipper {
pub fn new() -> Self {
Self {
r2_clipper: R2EdgeClipper::new(),
clip_face: Face::F0,
last_face: Face::F0,
face_uv_edge: R2Edge::default(),
uv_error: 0.0,
missed_face: false,
}
}
pub fn from_face_rect(face: Face, region: &r2::Rect) -> Self {
let mut c = Self::new();
c.init(face, region);
c
}
pub fn from_cell(cell: Cell) -> Self {
let mut c = Self::new();
c.init_cell(cell);
c
}
pub fn init(&mut self, face: Face, region: &r2::Rect) {
self.clip_face = face;
self.r2_clipper.init(region);
}
pub fn init_cell(&mut self, cell: Cell) {
self.init(cell.face(), &cell.bound_uv());
}
pub fn clip_face(&self) -> Face {
self.clip_face
}
pub fn clip_rect(&self) -> r2::Rect {
self.r2_clipper.clip_rect()
}
pub fn clip_edge(&mut self, v0: Point, v1: Point, connected: bool) -> bool {
let face0;
let face1;
let mut need_face_clip = false;
if connected {
face0 = self.last_face;
let (f1, _, _) = coords::xyz_to_face_uv(&v1.0);
face1 = f1;
if face0 != face1 || face0 != self.clip_face {
need_face_clip = true;
} else {
self.face_uv_edge.v0 = self.face_uv_edge.v1;
let (u, v) = coords::valid_face_xyz_to_uv(face1, &v1.0);
self.face_uv_edge.v1 = r2::Point::new(u, v);
}
} else {
let (f0, _, _) = coords::xyz_to_face_uv(&v0.0);
let (f1, _, _) = coords::xyz_to_face_uv(&v1.0);
face0 = f0;
face1 = f1;
if face0 != face1 || face0 != self.clip_face {
need_face_clip = true;
} else {
let (u0, v0_uv) = coords::valid_face_xyz_to_uv(face0, &v0.0);
let (u1, v1_uv) = coords::valid_face_xyz_to_uv(face0, &v1.0);
self.face_uv_edge.v0 = r2::Point::new(u0, v0_uv);
self.face_uv_edge.v1 = r2::Point::new(u1, v1_uv);
}
}
self.last_face = face1;
self.missed_face = false;
if need_face_clip {
if let Some((a_uv, b_uv)) = edge_clipping::clip_to_padded_face(
v0,
v1,
self.clip_face,
edge_clipping::FACE_CLIP_ERROR_UV_COORD,
) {
self.face_uv_edge.v0 = a_uv;
self.face_uv_edge.v1 = b_uv;
} else {
self.missed_face = true;
return false;
}
}
self.uv_error = coords::MAX_XYZ_TO_UV_ERROR;
if need_face_clip {
self.uv_error += edge_clipping::FACE_CLIP_ERROR_UV_COORD;
}
self.r2_clipper.clip_edge(&self.face_uv_edge, connected)
}
pub fn missed_face(&self) -> bool {
self.missed_face
}
pub fn uv_error(&self) -> f64 {
self.uv_error
}
pub fn clip_error(&self) -> f64 {
2.0 * (self.uv_error() + super::r2_edge_clipper::MAX_UNIT_CLIP_ERROR)
}
pub fn face_uv_edge(&self) -> &R2Edge {
&self.face_uv_edge
}
pub fn clipped_uv_edge(&self) -> &R2Edge {
&self.r2_clipper.clipped_edge
}
pub fn outcode(&self, vertex: usize) -> u8 {
match vertex {
0 => self.r2_clipper.outcode0,
1 => self.r2_clipper.outcode1,
_ => unreachable!("vertex must be 0 or 1"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::s2::LatLng;
use crate::s2::r2_edge_clipper::INSIDE;
#[test]
fn test_edge_inside_face0() {
let mut clipper = UVEdgeClipper::new();
let face = Face::F0;
let region = r2::Rect::from_points(r2::Point::new(-1.0, -1.0), r2::Point::new(1.0, 1.0));
clipper.init(face, ®ion);
let v0 = LatLng::from_degrees(10.0, 10.0).to_point();
let v1 = LatLng::from_degrees(20.0, 20.0).to_point();
assert!(clipper.clip_edge(v0, v1, false));
assert_eq!(clipper.outcode(0), INSIDE);
assert_eq!(clipper.outcode(1), INSIDE);
}
#[test]
fn test_edge_misses_face() {
let mut clipper = UVEdgeClipper::new();
let face = Face::F0;
let region = r2::Rect::from_points(r2::Point::new(-1.0, -1.0), r2::Point::new(1.0, 1.0));
clipper.init(face, ®ion);
let v0 = LatLng::from_degrees(10.0, -170.0).to_point();
let v1 = LatLng::from_degrees(20.0, -170.0).to_point();
assert!(!clipper.clip_edge(v0, v1, false));
assert!(clipper.missed_face());
}
#[test]
fn test_clip_cell() {
let cell_id = crate::s2::CellId::from_face(0).children()[0];
let cell = Cell::from_cell_id(cell_id);
let mut clipper = UVEdgeClipper::from_cell(cell);
let center = cell.center();
let v0 = center;
let v1 = LatLng::from_degrees(5.0, 5.0).to_point();
let hit = clipper.clip_edge(v0, v1, false);
assert!(!clipper.missed_face() || !hit);
}
#[test]
fn test_connected_edge() {
let mut clipper = UVEdgeClipper::new();
let face = Face::F0;
let region = r2::Rect::from_points(r2::Point::new(-1.0, -1.0), r2::Point::new(1.0, 1.0));
clipper.init(face, ®ion);
let a = LatLng::from_degrees(10.0, 10.0).to_point();
let b = LatLng::from_degrees(20.0, 20.0).to_point();
let c = LatLng::from_degrees(30.0, 15.0).to_point();
assert!(clipper.clip_edge(a, b, false));
assert!(clipper.clip_edge(b, c, true));
assert_eq!(clipper.outcode(0), INSIDE);
}
#[test]
fn test_error_bounds() {
let mut clipper = UVEdgeClipper::new();
let face = Face::F0;
let region = r2::Rect::from_points(r2::Point::new(-1.0, -1.0), r2::Point::new(1.0, 1.0));
clipper.init(face, ®ion);
let v0 = LatLng::from_degrees(10.0, 10.0).to_point();
let v1 = LatLng::from_degrees(20.0, 20.0).to_point();
clipper.clip_edge(v0, v1, false);
assert!(clipper.uv_error() > 0.0);
assert!(clipper.clip_error() > clipper.uv_error());
}
}