geometry 2 → 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.
@@ -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