geometry 5 → 6
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.
- data/Gemfile +4 -1
- data/README.markdown +26 -5
- data/Rakefile +3 -0
- data/geometry.gemspec +1 -1
- data/lib/geometry.rb +2 -33
- data/lib/geometry/arc.rb +21 -17
- data/lib/geometry/circle.rb +40 -12
- data/lib/geometry/edge.rb +22 -0
- data/lib/geometry/line.rb +21 -0
- data/lib/geometry/obround.rb +238 -0
- data/lib/geometry/path.rb +22 -10
- data/lib/geometry/point.rb +21 -9
- data/lib/geometry/point_zero.rb +39 -2
- data/lib/geometry/polygon.rb +180 -0
- data/lib/geometry/polyline.rb +5 -5
- data/lib/geometry/rectangle.rb +117 -54
- data/lib/geometry/regular_polygon.rb +104 -0
- data/lib/geometry/rotation.rb +5 -0
- data/lib/geometry/square.rb +22 -12
- data/lib/geometry/transformation.rb +12 -0
- data/lib/geometry/vector.rb +4 -0
- data/test/geometry.rb +0 -10
- data/test/geometry/arc.rb +18 -15
- data/test/geometry/circle.rb +55 -2
- data/test/geometry/edge.rb +31 -0
- data/test/geometry/line.rb +25 -0
- data/test/geometry/obround.rb +25 -0
- data/test/geometry/path.rb +4 -5
- data/test/geometry/point.rb +12 -12
- data/test/geometry/point_zero.rb +29 -3
- data/test/geometry/polygon.rb +61 -1
- data/test/geometry/polyline.rb +0 -1
- data/test/geometry/rectangle.rb +87 -32
- data/test/geometry/regular_polygon.rb +84 -0
- data/test/geometry/rotation.rb +8 -0
- data/test/geometry/size_zero.rb +2 -0
- data/test/geometry/square.rb +15 -5
- data/test/geometry/transformation.rb +13 -0
- data/test/geometry/vector.rb +18 -0
- metadata +15 -3
data/Gemfile
CHANGED
data/README.markdown
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
Geometry for Ruby
|
2
2
|
=================
|
3
3
|
|
4
|
+
[](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
data/geometry.gemspec
CHANGED
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(
|
23
|
+
# @overload new(center, start, end)
|
24
24
|
# Create a new {Arc} given center, start and end {Point}s
|
25
|
-
# @
|
26
|
-
# @
|
27
|
-
# @
|
28
|
-
# @
|
29
|
-
#
|
30
|
-
#
|
31
|
-
# @
|
32
|
-
# @
|
33
|
-
# @
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
|
data/lib/geometry/circle.rb
CHANGED
@@ -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], :
|
12
|
-
circle = Geometry::Circle.new [1,2], :
|
13
|
-
circle = Geometry::Circle.new :
|
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(
|
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(
|
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(
|
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
|
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
|