use crate::s2::Point;
use crate::s2::shape::{
Chain, ChainPosition, Dimension, Edge, ReferencePoint, Shape, reference_point_for_shape,
};
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LaxLoop {
vertices: Vec<Point>,
}
impl LaxLoop {
pub fn new(vertices: Vec<Point>) -> Self {
LaxLoop { vertices }
}
pub fn num_vertices(&self) -> usize {
self.vertices.len()
}
pub fn vertex(&self, i: usize) -> Point {
self.vertices[i]
}
}
impl Shape for LaxLoop {
fn num_edges(&self) -> usize {
self.vertices.len()
}
fn edge(&self, id: usize) -> Edge {
debug_assert!(id < self.num_edges());
let next = if id + 1 == self.vertices.len() {
0
} else {
id + 1
};
Edge::new(self.vertices[id], self.vertices[next])
}
fn reference_point(&self) -> ReferencePoint {
reference_point_for_shape(self)
}
fn num_chains(&self) -> usize {
usize::min(1, self.vertices.len())
}
fn chain(&self, _chain_id: usize) -> Chain {
Chain::new(0, self.vertices.len())
}
fn chain_edge(&self, _chain_id: usize, offset: usize) -> Edge {
debug_assert!(offset < self.num_edges());
self.edge(offset)
}
fn chain_position(&self, edge_id: usize) -> ChainPosition {
debug_assert!(edge_id < self.num_edges());
ChainPosition::new(0, edge_id)
}
fn dimension(&self) -> Dimension {
Dimension::Polygon
}
}
impl std::ops::Deref for LaxLoop {
type Target = [Point];
fn deref(&self) -> &[Point] {
&self.vertices
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::s2::LatLng;
fn p(lat: f64, lng: f64) -> Point {
LatLng::from_degrees(lat, lng).to_point()
}
fn is_send_sync<T: Sized + Send + Sync + Unpin>() {}
#[test]
fn lax_loop_is_send_sync() {
is_send_sync::<LaxLoop>();
}
#[test]
fn test_empty() {
let l = LaxLoop::new(vec![]);
assert_eq!(l.num_edges(), 0);
assert_eq!(l.num_chains(), 0);
assert_eq!(l.dimension(), Dimension::Polygon);
assert!(l.is_empty());
assert!(!l.is_full());
}
#[test]
fn test_single_vertex() {
let l = LaxLoop::new(vec![p(0.0, 0.0)]);
assert_eq!(l.num_edges(), 1);
assert_eq!(l.num_chains(), 1);
let e = l.edge(0);
assert!(e.is_degenerate());
}
#[test]
fn test_triangle() {
let l = LaxLoop::new(vec![p(0.0, 0.0), p(0.0, 90.0), p(90.0, 0.0)]);
assert_eq!(l.num_edges(), 3);
assert_eq!(l.num_chains(), 1);
assert_eq!(l.num_vertices(), 3);
let chain = l.chain(0);
assert_eq!(chain.start, 0);
assert_eq!(chain.length, 3);
let last_edge = l.edge(2);
assert_eq!(last_edge.v0, l.vertex(2));
assert_eq!(last_edge.v1, l.vertex(0));
}
#[test]
fn test_chain_position() {
let l = LaxLoop::new(vec![p(0.0, 0.0), p(0.0, 90.0), p(90.0, 0.0)]);
for i in 0..3 {
let cp = l.chain_position(i);
assert_eq!(cp.chain_id, 0);
assert_eq!(cp.offset, i);
}
}
#[test]
fn test_deref() {
let l = LaxLoop::new(vec![p(0.0, 0.0), p(0.0, 90.0)]);
let slice: &[Point] = &l;
assert_eq!(slice.len(), 2);
}
#[test]
fn test_has_interior() {
let l = LaxLoop::new(vec![p(0.0, 0.0), p(0.0, 90.0), p(90.0, 0.0)]);
assert!(l.has_interior());
}
#[test]
fn test_reference_point() {
let l = LaxLoop::new(vec![p(89.0, 0.0), p(89.0, 120.0), p(89.0, 240.0)]);
let rp = l.reference_point();
assert!(rp.point.0.norm() > 0.5);
}
#[cfg(feature = "serde")]
#[test]
fn test_serde_roundtrip() {
let ll = LaxLoop::new(vec![
Point::from_coords(1.0, 0.0, 0.0),
Point::from_coords(0.0, 1.0, 0.0),
Point::from_coords(0.0, 0.0, 1.0),
]);
let json = serde_json::to_string(&ll).unwrap();
let back: LaxLoop = serde_json::from_str(&json).unwrap();
assert_eq!(ll.num_vertices(), back.num_vertices());
for i in 0..ll.num_vertices() {
assert_eq!(ll.vertex(i), back.vertex(i));
}
}
}