ruby-geometry 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/algorithms/point_in_polygon.rb +64 -0
- data/lib/bounding_box.rb +11 -0
- data/lib/geometry.rb +5 -0
- data/lib/point.rb +5 -1
- data/lib/polygon.rb +17 -1
- data/lib/segment.rb +5 -1
- data/test/bounding_box/contains_test.rb +29 -0
- data/test/polygon/bounding_box_test.rb +43 -0
- data/test/polygon/contains_point_test.rb +73 -0
- metadata +72 -82
@@ -0,0 +1,64 @@
|
|
1
|
+
module Geometry
|
2
|
+
class PointInPolygon < Struct.new(:point, :polygon)
|
3
|
+
extend ActiveSupport::Memoizable
|
4
|
+
|
5
|
+
def inside?
|
6
|
+
point_location == :inside
|
7
|
+
end
|
8
|
+
|
9
|
+
def outside?
|
10
|
+
point_location == :outside
|
11
|
+
end
|
12
|
+
|
13
|
+
def on_the_boundary?
|
14
|
+
point_location == :on_the_boundary
|
15
|
+
end
|
16
|
+
|
17
|
+
def point_location
|
18
|
+
return :outside unless bounding_box.contains?(point)
|
19
|
+
return :on_the_boundary if point_is_vertex? || point_on_edge?
|
20
|
+
|
21
|
+
intersection_count(choose_good_ray).odd? ? :inside : :outside
|
22
|
+
end
|
23
|
+
|
24
|
+
delegate :vertices, :edges, :bounding_box, :to => :polygon
|
25
|
+
memoize :point_location, :edges, :bounding_box
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def point_is_vertex?
|
30
|
+
vertices.any? { |vertex| vertex == point }
|
31
|
+
end
|
32
|
+
|
33
|
+
def point_on_edge?
|
34
|
+
edges.any? { |edge| edge.contains_point?(point) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def choose_good_ray
|
38
|
+
ray = random_ray
|
39
|
+
while ! good_ray?(ray) do
|
40
|
+
ray = random_ray
|
41
|
+
end
|
42
|
+
ray
|
43
|
+
end
|
44
|
+
|
45
|
+
def good_ray?(ray)
|
46
|
+
edges.none? { |edge| edge.parallel_to?(ray) } && vertices.none? { |vertex| ray.contains_point?(vertex) }
|
47
|
+
end
|
48
|
+
|
49
|
+
def intersection_count(ray)
|
50
|
+
edges.select { |edge| edge.intersects_with?(ray) }.size
|
51
|
+
end
|
52
|
+
|
53
|
+
def random_ray
|
54
|
+
random_direction = rand * (2 * Math::PI)
|
55
|
+
|
56
|
+
ray_endpoint = Point sufficient_ray_radius * Math.cos(random_direction), sufficient_ray_radius * Math.sin(random_direction)
|
57
|
+
Segment point, ray_endpoint
|
58
|
+
end
|
59
|
+
|
60
|
+
def sufficient_ray_radius
|
61
|
+
@sufficient_ray_radius ||= bounding_box.diagonal.length * 2
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/bounding_box.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
module Geometry
|
2
|
+
class BoundingBox < Struct.new(:leftbottom, :righttop)
|
3
|
+
def diagonal
|
4
|
+
Segment leftbottom, righttop
|
5
|
+
end
|
6
|
+
|
7
|
+
def contains?(point)
|
8
|
+
point.x.between?(leftbottom.x, righttop.x) && point.y.between?(leftbottom.y, righttop.y)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/lib/geometry.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
+
require "active_support/core_ext/module/delegation"
|
2
|
+
require "active_support/memoizable"
|
3
|
+
|
1
4
|
require 'point'
|
2
5
|
require 'segment'
|
3
6
|
require 'vector'
|
4
7
|
require 'polygon'
|
5
8
|
require 'line'
|
9
|
+
require 'bounding_box'
|
10
|
+
require 'algorithms/point_in_polygon'
|
6
11
|
|
7
12
|
module Geometry
|
8
13
|
include Math
|
data/lib/point.rb
CHANGED
data/lib/polygon.rb
CHANGED
@@ -9,5 +9,21 @@ module Geometry
|
|
9
9
|
|
10
10
|
edges << Segment.new(vertices.last, vertices.first)
|
11
11
|
end
|
12
|
+
|
13
|
+
def bounding_box
|
14
|
+
leftbottom = Point vertices.map(&:x).min, vertices.map(&:y).min
|
15
|
+
righttop = Point vertices.map(&:x).max, vertices.map(&:y).max
|
16
|
+
|
17
|
+
BoundingBox.new leftbottom, righttop
|
18
|
+
end
|
19
|
+
|
20
|
+
def contains?(point)
|
21
|
+
point_in_polygon = PointInPolygon.new(point, self)
|
22
|
+
point_in_polygon.inside? || point_in_polygon.on_the_boundary?
|
23
|
+
end
|
12
24
|
end
|
13
|
-
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def Polygon(vertices)
|
28
|
+
Geometry::Polygon.new(vertices)
|
29
|
+
end
|
data/lib/segment.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'geometry'
|
3
|
+
|
4
|
+
class ContainsTest < Test::Unit::TestCase
|
5
|
+
include Geometry
|
6
|
+
|
7
|
+
def bounding_box
|
8
|
+
BoundingBox.new Point(-1, -1), Point(1, 1)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_contains
|
12
|
+
assert bounding_box.contains?(Point(0, 0))
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_does_not_contain
|
16
|
+
assert ! bounding_box.contains?(Point(2, 2))
|
17
|
+
assert ! bounding_box.contains?(Point(-2, -2))
|
18
|
+
assert ! bounding_box.contains?(Point(1, 2))
|
19
|
+
assert ! bounding_box.contains?(Point(0, -1.1))
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_on_the_edge
|
23
|
+
assert bounding_box.contains?(Point(1, 0))
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_at_vertex
|
27
|
+
assert bounding_box.contains?(Point(1, 1))
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'geometry'
|
3
|
+
|
4
|
+
class BoundingBoxTest < Test::Unit::TestCase
|
5
|
+
include Geometry
|
6
|
+
|
7
|
+
def test_rectangle
|
8
|
+
polygon = Polygon [
|
9
|
+
Point(-1, -1),
|
10
|
+
Point(1, -1),
|
11
|
+
Point(1, 1),
|
12
|
+
Point(-1, 1)
|
13
|
+
]
|
14
|
+
assert polygon.bounding_box.leftbottom == Point(-1, -1)
|
15
|
+
assert polygon.bounding_box.righttop == Point(1, 1)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_sloped_rectangle
|
19
|
+
polygon = Polygon [
|
20
|
+
Point(-1, 0),
|
21
|
+
Point(0, -1),
|
22
|
+
Point(1, 0),
|
23
|
+
Point(0, 1)
|
24
|
+
]
|
25
|
+
|
26
|
+
assert polygon.bounding_box.leftbottom == Point(-1, -1)
|
27
|
+
assert polygon.bounding_box.righttop == Point(1, 1)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_nonconvex
|
31
|
+
polygon = Polygon [
|
32
|
+
Point(0, 2),
|
33
|
+
Point(2, 0),
|
34
|
+
Point(5, 1),
|
35
|
+
Point(3, 8),
|
36
|
+
Point(3, 1),
|
37
|
+
Point(1, 4)
|
38
|
+
]
|
39
|
+
|
40
|
+
assert polygon.bounding_box.leftbottom == Point(0, 0)
|
41
|
+
assert polygon.bounding_box.righttop == Point(5, 8)
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'geometry'
|
3
|
+
|
4
|
+
class ContainsPointTest < Test::Unit::TestCase
|
5
|
+
include Geometry
|
6
|
+
|
7
|
+
def test_convex
|
8
|
+
rectangle = Polygon.new [
|
9
|
+
Point(0, 0),
|
10
|
+
Point(1, 0),
|
11
|
+
Point(1, 1),
|
12
|
+
Point(0, 1)
|
13
|
+
]
|
14
|
+
|
15
|
+
|
16
|
+
inner = Point(0.5, 0.5)
|
17
|
+
assert rectangle.contains?(inner)
|
18
|
+
|
19
|
+
outer = Point(1.5, 1.5)
|
20
|
+
assert ! rectangle.contains?(outer)
|
21
|
+
|
22
|
+
on_edge = Point(0.5, 1)
|
23
|
+
assert rectangle.contains?(on_edge)
|
24
|
+
|
25
|
+
at_vertex = Point(1, 1)
|
26
|
+
assert rectangle.contains?(at_vertex)
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
# +--------+
|
31
|
+
# | |
|
32
|
+
# | +---+
|
33
|
+
# | |
|
34
|
+
# | +---+
|
35
|
+
# | |
|
36
|
+
# +--------+
|
37
|
+
|
38
|
+
def test_nonconvex
|
39
|
+
nonconvex_polygon = Polygon.new [
|
40
|
+
Point(0, 0),
|
41
|
+
Point(0, 6),
|
42
|
+
Point(4, 6),
|
43
|
+
Point(4, 4),
|
44
|
+
Point(2, 4),
|
45
|
+
Point(2, 2),
|
46
|
+
Point(4, 2),
|
47
|
+
Point(4, 0)
|
48
|
+
]
|
49
|
+
|
50
|
+
inner_points = [
|
51
|
+
Point(1, 5),
|
52
|
+
Point(3, 5),
|
53
|
+
Point(1, 3),
|
54
|
+
Point(1, 1),
|
55
|
+
Point(3, 1)
|
56
|
+
]
|
57
|
+
|
58
|
+
outer_points = [
|
59
|
+
Point(7, 5),
|
60
|
+
Point(5, 3),
|
61
|
+
Point(7, 3),
|
62
|
+
Point(7, 1)
|
63
|
+
]
|
64
|
+
|
65
|
+
inner_points.each do |inner_point|
|
66
|
+
assert nonconvex_polygon.contains?(inner_point)
|
67
|
+
end
|
68
|
+
|
69
|
+
outer_points.each do |outer_point|
|
70
|
+
assert ! nonconvex_polygon.contains?(outer_point)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
metadata
CHANGED
@@ -1,125 +1,115 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-geometry
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 0
|
8
|
-
- 2
|
9
|
-
version: 0.0.2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
prerelease:
|
10
6
|
platform: ruby
|
11
|
-
authors:
|
7
|
+
authors:
|
12
8
|
- Daniel Vartanov
|
13
9
|
autorequire:
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
|
-
|
17
|
-
date: 2010-10-17 00:00:00 -04:00
|
18
|
-
default_executable:
|
12
|
+
date: 2011-12-04 00:00:00.000000000Z
|
19
13
|
dependencies: []
|
20
|
-
|
21
14
|
description:
|
22
15
|
email: daniel.vartanov@gmail.com
|
23
16
|
executables: []
|
24
|
-
|
25
17
|
extensions: []
|
26
|
-
|
27
18
|
extra_rdoc_files: []
|
28
|
-
|
29
|
-
|
30
|
-
- lib/
|
31
|
-
- lib/line.rb
|
19
|
+
files:
|
20
|
+
- lib/algorithms/point_in_polygon.rb
|
21
|
+
- lib/bounding_box.rb
|
32
22
|
- lib/comparison_with_precision.rb
|
33
|
-
- lib/polygon.rb
|
34
|
-
- lib/vector.rb
|
35
23
|
- lib/geometry.rb
|
24
|
+
- lib/line.rb
|
25
|
+
- lib/point.rb
|
26
|
+
- lib/polygon.rb
|
36
27
|
- lib/segment.rb
|
28
|
+
- lib/vector.rb
|
29
|
+
- test/bounding_box/contains_test.rb
|
37
30
|
- test/geometry/distance_test.rb
|
31
|
+
- test/line/angle_to_test.rb
|
32
|
+
- test/line/horizontal_test.rb
|
33
|
+
- test/line/initialize_test.rb
|
34
|
+
- test/line/intersect_x_test.rb
|
35
|
+
- test/line/parallel_to_test.rb
|
36
|
+
- test/line/slope_test.rb
|
37
|
+
- test/line/vertical_test.rb
|
38
|
+
- test/line/x_intercept_test.rb
|
39
|
+
- test/line/y_intercept_test.rb
|
40
|
+
- test/point/equals_test.rb
|
41
|
+
- test/point/initialize_test.rb
|
42
|
+
- test/polygon/bounding_box_test.rb
|
43
|
+
- test/polygon/contains_point_test.rb
|
38
44
|
- test/polygon/edges_test.rb
|
45
|
+
- test/segment/bounds_test.rb
|
39
46
|
- test/segment/contains_point_test.rb
|
40
|
-
- test/segment/
|
41
|
-
- test/segment/to_vector_test.rb
|
42
|
-
- test/segment/length_test.rb
|
47
|
+
- test/segment/initialize_test.rb
|
43
48
|
- test/segment/intersection_point_with_test.rb
|
44
|
-
- test/segment/parallel_to_test.rb
|
45
49
|
- test/segment/intersects_with_test.rb
|
46
|
-
- test/segment/
|
47
|
-
- test/segment/
|
48
|
-
- test/
|
49
|
-
- test/
|
50
|
-
- test/line/parallel_to_test.rb
|
51
|
-
- test/line/angle_to_test.rb
|
52
|
-
- test/line/initialize_test.rb
|
53
|
-
- test/line/vertical_test.rb
|
54
|
-
- test/line/slope_test.rb
|
55
|
-
- test/line/intersect_x_test.rb
|
56
|
-
- test/line/horizontal_test.rb
|
57
|
-
- test/vector/equals_test.rb
|
58
|
-
- test/vector/modulus_test.rb
|
50
|
+
- test/segment/length_test.rb
|
51
|
+
- test/segment/overlaps_test.rb
|
52
|
+
- test/segment/parallel_to_test.rb
|
53
|
+
- test/segment/to_vector_test.rb
|
59
54
|
- test/vector/arithmetics_test.rb
|
55
|
+
- test/vector/collinear_with_test.rb
|
60
56
|
- test/vector/cross_product_test.rb
|
57
|
+
- test/vector/equals_test.rb
|
58
|
+
- test/vector/modulus_test.rb
|
61
59
|
- test/vector/scalar_product_test.rb
|
62
|
-
- test/vector/collinear_with_test.rb
|
63
|
-
- test/point/equals_test.rb
|
64
|
-
- test/point/initialize_test.rb
|
65
|
-
has_rdoc: true
|
66
60
|
homepage: http://github.com/DanielVartanov/ruby-geometry
|
67
61
|
licenses: []
|
68
|
-
|
69
62
|
post_install_message:
|
70
63
|
rdoc_options: []
|
71
|
-
|
72
|
-
require_paths:
|
64
|
+
require_paths:
|
73
65
|
- lib
|
74
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
67
|
none: false
|
76
|
-
requirements:
|
77
|
-
- -
|
78
|
-
- !ruby/object:Gem::Version
|
79
|
-
|
80
|
-
|
81
|
-
version: "0"
|
82
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ! '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
73
|
none: false
|
84
|
-
requirements:
|
85
|
-
- -
|
86
|
-
- !ruby/object:Gem::Version
|
87
|
-
|
88
|
-
- 0
|
89
|
-
version: "0"
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
90
78
|
requirements: []
|
91
|
-
|
92
79
|
rubyforge_project:
|
93
|
-
rubygems_version: 1.
|
80
|
+
rubygems_version: 1.8.10
|
94
81
|
signing_key:
|
95
82
|
specification_version: 3
|
96
83
|
summary: Implementation of basic 2D geometry algorithms in Ruby
|
97
|
-
test_files:
|
84
|
+
test_files:
|
85
|
+
- test/bounding_box/contains_test.rb
|
98
86
|
- test/geometry/distance_test.rb
|
87
|
+
- test/line/angle_to_test.rb
|
88
|
+
- test/line/horizontal_test.rb
|
89
|
+
- test/line/initialize_test.rb
|
90
|
+
- test/line/intersect_x_test.rb
|
91
|
+
- test/line/parallel_to_test.rb
|
92
|
+
- test/line/slope_test.rb
|
93
|
+
- test/line/vertical_test.rb
|
94
|
+
- test/line/x_intercept_test.rb
|
95
|
+
- test/line/y_intercept_test.rb
|
96
|
+
- test/point/equals_test.rb
|
97
|
+
- test/point/initialize_test.rb
|
98
|
+
- test/polygon/bounding_box_test.rb
|
99
|
+
- test/polygon/contains_point_test.rb
|
99
100
|
- test/polygon/edges_test.rb
|
101
|
+
- test/segment/bounds_test.rb
|
100
102
|
- test/segment/contains_point_test.rb
|
101
|
-
- test/segment/
|
102
|
-
- test/segment/to_vector_test.rb
|
103
|
-
- test/segment/length_test.rb
|
103
|
+
- test/segment/initialize_test.rb
|
104
104
|
- test/segment/intersection_point_with_test.rb
|
105
|
-
- test/segment/parallel_to_test.rb
|
106
105
|
- test/segment/intersects_with_test.rb
|
107
|
-
- test/segment/
|
108
|
-
- test/segment/
|
109
|
-
- test/
|
110
|
-
- test/
|
111
|
-
- test/line/parallel_to_test.rb
|
112
|
-
- test/line/angle_to_test.rb
|
113
|
-
- test/line/initialize_test.rb
|
114
|
-
- test/line/vertical_test.rb
|
115
|
-
- test/line/slope_test.rb
|
116
|
-
- test/line/intersect_x_test.rb
|
117
|
-
- test/line/horizontal_test.rb
|
118
|
-
- test/vector/equals_test.rb
|
119
|
-
- test/vector/modulus_test.rb
|
106
|
+
- test/segment/length_test.rb
|
107
|
+
- test/segment/overlaps_test.rb
|
108
|
+
- test/segment/parallel_to_test.rb
|
109
|
+
- test/segment/to_vector_test.rb
|
120
110
|
- test/vector/arithmetics_test.rb
|
111
|
+
- test/vector/collinear_with_test.rb
|
121
112
|
- test/vector/cross_product_test.rb
|
113
|
+
- test/vector/equals_test.rb
|
114
|
+
- test/vector/modulus_test.rb
|
122
115
|
- test/vector/scalar_product_test.rb
|
123
|
-
- test/vector/collinear_with_test.rb
|
124
|
-
- test/point/equals_test.rb
|
125
|
-
- test/point/initialize_test.rb
|