geometry 2 → 3

Sign up to get free protection for your applications and to get access to all the features.
@@ -21,6 +21,7 @@ Primitives
21
21
  - Point
22
22
  - Line
23
23
  - Circle
24
+ - Rectangle
24
25
 
25
26
  Examples
26
27
  --------
@@ -63,5 +64,5 @@ Examples
63
64
  ### Circle
64
65
  ```ruby
65
66
  # A circle at Point[1,2] with a radius of 3
66
- circle = Geometry::Circle [1,2], 3
67
+ circle = Geometry::Circle.new [1,2], 3
67
68
  ```
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "geometry"
6
- s.version = '2'
6
+ s.version = '3'
7
7
  s.authors = ["Brandon Fosdick"]
8
8
  s.email = ["bfoz@bfoz.net"]
9
9
  s.homepage = "http://github.com/bfoz/geometry"
@@ -2,6 +2,8 @@ require_relative 'geometry/circle'
2
2
  require_relative 'geometry/point'
3
3
  require_relative 'geometry/line'
4
4
  require_relative 'geometry/polygon'
5
+ require_relative 'geometry/rectangle'
6
+ require_relative 'geometry/size'
5
7
 
6
8
  module Geometry
7
9
  # @overload Line(array0, array1)
@@ -4,14 +4,18 @@ module Geometry
4
4
 
5
5
  =begin rdoc
6
6
  Circles come in all shapes and sizes, but they're usually round.
7
-
7
+
8
8
  == Usage
9
- circle = Geometry::Circle [1,1], 2
9
+ circle = Geometry::Circle.new [1,1], 2
10
10
  =end
11
-
11
+
12
12
  class Circle
13
- attr_reader :center, :radius
14
-
13
+ # @return [Point] The Circle's center point
14
+ attr_reader :center
15
+
16
+ # @return [Number] The Circle's radius
17
+ attr_reader :radius
18
+
15
19
  # Construct a new {Circle} from a centerpoint and radius
16
20
  # @param [Point] center The center point of the Circle
17
21
  # @param [Number] radius The radius of the Circle
@@ -55,5 +55,20 @@ geometry class (x, y, z).
55
55
  def z
56
56
  @elements[2]
57
57
  end
58
+
59
+ # @group Arithmetic
60
+ # Override the arithmetic operators to force them to return {Point}s instead
61
+ # of {Vector}s
62
+
63
+ def +(other)
64
+ Point[super]
65
+ end
66
+
67
+ def -(other)
68
+ Point[super]
69
+ end
70
+
71
+ # @endgroup
72
+
58
73
  end
59
74
  end
@@ -0,0 +1,261 @@
1
+ require_relative 'edge'
2
+ require_relative 'point'
3
+ require_relative 'size'
4
+
5
+ module Geometry
6
+ =begin
7
+ The {Rectangle} class cluster represents your typical arrangement of 4 corners and 4 sides.
8
+
9
+ == Usage
10
+
11
+ === Constructors
12
+ rect = Rectangle[[1,2], [2,3]] # Using two corners
13
+ rect = Rectangle[[1,2], Size[1,1]] # Using origin and size
14
+ rect = Rectangle[1,2,2,3] # Using four sides
15
+
16
+ rect = Rectangle[10, 20] # origin = [0,0], size = [10, 20]
17
+ rect = Rectangle[Size[10, 20]] # origin = [0,0], size = [10, 20]
18
+
19
+ =end
20
+
21
+ class Rectangle
22
+ # @return [Point] The {Rectangle}'s center
23
+ attr_reader :center
24
+ # @return [Number] Height of the {Rectangle}
25
+ attr_reader :height
26
+ # @return [Point] The {Rectangle}'s origin
27
+ attr_reader :origin
28
+ # @return [Size] The {Size} of the {Rectangle}
29
+ attr_reader :size
30
+ # @return [Number] Width of the {Rectangle}
31
+ attr_reader :width
32
+
33
+ class << self
34
+ alias :original_new :new
35
+ end
36
+
37
+ # @overload new(width, height)
38
+ # Creates a {Rectangle} of the given width and height, centered on the origin
39
+ # @param [Number] height Height
40
+ # @param [Number] width Width
41
+ # @return [CenteredRectangle]
42
+ # @overload new(size)
43
+ # Creates a {Rectangle} of the given {Size} centered on the origin
44
+ # @param [Size] size Width and height
45
+ # @return [CenteredRectangle]
46
+ # @overload new(point0, point1)
47
+ # Creates a {Rectangle} using the given {Point}s
48
+ # @param [Point,Array] point0 A corner
49
+ # @param [Point,Array] point1 The other corner
50
+ # @overload new(origin, size)
51
+ # Creates a {Rectangle} from the given origin and size
52
+ # @param [Point] origin Lower-left corner
53
+ # @param [Size] size Width and height
54
+ # @return [SizedRectangle]
55
+ # @overload new(left, bottom, right, top)
56
+ # Creates a {Rectangle} from the locations of each side
57
+ # @param [Number] left X-coordinate of the left side
58
+ # @param [Number] bottom Y-coordinate of the bottom edge
59
+ # @param [Number] right X-coordinate of the right side
60
+ # @param [Number] top Y-coordinate of the top edge
61
+ def self.new(*args)
62
+ case args.size
63
+ when 1
64
+ CenteredRectangle.new(args[0])
65
+ when 2
66
+ if args.all? {|a| a.is_a?(Numeric) }
67
+ CenteredRectangle.new(Size[*args])
68
+ elsif args.all? {|a| a.is_a?(Array) || a.is_a?(Point) }
69
+ original_new(*args)
70
+ elsif args[0].is_a?(Point) and args[1].is_a?(Size)
71
+ SizedRectangle.new(*args)
72
+ end
73
+ when 4
74
+ raise ArgumentError unless args.all? {|a| a.is_a?(Numeric)}
75
+ left, bottom, right, top = *args
76
+ original_new(Point[left, bottom], Point[right, top])
77
+ end
78
+ end
79
+
80
+ # Creates a {Rectangle} using the given {Point}s
81
+ # @param [Point] point0 A corner (ie. bottom-left)
82
+ # @param [Point] point1 The other corner (ie. top-right)
83
+ def initialize(point0, point1)
84
+ point0 = point0.is_a?(Point) ? point0 : Point[point0]
85
+ point1 = point1.is_a?(Point) ? point1 : Point[point1]
86
+ raise(ArgumentError, "Point sizes must match") unless point0.size == point1.size
87
+
88
+ # Reorder the points to get lower-left and upper-right
89
+ if (point0.x > point1.x) && (point0.y > point1.y)
90
+ point0, point1 = point1, point0
91
+ else
92
+ if point0.x > point1.x
93
+ point0.x, point1.x = point1.x, point0.x
94
+ end
95
+ if point0.y > point1.y
96
+ point0.y, point1.y = point1.y, point0.y
97
+ end
98
+ end
99
+ @points = [point0, point1]
100
+ end
101
+
102
+ # @group Accessors
103
+ def center
104
+ min, max = @points.minmax {|a,b| a.y <=> b.y}
105
+ Point[(max.x+min.x)/2, (max.y+min.y)/2]
106
+ end
107
+
108
+ # @return [Array<Edge>] The {Rectangle}'s four edges
109
+ def edges
110
+ point0, point2 = *@points
111
+ point1 = Point[point0.x,point2.y]
112
+ point3 = Point[point2.x, point0.y]
113
+ [Edge.new(point0, point1),
114
+ Edge.new(point1, point2),
115
+ Edge.new(point2, point3),
116
+ Edge.new(point3, point0)]
117
+ end
118
+
119
+ def origin
120
+ minx = @points.min {|a,b| a.x <=> b.x}
121
+ miny = @points.min {|a,b| a.y <=> b.y}
122
+ Point[minx.x, miny.y]
123
+ end
124
+
125
+ def height
126
+ min, max = @points.minmax {|a,b| a.y <=> b.y}
127
+ max.y - min.y
128
+ end
129
+
130
+ def width
131
+ min, max = @points.minmax {|a,b| a.x <=> b.x}
132
+ max.x - min.x
133
+ end
134
+ # @endgroup
135
+ end
136
+
137
+ class CenteredRectangle < Rectangle
138
+ # @return [Point] The {Rectangle}'s center
139
+ attr_accessor :center
140
+ attr_reader :origin
141
+ # @return [Size] The {Size} of the {Rectangle}
142
+ attr_accessor :size
143
+
144
+ def self.new(*args)
145
+ original_new(*args)
146
+ end
147
+
148
+ # @overload new(width, height)
149
+ # Creates a {Rectangle} of the given width and height, centered on the origin
150
+ # @param [Number] height Height
151
+ # @param [Number] width Width
152
+ # @return [CenteredRectangle]
153
+ # @overload new(size)
154
+ # Creates a {Rectangle} of the given {Size} centered on the origin
155
+ # @param [Size] size Width and height
156
+ # @return [CenteredRectangle]
157
+ # @overload new(center, size)
158
+ # Creates a {Rectangle} with the given center point and size
159
+ # @param [Point] center
160
+ # @param [Size] size
161
+ def initialize(*args)
162
+ if args[0].is_a?(Size)
163
+ @center = Point[0,0]
164
+ @size = args[0]
165
+ elsif args[0].is_a?(Geometry::Point) and args[1].is_a?(Geometry::Size)
166
+ @center, @size = args[0,1]
167
+ elsif (2 == args.size) and args.all? {|a| a.is_a?(Numeric)}
168
+ @center = Point[0,0]
169
+ @size = Geometry::Size[*args]
170
+ end
171
+ end
172
+
173
+ # @group Accessors
174
+ # @return [Array<Edge>] The {Rectangle}'s four edges
175
+ def edges
176
+ point0 = @center - @size/2
177
+ point2 = @center + @size/2
178
+ point1 = Point[point0.x,point2.y]
179
+ point3 = Point[point2.x, point0.y]
180
+ [Edge.new(point0, point1),
181
+ Edge.new(point1, point2),
182
+ Edge.new(point2, point3),
183
+ Edge.new(point3, point0)]
184
+ end
185
+
186
+ def height
187
+ @size.height
188
+ end
189
+
190
+ def width
191
+ @size.width
192
+ end
193
+ # @endgroup
194
+ end
195
+
196
+ class SizedRectangle < Rectangle
197
+ # @return [Point] The {Rectangle}'s center
198
+ attr_reader :center
199
+ # @return [Point] The {Rectangle}'s origin
200
+ attr_accessor :origin
201
+ # @return [Size] The {Size} of the {Rectangle}
202
+ attr_accessor :size
203
+
204
+ def self.new(*args)
205
+ original_new(*args)
206
+ end
207
+
208
+ # @overload new(width, height)
209
+ # Creates a {Rectangle} of the given width and height with its origin at [0,0]
210
+ # @param [Number] height Height
211
+ # @param [Number] width Width
212
+ # @return SizedRectangle
213
+ # @overload new(size)
214
+ # Creates a {Rectangle} of the given {Size} with its origin at [0,0]
215
+ # @param [Size] size Width and height
216
+ # @return SizedRectangle
217
+ # @overload new(origin, size)
218
+ # Creates a {Rectangle} with the given origin point and size
219
+ # @param [Point] origin
220
+ # @param [Size] size
221
+ # @return SizedRectangle
222
+ def initialize(*args)
223
+ if args[0].is_a?(Size)
224
+ @origin = Point[0,0]
225
+ @size = args[0]
226
+ elsif args[0].is_a?(Geometry::Point) and args[1].is_a?(Geometry::Size)
227
+ @origin, @size = args[0], args[1]
228
+ elsif (2 == args.size) and args.all? {|a| a.is_a?(Numeric)}
229
+ @origin = Point[0,0]
230
+ @size = Geometry::Size[*args]
231
+ end
232
+ end
233
+
234
+
235
+ # @group Accessors
236
+ def center
237
+ @origin + @size/2
238
+ end
239
+
240
+ # @return [Array<Edge>] The {Rectangle}'s four edges
241
+ def edges
242
+ point0 = @origin
243
+ point2 = @origin + @size
244
+ point1 = Point[point0.x,point2.y]
245
+ point3 = Point[point2.x, point0.y]
246
+ [Edge.new(point0, point1),
247
+ Edge.new(point1, point2),
248
+ Edge.new(point2, point3),
249
+ Edge.new(point3, point0)]
250
+ end
251
+
252
+ def height
253
+ @size.height
254
+ end
255
+
256
+ def width
257
+ @size.width
258
+ end
259
+ # @endgroup
260
+ end
261
+ end
@@ -0,0 +1,75 @@
1
+ require 'matrix'
2
+
3
+ module Geometry
4
+ =begin
5
+ An object representing the size of something.
6
+
7
+ Supports all of the familiar {Vector} methods as well as a few convenience
8
+ methods (width, height and depth).
9
+
10
+ == Usage
11
+
12
+ === Constructor
13
+ size = Geometry::Size[x,y,z]
14
+ =end
15
+
16
+ class Size < Vector
17
+ attr_reader :x, :y, :z
18
+
19
+ # Allow vector-style initialization, but override to support copy-init
20
+ # from Vector, Point or another Size
21
+ #
22
+ # @overload [](x,y,z,...)
23
+ # @overload [](Point)
24
+ # @overload [](Size)
25
+ # @overload [](Vector)
26
+ # @return [Size] A new {Size} object
27
+ def self.[](*array)
28
+ array = array[0].to_a unless array[0].is_a?(Numeric)
29
+ super *array
30
+ end
31
+
32
+ # Allow comparison with an Array, otherwise do the normal thing
33
+ def ==(other)
34
+ return @elements == other if other.is_a?(Array)
35
+ super other
36
+ end
37
+
38
+ def inspect
39
+ 'Size' + @elements.inspect
40
+ end
41
+ def to_s
42
+ 'Size' + @elements.to_s
43
+ end
44
+
45
+ # @return [Number] The size along the Z axis
46
+ def depth
47
+ z
48
+ end
49
+
50
+ # @return [Number] The size along the Y axis
51
+ def height
52
+ y
53
+ end
54
+
55
+ # @return [Number] The size along the X axis
56
+ def width
57
+ x
58
+ end
59
+
60
+ # @return [Number] X-component (width)
61
+ def x
62
+ @elements[0]
63
+ end
64
+
65
+ # @return [Number] Y-component (height)
66
+ def y
67
+ @elements[1]
68
+ end
69
+
70
+ # @return [Number] Z-component (depth)
71
+ def z
72
+ @elements[2]
73
+ end
74
+ end
75
+ end
@@ -1,9 +1,9 @@
1
- require_relative 'helper'
2
- require_relative '../lib/geometry/point'
1
+ require_relative '../helper'
2
+ require_relative '../../lib/geometry/point'
3
3
 
4
- class PointTest < Test::Unit::TestCase
5
- Point = Geometry::Point
4
+ Point = Geometry::Point
6
5
 
6
+ class PointTest < Test::Unit::TestCase
7
7
  must "create a Point object using list syntax" do
8
8
  point = Geometry::Point[2,1]
9
9
  assert_equal(2, point.size)
@@ -86,3 +86,18 @@ class PointTest < Test::Unit::TestCase
86
86
  assert_equal('Point[10, 11]', point.to_s)
87
87
  end
88
88
  end
89
+
90
+ class PointArithmeticTest < Test::Unit::TestCase
91
+ def setup
92
+ @left = Point[1,2]
93
+ @right = Point[3,4]
94
+ end
95
+
96
+ must "return a Point when adding two Points" do
97
+ assert_kind_of(Point, @left+@right)
98
+ end
99
+
100
+ must "return a Point when subtracting two Points" do
101
+ assert_kind_of(Point, @left-@right)
102
+ end
103
+ end
@@ -0,0 +1,77 @@
1
+ require_relative '../helper'
2
+ require_relative '../../lib/geometry/rectangle'
3
+
4
+ def Rectangle(*args)
5
+ Geometry::Rectangle.new(*args)
6
+ end
7
+
8
+ Point = Geometry::Point
9
+ Size = Geometry::Size
10
+
11
+ class RectangleConstructorTest < Test::Unit::TestCase
12
+
13
+ must "create a Rectangle object from two Points" do
14
+ rectangle = Rectangle [1,2], [3,4]
15
+ assert_kind_of(Geometry::Rectangle, rectangle)
16
+ end
17
+
18
+ must "create a Rectangle from a width and height" do
19
+ rectangle = Rectangle 2, 3
20
+ assert_kind_of(Geometry::Rectangle, rectangle)
21
+ assert_equal(2, rectangle.width)
22
+ assert_equal(3, rectangle.height)
23
+ assert_equal(Point[0,0], rectangle.center)
24
+ end
25
+
26
+ must "create a Rectangle from a Size" do
27
+ rectangle = Rectangle Size[2, 3]
28
+ assert_kind_of(Geometry::Rectangle, rectangle)
29
+ assert_equal(2, rectangle.width)
30
+ assert_equal(3, rectangle.height)
31
+ assert_equal(Point[0,0], rectangle.center)
32
+ end
33
+
34
+ must "create a Rectangle from an origin Point and Size" do
35
+ rectangle = Rectangle Point[1,2], Size[2, 3]
36
+ assert_kind_of(Geometry::Rectangle, rectangle)
37
+ assert_equal(2, rectangle.width)
38
+ assert_equal(3, rectangle.height)
39
+ assert_equal(Point[1,2], rectangle.origin)
40
+ end
41
+
42
+ must "create a Rectangle from sides" do
43
+ rectangle = Rectangle 1,2,3,4
44
+ assert_kind_of(Geometry::Rectangle, rectangle)
45
+ assert_equal(2, rectangle.width)
46
+ assert_equal(2, rectangle.height)
47
+ assert_equal(Point[1,2], rectangle.origin)
48
+ end
49
+ end
50
+
51
+ class RectangleTest < Test::Unit::TestCase
52
+ def setup
53
+ @rectangle = Rectangle [1,2], [3,4]
54
+ end
55
+
56
+ must "have a center point property" do
57
+ assert_equal(@rectangle.center, [2,3])
58
+ end
59
+
60
+ must "have a width property" do
61
+ assert_equal(2, @rectangle.width)
62
+ end
63
+
64
+ must "have a height property" do
65
+ assert_equal(2, @rectangle.height)
66
+ end
67
+
68
+ must "have an origin property" do
69
+ assert_equal(Point[1,2], @rectangle.origin)
70
+ end
71
+
72
+ must "have an edges property that returns 4 edges" do
73
+ edges = @rectangle.edges
74
+ assert_equal(4, edges.size)
75
+ edges.each {|edge| assert_kind_of(Geometry::Edge, edge)}
76
+ end
77
+ end
@@ -0,0 +1,97 @@
1
+ require_relative '../helper'
2
+ require_relative '../../lib/geometry/size'
3
+
4
+ class SizeTest < Test::Unit::TestCase
5
+ Size = Geometry::Size
6
+
7
+ must "create a Size object using list syntax" do
8
+ size = Geometry::Size[2,1]
9
+ assert_equal(2, size.size)
10
+ assert_equal(2, size.x)
11
+ assert_equal(1, size.y)
12
+ end
13
+
14
+ must "create a Size object from an array" do
15
+ size = Geometry::Size[[3,4]]
16
+ assert_equal(2, size.size)
17
+ assert_equal(3, size.x)
18
+ assert_equal(4, size.y)
19
+ end
20
+
21
+ must "create a Size object from individual parameters" do
22
+ size = Geometry::Size[3,4]
23
+ assert_equal(2, size.size)
24
+ assert_equal(3, size.x)
25
+ assert_equal(4, size.y)
26
+ end
27
+
28
+ must "create a Size object from a Size" do
29
+ size = Geometry::Size[Geometry::Size[3,4]]
30
+ assert_equal(2, size.size)
31
+ assert_equal(3, size.x)
32
+ assert_equal(4, size.y)
33
+ end
34
+
35
+ must "create a Size object from a Vector" do
36
+ size = Geometry::Size[Vector[3,4]]
37
+ assert_equal(2, size.size)
38
+ assert_equal(3, size.x)
39
+ assert_equal(4, size.y)
40
+ end
41
+
42
+ must "allow indexed element access" do
43
+ size = Geometry::Size[5,6]
44
+ assert_equal(2, size.size)
45
+ assert_equal(5, size[0])
46
+ assert_equal(6, size[1])
47
+ end
48
+ must "allow named element access" do
49
+ size = Geometry::Size[5,6,7]
50
+ assert_equal(3, size.size)
51
+ assert_equal(5, size.x)
52
+ assert_equal(6, size.y)
53
+ assert_equal(7, size.z)
54
+ end
55
+
56
+ must "have a width accessor" do
57
+ size = Geometry::Size[5,6,7]
58
+ assert_equal(5, size.width)
59
+ end
60
+
61
+ must "have a height accessor" do
62
+ size = Geometry::Size[5,6,7]
63
+ assert_equal(6, size.height)
64
+ end
65
+
66
+ must "have a depth accessor" do
67
+ size = Geometry::Size[5,6,7]
68
+ assert_equal(7, size.depth)
69
+ end
70
+
71
+ must "compare equal" do
72
+ size1 = Geometry::Size[1,2]
73
+ size2 = Geometry::Size[1,2]
74
+ size3 = Geometry::Size[3,4]
75
+ assert_equal(size1, size2)
76
+ assert_not_equal(size2, size3)
77
+ end
78
+
79
+ must "compare equal to an array with equal elements" do
80
+ size1 = Size[1,2]
81
+ assert_equal(size1, [1,2])
82
+ end
83
+
84
+ must "not compare equal to an array with unequal elements" do
85
+ size1 = Size[1,2]
86
+ assert_not_equal(size1, [3,2])
87
+ end
88
+
89
+ must "implement inspect" do
90
+ size = Geometry::Size[8,9]
91
+ assert_equal('Size[8, 9]', size.inspect)
92
+ end
93
+ must "implement to_s" do
94
+ size = Geometry::Size[10,11]
95
+ assert_equal('Size[10, 11]', size.to_s)
96
+ end
97
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geometry
3
3
  version: !ruby/object:Gem::Version
4
- version: '2'
4
+ version: '3'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-22 00:00:00.000000000 Z
12
+ date: 2012-05-04 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Geometric primitives and algorithms for Ruby
15
15
  email:
@@ -30,10 +30,14 @@ files:
30
30
  - lib/geometry/line.rb
31
31
  - lib/geometry/point.rb
32
32
  - lib/geometry/polygon.rb
33
+ - lib/geometry/rectangle.rb
34
+ - lib/geometry/size.rb
33
35
  - test/geometry/circle.rb
34
36
  - test/geometry/edge.rb
35
37
  - test/geometry/point.rb
36
38
  - test/geometry/polygon.rb
39
+ - test/geometry/rectangle.rb
40
+ - test/geometry/size.rb
37
41
  - test/helper.rb
38
42
  - test/test_geometry.rb
39
43
  - test/test_line.rb
@@ -58,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
58
62
  version: '0'
59
63
  requirements: []
60
64
  rubyforge_project: geometry
61
- rubygems_version: 1.8.17
65
+ rubygems_version: 1.8.23
62
66
  signing_key:
63
67
  specification_version: 3
64
68
  summary: Geometric primitives and algoritms
@@ -67,6 +71,8 @@ test_files:
67
71
  - test/geometry/edge.rb
68
72
  - test/geometry/point.rb
69
73
  - test/geometry/polygon.rb
74
+ - test/geometry/rectangle.rb
75
+ - test/geometry/size.rb
70
76
  - test/helper.rb
71
77
  - test/test_geometry.rb
72
78
  - test/test_line.rb