diff --git a/lib/algorithms/point_in_polygon.rb b/lib/algorithms/point_in_polygon.rb index b4e76a5..d43fd41 100644 --- a/lib/algorithms/point_in_polygon.rb +++ b/lib/algorithms/point_in_polygon.rb @@ -1,7 +1,9 @@ module Geometry class PointInPolygon < Struct.new(:point, :polygon) extend Memoist - + + MAX_SEED = 1000000.0 + def inside? point_location == :inside end @@ -36,8 +38,10 @@ def point_on_edge? def choose_good_ray ray = random_ray - while ! good_ray?(ray) do - ray = random_ray + seed = 1.0 + while (seed < MAX_SEED) && (! good_ray?(ray)) do + seed += 1.0 + ray = random_ray(seed) end ray end @@ -45,14 +49,14 @@ def choose_good_ray 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) - + + def random_ray(seed = 1.0) + random_direction = (Math::PI / 2.0 * seed + Math::PI / (seed + 1.0)) % (Math::PI * 2.0) + ray_endpoint = Point sufficient_ray_radius * Math.cos(random_direction), sufficient_ray_radius * Math.sin(random_direction) Segment point, ray_endpoint end diff --git a/test/polygon/contains_point_test.rb b/test/polygon/contains_point_test.rb index ef98158..626cccb 100644 --- a/test/polygon/contains_point_test.rb +++ b/test/polygon/contains_point_test.rb @@ -70,4 +70,21 @@ def test_nonconvex assert ! nonconvex_polygon.contains?(outer_point) end end + + def test_closed_polygon + coordinates_raw = "114.146011,22.284090 114.145050,22.282860 114.146240,22.279619 114.151176,22.277014 114.151833,22.276251 114.151443,22.275000 114.151550,22.273470 114.153198,22.272249 114.156143,22.272970 114.161690,22.271900 114.162521,22.274820 114.162697,22.276211 114.160957,22.275850 114.159927,22.276190 114.158340,22.276270 114.157082,22.276661 114.155312,22.276310 114.153732,22.278000 114.154312,22.278870 114.153992,22.279900 114.152321,22.280890 114.150261,22.282721 114.146011,22.284090" + coordinates = coordinates_raw.split(/\s+/).map do |raw_coor| + # latitude and longitude are reversed since they are from KML + # most important point : the last point is the same as the first one + raw_coor.split(',').map(&:to_f).reverse + end + + vertices = coordinates.map do |coordinate_array| + Point.new_by_array(coordinate_array) + end + rectangle = Polygon.new(vertices) + + inner = Point(22.2817, 114.14932) + assert rectangle.contains?(inner) + end end