geometry 6.4 → 6.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ruby.yml +21 -0
  3. data/Gemfile +3 -0
  4. data/README.markdown +1 -2
  5. data/geometry.gemspec +5 -1
  6. data/lib/geometry/annulus.rb +20 -2
  7. data/lib/geometry/arc.rb +72 -1
  8. data/lib/geometry/edge.rb +21 -4
  9. data/lib/geometry/line.rb +84 -18
  10. data/lib/geometry/obround.rb +1 -2
  11. data/lib/geometry/path.rb +27 -0
  12. data/lib/geometry/point.rb +59 -8
  13. data/lib/geometry/point_iso.rb +12 -2
  14. data/lib/geometry/point_one.rb +44 -34
  15. data/lib/geometry/point_zero.rb +12 -1
  16. data/lib/geometry/polygon.rb +13 -12
  17. data/lib/geometry/polyline.rb +6 -6
  18. data/lib/geometry/rectangle.rb +12 -12
  19. data/lib/geometry/rotation.rb +5 -3
  20. data/lib/geometry/size.rb +1 -3
  21. data/lib/geometry/square.rb +13 -11
  22. data/lib/geometry/transformation/composition.rb +1 -1
  23. data/lib/geometry/transformation.rb +15 -0
  24. data/lib/geometry/triangle.rb +1 -1
  25. data/test/geometry/annulus.rb +12 -0
  26. data/test/geometry/arc.rb +98 -0
  27. data/test/geometry/circle.rb +3 -3
  28. data/test/geometry/edge.rb +16 -2
  29. data/test/geometry/line.rb +73 -0
  30. data/test/geometry/path.rb +16 -0
  31. data/test/geometry/point.rb +78 -2
  32. data/test/geometry/point_iso.rb +31 -21
  33. data/test/geometry/point_one.rb +11 -1
  34. data/test/geometry/point_zero.rb +46 -36
  35. data/test/geometry/polygon.rb +1 -1
  36. data/test/geometry/rectangle.rb +6 -1
  37. data/test/geometry/rotation.rb +1 -1
  38. data/test/geometry/size_one.rb +1 -1
  39. data/test/geometry/size_zero.rb +1 -1
  40. data/test/geometry/square.rb +15 -10
  41. data/test/geometry/transformation.rb +15 -1
  42. data/test/geometry/vector.rb +1 -1
  43. metadata +36 -9
  44. data/.travis.yml +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 2071223ccd3ab40dc84675595b6b204aa0d7df10
4
- data.tar.gz: ed6b83fe034a5734ca9938c7ab54f4a4fcdda736
2
+ SHA256:
3
+ metadata.gz: 18aa25c45e739b77377ac376b9575f9828e2eb0566b4349a073be5b4deeea20e
4
+ data.tar.gz: 1ddbf89f0ad3854c222ffd344c0eaedd129d315ec9b1488f503f519dc8b5ca5b
5
5
  SHA512:
6
- metadata.gz: d78e31275dd418edf77a9067157213b63690bb7139ccf8c8561b228bb51e7be9e8b72e3a9187f02beb8d77ddc95427f882454f49c4827196f832acadec131e3c
7
- data.tar.gz: ba61dfc2b8d83090d59fe4d5348d3b184edeca188bf59c604d6888644a7392b943f8979ac4ea82392b1d8a026b53873128bab58c64702af149c3be68c1852057
6
+ metadata.gz: 69645a43932bb7b4c36a6401daabf49d2bbcbf3450f750f85d1c2f60f8d1e7944cb0f1c5110569912bc10d656f3ef8a1f5ffaa80ee9eb228a8a8f7855aaed0c8
7
+ data.tar.gz: 8e68aa085655950d9f499a887cf9ec0aeaac2c183e53a242675105f093652fdcb141aae4cc91226bedadf97e0dd94651b25956026c78c4a98e60b57d7b232dfa
@@ -0,0 +1,21 @@
1
+ name: Ruby
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ test:
7
+ strategy:
8
+ fail-fast: false
9
+ matrix:
10
+ ruby-version: ['3.0', '3.1', '3.2', '3.3']
11
+ os: [ubuntu-latest]
12
+
13
+ runs-on: ${{ matrix.os }}
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: ${{ matrix.ruby-version }}
20
+ bundler-cache: true
21
+ - run: bundle exec rake
data/Gemfile CHANGED
@@ -3,5 +3,8 @@ source "http://rubygems.org"
3
3
  gemspec
4
4
 
5
5
  group :test do
6
+ gem 'minitest'
6
7
  gem 'rake'
7
8
  end
9
+
10
+ gem "matrix", "~> 0.4.2"
data/README.markdown CHANGED
@@ -1,7 +1,6 @@
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
  [![Gem Version](https://badge.fury.io/rb/geometry.svg)](http://badge.fury.io/rb/geometry)
6
5
 
7
6
  Classes and methods for the handling of all of the basic geometry that you
@@ -15,7 +14,7 @@ that don't work in higher dimensions and I'll do my best to fix them.
15
14
  License
16
15
  -------
17
16
 
18
- Copyright 2012-2014 Brandon Fosdick <bfoz@bfoz.net> and released under the BSD license.
17
+ Copyright 2012-2024 Brandon Fosdick <bfoz@bfoz.net> and released under the BSD license.
19
18
 
20
19
  Primitives
21
20
  ----------
data/geometry.gemspec CHANGED
@@ -2,8 +2,9 @@
2
2
  $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
+ spec = s
5
6
  s.name = "geometry"
6
- s.version = '6.4'
7
+ spec.version = '6.6'
7
8
  s.authors = ["Brandon Fosdick"]
8
9
  s.email = ["bfoz@bfoz.net"]
9
10
  s.homepage = "http://github.com/bfoz/geometry"
@@ -18,4 +19,7 @@ Gem::Specification.new do |s|
18
19
  s.require_paths = ["lib"]
19
20
 
20
21
  s.required_ruby_version = '>= 2.0'
22
+
23
+ spec.add_development_dependency "bundler", "~> 2"
24
+ spec.add_development_dependency "rake", "~> 13"
21
25
  end
@@ -22,7 +22,7 @@ known as a Ring, is a circle that ate another circle.
22
22
  # @!attribute inner_radius
23
23
  # @return [Number] the radius of the inside of the {Annulus}
24
24
  def inner_radius
25
- @inner_radius || (@inner_diameter && @inner_diameter/2)
25
+ @inner_radius || (@inner_diameter && @inner_diameter.to_r/2)
26
26
  end
27
27
 
28
28
  # @!attribute outer_diameter
@@ -34,7 +34,7 @@ known as a Ring, is a circle that ate another circle.
34
34
  # @!attribute outer_radius
35
35
  # @return [Number] the outer radius
36
36
  def outer_radius
37
- @outer_radius || (@outer_diameter && @outer_diameter/2)
37
+ @outer_radius || ((@outer_diameter && @outer_diameter).to_r/2)
38
38
  end
39
39
 
40
40
  # @!attribute diameter
@@ -66,6 +66,24 @@ known as a Ring, is a circle that ate another circle.
66
66
  @outer_diameter = options[:outer_diameter] || options[:diameter]
67
67
  @outer_radius = options[:outer_radius] || options[:radius]
68
68
  end
69
+
70
+ # @!attribute max
71
+ # @return [Point] The upper right corner of the bounding {Rectangle}
72
+ def max
73
+ @center+radius
74
+ end
75
+
76
+ # @!attribute min
77
+ # @return [Point] The lower left corner of the bounding {Rectangle}
78
+ def min
79
+ @center-radius
80
+ end
81
+
82
+ # @!attribute minmax
83
+ # @return [Array<Point>] The lower left and upper right corners of the bounding {Rectangle}
84
+ def minmax
85
+ [self.min, self.max]
86
+ end
69
87
  end
70
88
 
71
89
  # Ring is an alias of Annulus because that's the word that most people use,
data/lib/geometry/arc.rb CHANGED
@@ -17,8 +17,15 @@ An {Arc} with its center at [1,1] and a radius of 2 that starts at the X-axis an
17
17
  include ClusterFactory
18
18
 
19
19
  attr_reader :center
20
+
21
+ # @return [Number] the radius of the {Arc}
20
22
  attr_reader :radius
21
- attr_reader :start_angle, :end_angle
23
+
24
+ # @return [Number] the starting angle of the {Arc} as radians from the x-axis
25
+ attr_reader :start_angle
26
+
27
+ # @return [Number] the ending angle of the {Arc} as radians from the x-axis
28
+ attr_reader :end_angle
22
29
 
23
30
  # @overload new(center, start, end)
24
31
  # Create a new {Arc} given center, start and end {Point}s
@@ -90,5 +97,69 @@ An {Arc} with its center at [1,1] and a radius of 2 that starts at the X-axis an
90
97
  # The end point of the {Arc}
91
98
  # @return [Point]
92
99
  alias :last :end
100
+
101
+ def ==(other)
102
+ if other.is_a?(ThreePointArc)
103
+ (self.center == other.center) && (self.end == other.end) && (self.start == other.start)
104
+ else
105
+ super other
106
+ end
107
+ end
108
+
109
+ # @group Attributes
110
+
111
+ # @return [Point] The upper-right corner of the bounding rectangle that encloses the {Path}
112
+ def max
113
+ minmax.last
114
+ end
115
+
116
+ # @return [Point] The lower-left corner of the bounding rectangle that encloses the {Path}
117
+ def min
118
+ minmax.first
119
+ end
120
+
121
+ # @return [Array<Point>] The lower-left and upper-right corners of the enclosing bounding rectangle
122
+ def minmax
123
+ a = [self.start, self.end]
124
+ quadrants = a.map(&:quadrant)
125
+
126
+ # If the Arc spans more than one quadrant, then it must cross at
127
+ # least one axis. Each axis-crossing is a potential extrema.
128
+ if quadrants.first != quadrants.last
129
+ range = (quadrants.first...quadrants.last)
130
+ # If the Arc crosses the X axis...
131
+ if quadrants.first > quadrants.last
132
+ range = (quadrants.first..4).to_a + (1...quadrants.last).to_a
133
+ end
134
+
135
+ a = range.map do |q|
136
+ case q
137
+ when 1 then self.center + Point[0,radius]
138
+ when 2 then self.center + Point[-radius, 0]
139
+ when 3 then self.center + Point[0,-radius]
140
+ when 4 then self.center + Point[radius,0]
141
+ end
142
+ end.push(*a)
143
+ a.reduce([a.first, a.first]) {|memo, e| [memo.first.min(e), memo.last.max(e)] }
144
+ else
145
+ [a.first.min(a.last), a.first.max(a.last)]
146
+ end
147
+ end
148
+
149
+ def end_angle
150
+ a = (self.end - self.center)
151
+ Math.atan2(a.y, a.x)
152
+ end
153
+
154
+ def radius
155
+ (self.start - self.center).magnitude
156
+ end
157
+
158
+ def start_angle
159
+ a = (self.start - self.center)
160
+ Math.atan2(a.y, a.x)
161
+ end
162
+
163
+ # @endgroup
93
164
  end
94
165
  end
data/lib/geometry/edge.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'mathn'
2
-
3
1
  require_relative 'point'
4
2
 
5
3
  module Geometry
@@ -42,6 +40,25 @@ An edge. It's a line segment between 2 points. Generally part of a {Polygon}.
42
40
  end
43
41
  end
44
42
 
43
+ # @group Attributes
44
+
45
+ # @return [Point] The upper-right corner of the bounding rectangle that encloses the {Edge}
46
+ def max
47
+ first.max(last)
48
+ end
49
+
50
+ # @return [Point] The lower-left corner of the bounding rectangle that encloses the {Edge}
51
+ def min
52
+ first.min(last)
53
+ end
54
+
55
+ # @return [Array<Point>] The lower-left and upper-right corners of the enclosing bounding rectangle
56
+ def minmax
57
+ first.minmax(last)
58
+ end
59
+
60
+ # @endgroup
61
+
45
62
  # Return a new {Edge} with swapped endpoints
46
63
  def reverse
47
64
  self.class.new(@last, @first)
@@ -128,8 +145,8 @@ An edge. It's a line segment between 2 points. Generally part of a {Polygon}.
128
145
  nil
129
146
  end
130
147
  else
131
- s = (-v1[1] * p.x + v1[0] * p.y) / denominator # v1 x (p0 - p2) / denominator
132
- t = ( v2[0] * p.y - v2[1] * p.x) / denominator # v2 x (p0 - p2) / denominator
148
+ s = (-v1[1] * p.x + v1[0] * p.y).to_r / denominator # v1 x (p0 - p2) / denominator
149
+ t = ( v2[0] * p.y - v2[1] * p.x).to_r / denominator # v2 x (p0 - p2) / denominator
133
150
 
134
151
  p0 + v1 * t if ((0..1) === s) && ((0..1) === t)
135
152
  end
data/lib/geometry/line.rb CHANGED
@@ -31,6 +31,15 @@ Supports two-point, slope-intercept, and point-slope initializer forms
31
31
  class Line
32
32
  include ClusterFactory
33
33
 
34
+ # @!attribute [r] horizontal?
35
+ # @return [Boolean] true if the slope is zero
36
+
37
+ # @!attribute [r] slope
38
+ # @return [Number] the slope of the {Line}
39
+
40
+ # @!attribute [r] vertical?
41
+ # @return [Boolean] true if the slope is infinite
42
+
34
43
  # @overload [](Array, Array)
35
44
  # @return [TwoPointLine]
36
45
  # @overload [](Point, Point)
@@ -85,15 +94,34 @@ Supports two-point, slope-intercept, and point-slope initializer forms
85
94
  end
86
95
  end
87
96
 
97
+ module SlopedLine
98
+ # @!attribute slope
99
+ # @return [Number] the slope of the {Line}
100
+ attr_reader :slope
101
+
102
+ # @!attribute horizontal?
103
+ # @return [Boolean] true if the slope is zero
104
+ def horizontal?
105
+ slope.zero?
106
+ end
107
+
108
+ # @!attribute vertical?
109
+ # @return [Boolean] true if the slope is infinite
110
+ def vertical?
111
+ slope.infinite? != nil
112
+ rescue # Non-Float's don't have an infinite? method
113
+ false
114
+ end
115
+ end
116
+
88
117
  # @private
89
118
  class PointSlopeLine < Line
119
+ include SlopedLine
120
+
90
121
  # @!attribute point
91
122
  # @return [Point] the stating point
92
123
  attr_reader :point
93
124
 
94
- # @return [Number] the slope of the {Line}
95
- attr_reader :slope
96
-
97
125
  # @param point [Point] a {Point} that lies on the {Line}
98
126
  # @param slope [Number] the slope of the {Line}
99
127
  def initialize(point, slope)
@@ -126,12 +154,23 @@ Supports two-point, slope-intercept, and point-slope initializer forms
126
154
  def to_s
127
155
  'Line(' + @slope.to_s + ',' + @point.to_s + ')'
128
156
  end
157
+
158
+ # Find the requested axis intercept
159
+ # @param axis [Symbol] the axis to intercept (either :x or :y)
160
+ # @return [Number] the location of the intercept
161
+ def intercept(axis=:y)
162
+ case axis
163
+ when :x
164
+ vertical? ? point.x : (horizontal? ? nil : (slope * point.x - point.y))
165
+ when :y
166
+ vertical? ? nil : (horizontal? ? point.y : (point.y - slope * point.x))
167
+ end
168
+ end
129
169
  end
130
170
 
131
171
  # @private
132
172
  class SlopeInterceptLine < Line
133
- # @return [Number] the slope of the {Line}
134
- attr_reader :slope
173
+ include SlopedLine
135
174
 
136
175
  # @param slope [Number] the slope
137
176
  # @param intercept [Number] the location of the y-axis intercept
@@ -151,7 +190,7 @@ Supports two-point, slope-intercept, and point-slope initializer forms
151
190
  ((other.first.y == slope * other.first.x + intercept)) && (other.last.y == (slope * other.last.x + intercept))
152
191
  else
153
192
  self.eql? other
154
- end
193
+ end
155
194
  end
156
195
 
157
196
  # Two {SlopeInterceptLine}s are equal if both have equal slopes and intercepts
@@ -160,13 +199,9 @@ Supports two-point, slope-intercept, and point-slope initializer forms
160
199
  (intercept == other.intercept) && (slope == other.slope)
161
200
  end
162
201
 
163
- def horizontal?
164
- 0 == @slope
165
- end
166
- def vertical?
167
- (1/0.0) == @slope
168
- end
169
-
202
+ # Find the requested axis intercept
203
+ # @param axis [Symbol] the axis to intercept (either :x or :y)
204
+ # @return [Number] the location of the intercept
170
205
  def intercept(axis=:y)
171
206
  case axis
172
207
  when :x
@@ -183,11 +218,21 @@ Supports two-point, slope-intercept, and point-slope initializer forms
183
218
 
184
219
  # @private
185
220
  class TwoPointLine < Line
186
- attr_reader :first, :last
187
-
188
- def initialize(point0, point1)
189
- @first, @last = [Point[point0], Point[point1]]
221
+ # @!attribute first
222
+ # @return [Point] the {Line}'s starting point
223
+ attr_reader :first
224
+
225
+ # @!attribute last
226
+ # @return [Point] the {Line}'s end point
227
+ attr_reader :last
228
+
229
+ # @param first [Point] the starting point
230
+ # @param last [Point] the end point
231
+ def initialize(first, last)
232
+ @first = Point[first]
233
+ @last = Point[last]
190
234
  end
235
+
191
236
  def inspect
192
237
  'Line(' + @first.inspect + ', ' + @last.inspect + ')'
193
238
  end
@@ -206,7 +251,7 @@ Supports two-point, slope-intercept, and point-slope initializer forms
206
251
  ((first.y == other.slope * first.x + other.intercept)) && (last.y == (other.slope * last.x + other.intercept))
207
252
  else
208
253
  self.eql?(other) || ((first == other.last) && (last == other.first))
209
- end
254
+ end
210
255
  end
211
256
 
212
257
  # Two {TwoPointLine}s are equal if both have equal endpoints
@@ -221,6 +266,27 @@ Supports two-point, slope-intercept, and point-slope initializer forms
221
266
  def slope
222
267
  (last.y - first.y)/(last.x - first.x)
223
268
  end
269
+
270
+ def horizontal?
271
+ first.y == last.y
272
+ end
273
+
274
+ def vertical?
275
+ first.x == last.x
276
+ end
277
+
278
+ # Find the requested axis intercept
279
+ # @param axis [Symbol] the axis to intercept (either :x or :y)
280
+ # @return [Number] the location of the intercept
281
+ def intercept(axis=:y)
282
+ case axis
283
+ when :x
284
+ vertical? ? first.x : (horizontal? ? nil : (first.x - first.y/slope))
285
+ when :y
286
+ vertical? ? nil : (horizontal? ? first.y : (first.y - slope * first.x))
287
+ end
288
+ end
289
+
224
290
  # @endgroup
225
291
  end
226
292
  end
@@ -180,8 +180,6 @@ The {Obround} class cluster represents a rectangle with semicircular end caps
180
180
  end
181
181
 
182
182
  class SizedObround < Obround
183
- # @return [Point] The {Obround}'s center
184
- attr_reader :center
185
183
  # @return [Point] The {Obround}'s origin
186
184
  attr_accessor :origin
187
185
  # @return [Size] The {Size} of the {Obround}
@@ -219,6 +217,7 @@ The {Obround} class cluster represents a rectangle with semicircular end caps
219
217
  alias :== :eql?
220
218
 
221
219
  # @group Accessors
220
+ # @return [Point] The {Obround}'s center
222
221
  def center
223
222
  @origin + @size/2
224
223
  end
data/lib/geometry/path.rb CHANGED
@@ -52,11 +52,38 @@ An object representing a set of connected elements, each of which could be an
52
52
  end
53
53
  end
54
54
 
55
+ def ==(other)
56
+ if other.is_a?(Path)
57
+ @elements == other.elements
58
+ else
59
+ super other
60
+ end
61
+ end
62
+
63
+ # @group Attributes
64
+
65
+ # @return [Point] The upper-right corner of the bounding rectangle that encloses the {Path}
66
+ def max
67
+ elements.reduce(elements.first.max) {|memo, e| memo.max(e.max) }
68
+ end
69
+
70
+ # @return [Point] The lower-left corner of the bounding rectangle that encloses the {Path}
71
+ def min
72
+ elements.reduce(elements.first.min) {|memo, e| memo.min(e.max) }
73
+ end
74
+
75
+ # @return [Array<Point>] The lower-left and upper-right corners of the enclosing bounding rectangle
76
+ def minmax
77
+ elements.reduce(elements.first.minmax) {|memo, e| [memo.first.min(e.min), memo.last.max(e.max)] }
78
+ end
79
+
55
80
  # @return [Geometry] The last element in the {Path}
56
81
  def last
57
82
  @elements.last
58
83
  end
59
84
 
85
+ # @endgroup
86
+
60
87
  # Append a new geometry element to the {Path}
61
88
  # @return [Path]
62
89
  def push(arg)
@@ -21,8 +21,6 @@ geometry class (x, y, z).
21
21
  point = Geometry::Point[x,y]
22
22
  =end
23
23
  class Point < Vector
24
- attr_reader :x, :y, :z
25
-
26
24
  # Allow vector-style initialization, but override to support copy-init
27
25
  # from Vector or another Point
28
26
  #
@@ -34,15 +32,15 @@ geometry class (x, y, z).
34
32
  return array[0] if array[0].is_a?(Point)
35
33
  array = array[0] if array[0].is_a?(Array)
36
34
  array = array[0].to_a if array[0].is_a?(Vector)
37
- super *array
35
+ super(*array)
38
36
  end
39
37
 
40
- # Creates and returns a new {PointIso} instance. Or, a {Point} full of ones if the size argument is given.
38
+ # Creates and returns a new {PointIso} instance. Or, a {Point} full of the given value if the size argument is given.
41
39
  # @param value [Number] the value of the elements
42
40
  # @param size [Number] the size of the new {Point} full of ones
43
41
  # @return [PointIso] A new {PointIso} instance
44
42
  def self.iso(value, size=nil)
45
- size ? Point[Array.new(size, 1)] : PointIso.new(value)
43
+ size ? Point[Array.new(size, value)] : PointIso.new(value)
46
44
  end
47
45
 
48
46
  # Creates and returns a new {PointOne} instance. Or, a {Point} full of ones if the size argument is given.
@@ -119,6 +117,8 @@ geometry class (x, y, z).
119
117
  'Point' + @elements.to_s
120
118
  end
121
119
 
120
+ # @group Attributes
121
+
122
122
  # @override max()
123
123
  # @return [Number] The maximum value of the {Point}'s elements
124
124
  # @override max(point)
@@ -128,7 +128,13 @@ geometry class (x, y, z).
128
128
  @elements.max
129
129
  else
130
130
  args = args.first if 1 == args.size
131
- self.class[@elements.zip(args).map(&:max)]
131
+ case args
132
+ when PointIso then self.class[@elements.map {|e| [e, args.value].max }]
133
+ when PointOne then self.class[@elements.map {|e| [e, 1].max }]
134
+ when PointZero then self.class[@elements.map {|e| [e, 0].max }]
135
+ else
136
+ self.class[@elements.zip(args).map(&:max)]
137
+ end
132
138
  end
133
139
  end
134
140
 
@@ -141,7 +147,13 @@ geometry class (x, y, z).
141
147
  @elements.min
142
148
  else
143
149
  args = args.first if 1 == args.size
144
- self.class[@elements.zip(args).map(&:min)]
150
+ case args
151
+ when PointIso then self.class[@elements.map {|e| [e, args.value].min }]
152
+ when PointOne then self.class[@elements.map {|e| [e, 1].min }]
153
+ when PointZero then self.class[@elements.map {|e| [e, 0].min }]
154
+ else
155
+ self.class[@elements.zip(args).map(&:min)]
156
+ end
145
157
  end
146
158
  end
147
159
 
@@ -157,6 +169,46 @@ geometry class (x, y, z).
157
169
  end
158
170
  end
159
171
 
172
+ # Return the {Point}'s quadrant in the 2D Cartesian Euclidean Plane
173
+ # https://en.wikipedia.org/wiki/Quadrant_(plane_geometry)
174
+ # @note Undefined for all points on the axes, and for dimensionalities other than 2
175
+ # @todo Define the results for points on the axes
176
+ # @return [Bool] The {Point}'s quadrant in the 2D Cartesian Euclidean Plane
177
+ def quadrant
178
+ return nil unless elements[1]
179
+ if elements.first > 0
180
+ (elements[1] > 0) ? 1 : 4
181
+ else
182
+ (elements[1] > 0) ? 2 : 3
183
+ end
184
+ end
185
+
186
+ # @endgroup
187
+
188
+ # Returns a new {Point} with the given number of elements removed from the end
189
+ # @return [Point] the popped elements
190
+ def pop(count=1)
191
+ self.class[to_a.pop(count)]
192
+ end
193
+
194
+ # Returns a new {Point} with the given elements appended
195
+ # @return [Point]
196
+ def push(*args)
197
+ self.class[to_a.push(*args)]
198
+ end
199
+
200
+ # Removes the first element and returns it
201
+ # @return [Point] the shifted elements
202
+ def shift(count=1)
203
+ self.class[to_a.shift(count)]
204
+ end
205
+
206
+ # Prepend the given objects and return a new {Point}
207
+ # @return [Point]
208
+ def unshift(*args)
209
+ self.class[to_a.unshift(*args)]
210
+ end
211
+
160
212
  # @group Accessors
161
213
  # @param [Integer] i Index into the {Point}'s elements
162
214
  # @return [Numeric] Element i (starting at 0)
@@ -262,4 +314,3 @@ geometry class (x, y, z).
262
314
 
263
315
  end
264
316
  end
265
-
@@ -1,5 +1,3 @@
1
- require_relative 'point'
2
-
3
1
  module Geometry
4
2
  =begin rdoc
5
3
  An object repesenting a N-dimensional {Point} with identical elements.
@@ -91,6 +89,18 @@ An object repesenting a N-dimensional {Point} with identical elements.
91
89
  end
92
90
  end
93
91
 
92
+ # Returns a new {Point} with the given number of elements removed from the end
93
+ # @return [Point] the popped elements
94
+ def pop(count=1)
95
+ Point[Array.new(count, @value)]
96
+ end
97
+
98
+ # Removes the first element and returns it
99
+ # @return [Point] the shifted elements
100
+ def shift(count=1)
101
+ Point[Array.new(count, @value)]
102
+ end
103
+
94
104
  # @group Accessors
95
105
  # @param i [Integer] Index into the {Point}'s elements
96
106
  # @return [Numeric] Element i (starting at 0)