geometry 5 → 6

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,4 +1,7 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- # Specify your gem's dependencies in geometry.gemspec
4
3
  gemspec
4
+
5
+ group :test do
6
+ gem 'rake'
7
+ end
data/README.markdown CHANGED
@@ -1,6 +1,8 @@
1
1
  Geometry for Ruby
2
2
  =================
3
3
 
4
+ [![Build Status](https://travis-ci.org/bfoz/geometry.png)](https://travis-ci.org/bfoz/geometry)
5
+
4
6
  Classes and methods for the handling of all of the basic geometry that you
5
7
  learned in high school (and then forgot).
6
8
 
@@ -12,8 +14,7 @@ that don't work in higher dimensions and I'll do my best to fix them.
12
14
  License
13
15
  -------
14
16
 
15
- Copyright 2012 Brandon Fosdick <bfoz@bfoz.net> and released under the BSD
16
- license.
17
+ Copyright 2012-2013 Brandon Fosdick <bfoz@bfoz.net> and released under the BSD license.
17
18
 
18
19
  Primitives
19
20
  ----------
@@ -22,9 +23,9 @@ Primitives
22
23
  - Size
23
24
  - Line
24
25
  - Edge
25
- - Circle
26
+ - [Arc](http://en.wikipedia.org/wiki/Arc_(geometry)), Circle
26
27
  - Rectangle, Square
27
- - Path, [Polyline](http://en.wikipedia.org/wiki/Polyline), [Polygon](http://en.wikipedia.org/wiki/Polygon)
28
+ - Path, [Polyline](http://en.wikipedia.org/wiki/Polyline), [Polygon](http://en.wikipedia.org/wiki/Polygon), [RegularPolygon](http://en.wikipedia.org/wiki/Regular_polygon)
28
29
  - Transformation
29
30
  - [Triangle](http://en.wikipedia.org/wiki/Triangle)
30
31
  - [Obround](http://en.wiktionary.org/wiki/obround)
@@ -66,10 +67,25 @@ Examples
66
67
  Geometry::Line.vertical(x=0)
67
68
  ```
68
69
 
70
+ ### Rectangle
71
+ ```ruby
72
+ # A Rectangle made from two corner points
73
+ Geometry::Rectangle.new [1,2], [2,3]
74
+ Geometry::Rectangle.new from:[1,2], to:[2,3]
75
+
76
+ Geometry::Rectangle.new center:[1,2], size:[1,1] # Using a center point and a size
77
+ Geometry::Rectangle.new origin:[1,2], size:[1,1] # Using an origin point and a size
78
+
79
+ # A Rectangle with its origin at [0, 0] and a size of [10, 20]
80
+ Geometry::Rectangle.new size: [10, 20]
81
+ Geometry::Rectangle.new size: Size[10, 20]
82
+ Geometry::Rectangle.new width: 10, height: 20
83
+ ```
84
+
69
85
  ### Circle
70
86
  ```ruby
71
87
  # A circle at Point[1,2] with a radius of 3
72
- circle = Geometry::Circle.new [1,2], 3
88
+ circle = Geometry::Circle.new center:[1,2], radius:3
73
89
  ```
74
90
 
75
91
  ### Polygon
@@ -77,3 +93,8 @@ Examples
77
93
  # A polygon that looks a lot like a square
78
94
  polygon = Geometry::Polygon.new [0,0], [1,0], [1,1], [0,1]
79
95
  ```
96
+ ### Regular Polygon
97
+ ```ruby
98
+ # Everyone loves a good hexagon
99
+ hexagon = Geometry::RegularPolygon.new 6, :diameter => 3
100
+ ```
data/Rakefile CHANGED
@@ -1,8 +1,11 @@
1
1
  require "bundler/gem_tasks"
2
2
  require 'rake/testtask'
3
3
 
4
+ task :default => :test
5
+
4
6
  Rake::TestTask.new do |t|
5
7
  t.libs.push "lib"
8
+ # t.test_files = FileList['test/**/rectangle.rb']
6
9
  t.test_files = FileList['test/**/*.rb']
7
10
  t.verbose = true
8
11
  end
data/geometry.gemspec CHANGED
@@ -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 = '5'
6
+ s.version = '6'
7
7
  s.authors = ["Brandon Fosdick"]
8
8
  s.email = ["bfoz@bfoz.net"]
9
9
  s.homepage = "http://github.com/bfoz/geometry"
data/lib/geometry.rb CHANGED
@@ -1,12 +1,14 @@
1
1
  require_relative 'geometry/arc'
2
2
  require_relative 'geometry/circle'
3
3
  require_relative 'geometry/line'
4
+ require_relative 'geometry/obround'
4
5
  require_relative 'geometry/path'
5
6
  require_relative 'geometry/point'
6
7
  require_relative 'geometry/point_zero'
7
8
  require_relative 'geometry/polygon'
8
9
  require_relative 'geometry/polyline'
9
10
  require_relative 'geometry/rectangle'
11
+ require_relative 'geometry/regular_polygon'
10
12
  require_relative 'geometry/rotation'
11
13
  require_relative 'geometry/size'
12
14
  require_relative 'geometry/size_zero'
@@ -16,37 +18,4 @@ require_relative 'geometry/triangle'
16
18
  require_relative 'geometry/vector'
17
19
 
18
20
  module Geometry
19
- # @overload Line(array0, array1)
20
- # @param [Array] array0 First endpoint
21
- # @param [Array] array1 Second endpoint
22
- # @return [TwoPointLine]
23
- # @overload Line(point0, point1)
24
- # @param [Point] point0 First endpoint
25
- # @param [Point] point1 Second endpoint
26
- # @return [TwoPointLine]
27
- # @overload Line(vector0, vector1)
28
- # @param [Vector] vector0 First endpoint
29
- # @param [Vector] vector1 Second endpoint
30
- # @return [TwoPointLine]
31
- # @overload Line(y_intercept, slope)
32
- # @param [Numeric] y_intercept Y-intercept
33
- # @param [Numeric] slope Slope
34
- # @return [SlopeInterceptLine]
35
- # @overload Line(point, slope)
36
- # @param [Point] point Starting point
37
- # @param [Numeric] slope Slope
38
- # @return [PointSlopeLine]
39
- def self.line(*args)
40
- Line[*args]
41
- end
42
-
43
- # @overload Point(x,y,z,...)
44
- # @return [Point]
45
- # @overload Point(Point)
46
- # @return [Point]
47
- # @overload Point(Vector)
48
- # @return [Point]
49
- def self.point(*args)
50
- Point[*args]
51
- end
52
21
  end
data/lib/geometry/arc.rb CHANGED
@@ -6,11 +6,11 @@ require_relative 'point'
6
6
  module Geometry
7
7
 
8
8
  =begin rdoc
9
- Arcs are Circles that don't quite go all the way around
9
+ {http://en.wikipedia.org/wiki/Arc_(geometry) Arcs} are Circles that don't quite go all the way around
10
10
 
11
11
  == Usage
12
12
  An {Arc} with its center at [1,1] and a radius of 2 that starts at the X-axis and goes to the Y-axis (counter-clockwise)
13
- arc = Geometry::Arc.new [1,1], 2, 0, 90
13
+ arc = Geometry::Arc.new center:[1,1], radius:2, start:0, end:90
14
14
  =end
15
15
 
16
16
  class Arc
@@ -20,22 +20,26 @@ An {Arc} with its center at [1,1] and a radius of 2 that starts at the X-axis an
20
20
  attr_reader :radius
21
21
  attr_reader :start_angle, :end_angle
22
22
 
23
- # @overload new(center_point, start_point, end_point)
23
+ # @overload new(center, start, end)
24
24
  # Create a new {Arc} given center, start and end {Point}s
25
- # @param [Point] center_point The {Point} at the center
26
- # @param [Point] start_point The {Arc} starts at the start {Point}
27
- # @param [Point] end_point The {Point} where it all ends
28
- # @overload initialize(center, radius, start_angle, end_angle)
29
- # Create a new {Arc} given a center, a radius and start and end angles
30
- # @param [Point] center The {Point} at the center of it all
31
- # @param [Numeric] radius Radius
32
- # @param [Numeric] start_angle Starting angle
33
- # @param [Numeric] end_angle Ending angle
34
- def self.new(*args)
35
- if 4 == args.size
36
- original_new(*args)
37
- elsif 3 == args.size
38
- ThreePointArc.new(*args)
25
+ # @option options [Point] :center (PointZero) The {Point} at the center
26
+ # @option options [Point] :start The {Arc} starts at the start {Point}
27
+ # @option options [Point] :end The {Point} where it all ends
28
+ # @return [Arc]
29
+ # @overload new(center, radius, start, end)
30
+ # Create a new {Arc} given a center {Point}, a radius and start and end angles
31
+ # @option options [Point] :center (PointZero) The {Point} at the center of it all
32
+ # @option options [Numeric] :radius Radius
33
+ # @option options [Numeric] :start Starting angle
34
+ # @option options [Numeric] :end Ending angle
35
+ # @return [ThreePointArc]
36
+ def self.new(options={})
37
+ center = options.delete(:center) || PointZero.new
38
+
39
+ if options.has_key?(:radius)
40
+ original_new(center, options[:radius], options[:start], options[:end])
41
+ else
42
+ ThreePointArc.new(center, options[:start], options[:end])
39
43
  end
40
44
  end
41
45
 
@@ -8,9 +8,9 @@ Circles come in all shapes and sizes, but they're usually round.
8
8
 
9
9
  == Usage
10
10
  circle = Geometry::Circle.new [1,2], 3
11
- circle = Geometry::Circle.new [1,2], :radius => 3
12
- circle = Geometry::Circle.new [1,2], :diameter => 6
13
- circle = Geometry::Circle.new :center => [1,2], :diameter => 6
11
+ circle = Geometry::Circle.new center:[1,2], radius:3
12
+ circle = Geometry::Circle.new center:[1,2], diameter:6
13
+ circle = Geometry::Circle.new diameter:6
14
14
  =end
15
15
 
16
16
  class Circle
@@ -22,26 +22,24 @@ Circles come in all shapes and sizes, but they're usually round.
22
22
  # @return [Number] The {Circle}'s radius
23
23
  attr_reader :radius
24
24
 
25
- # @overload new(circle, radius)
25
+ # @overload new(center, radius)
26
26
  # Construct a {Circle} using a centerpoint and radius
27
27
  # @param [Point] center The center point of the {Circle}
28
28
  # @param [Number] radius The radius of the {Circle}
29
- # @overload new(options)
29
+ # @overload new(center, radius)
30
30
  # Construct a circle using named center and radius parameters
31
- # @option options [Point] :center
31
+ # @option options [Point] :center (PointZero)
32
32
  # @option options [Number] :radius
33
- # @overload new(options)
33
+ # @overload new(center, diameter)
34
34
  # Construct a circle using named center and diameter parameters
35
- # @option options [Point] :center
35
+ # @option options [Point] :center (PointZero)
36
36
  # @option options [Number] :diameter
37
37
  def self.new(*args, &block)
38
38
  options, args = args.partition {|a| a.is_a? Hash}
39
39
  options = options.reduce({}, :merge)
40
40
  center, radius = args[0..1]
41
41
 
42
- center = Point[center || options[:center]]
43
- raise ArgumentError, "Circle.new requires a center" unless center
44
-
42
+ center ||= (options[:center] || PointZero.new)
45
43
  radius ||= options[:radius]
46
44
 
47
45
  if radius
@@ -62,12 +60,37 @@ Circles come in all shapes and sizes, but they're usually round.
62
60
  @radius = radius
63
61
  end
64
62
 
63
+ def eql?(other)
64
+ (self.center == other.center) && (self.radius == other.radius)
65
+ end
66
+ alias :== :eql?
67
+
65
68
  # @!group Accessors
69
+ # @return [Rectangle] The smallest axis-aligned {Rectangle} that bounds the receiver
70
+ def bounds
71
+ return Rectangle.new(self.min, self.max)
72
+ end
73
+
66
74
  # @!attribute [r] diameter
67
75
  # @return [Numeric] The diameter of the {Circle}
68
76
  def diameter
69
77
  @radius*2
70
78
  end
79
+
80
+ # @return [Point] The upper right corner of the bounding {Rectangle}
81
+ def max
82
+ @center+radius
83
+ end
84
+
85
+ # @return [Point] The lower left corner of the bounding {Rectangle}
86
+ def min
87
+ @center-radius
88
+ end
89
+
90
+ # @return [Array<Point>] The lower left and upper right corners of the bounding {Rectangle}
91
+ def minmax
92
+ [self.min, self.max]
93
+ end
71
94
  # @!endgroup
72
95
  end
73
96
 
@@ -84,8 +107,13 @@ Circles come in all shapes and sizes, but they're usually round.
84
107
  @diameter = diameter
85
108
  end
86
109
 
110
+ def eql?(other)
111
+ (self.center == other.center) && (self.diameter == other.diameter)
112
+ end
113
+ alias :== :eql?
114
+
87
115
  # @!group Accessors
88
- # @return The {Circle}'s radius
116
+ # @return [Number] The {Circle}'s radius
89
117
  def radius
90
118
  @diameter/2
91
119
  end
data/lib/geometry/edge.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'mathn'
2
+
1
3
  require_relative 'point'
2
4
 
3
5
  module Geometry
@@ -24,6 +26,22 @@ An edge. It's a line segment between 2 points. Generally part of a {Polygon}.
24
26
  (@first == other.first) && (@last == other.last)
25
27
  end
26
28
 
29
+ # @param [Point] point A {Point} to spaceship with
30
+ # @return [Boolean] Returns 1 if the {Point} is strictly to the left of the receiver, -1 to the right, and 0 if the point is on the receiver
31
+ def <=>(point)
32
+ case point
33
+ when Point
34
+ k = (@last.x - @first.x) * (point.y - @first.y) - (point.x - @first.x) * (@last.y - @first.y)
35
+ if 0 == k
36
+ (((@first.x <=> point.x) + (@last.x <=> point.x)).abs <= 1) && (((@first.y <=> point.y) + (@last.y <=> point.y)).abs <= 1) ? 0 : nil
37
+ else
38
+ k <=> 0
39
+ end
40
+ else
41
+ raise ArgumentError, "Can't spaceship with #{point.class}"
42
+ end
43
+ end
44
+
27
45
  # Return a new {Edge} with swapped endpoints
28
46
  def reverse
29
47
  Edge.new(@last, @first)
@@ -77,8 +95,12 @@ An edge. It's a line segment between 2 points. Generally part of a {Polygon}.
77
95
  end
78
96
 
79
97
  # Find the intersection of two {Edge}s (http://bloggingmath.wordpress.com/2009/05/29/line-segment-intersection/)
98
+ # @param [Edge] other The other {Edge}
80
99
  # @return [Point] The intersection of the two {Edge}s, nil if they don't intersect, true if they're collinear and overlapping, and false if they're collinear and non-overlapping
81
100
  def intersection(other)
101
+ return self.first if (self.first == other.first) or (self.first == other.last)
102
+ return self.last if (self.last == other.first) or (self.last == other.last)
103
+
82
104
  p0, p1 = self.first, self.last
83
105
  p2, p3 = other.first, other.last
84
106
  v1, v2 = self.vector, other.vector
data/lib/geometry/line.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require_relative 'cluster_factory'
1
2
  require_relative 'point'
2
3
 
3
4
  module Geometry
@@ -28,6 +29,7 @@ Supports two-point, slope-intercept, and point-slope initializer forms
28
29
  =end
29
30
 
30
31
  class Line
32
+ include ClusterFactory
31
33
 
32
34
  # @overload [](Array, Array)
33
35
  # @return [TwoPointLine]
@@ -56,6 +58,25 @@ Supports two-point, slope-intercept, and point-slope initializer forms
56
58
  end
57
59
  end
58
60
 
61
+ # @overload new(from, to)
62
+ # @option options [Point] :from A starting {Point}
63
+ # @option options [Point] :to An end {Point}
64
+ # @return [TwoPointLine]
65
+ # @overload new(start, end)
66
+ # @option options [Point] :start A starting {Point}
67
+ # @option options [Point] :end An end {Point}
68
+ # @return [TwoPointLine]
69
+ def self.new(options={})
70
+ from = options[:from] || options[:start]
71
+ to = options[:end] || options[:to]
72
+
73
+ if from and to
74
+ TwoPointLine.new(from, to)
75
+ else
76
+ raise ArgumentError, "Start and end Points must be provided"
77
+ end
78
+ end
79
+
59
80
  def self.horizontal(y_intercept=0)
60
81
  SlopeInterceptLine.new(0, y_intercept)
61
82
  end
@@ -0,0 +1,238 @@
1
+ require_relative 'cluster_factory'
2
+ require_relative 'point'
3
+
4
+ module Geometry
5
+
6
+ =begin
7
+ The {Obround} class cluster represents a rectangle with semicircular end caps
8
+
9
+ {http://en.wiktionary.org/wiki/obround}
10
+ =end
11
+
12
+ class Obround
13
+ include ClusterFactory
14
+
15
+ # @overload new(width, height)
16
+ # Creates a {Obround} of the given width and height, centered on the origin
17
+ # @param [Number] height Height
18
+ # @param [Number] width Width
19
+ # @return [CenteredObround]
20
+ # @overload new(size)
21
+ # Creates a {Obround} of the given {Size} centered on the origin
22
+ # @param [Size] size Width and height
23
+ # @return [CenteredObround]
24
+ # @overload new(point0, point1)
25
+ # Creates a {Obround} using the given {Point}s
26
+ # @param [Point] point0 A corner
27
+ # @param [Point] point1 The other corner
28
+ # @overload new(origin, size)
29
+ # Creates a {Obround} from the given origin and size
30
+ # @param [Point] origin Lower-left corner
31
+ # @param [Size] size Width and height
32
+ # @return [SizedObround]
33
+ # @overload new(left, bottom, right, top)
34
+ # Creates a {Obround} from the locations of each side
35
+ # @param [Number] left X-coordinate of the left side
36
+ # @param [Number] bottom Y-coordinate of the bottom edge
37
+ # @param [Number] right X-coordinate of the right side
38
+ # @param [Number] top Y-coordinate of the top edge
39
+ # @return [Obround]
40
+ def self.new(*args)
41
+ case args.size
42
+ when 1
43
+ CenteredObround.new(args[0])
44
+ when 2
45
+ if args.all? {|a| a.is_a?(Numeric) }
46
+ CenteredObround.new(Size[*args])
47
+ elsif args.all? {|a| a.is_a?(Array) || a.is_a?(Point) }
48
+ original_new(*args)
49
+ elsif (args[0].is_a?(Point) or args[0].is_a?(Array))and args[1].is_a?(Size)
50
+ SizedObround.new(*args)
51
+ else
52
+ raise ArgumentError, "Invalid arguments #{args}"
53
+ end
54
+ when 4
55
+ raise ArgumentError unless args.all? {|a| a.is_a?(Numeric)}
56
+ left, bottom, right, top = *args
57
+ original_new(Point[left, bottom], Point[right, top])
58
+ end
59
+ end
60
+
61
+ # Create a {Obround} using the given {Point}s
62
+ # @param [Point0] point0 The bottom-left corner (closest to the origin)
63
+ # @param [Point1] point1 The top-right corner (farthest from the origin)
64
+ def initialize(point0, point1)
65
+ point0, point1 = Point[point0], Point[point1]
66
+ raise(ArgumentError, "Point sizes must match") unless point0.size == point1.size
67
+
68
+ # Reorder the points to get lower-left and upper-right
69
+ if (point0.x > point1.x) && (point0.y > point1.y)
70
+ point0, point1 = point1, point0
71
+ else
72
+ p0x, p1x = [point0.x, point1.x].minmax
73
+ p0y, p1y = [point0.y, point1.y].minmax
74
+ point0 = Point[p0x, p0y]
75
+ point1 = Point[p1x, p1y]
76
+ end
77
+ @points = [point0, point1]
78
+ end
79
+
80
+ def eql?(other)
81
+ self.points == other.points
82
+ end
83
+ alias :== :eql?
84
+
85
+ # @group Accessors
86
+
87
+ # @return [Point] The {Obround}'s center
88
+ def center
89
+ min, max = @points.minmax {|a,b| a.y <=> b.y}
90
+ Point[(max.x+min.x)/2, (max.y+min.y)/2]
91
+ end
92
+
93
+ # @return [Array<Point>] The {Obround}'s four points (counterclockwise)
94
+ def points
95
+ point0, point2 = *@points
96
+ point1 = Point[point2.x, point0.y]
97
+ point3 = Point[point0.x, point2.y]
98
+ [point0, point1, point2, point3]
99
+ end
100
+
101
+ def origin
102
+ minx = @points.min {|a,b| a.x <=> b.x}
103
+ miny = @points.min {|a,b| a.y <=> b.y}
104
+ Point[minx.x, miny.y]
105
+ end
106
+
107
+ def height
108
+ min, max = @points.minmax {|a,b| a.y <=> b.y}
109
+ max.y - min.y
110
+ end
111
+
112
+ def width
113
+ min, max = @points.minmax {|a,b| a.x <=> b.x}
114
+ max.x - min.x
115
+ end
116
+ # @endgroup
117
+ end
118
+
119
+ class CenteredObround < Obround
120
+ # @return [Point] The {Obround}'s center
121
+ attr_accessor :center
122
+ attr_reader :origin
123
+ # @return [Size] The {Size} of the {Obround}
124
+ attr_accessor :size
125
+
126
+ # @overload new(width, height)
127
+ # Creates a {Obround} of the given width and height, centered on the origin
128
+ # @param [Number] height Height
129
+ # @param [Number] width Width
130
+ # @return [CenteredObround]
131
+ # @overload new(size)
132
+ # Creates a {Obround} of the given {Size} centered on the origin
133
+ # @param [Size] size Width and height
134
+ # @return [CenteredObround]
135
+ # @overload new(center, size)
136
+ # Creates a {Obround} with the given center point and size
137
+ # @param [Point] center
138
+ # @param [Size] size
139
+ def initialize(*args)
140
+ if args[0].is_a?(Size)
141
+ @center = Point[0,0]
142
+ @size = args[0]
143
+ elsif args[0].is_a?(Geometry::Point) and args[1].is_a?(Geometry::Size)
144
+ @center, @size = args[0,1]
145
+ elsif (2 == args.size) and args.all? {|a| a.is_a?(Numeric)}
146
+ @center = Point[0,0]
147
+ @size = Geometry::Size[*args]
148
+ end
149
+ end
150
+
151
+ def eql?(other)
152
+ (self.center == other.center) && (self.size == other.size)
153
+ end
154
+ alias :== :eql?
155
+
156
+ # @group Accessors
157
+ # @return [Array<Point>] The {Obround}'s four points (clockwise)
158
+ def points
159
+ point0 = @center - @size/2.0
160
+ point2 = @center + @size/2.0
161
+ point1 = Point[point0.x,point2.y]
162
+ point3 = Point[point2.x, point0.y]
163
+ [point0, point1, point2, point3]
164
+ end
165
+
166
+ def height
167
+ @size.height
168
+ end
169
+
170
+ def width
171
+ @size.width
172
+ end
173
+ # @endgroup
174
+ end
175
+
176
+ class SizedObround < Obround
177
+ # @return [Point] The {Obround}'s center
178
+ attr_reader :center
179
+ # @return [Point] The {Obround}'s origin
180
+ attr_accessor :origin
181
+ # @return [Size] The {Size} of the {Obround}
182
+ attr_accessor :size
183
+
184
+ # @overload new(width, height)
185
+ # Creates an {Obround} of the given width and height with its origin at [0,0]
186
+ # @param [Number] height Height
187
+ # @param [Number] width Width
188
+ # @return SizedObround
189
+ # @overload new(size)
190
+ # Creates an {Obround} of the given {Size} with its origin at [0,0]
191
+ # @param [Size] size Width and height
192
+ # @return SizedObround
193
+ # @overload new(origin, size)
194
+ # Creates an {Obround} with the given origin point and size
195
+ # @param [Point] origin
196
+ # @param [Size] size
197
+ # @return SizedObround
198
+ def initialize(*args)
199
+ if args[0].is_a?(Size)
200
+ @origin = Point[0,0]
201
+ @size = args[0]
202
+ elsif (args[0].is_a?(Point) or args[0].is_a?(Array)) and args[1].is_a?(Geometry::Size)
203
+ @origin, @size = Point[args[0]], args[1]
204
+ elsif (2 == args.size) and args.all? {|a| a.is_a?(Numeric)}
205
+ @origin = Point[0,0]
206
+ @size = Geometry::Size[*args]
207
+ end
208
+ end
209
+
210
+ def eql?(other)
211
+ (self.origin == other.origin) && (self.size == other.size)
212
+ end
213
+ alias :== :eql?
214
+
215
+ # @group Accessors
216
+ def center
217
+ @origin + @size/2
218
+ end
219
+
220
+ # @return [Array<Point>] The {Obround}'s four points (clockwise)
221
+ def points
222
+ point0 = @origin
223
+ point2 = @origin + @size
224
+ point1 = Point[point0.x,point2.y]
225
+ point3 = Point[point2.x, point0.y]
226
+ [point0, point1, point2, point3]
227
+ end
228
+
229
+ def height
230
+ @size.height
231
+ end
232
+
233
+ def width
234
+ @size.width
235
+ end
236
+ # @endgroup
237
+ end
238
+ end