geometry 6.2 → 6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c1d1683621a91df0a25df4e3f2cc4fe6ecdf3dfb
4
- data.tar.gz: 597979326e97270c5fd7f037615f391e7f50fc18
3
+ metadata.gz: 8d541e7a304b060e2bc7bd392a4530fbfca3040b
4
+ data.tar.gz: 9f4bfc263c8c735ed3d28a6f32f676e0fe3606c4
5
5
  SHA512:
6
- metadata.gz: 2fbe85bbe712dff07a728326e3aafc40325849f03cb995221b8a6f65e6f4b3a6ba6b10de21f13e0ed4b18b92664f92ecb1b99a67b013430173494a35449354b2
7
- data.tar.gz: 5f1e1ddc4670af310745a74951eb6280bd4d515122ade09df4bc9cbbc40c6247fc5131c3ee7ef46f9d9ea4e707850e8462c03e458e6443e1793f374f950344d4
6
+ metadata.gz: e9e952e9d9c66085aa7fc2852605467b8456ab30ac05d6106f5593b183d6ff865ec812d47d63f32f01b280838b51d93ec58811542b1adfc5b6632b9c57cfc7fc
7
+ data.tar.gz: 53f91b55768b360e9f07026228b150d7f250dc866098eba904b01ad0ee4d2cf9eb693399ec95a44b1a5ab6640ec8898ce68690e9971e5500456ad06db6a84b7d
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.1
@@ -2,6 +2,7 @@ Geometry for Ruby
2
2
  =================
3
3
 
4
4
  [![Build Status](https://travis-ci.org/bfoz/geometry.png)](https://travis-ci.org/bfoz/geometry)
5
+ [![Gem Version](https://badge.fury.io/rb/geometry.svg)](http://badge.fury.io/rb/geometry)
5
6
 
6
7
  Classes and methods for the handling of all of the basic geometry that you
7
8
  learned in high school (and then forgot).
@@ -23,6 +24,7 @@ Primitives
23
24
  - Size
24
25
  - Line
25
26
  - Edge
27
+ - [Annulus](http://en.wikipedia.org/wiki/Annulus_(mathematics))
26
28
  - [Arc](http://en.wikipedia.org/wiki/Arc_(geometry)), Circle
27
29
  - Rectangle, Square
28
30
  - Path, [Polyline](http://en.wikipedia.org/wiki/Polyline), [Polygon](http://en.wikipedia.org/wiki/Polygon), [RegularPolygon](http://en.wikipedia.org/wiki/Regular_polygon)
@@ -35,71 +37,83 @@ Examples
35
37
 
36
38
  ### Point
37
39
  ```ruby
38
- point = Geometry::Point[3,4] # 2D Point at coordinate 3, 4
40
+ point = Geometry::Point[3,4] # 2D Point at coordinate 3, 4
39
41
 
40
- # Copy constructors
41
- point2 = Geometry::Point[point]
42
- point2 = Geometry::Point[Vector[5,6]]
42
+ # Copy constructors
43
+ point2 = Geometry::Point[point]
44
+ point2 = Geometry::Point[Vector[5,6]]
43
45
 
44
- # Accessors
45
- point.x
46
- point.y
47
- point[2] # Same as point.z
46
+ # Accessors
47
+ point.x
48
+ point.y
49
+ point[2] # Same as point.z
48
50
 
49
- # Zero
50
- PointZero.new # A Point full of zeros of unspecified length
51
- Point.zero # Another way to do the same thing
52
- Point.zero(3) # => Point[0,0,0]
51
+ # Zero
52
+ PointZero.new # A Point full of zeros of unspecified length
53
+ Point.zero # Another way to do the same thing
54
+ Point.zero(3) # => Point[0,0,0]
53
55
  ```
54
56
 
55
57
  ### Line
56
58
  ```ruby
57
- # Two-point constructors
58
- line = Geometry::Line[[0,0], [10,10]]
59
- line = Geometry::Line[Geometry::Point[0,0], Geometry::Point[10,10]]
60
- line = Geometry::Line[Vector[0,0], Vector[10,10]]
61
-
62
- # Slope-intercept constructors
63
- Geometry::Line[Rational(3,4), 5] # Slope = 3/4, Intercept = 5
64
- Geometry::Line[0.75, 5]
65
-
66
- # Point-slope constructors
67
- Geometry::Line(Geometry::Point[0,0], 0.75)
68
- Geometry::Line(Vector[0,0], Rational(3,4))
69
-
70
- # Special constructors (2D only)
71
- Geometry::Line.horizontal(y=0)
72
- Geometry::Line.vertical(x=0)
59
+ # Two-point constructors
60
+ line = Geometry::Line[[0,0], [10,10]]
61
+ line = Geometry::Line[Geometry::Point[0,0], Geometry::Point[10,10]]
62
+ line = Geometry::Line[Vector[0,0], Vector[10,10]]
63
+
64
+ # Slope-intercept constructors
65
+ Geometry::Line[Rational(3,4), 5] # Slope = 3/4, Intercept = 5
66
+ Geometry::Line[0.75, 5]
67
+
68
+ # Point-slope constructors
69
+ Geometry::Line(Geometry::Point[0,0], 0.75)
70
+ Geometry::Line(Vector[0,0], Rational(3,4))
71
+
72
+ # Special constructors (2D only)
73
+ Geometry::Line.horizontal(y=0)
74
+ Geometry::Line.vertical(x=0)
73
75
  ```
74
76
 
75
77
  ### Rectangle
76
78
  ```ruby
77
- # A Rectangle made from two corner points
78
- Geometry::Rectangle.new [1,2], [2,3]
79
- Geometry::Rectangle.new from:[1,2], to:[2,3]
79
+ # A Rectangle made from two corner points
80
+ Geometry::Rectangle.new [1,2], [2,3]
81
+ Geometry::Rectangle.new from:[1,2], to:[2,3]
80
82
 
81
- Geometry::Rectangle.new center:[1,2], size:[1,1] # Using a center point and a size
82
- Geometry::Rectangle.new origin:[1,2], size:[1,1] # Using an origin point and a size
83
+ Geometry::Rectangle.new center:[1,2], size:[1,1] # Using a center point and a size
84
+ Geometry::Rectangle.new origin:[1,2], size:[1,1] # Using an origin point and a size
83
85
 
84
- # A Rectangle with its origin at [0, 0] and a size of [10, 20]
85
- Geometry::Rectangle.new size: [10, 20]
86
- Geometry::Rectangle.new size: Size[10, 20]
87
- Geometry::Rectangle.new width: 10, height: 20
86
+ # A Rectangle with its origin at [0, 0] and a size of [10, 20]
87
+ Geometry::Rectangle.new size: [10, 20]
88
+ Geometry::Rectangle.new size: Size[10, 20]
89
+ Geometry::Rectangle.new width: 10, height: 20
88
90
  ```
89
91
 
90
92
  ### Circle
91
93
  ```ruby
92
- # A circle at Point[1,2] with a radius of 3
93
- circle = Geometry::Circle.new center:[1,2], radius:3
94
+ # A circle at Point[1,2] with a radius of 3
95
+ circle = Geometry::Circle.new center:[1,2], radius:3
94
96
  ```
95
97
 
96
98
  ### Polygon
97
99
  ```ruby
98
- # A polygon that looks a lot like a square
99
- polygon = Geometry::Polygon.new [0,0], [1,0], [1,1], [0,1]
100
+ # A polygon that looks a lot like a square
101
+ polygon = Geometry::Polygon.new [0,0], [1,0], [1,1], [0,1]
100
102
  ```
101
103
  ### Regular Polygon
102
104
  ```ruby
103
- # Everyone loves a good hexagon
104
- hexagon = Geometry::RegularPolygon.new 6, :diameter => 3
105
- ```
105
+ # Everyone loves a good hexagon
106
+ hexagon = Geometry::RegularPolygon.new 6, :diameter => 3
107
+ ```
108
+
109
+ ### Zeros and Ones
110
+ ```ruby
111
+ # For when you know you need a zero, but you don't know how big it should be
112
+ zero = Point.zero # Returns a Point of indeterminate length that always compares equal to zero
113
+
114
+ # Oh, you wanted ones instead? No problem.
115
+ ones = Point.one # => Point[1,1,1...1]
116
+
117
+ # Looking for something more exotic that a mere 1?
118
+ iso = Point.iso(5) # => Point[5,5,5...5]
119
+ ```
@@ -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 = '6.2'
6
+ s.version = '6.3'
7
7
  s.authors = ["Brandon Fosdick"]
8
8
  s.email = ["bfoz@bfoz.net"]
9
9
  s.homepage = "http://github.com/bfoz/geometry"
@@ -1,3 +1,4 @@
1
+ require_relative 'geometry/annulus'
1
2
  require_relative 'geometry/arc'
2
3
  require_relative 'geometry/circle'
3
4
  require_relative 'geometry/line'
@@ -0,0 +1,74 @@
1
+ module Geometry
2
+
3
+ =begin rdoc
4
+ An {http://en.wikipedia.org/wiki/Annulus_(mathematics) Annulus}, more commonly
5
+ known as a Ring, is a circle that ate another circle.
6
+
7
+ == Usage
8
+ ring = Geometry::Annulus.new center:[1,2], inner_radius:5, radius:10
9
+ ring = Geometry::Ring.new center:[1,2], inner_radius:5, radius:10
10
+ =end
11
+ class Annulus
12
+ # @!attribute center
13
+ # @return [Point] The center point of the {Annulus}
14
+ attr_accessor :center
15
+
16
+ # @!attribute inner_diameter
17
+ # @return [Number] the diameter of the inside of the {Annulus}
18
+ def inner_diameter
19
+ @inner_diameter || (@inner_radius && 2*@inner_radius)
20
+ end
21
+
22
+ # @!attribute inner_radius
23
+ # @return [Number] the radius of the inside of the {Annulus}
24
+ def inner_radius
25
+ @inner_radius || (@inner_diameter && @inner_diameter/2)
26
+ end
27
+
28
+ # @!attribute outer_diameter
29
+ # @return [Number] the out diameter
30
+ def outer_diameter
31
+ @outer_diameter || (@outer_radius && 2*@outer_radius)
32
+ end
33
+
34
+ # @!attribute outer_radius
35
+ # @return [Number] the outer radius
36
+ def outer_radius
37
+ @outer_radius || (@outer_diameter && @outer_diameter/2)
38
+ end
39
+
40
+ # @!attribute diameter
41
+ # @return [Number] the outer diameter
42
+ alias :diameter :outer_diameter
43
+
44
+ # @!attribute radius
45
+ # @return [Number] the outer radius
46
+ alias :radius :outer_radius
47
+
48
+ # @note
49
+ # The 'center' argument can also be passed as a named argument of the same name
50
+ # @overload initialize(center, :inner_radius, :outer_radius)
51
+ # @param center [Point] The center {Point}, defaults to the origin
52
+ # @param :inner_radius [Number] The radius of the hole that's in the center
53
+ # @param :outer_radius [Number] The overall radius of the whole thing
54
+ # @overload initialize(center, :inner_diameter, :outer_diameter)
55
+ # @param center [Point] The center {Point}, defaults to the origin
56
+ # @param :inner_diameter [Number] The radius of the hole that's in the center
57
+ # @param :outer_diameter [Number] The overall radius of the whole thing
58
+ def initialize(center = Point.zero, **options)
59
+ @center = Point[options.fetch(:center, center)]
60
+
61
+ options.delete :center
62
+ raise ArgumentError, 'Annulus requires more than a center' if options.empty?
63
+
64
+ @inner_diameter = options[:inner_diameter]
65
+ @inner_radius = options[:inner_radius]
66
+ @outer_diameter = options[:outer_diameter] || options[:diameter]
67
+ @outer_radius = options[:outer_radius] || options[:radius]
68
+ end
69
+ end
70
+
71
+ # Ring is an alias of Annulus because that's the word that most people use,
72
+ # despite the proclivities of mathmeticians.
73
+ Ring = Annulus
74
+ end
@@ -71,6 +71,12 @@ Circles come in all shapes and sizes, but they're usually round.
71
71
  return Rectangle.new(self.min, self.max)
72
72
  end
73
73
 
74
+ # @!attribute closed?
75
+ # @return [Bool] always true
76
+ def closed?
77
+ true
78
+ end
79
+
74
80
  # @!attribute [r] diameter
75
81
  # @return [Numeric] The diameter of the {Circle}
76
82
  def diameter
@@ -70,7 +70,8 @@ An edge. It's a line segment between 2 points. Generally part of a {Polygon}.
70
70
 
71
71
  # @return [Bool] Returns true if the passed {Edge} is parallel to the receiver
72
72
  def parallel?(edge)
73
- v1, v2 = self.direction, edge.direction
73
+ v1 = self.direction
74
+ v2 = edge.direction
74
75
  winding = v1[0]*v2[1] - v1[1]*v2[0]
75
76
  if 0 == winding # collinear?
76
77
  if v1 == v2
@@ -89,9 +90,10 @@ An edge. It's a line segment between 2 points. Generally part of a {Polygon}.
89
90
  (@first == other.last) || (@last == other.first) || (@first == other.first) || (@last == other.last)
90
91
  end
91
92
 
93
+ # @!attribute [r] direction
92
94
  # @return [Vector] A unit {Vector} pointing from first to last
93
95
  def direction
94
- self.vector.normalize
96
+ @direction ||= self.vector.normalize
95
97
  end
96
98
 
97
99
  # Find the intersection of two {Edge}s (http://bloggingmath.wordpress.com/2009/05/29/line-segment-intersection/)
@@ -128,9 +130,10 @@ An edge. It's a line segment between 2 points. Generally part of a {Polygon}.
128
130
  end
129
131
  end
130
132
 
133
+ # @!attribute [r] vector
131
134
  # @return [Vector] A {Vector} pointing from first to last
132
135
  def vector
133
- last - first
136
+ @vector ||= last - first
134
137
  end
135
138
 
136
139
  def to_a
@@ -90,6 +90,12 @@ The {Obround} class cluster represents a rectangle with semicircular end caps
90
90
  Point[(max.x+min.x)/2, (max.y+min.y)/2]
91
91
  end
92
92
 
93
+ # @!attribute closed?
94
+ # @return [Bool] always true
95
+ def closed?
96
+ true
97
+ end
98
+
93
99
  # @return [Array<Point>] The {Obround}'s four points (counterclockwise)
94
100
  def points
95
101
  point0, point2 = *@points
@@ -1,5 +1,7 @@
1
1
  require 'matrix'
2
2
 
3
+ require_relative 'point_iso'
4
+ require_relative 'point_one'
3
5
  require_relative 'point_zero'
4
6
 
5
7
  module Geometry
@@ -29,12 +31,27 @@ geometry class (x, y, z).
29
31
  # @overload [](Point)
30
32
  # @overload [](Vector)
31
33
  def self.[](*array)
32
- return array[0] if array[0].is_a?(Point) or array[0].is_a?(PointZero)
34
+ return array[0] if array[0].is_a?(Point)
33
35
  array = array[0] if array[0].is_a?(Array)
34
36
  array = array[0].to_a if array[0].is_a?(Vector)
35
37
  super *array
36
38
  end
37
39
 
40
+ # Creates and returns a new {PointIso} instance. Or, a {Point} full of ones if the size argument is given.
41
+ # @param value [Number] the value of the elements
42
+ # @param size [Number] the size of the new {Point} full of ones
43
+ # @return [PointIso] A new {PointIso} instance
44
+ def self.iso(value, size=nil)
45
+ size ? Point[Array.new(size, 1)] : PointIso.new(value)
46
+ end
47
+
48
+ # Creates and returns a new {PointOne} instance. Or, a {Point} full of ones if the size argument is given.
49
+ # @param size [Number] the size of the new {Point} full of ones
50
+ # @return [PointOne] A new {PointOne} instance
51
+ def self.one(size=nil)
52
+ size ? Point[Array.new(size, 1)] : PointOne.new
53
+ end
54
+
38
55
  # Creates and returns a new {PointZero} instance. Or, a {Point} full of zeros if the size argument is given.
39
56
  # @param size [Number] the size of the new {Point} full of zeros
40
57
  # @return [PointZero] A new {PointZero} instance
@@ -51,6 +68,11 @@ geometry class (x, y, z).
51
68
  def eql?(other)
52
69
  if other.is_a?(Array)
53
70
  @elements.eql? other
71
+ elsif other.is_a?(PointIso)
72
+ value = other.value
73
+ @elements.all? {|e| e.eql? value }
74
+ elsif other.is_a?(PointOne)
75
+ @elements.all? {|e| e.eql? 1 }
54
76
  elsif other.is_a?(PointZero)
55
77
  @elements.all? {|e| e.eql? 0 }
56
78
  else
@@ -62,6 +84,11 @@ geometry class (x, y, z).
62
84
  def ==(other)
63
85
  if other.is_a?(Array)
64
86
  @elements.eql? other
87
+ elsif other.is_a?(PointIso)
88
+ value = other.value
89
+ @elements.all? {|e| e.eql? value }
90
+ elsif other.is_a?(PointOne)
91
+ @elements.all? {|e| e.eql? 1 }
65
92
  elsif other.is_a?(PointZero)
66
93
  @elements.all? {|e| e.eql? 0 }
67
94
  else
@@ -95,8 +122,8 @@ geometry class (x, y, z).
95
122
  # @group Accessors
96
123
  # @param [Integer] i Index into the {Point}'s elements
97
124
  # @return [Numeric] Element i (starting at 0)
98
- def [](i)
99
- @elements[i]
125
+ def [](*args)
126
+ @elements[*args]
100
127
  end
101
128
 
102
129
  # @attribute [r] x
@@ -134,6 +161,11 @@ geometry class (x, y, z).
134
161
  case other
135
162
  when Numeric
136
163
  Point[@elements.map {|e| e + other}]
164
+ when PointIso
165
+ value = other.value
166
+ Point[@elements.map {|e| e + value}]
167
+ when PointOne
168
+ Point[@elements.map {|e| e + 1}]
137
169
  when PointZero, NilClass
138
170
  self.dup
139
171
  else
@@ -147,6 +179,11 @@ geometry class (x, y, z).
147
179
  case other
148
180
  when Numeric
149
181
  Point[@elements.map {|e| e - other}]
182
+ when PointIso
183
+ value = other.value
184
+ Point[@elements.map {|e| e - value}]
185
+ when PointOne
186
+ Point[@elements.map {|e| e - 1}]
150
187
  when PointZero, NilClass
151
188
  self.dup
152
189
  else
@@ -0,0 +1,133 @@
1
+ require_relative 'point'
2
+
3
+ module Geometry
4
+ =begin rdoc
5
+ An object repesenting a N-dimensional {Point} with identical elements.
6
+ =end
7
+ class PointIso
8
+ # @!attribute value
9
+ # @return [Number] the value for every element
10
+ attr_accessor :value
11
+
12
+ # Initialize to the given value
13
+ # @param value [Number] the value for every element of the new {PointIso}
14
+ def initialize(value)
15
+ @value = value
16
+ end
17
+
18
+ def eql?(other)
19
+ if other.respond_to? :all?
20
+ other.all? {|e| e.eql? @value}
21
+ else
22
+ other == @value
23
+ end
24
+ end
25
+ alias == eql?
26
+
27
+ def coerce(other)
28
+ if other.is_a? Numeric
29
+ [other, @value]
30
+ elsif other.is_a? Array
31
+ [other, Array.new(other.size, @value)]
32
+ elsif other.is_a? Vector
33
+ [other, Vector[*Array.new(other.size, @value)]]
34
+ else
35
+ [Point[other], Point[Array.new(other.size, @value)]]
36
+ end
37
+ end
38
+
39
+ def inspect
40
+ 'PointIso<' + @value.inspect + '>'
41
+ end
42
+ def to_s
43
+ 'PointIso<' + @value.to_s + '>'
44
+ end
45
+
46
+ def is_a?(klass)
47
+ (klass == Point) || super
48
+ end
49
+ alias :kind_of? :is_a?
50
+
51
+ # This is a hack to get Array#== to work properly. It works on ruby 2.0 and 1.9.3.
52
+ def to_ary
53
+ []
54
+ end
55
+
56
+ # @group Accessors
57
+ # @param i [Integer] Index into the {Point}'s elements
58
+ # @return [Numeric] Element i (starting at 0)
59
+ def [](i)
60
+ @value
61
+ end
62
+
63
+ # @attribute [r] x
64
+ # @return [Numeric] X-component
65
+ def x
66
+ @value
67
+ end
68
+
69
+ # @attribute [r] y
70
+ # @return [Numeric] Y-component
71
+ def y
72
+ @value
73
+ end
74
+
75
+ # @attribute [r] z
76
+ # @return [Numeric] Z-component
77
+ def z
78
+ @value
79
+ end
80
+ # @endgroup
81
+
82
+ # @group Arithmetic
83
+
84
+ # @group Unary operators
85
+ def +@
86
+ self
87
+ end
88
+
89
+ def -@
90
+ self.class.new(-@value)
91
+ end
92
+ # @endgroup
93
+
94
+ def +(other)
95
+ case other
96
+ when Numeric
97
+ other + @value
98
+ when Size
99
+ Point[other.map {|a| a + @value }]
100
+ else
101
+ if other.respond_to?(:map)
102
+ other.map {|a| a + @value }
103
+ else
104
+ Point[other + @value]
105
+ end
106
+ end
107
+ end
108
+
109
+ def -(other)
110
+ if other.is_a? Size
111
+ Point[other.map {|a| @value - a }]
112
+ elsif other.respond_to? :map
113
+ other.map {|a| @value - a }
114
+ else
115
+ @value - other
116
+ end
117
+ end
118
+
119
+ def *(other)
120
+ raise OperationNotDefined unless other.is_a? Numeric
121
+ self.class.new(other * @value)
122
+ end
123
+
124
+ def /(other)
125
+ raise OperationNotDefined unless other.is_a? Numeric
126
+ raise ZeroDivisionError if 0 == other
127
+ self.class.new(@value / other)
128
+ end
129
+ # @endgroup
130
+
131
+ end
132
+ end
133
+