geometry 3 → 4
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/README.markdown +10 -1
- data/Rakefile +11 -0
- data/geometry.gemspec +1 -1
- data/lib/geometry.rb +8 -1
- data/lib/geometry/arc.rb +31 -0
- data/lib/geometry/circle.rb +1 -1
- data/lib/geometry/edge.rb +1 -1
- data/lib/geometry/line.rb +2 -2
- data/lib/geometry/point.rb +67 -7
- data/lib/geometry/point_zero.rb +69 -0
- data/lib/geometry/polygon.rb +1 -0
- data/lib/geometry/rectangle.rb +38 -10
- data/lib/geometry/rotation.rb +46 -0
- data/lib/geometry/size_zero.rb +69 -0
- data/lib/geometry/square.rb +103 -0
- data/lib/geometry/transformation.rb +106 -0
- data/lib/geometry/vector.rb +14 -0
- data/test/{test_geometry.rb → geometry.rb} +6 -5
- data/test/geometry/arc.rb +13 -0
- data/test/geometry/circle.rb +6 -6
- data/test/geometry/edge.rb +15 -15
- data/test/{test_line.rb → geometry/line.rb} +18 -18
- data/test/geometry/point.rb +135 -46
- data/test/geometry/point_zero.rb +151 -0
- data/test/geometry/polygon.rb +10 -7
- data/test/geometry/rectangle.rb +60 -55
- data/test/geometry/rotation.rb +35 -0
- data/test/geometry/size.rb +46 -46
- data/test/geometry/size_zero.rb +151 -0
- data/test/geometry/square.rb +56 -0
- data/test/geometry/transformation.rb +90 -0
- data/test/geometry/vector.rb +17 -0
- metadata +28 -11
- data/test/helper.rb +0 -2
- data/test/test_unit_extensions.rb +0 -23
data/README.markdown
CHANGED
@@ -19,9 +19,13 @@ Primitives
|
|
19
19
|
----------
|
20
20
|
|
21
21
|
- Point
|
22
|
+
- Size
|
22
23
|
- Line
|
24
|
+
- Edge
|
23
25
|
- Circle
|
24
26
|
- Rectangle
|
27
|
+
- Polygon
|
28
|
+
- Transformation
|
25
29
|
|
26
30
|
Examples
|
27
31
|
--------
|
@@ -29,7 +33,6 @@ Examples
|
|
29
33
|
### Point
|
30
34
|
```ruby
|
31
35
|
point = Geometry::Point[3,4] # 2D Point at coordinate 3, 4
|
32
|
-
point = Geometry.point(1,2) # Functional constructor
|
33
36
|
|
34
37
|
# Copy constructors
|
35
38
|
point2 = Geometry::Point[point]
|
@@ -66,3 +69,9 @@ Examples
|
|
66
69
|
# A circle at Point[1,2] with a radius of 3
|
67
70
|
circle = Geometry::Circle.new [1,2], 3
|
68
71
|
```
|
72
|
+
|
73
|
+
### Polygon
|
74
|
+
```ruby
|
75
|
+
# A polygon that looks a lot like a square
|
76
|
+
polygon = Geometry::Polygon.new [0,0], [1,0], [1,1], [0,1]
|
77
|
+
```
|
data/Rakefile
CHANGED
@@ -1,4 +1,11 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
Rake::TestTask.new do |t|
|
5
|
+
t.libs.push "lib"
|
6
|
+
t.test_files = FileList['test/**/*.rb']
|
7
|
+
t.verbose = true
|
8
|
+
end
|
2
9
|
|
3
10
|
task :fixdates do
|
4
11
|
branch = `git branch --no-color -r --merged`.strip
|
@@ -14,6 +21,10 @@ task :trim_whitespace do
|
|
14
21
|
system(%Q[git status --short | awk '{if ($1 != "D" && $1 != "R") print $2}' | grep -e '.*\.rb$' | xargs sed -i '' -e 's/[ \t]*$//g;'])
|
15
22
|
end
|
16
23
|
|
24
|
+
task :docs do
|
25
|
+
`yard`
|
26
|
+
end
|
27
|
+
|
17
28
|
task :uninstall do
|
18
29
|
`gem uninstall geometry`
|
19
30
|
end
|
data/geometry.gemspec
CHANGED
data/lib/geometry.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
|
+
require_relative 'geometry/arc'
|
1
2
|
require_relative 'geometry/circle'
|
2
|
-
require_relative 'geometry/point'
|
3
3
|
require_relative 'geometry/line'
|
4
|
+
require_relative 'geometry/point'
|
5
|
+
require_relative 'geometry/point_zero'
|
4
6
|
require_relative 'geometry/polygon'
|
5
7
|
require_relative 'geometry/rectangle'
|
8
|
+
require_relative 'geometry/rotation'
|
6
9
|
require_relative 'geometry/size'
|
10
|
+
require_relative 'geometry/size_zero'
|
11
|
+
require_relative 'geometry/square'
|
12
|
+
require_relative 'geometry/transformation'
|
13
|
+
require_relative 'geometry/vector'
|
7
14
|
|
8
15
|
module Geometry
|
9
16
|
# @overload Line(array0, array1)
|
data/lib/geometry/arc.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative 'point'
|
2
|
+
|
3
|
+
module Geometry
|
4
|
+
|
5
|
+
=begin rdoc
|
6
|
+
Arcs are Circles that don't quite go all the way around
|
7
|
+
|
8
|
+
== Usage
|
9
|
+
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)
|
10
|
+
arc = Geometry::Arc.new [1,1], 2, 0, 90
|
11
|
+
=end
|
12
|
+
|
13
|
+
class Arc
|
14
|
+
attr_reader :center
|
15
|
+
attr_reader :radius
|
16
|
+
attr_reader :start_angle, :end_angle
|
17
|
+
|
18
|
+
# Construct a new {Arc}
|
19
|
+
# @overload initialize(center, radius, start_angle, end_angle)
|
20
|
+
# @param [Point] center The {Point} at the center of it all
|
21
|
+
# @param [Numeric] radius Radius
|
22
|
+
# @param [Numeric] start_angle Starting angle
|
23
|
+
# @param [Numeric] end_angle Ending angle
|
24
|
+
def initialize(center, radius, start_angle, end_angle)
|
25
|
+
@center = Point[center]
|
26
|
+
@radius = radius
|
27
|
+
@start_angle = start_angle
|
28
|
+
@end_angle = end_angle
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/geometry/circle.rb
CHANGED
@@ -21,7 +21,7 @@ Circles come in all shapes and sizes, but they're usually round.
|
|
21
21
|
# @param [Number] radius The radius of the Circle
|
22
22
|
# @return [Circle] A new Circle object
|
23
23
|
def initialize(center, radius)
|
24
|
-
@center =
|
24
|
+
@center = Point[center]
|
25
25
|
@radius = radius
|
26
26
|
end
|
27
27
|
end
|
data/lib/geometry/edge.rb
CHANGED
@@ -16,7 +16,7 @@ An edge. It's a line segment between 2 points. Generally part of a {Polygon}.
|
|
16
16
|
# Construct a new {Edge} object from any two things that can be converted
|
17
17
|
# to a {Point}.
|
18
18
|
def initialize(point0, point1)
|
19
|
-
@first, @last = [point0,
|
19
|
+
@first, @last = [Point[point0], Point[point1]]
|
20
20
|
end
|
21
21
|
|
22
22
|
# Two Edges are equal if both have equal {Point}s in the same order
|
data/lib/geometry/line.rb
CHANGED
@@ -67,7 +67,7 @@ Supports two-point, slope-intercept, and point-slope initializer forms
|
|
67
67
|
# @private
|
68
68
|
class PointSlopeLine < Line
|
69
69
|
def initialize(point, slope)
|
70
|
-
@point =
|
70
|
+
@point = Point[point]
|
71
71
|
@slope = slope
|
72
72
|
end
|
73
73
|
def to_s
|
@@ -111,7 +111,7 @@ Supports two-point, slope-intercept, and point-slope initializer forms
|
|
111
111
|
attr_reader :first, :last
|
112
112
|
|
113
113
|
def initialize(point0, point1)
|
114
|
-
@first, @last = [point0,
|
114
|
+
@first, @last = [Point[point0], Point[point1]]
|
115
115
|
end
|
116
116
|
def inspect
|
117
117
|
'Line(' + @first.inspect + ', ' + @last.inspect + ')'
|
data/lib/geometry/point.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'matrix'
|
2
2
|
|
3
3
|
module Geometry
|
4
|
+
DimensionMismatch = Class.new(StandardError)
|
5
|
+
OperationNotDefined = Class.new(StandardError)
|
6
|
+
|
4
7
|
=begin rdoc
|
5
8
|
An object repesenting a Point in N-dimensional space
|
6
9
|
|
@@ -23,16 +26,33 @@ geometry class (x, y, z).
|
|
23
26
|
# @overload [](Point)
|
24
27
|
# @overload [](Vector)
|
25
28
|
def self.[](*array)
|
29
|
+
return array[0] if array[0].is_a? Point
|
26
30
|
array = array[0] if array[0].is_a?(Array)
|
27
31
|
array = array[0].to_a if array[0].is_a?(Vector)
|
28
32
|
super *array
|
29
33
|
end
|
30
34
|
|
35
|
+
# Creates and returns a new {PointZero} instance
|
36
|
+
# @return [PointZero] A new {PointZero} instance
|
37
|
+
def self.zero
|
38
|
+
PointZero.new
|
39
|
+
end
|
40
|
+
|
31
41
|
# Allow comparison with an Array, otherwise do the normal thing
|
32
|
-
def
|
42
|
+
def eql?(other)
|
33
43
|
return @elements == other if other.is_a?(Array)
|
34
44
|
super other
|
35
45
|
end
|
46
|
+
alias == eql?
|
47
|
+
|
48
|
+
def coerce(other)
|
49
|
+
case other
|
50
|
+
when Array then [Point[*other], self]
|
51
|
+
when Vector then [Point[*other], self]
|
52
|
+
else
|
53
|
+
raise TypeError, "#{self.class} can't be coerced into #{other.class}"
|
54
|
+
end
|
55
|
+
end
|
36
56
|
|
37
57
|
def inspect
|
38
58
|
'Point' + @elements.inspect
|
@@ -41,34 +61,74 @@ geometry class (x, y, z).
|
|
41
61
|
'Point' + @elements.to_s
|
42
62
|
end
|
43
63
|
|
64
|
+
# !@group Accessors
|
65
|
+
# @param [Integer] i Index into the {Point}'s elements
|
66
|
+
# @return [Numeric] Element i (starting at 0)
|
67
|
+
def [](i)
|
68
|
+
@elements[i]
|
69
|
+
end
|
70
|
+
|
71
|
+
# !@attribute [r] x
|
44
72
|
# @return [Numeric] X-component
|
45
73
|
def x
|
46
74
|
@elements[0]
|
47
75
|
end
|
48
76
|
|
77
|
+
# !@attribute [r] y
|
49
78
|
# @return [Numeric] Y-component
|
50
79
|
def y
|
51
80
|
@elements[1]
|
52
81
|
end
|
53
82
|
|
83
|
+
# !@attribute [r] z
|
54
84
|
# @return [Numeric] Z-component
|
55
85
|
def z
|
56
86
|
@elements[2]
|
57
87
|
end
|
88
|
+
# !@endgroup
|
89
|
+
|
90
|
+
# !@group Arithmetic
|
91
|
+
|
92
|
+
# !@group Unary operators
|
93
|
+
def +@
|
94
|
+
self
|
95
|
+
end
|
58
96
|
|
59
|
-
|
60
|
-
|
61
|
-
|
97
|
+
def -@
|
98
|
+
Point[@elements.map {|e| -e }]
|
99
|
+
end
|
100
|
+
# !@endgroup
|
62
101
|
|
63
102
|
def +(other)
|
64
|
-
|
103
|
+
case other
|
104
|
+
when Numeric
|
105
|
+
raise DimensionMismatch, "A scalar can't be added to a Point of dimension greater than 1" if size != 1
|
106
|
+
Point[@elements.first + other]
|
107
|
+
when PointZero
|
108
|
+
self
|
109
|
+
else
|
110
|
+
raise OperationNotDefined, "#{other.class} must respond to :size and :[]" unless other.respond_to?(:size) && other.respond_to?(:[])
|
111
|
+
raise DimensionMismatch, "Can't add #{other} to #{self}" if size != other.size
|
112
|
+
Point[Array.new(size) {|i| @elements[i] + other[i] }]
|
113
|
+
end
|
65
114
|
end
|
66
115
|
|
67
116
|
def -(other)
|
68
|
-
|
117
|
+
case other
|
118
|
+
when Numeric
|
119
|
+
raise DimensionMismatch, "A scalar can't be subtracted from a Point of dimension greater than 1" if size != 1
|
120
|
+
Point[@elements.first - other]
|
121
|
+
when PointZero
|
122
|
+
self
|
123
|
+
else
|
124
|
+
raise OperationNotDefined, "#{other.class} must respond to :size and :[]" unless other.respond_to?(:size) && other.respond_to?(:[])
|
125
|
+
raise DimensionMismatch, "Can't subtract #{other} from #{self}" if size != other.size
|
126
|
+
Point[Array.new(size) {|i| @elements[i] - other[i] }]
|
127
|
+
end
|
69
128
|
end
|
70
129
|
|
71
|
-
#
|
130
|
+
# !@endgroup
|
72
131
|
|
73
132
|
end
|
74
133
|
end
|
134
|
+
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative 'point'
|
2
|
+
|
3
|
+
module Geometry
|
4
|
+
=begin rdoc
|
5
|
+
An object repesenting a {Point} at the origin in N-dimensional space
|
6
|
+
|
7
|
+
A {PointZero} object is a {Point} that will always compare equal to zero and unequal to
|
8
|
+
everything else, regardless of size.
|
9
|
+
=end
|
10
|
+
class PointZero
|
11
|
+
def eql?(other)
|
12
|
+
if other.respond_to? :all?
|
13
|
+
other.all? {|e| e.eql? 0}
|
14
|
+
else
|
15
|
+
other == 0
|
16
|
+
end
|
17
|
+
end
|
18
|
+
alias == eql?
|
19
|
+
|
20
|
+
def coerce(other)
|
21
|
+
if other.is_a? Numeric
|
22
|
+
[other, 0]
|
23
|
+
elsif other.is_a? Array
|
24
|
+
[other, Array.new(other.size,0)]
|
25
|
+
elsif other.is_a? Vector
|
26
|
+
[other, Vector[*Array.new(other.size,0)]]
|
27
|
+
else
|
28
|
+
[Point[other], Point[Array.new(other.size,0)]]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# !@group Arithmetic
|
33
|
+
|
34
|
+
# !@group Unary operators
|
35
|
+
def +@
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def -@
|
40
|
+
self
|
41
|
+
end
|
42
|
+
# !@endgroup
|
43
|
+
|
44
|
+
def +(other)
|
45
|
+
other
|
46
|
+
end
|
47
|
+
|
48
|
+
def -(other)
|
49
|
+
if other.respond_to? :-@
|
50
|
+
-other
|
51
|
+
elsif other.respond_to? :map
|
52
|
+
other.map {|a| -a }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def *(other)
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def /(other)
|
61
|
+
raise OperationNotDefined unless other.is_a? Numeric
|
62
|
+
raise ZeroDivisionError if 0 == other
|
63
|
+
self
|
64
|
+
end
|
65
|
+
# !@endgroup
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
data/lib/geometry/polygon.rb
CHANGED
@@ -13,6 +13,7 @@ An object representing a closed set of vertices and edges.
|
|
13
13
|
|
14
14
|
class Polygon
|
15
15
|
attr_reader :edges, :vertices
|
16
|
+
alias :points :vertices
|
16
17
|
|
17
18
|
# Construct a new Polygon from Points and/or Edges
|
18
19
|
# The constructor will try to convert all of its arguments into Points and
|
data/lib/geometry/rectangle.rb
CHANGED
@@ -9,12 +9,12 @@ The {Rectangle} class cluster represents your typical arrangement of 4 corners a
|
|
9
9
|
== Usage
|
10
10
|
|
11
11
|
=== Constructors
|
12
|
-
rect = Rectangle[[1,2], [2,3]]
|
13
|
-
rect = Rectangle[[1,2], Size[1,1]]
|
14
|
-
rect = Rectangle[1,2,2,3]
|
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
15
|
|
16
|
-
rect = Rectangle[10, 20]
|
17
|
-
rect = Rectangle[Size[10, 20]]
|
16
|
+
rect = Rectangle[10, 20] # origin = [0,0], size = [10, 20]
|
17
|
+
rect = Rectangle[Size[10, 20]] # origin = [0,0], size = [10, 20]
|
18
18
|
|
19
19
|
=end
|
20
20
|
|
@@ -81,8 +81,8 @@ The {Rectangle} class cluster represents your typical arrangement of 4 corners a
|
|
81
81
|
# @param [Point] point0 A corner (ie. bottom-left)
|
82
82
|
# @param [Point] point1 The other corner (ie. top-right)
|
83
83
|
def initialize(point0, point1)
|
84
|
-
point0 =
|
85
|
-
point1 =
|
84
|
+
point0 = Point[point0]
|
85
|
+
point1 = Point[point1]
|
86
86
|
raise(ArgumentError, "Point sizes must match") unless point0.size == point1.size
|
87
87
|
|
88
88
|
# Reorder the points to get lower-left and upper-right
|
@@ -100,9 +100,11 @@ The {Rectangle} class cluster represents your typical arrangement of 4 corners a
|
|
100
100
|
end
|
101
101
|
|
102
102
|
# @group Accessors
|
103
|
+
|
104
|
+
# @return [Point] The {Rectangle}'s center
|
103
105
|
def center
|
104
106
|
min, max = @points.minmax {|a,b| a.y <=> b.y}
|
105
|
-
Point[(max.x+min.x)/2, (max.y+min.y)/2]
|
107
|
+
Point[(max.x+min.x)/2.0, (max.y+min.y)/2.0]
|
106
108
|
end
|
107
109
|
|
108
110
|
# @return [Array<Edge>] The {Rectangle}'s four edges
|
@@ -116,6 +118,14 @@ The {Rectangle} class cluster represents your typical arrangement of 4 corners a
|
|
116
118
|
Edge.new(point3, point0)]
|
117
119
|
end
|
118
120
|
|
121
|
+
# @return [Array<Point>] The {Rectangle}'s four points (clockwise)
|
122
|
+
def points
|
123
|
+
point0, point2 = *@points
|
124
|
+
point1 = Point[point0.x,point2.y]
|
125
|
+
point3 = Point[point2.x, point0.y]
|
126
|
+
[point0, point1, point2, point3]
|
127
|
+
end
|
128
|
+
|
119
129
|
def origin
|
120
130
|
minx = @points.min {|a,b| a.x <=> b.x}
|
121
131
|
miny = @points.min {|a,b| a.y <=> b.y}
|
@@ -173,8 +183,8 @@ The {Rectangle} class cluster represents your typical arrangement of 4 corners a
|
|
173
183
|
# @group Accessors
|
174
184
|
# @return [Array<Edge>] The {Rectangle}'s four edges
|
175
185
|
def edges
|
176
|
-
point0 = @center - @size/2
|
177
|
-
point2 = @center + @size/2
|
186
|
+
point0 = @center - @size/2.0
|
187
|
+
point2 = @center + @size/2.0
|
178
188
|
point1 = Point[point0.x,point2.y]
|
179
189
|
point3 = Point[point2.x, point0.y]
|
180
190
|
[Edge.new(point0, point1),
|
@@ -183,6 +193,15 @@ The {Rectangle} class cluster represents your typical arrangement of 4 corners a
|
|
183
193
|
Edge.new(point3, point0)]
|
184
194
|
end
|
185
195
|
|
196
|
+
# @return [Array<Point>] The {Rectangle}'s four points (clockwise)
|
197
|
+
def points
|
198
|
+
point0 = @center - @size/2.0
|
199
|
+
point2 = @center + @size/2.0
|
200
|
+
point1 = Point[point0.x,point2.y]
|
201
|
+
point3 = Point[point2.x, point0.y]
|
202
|
+
[point0, point1, point2, point3]
|
203
|
+
end
|
204
|
+
|
186
205
|
def height
|
187
206
|
@size.height
|
188
207
|
end
|
@@ -249,6 +268,15 @@ The {Rectangle} class cluster represents your typical arrangement of 4 corners a
|
|
249
268
|
Edge.new(point3, point0)]
|
250
269
|
end
|
251
270
|
|
271
|
+
# @return [Array<Point>] The {Rectangle}'s four points (clockwise)
|
272
|
+
def points
|
273
|
+
point0 = @origin
|
274
|
+
point2 = @origin + @size
|
275
|
+
point1 = Point[point0.x,point2.y]
|
276
|
+
point3 = Point[point2.x, point0.y]
|
277
|
+
[point0, point1, point2, point3]
|
278
|
+
end
|
279
|
+
|
252
280
|
def height
|
253
281
|
@size.height
|
254
282
|
end
|