ruby-geometry 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
@@ -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
@@ -8,4 +8,8 @@ module Geometry
8
8
  x === another_point.x && y === another_point.y
9
9
  end
10
10
  end
11
- end
11
+ end
12
+
13
+ def Point(x, y)
14
+ Geometry::Point.new(x, y)
15
+ end
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
@@ -102,4 +102,8 @@ module Geometry
102
102
  self.to_vector.cross_product(vector_to_second_endpoint) <= 0
103
103
  end
104
104
  end
105
- end
105
+ end
106
+
107
+ def Segment(point1, point2)
108
+ Geometry::Segment.new point1, point2
109
+ end
@@ -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
- prerelease: false
5
- segments:
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
- files:
30
- - lib/point.rb
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/overlaps_test.rb
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/bounds_test.rb
47
- - test/segment/initialize_test.rb
48
- - test/line/x_intercept_test.rb
49
- - test/line/y_intercept_test.rb
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
- segments:
80
- - 0
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
- segments:
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.3.7
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/overlaps_test.rb
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/bounds_test.rb
108
- - test/segment/initialize_test.rb
109
- - test/line/x_intercept_test.rb
110
- - test/line/y_intercept_test.rb
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