From 265f35b2d347e7902502741655ce5daeb396a823 Mon Sep 17 00:00:00 2001 From: swipesense Date: Sat, 6 May 2017 12:13:50 -0500 Subject: [PATCH 1/3] deterministic algorithm to determine whether a point is in a polygon --- lib/geometry/polygon.rb | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/geometry/polygon.rb b/lib/geometry/polygon.rb index 87d931e..aaad8ec 100644 --- a/lib/geometry/polygon.rb +++ b/lib/geometry/polygon.rb @@ -13,13 +13,25 @@ def edges def bounding_box leftbottom = Point vertices.map(&:x).min, vertices.map(&:y).min righttop = Point vertices.map(&:x).max, vertices.map(&:y).max - + BoundingBox.new leftbottom, righttop end def contains?(point) - point_in_polygon = PointInPolygon.new(point, self) - point_in_polygon.inside? || point_in_polygon.on_the_boundary? + # Algorithm source: + # https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html + + result = false + + vertices.each_with_index do |vertex, i| + previous_vertex = vertices[i - 1] || vertex.last + if ((vertex.y > point.y) != (previous_vertex.y > point.y)) && + (point.x < (previous_vertex.x - vertex.x) * (point.y - vertex.y) / (previous_vertex.y - vertex.y) + vertex.x) + result = !result + end + end + + result end def area From bc692341b0ea2e43868370d5091b49d462f7a062 Mon Sep 17 00:00:00 2001 From: swipesense Date: Sat, 6 May 2017 12:29:51 -0500 Subject: [PATCH 2/3] use previous method of edge checking --- lib/geometry/algorithms/point_in_polygon.rb | 40 ++++++++------------- lib/geometry/polygon.rb | 18 ++-------- 2 files changed, 18 insertions(+), 40 deletions(-) diff --git a/lib/geometry/algorithms/point_in_polygon.rb b/lib/geometry/algorithms/point_in_polygon.rb index b4e76a5..4634cde 100644 --- a/lib/geometry/algorithms/point_in_polygon.rb +++ b/lib/geometry/algorithms/point_in_polygon.rb @@ -1,7 +1,7 @@ module Geometry class PointInPolygon < Struct.new(:point, :polygon) extend Memoist - + def inside? point_location == :inside end @@ -18,7 +18,7 @@ def point_location return :outside unless bounding_box.contains?(point) return :on_the_boundary if point_is_vertex? || point_on_edge? - intersection_count(choose_good_ray).odd? ? :inside : :outside + point_inside_polygon? ? :inside : :outside end delegate :vertices, :edges, :bounding_box, :to => :polygon @@ -34,31 +34,21 @@ def point_on_edge? edges.any? { |edge| edge.contains_point?(point) } end - def choose_good_ray - ray = random_ray - while ! good_ray?(ray) do - ray = random_ray - end - ray - end + def point_inside_polygon? + # Algorithm source: + # https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html - def good_ray?(ray) - edges.none? { |edge| !edge.length.zero? && edge.parallel_to?(ray) } && vertices.none? { |vertex| ray.contains_point?(vertex) } - end - - def intersection_count(ray) - edges.select { |edge| edge.intersects_with?(ray) }.size - end - - def random_ray - random_direction = rand * (2 * Math::PI) - - ray_endpoint = Point sufficient_ray_radius * Math.cos(random_direction), sufficient_ray_radius * Math.sin(random_direction) - Segment point, ray_endpoint - end + result = false + + vertices.each_with_index do |vertex, i| + previous_vertex = vertices[i - 1] || vertex.last + if ((vertex.y > point.y) != (previous_vertex.y > point.y)) && + (point.x < (previous_vertex.x - vertex.x) * (point.y - vertex.y) / (previous_vertex.y - vertex.y) + vertex.x) + result = !result + end + end - def sufficient_ray_radius - @sufficient_ray_radius ||= bounding_box.diagonal.length * 2 + result end end end diff --git a/lib/geometry/polygon.rb b/lib/geometry/polygon.rb index aaad8ec..87d931e 100644 --- a/lib/geometry/polygon.rb +++ b/lib/geometry/polygon.rb @@ -13,25 +13,13 @@ def edges def bounding_box leftbottom = Point vertices.map(&:x).min, vertices.map(&:y).min righttop = Point vertices.map(&:x).max, vertices.map(&:y).max - + BoundingBox.new leftbottom, righttop end def contains?(point) - # Algorithm source: - # https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html - - result = false - - vertices.each_with_index do |vertex, i| - previous_vertex = vertices[i - 1] || vertex.last - if ((vertex.y > point.y) != (previous_vertex.y > point.y)) && - (point.x < (previous_vertex.x - vertex.x) * (point.y - vertex.y) / (previous_vertex.y - vertex.y) + vertex.x) - result = !result - end - end - - result + point_in_polygon = PointInPolygon.new(point, self) + point_in_polygon.inside? || point_in_polygon.on_the_boundary? end def area From 0549ceb6948c78259364222cce703b59f2d3833c Mon Sep 17 00:00:00 2001 From: swipesense Date: Sat, 6 May 2017 13:03:57 -0500 Subject: [PATCH 3/3] Fix style error --- lib/geometry/algorithms/point_in_polygon.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/geometry/algorithms/point_in_polygon.rb b/lib/geometry/algorithms/point_in_polygon.rb index 4634cde..c728b08 100644 --- a/lib/geometry/algorithms/point_in_polygon.rb +++ b/lib/geometry/algorithms/point_in_polygon.rb @@ -43,7 +43,8 @@ def point_inside_polygon? vertices.each_with_index do |vertex, i| previous_vertex = vertices[i - 1] || vertex.last if ((vertex.y > point.y) != (previous_vertex.y > point.y)) && - (point.x < (previous_vertex.x - vertex.x) * (point.y - vertex.y) / (previous_vertex.y - vertex.y) + vertex.x) + (point.x < (previous_vertex.x - vertex.x) * (point.y - vertex.y) / + (previous_vertex.y - vertex.y) + vertex.x) result = !result end end