Skip to content

Commit

Permalink
polygon
Browse files Browse the repository at this point in the history
  • Loading branch information
coado committed Jul 17, 2024
1 parent da0179d commit 2516c5c
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 25 deletions.
5 changes: 0 additions & 5 deletions src/geometry/convex_hull.rs

This file was deleted.

2 changes: 1 addition & 1 deletion src/geometry/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pub mod convex_hull;
pub mod objects;
pub mod polygon;
31 changes: 12 additions & 19 deletions src/geometry/objects.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,18 @@
use std::cmp::Ordering;

const EPS: f64 = 1e-9;
pub const EPS: f64 = 1e-9;

#[derive(Debug, Clone, Default, Copy)]
pub struct Point2D {
x: f64,
y: f64,
pub x: f64,
pub y: f64,
}

impl Point2D {
pub fn new(x: f64, y: f64) -> Self {
Self { x, y }
}

pub fn x(&self) -> &f64 {
&self.x
}

pub fn y(&self) -> &f64 {
&self.y
}

pub fn set_x(&mut self, x: f64) {
self.x = x;
}

pub fn set_y(&mut self, y: f64) {
self.y = y;
}

pub fn distance(&self, other: &Self) -> f64 {
((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt()
}
Expand All @@ -53,6 +37,7 @@ impl PartialEq for Point2D {
}

impl PartialOrd for Point2D {
#[allow(clippy::non_canonical_partial_ord_impl)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if (self.x - other.x).abs() < EPS {
if (self.y - other.y).abs() < EPS {
Expand All @@ -70,6 +55,14 @@ impl PartialOrd for Point2D {
}
}

impl Eq for Point2D {}

impl Ord for Point2D {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}

#[derive(Debug, Clone, Default, Copy)]

pub struct Line2D {
Expand Down
229 changes: 229 additions & 0 deletions src/geometry/polygon.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
use super::objects::{Point2D, Vector2D, EPS};
use std::cmp::Ordering;
use std::f64::consts::PI;

pub fn is_convex(points: Vec<Point2D>) -> bool {
let n = points.len();

if n <= 3 {
return false;
}

let first_turn = Vector2D::ccw(&points[0], &points[1], &points[2]);

for (i, _) in points.iter().enumerate().skip(1) {
let j = (i + 1) % n;
let k = (i + 2) % n;

if Vector2D::ccw(&points[i], &points[j], &points[k]) != first_turn {
return false;
}
}

true
}

#[derive(Debug, PartialEq)]
pub enum InPolygon {
Inside,
OnEdge,
Outside,
}

pub fn in_polygon(pt: &Point2D, points: Vec<Point2D>) -> InPolygon {
let n = points.len();
if n <= 3 {
return InPolygon::Outside;
}

for i in 0..n {
let j = (i + 1) % n;

let a = points[i];
let b = points[j];

if a.distance(pt) + b.distance(pt) - a.distance(&b) < EPS {
return InPolygon::OnEdge;
}
}

let mut sum = 0.0;
for i in 0..n {
let j = (i + 1) % n;

let a = points[i];
let b = points[j];
let vector_a = Vector2D::from_points(pt, &a);
let vector_b = Vector2D::from_points(pt, &b);
let angle = vector_a.angle(&vector_b);

if Vector2D::ccw(pt, &a, &b) {
sum += angle;
} else {
sum -= angle;
}
}

if (sum - 2.0 * PI).abs() < EPS {
InPolygon::Inside
} else {
InPolygon::Outside
}
}

pub fn convex_hull(mut pts: Vec<Point2D>) -> Vec<Point2D> {
pts.sort_by(|a, b| {
if a.x < b.x {
Ordering::Less
} else if a.x > b.x {
Ordering::Greater
} else {
a.y.partial_cmp(&b.y).unwrap()
}
});

let dummy = Point2D::default();
let mut hull: Vec<Point2D> = vec![dummy; 2 * pts.len()];
let mut i = 0;

for pt in pts.iter() {
while i >= 2 && !Vector2D::ccw(&hull[i - 1], &hull[i - 2], pt) {
i -= 1
}
hull[i] = *pt;
i += 1;
}

let j = i + 1;
for pt in pts.iter().rev() {
while i >= j && !Vector2D::ccw(&hull[i - 1], &hull[i - 2], pt) {
i -= 1
}
hull[i] = *pt;
i += 1;
}

hull.resize(i - 1, dummy);
hull
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_is_convex() {
let points = vec![
Point2D::new(0.0, 0.0),
Point2D::new(1.0, 0.0),
Point2D::new(1.0, 1.0),
Point2D::new(0.0, 1.0),
];

assert_eq!(is_convex(points), true);

let points = vec![
Point2D::new(0.0, 0.0),
Point2D::new(1.0, 0.0),
Point2D::new(0.5, 0.5),
Point2D::new(0.0, 1.0),
];

assert_eq!(is_convex(points), false);
}

#[test]
fn test_in_polygon() {
let points = vec![
Point2D::new(0.0, 0.0),
Point2D::new(1.0, 0.0),
Point2D::new(1.0, 1.0),
Point2D::new(0.0, 1.0),
];

let pt = Point2D::new(0.5, 0.5);
assert_eq!(in_polygon(&pt, points.clone()), InPolygon::Inside);

let pt = Point2D::new(0.0, 0.0);
assert_eq!(in_polygon(&pt, points.clone()), InPolygon::OnEdge);

let pt = Point2D::new(0.5, 0.0);
assert_eq!(in_polygon(&pt, points.clone()), InPolygon::OnEdge);

let pt = Point2D::new(0.5, 1.0);
assert_eq!(in_polygon(&pt, points.clone()), InPolygon::OnEdge);

let pt = Point2D::new(0.0, 0.5);
assert_eq!(in_polygon(&pt, points.clone()), InPolygon::OnEdge);

let pt = Point2D::new(1.0, 0.5);
assert_eq!(in_polygon(&pt, points.clone()), InPolygon::OnEdge);

let pt = Point2D::new(1.0, 1.0);
assert_eq!(in_polygon(&pt, points.clone()), InPolygon::OnEdge);

let pt = Point2D::new(0.0, 1.0);
assert_eq!(in_polygon(&pt, points.clone()), InPolygon::OnEdge);

let pt = Point2D::new(1.5, 0.5);
assert_eq!(in_polygon(&pt, points.clone()), InPolygon::Outside);

let pt = Point2D::new(0.5, 1.5);
assert_eq!(in_polygon(&pt, points.clone()), InPolygon::Outside);

let pt = Point2D::new(-0.5, 0.5);
assert_eq!(in_polygon(&pt, points.clone()), InPolygon::Outside);
}

#[test]
fn test_convex_hull() {
let points = vec![
Point2D::new(0.0, 0.0),
Point2D::new(1.0, 0.0),
Point2D::new(1.0, 1.0),
Point2D::new(0.0, 1.0),
Point2D::new(0.5, 0.5),
];

let mut hull = convex_hull(points);
let mut expected = vec![
Point2D::new(0.0, 0.0),
Point2D::new(1.0, 0.0),
Point2D::new(1.0, 1.0),
Point2D::new(0.0, 1.0),
];

hull.sort();
expected.sort();

assert_eq!(hull, expected);
}

#[test]
fn test_convex_hull2() {
let points = vec![
Point2D::new(0.0, 0.0),
Point2D::new(1.0, 0.0),
Point2D::new(1.0, 1.0),
Point2D::new(0.0, 1.0),
Point2D::new(0.5, 0.5),
Point2D::new(0.5, 0.0),
Point2D::new(0.5, 1.0),
Point2D::new(0.0, 0.5),
Point2D::new(1.0, 0.5),
];

let mut hull = convex_hull(points);
let mut expected = vec![
Point2D::new(0.0, 0.0),
Point2D::new(1.0, 0.0),
Point2D::new(1.0, 1.0),
Point2D::new(0.0, 1.0),
];

hull.sort();
expected.sort();

assert_eq!(hull, expected);
}
}

0 comments on commit 2516c5c

Please sign in to comment.