use crate::wgs84::Wgs84;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i8)]
pub enum Orientation {
Clockwise = -1,
InvalidOrientation = 0,
CounterClockwise = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum PolygonType {
SimplePolygon = 0,
TriangleStrip = 1,
TriangleFan = 2,
TriangleList = 3,
Unknown = 4,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Polygon {
polygon_type: PolygonType,
vertices: Vec<Wgs84>,
}
impl Polygon {
pub fn new(polygon_type: PolygonType) -> Self {
Polygon {
polygon_type,
vertices: Vec::new(),
}
}
pub fn with_vertices(polygon_type: PolygonType, vertices: Vec<Wgs84>) -> Self {
Polygon {
polygon_type,
vertices,
}
}
pub fn add_vertex(&mut self, position: Wgs84) {
self.vertices.push(position);
}
pub fn add_vertices(&mut self, vertices: &[Wgs84]) {
self.vertices.extend_from_slice(vertices);
}
pub fn get(&self, index: usize) -> Wgs84 {
self.vertices[index]
}
pub fn set(&mut self, index: usize, value: Wgs84) {
self.vertices[index] = value;
}
pub fn len(&self) -> usize {
self.vertices.len()
}
pub fn is_empty(&self) -> bool {
self.vertices.is_empty()
}
pub fn polygon_type(&self) -> PolygonType {
self.polygon_type
}
pub fn set_type(&mut self, polygon_type: PolygonType) {
self.polygon_type = polygon_type;
}
pub fn is_valid(&self) -> bool {
!self.vertices.is_empty()
}
pub fn vertices(&self) -> &[Wgs84] {
&self.vertices
}
pub fn vertices_mut(&mut self) -> &mut Vec<Wgs84> {
&mut self.vertices
}
pub fn orientation(&self) -> Orientation {
let is_supported = self.polygon_type == PolygonType::SimplePolygon
|| (self.polygon_type == PolygonType::TriangleList && self.vertices.len() == 3);
if !is_supported {
return Orientation::InvalidOrientation;
}
let n = self.vertices.len();
let mut area = 0.0_f64;
for i in 0..n {
let j = if i + 1 == n { 0 } else { i + 1 };
area += self.vertices[i].lon * self.vertices[j].lat;
area -= self.vertices[i].lat * self.vertices[j].lon;
}
if area > 0.0 {
Orientation::CounterClockwise
} else if area < 0.0 {
Orientation::Clockwise
} else {
Orientation::InvalidOrientation
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn pts(coords: &[(f64, f64)]) -> Vec<Wgs84> {
coords.iter().map(|&(x, y)| Wgs84::new(x, y)).collect()
}
#[test]
fn base_is_valid_needs_one_vertex() {
let empty = Polygon::new(PolygonType::SimplePolygon);
assert!(!empty.is_valid());
assert!(empty.is_empty());
let mut one = Polygon::new(PolygonType::SimplePolygon);
one.add_vertex(Wgs84::new(0.0, 0.0));
assert!(one.is_valid());
}
#[test]
fn ccw_and_cw() {
let ccw = Polygon::with_vertices(
PolygonType::SimplePolygon,
pts(&[(0.0, 0.0), (1.0, 0.0), (0.0, 1.0)]),
);
assert_eq!(ccw.orientation(), Orientation::CounterClockwise);
let cw = Polygon::with_vertices(
PolygonType::SimplePolygon,
pts(&[(0.0, 0.0), (0.0, 1.0), (1.0, 0.0)]),
);
assert_eq!(cw.orientation(), Orientation::Clockwise);
}
#[test]
fn collinear_is_invalid() {
let p = Polygon::with_vertices(
PolygonType::SimplePolygon,
pts(&[(0.0, 0.0), (1.0, 1.0), (2.0, 2.0)]),
);
assert_eq!(p.orientation(), Orientation::InvalidOrientation);
}
#[test]
fn unsupported_type_is_invalid() {
let strip = Polygon::with_vertices(
PolygonType::TriangleStrip,
pts(&[(0.0, 0.0), (1.0, 0.0), (0.0, 1.0)]),
);
assert_eq!(strip.orientation(), Orientation::InvalidOrientation);
let multi = Polygon::with_vertices(
PolygonType::TriangleList,
pts(&[(0.0, 0.0), (4.0, 0.0), (4.0, 4.0), (0.0, 4.0)]),
);
assert_eq!(multi.orientation(), Orientation::InvalidOrientation);
}
#[test]
fn single_triangle_list_allowed() {
let t = Polygon::with_vertices(
PolygonType::TriangleList,
pts(&[(0.0, 0.0), (1.0, 0.0), (0.0, 1.0)]),
);
assert_eq!(t.orientation(), Orientation::CounterClockwise);
}
#[test]
fn get_set_and_accessors() {
let mut p = Polygon::new(PolygonType::Unknown);
p.add_vertices(&pts(&[(1.0, 2.0), (3.0, 4.0)]));
assert_eq!(p.len(), 2);
assert_eq!(p.polygon_type(), PolygonType::Unknown);
assert_eq!(p.get(0), Wgs84::new(1.0, 2.0));
p.set(0, Wgs84::new(5.0, 6.0));
assert_eq!(p.get(0), Wgs84::new(5.0, 6.0));
p.set_type(PolygonType::SimplePolygon);
assert_eq!(p.polygon_type(), PolygonType::SimplePolygon);
assert_eq!(p.vertices().len(), 2);
p.vertices_mut().push(Wgs84::new(7.0, 8.0));
assert_eq!(p.len(), 3);
}
}