geometry 6.2 → 6.3

Sign up to get free protection for your applications and to get access to all the features.
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
+