perfect-shape 0.0.5 → 0.0.9

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
  SHA256:
3
- metadata.gz: b287e3d8aee766314feeccc80abe127dc1ec6e8d169af61e8c6efc1097a2b5c2
4
- data.tar.gz: 96b934dde2686223634244d6ff550b578c15971b2198cb94fb0cec3ded97e06c
3
+ metadata.gz: 934071723ece0f7ebe64e6e67880b5a1f4ec6781517d2ee2a7dea70ceae4a9be
4
+ data.tar.gz: d436da8745d7f4d971a7df04b5d54701e6e89abb9f8624273f79a6ac1d28ea77
5
5
  SHA512:
6
- metadata.gz: 6743fed8d87b2ceb4bd69b91fd2ecc3b6655eca986e4a67c2aa755d53f29de60ffbb6fcc539fc45e480f55e06c176cad1885dd3c4af0330a42218b8291837763
7
- data.tar.gz: b3669ef0aabb19553bad2dc51cdca4b1c188d05cca41c73c13570f0faff19d03d373f7727c423531700cd85e562b340003995558f2c7e4c8c195947e44b232fa
6
+ metadata.gz: bcea5c4c02f2e056c0e18a857404bbef4b411420c2c3a7e438a2f122df90a30466979c60cc7804c61b5d454ed2879cd81fd1fc750360839393c20a4561aebce6
7
+ data.tar.gz: c321b5b1697b28e9608c11d4bc5e04038c3e269df311b9c7263e468c6f952c60bec6ecf4244ff044eb8fb33cf68462e9d362326962c0473bbd72150847181e0e
data/CHANGELOG.md CHANGED
@@ -1,9 +1,30 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.0.9
4
+
5
+ - `PerfectShape::Line#contain?(x_or_point, y=nil, distance: 0)` (add a distance tolerance fuzz factor option)
6
+
7
+ ## 0.0.8
8
+
9
+ - `PerfectShape::Line`
10
+ - `PerfectShape::Line#contain?(x_or_point, y=nil)`
11
+ - `PerfectShape::Line#relative_counterclockwise`
12
+ - `PerfectShape::Line#point_segment_distance`
13
+ - Update `PerfectShape::Math::radians_to_degrees`, `PerfectShape::Math::degrees_to_radians`, and `PerfectShape::Math::normalize_degrees` to normalize numbers to `BigDecimal`
14
+
15
+ ## 0.0.7
16
+
17
+ - `PerfectShape::Shape#min_x`/`PerfectShape::Shape#min_y`/`PerfectShape::Shape#max_x`/`PerfectShape::Shape#max_y`/`PerfectShape::Shape#center_x`/`PerfectShape::Shape#center_y`/`PerfectShape::Shape#bounding_box`
18
+
19
+ ## 0.0.6
20
+
21
+ - `PerfectShape::Circle`
22
+ - `PerfectShape::Circle#contain?(x_or_point, y=nil)`
23
+
3
24
  ## 0.0.5
4
25
 
5
26
  - `PerfectShape::Ellipse`
6
- - `PerfectShape::Ellipse#contain?`
27
+ - `PerfectShape::Ellipse#contain?(x_or_point, y=nil)`
7
28
 
8
29
  ## 0.0.4
9
30
 
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # Perfect Shape 0.0.5
1
+ # Perfect Shape 0.0.9
2
2
  ## Geometric Algorithms
3
3
  [![Gem Version](https://badge.fury.io/rb/perfect-shape.svg)](http://badge.fury.io/rb/perfect-shape)
4
4
 
5
- `PerfectShape` is a collection of pure Ruby geometric algorithms that are mostly useful for GUI (Graphical User Interface) manipulation like checking containment of a mouse click point in popular geometry shapes such as rectangle, square, arc (open, chord, and pie), ellipse, circle, polygon, polyline, polyquad, polycubic, and paths containing lines, bezier curves, and quadratic curves.
5
+ `PerfectShape` is a collection of pure Ruby geometric algorithms that are mostly useful for GUI (Graphical User Interface) manipulation like checking containment of a mouse click point in popular geometry shapes such as rectangle, square, arc (open, chord, and pie), ellipse, circle, polygon (ray casting algorithm/even-odd rule), polyline, polyquad, polycubic, and paths containing lines, bezier curves, and quadratic curves.
6
6
 
7
7
  Additionally, `PerfectShape::Math` contains some purely mathematical algorithms.
8
8
 
@@ -13,13 +13,13 @@ To ensure high accuracy, this library does all its mathematical operations with
13
13
  Run:
14
14
 
15
15
  ```
16
- gem install perfect-shape -v 0.0.5
16
+ gem install perfect-shape -v 0.0.9
17
17
  ```
18
18
 
19
19
  Or include in Bundler `Gemfile`:
20
20
 
21
21
  ```ruby
22
- gem 'perfect-shape', '~> 0.0.5'
22
+ gem 'perfect-shape', '~> 0.0.9'
23
23
  ```
24
24
 
25
25
  And, run:
@@ -32,17 +32,76 @@ bundle
32
32
 
33
33
  ### `PerfectShape::Math`
34
34
 
35
+ Module
36
+
35
37
  - `::degrees_to_radians(angle)`: converts degrees to radians
36
38
  - `::radians_to_degrees(angle)`: converts radians to degrees
37
39
  - `::normalize_degrees(angle)`: normalizes the specified angle into the range -180 to 180.
38
40
  - `::ieee_remainder(x, y)` (alias: `ieee754_remainder`): [IEEE 754-1985 Remainder](https://en.wikipedia.org/wiki/IEEE_754-1985) (different from standard % modulo operator as it operates on floats and could return a negative result)
39
41
 
42
+ ### `PerfectShape::Shape`
43
+
44
+ Class
45
+
46
+ - `#min_x`: min x
47
+ - `#min_y`: min y
48
+ - `#max_x`: max x
49
+ - `#max_y`: max y
50
+ - `#width`: width
51
+ - `#height`: height
52
+ - `#center_x`: center x
53
+ - `#center_y`: center y
54
+ - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height just as those of shape
55
+ - `#normalize_point(x_or_point, y = nil)`: normalizes point into an `Array` of (x,y) coordinates
56
+
57
+ ### `PerfectShape::RectangularShape`
58
+
59
+ Module
60
+
61
+ - `#initialize(x: 0, y: 0, width: 1, height: 1)`: initializes a rectangular shape
62
+ - `#x`: top-left x
63
+ - `#y`: top-left y
64
+ - `#width`: width
65
+ - `#height`: height
66
+ - `#min_x`: min x
67
+ - `#min_y`: min y
68
+ - `#max_x`: max x
69
+ - `#max_y`: max y
70
+ - `#center_x`: center x
71
+ - `#center_y`: center y
72
+ - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
73
+
40
74
  ### `PerfectShape::Line`
41
75
 
42
- - `::relative_ccw(x1, y1, x2, y2, px, py)`: Returns an indicator of where the specified point (px,py) lies with respect to the line segment from (x1,y1) to (x2,y2). The return value can be either 1, -1, or 0 and indicates in which direction the specified line must pivot around its first end point, (x1,y1), in order to point at the specified point (px,py). A return value of 1 indicates that the line segment must turn in the direction that takes the positive X axis towards the negative Y axis. In the default coordinate system used by Java 2D, this direction is counterclockwise. A return value of -1 indicates that the line segment must turn in the direction that takes the positive X axis towards the positive Y axis. In the default coordinate system, this direction is clockwise. A return value of 0 indicates that the point lies exactly on the line segment. Note that an indicator value of 0 is rare and not useful for determining collinearity because of floating point rounding issues. If the point is colinear with the line segment, but not between the end points, then the value will be -1 if the point lies “beyond (x1,y1)” or 1 if the point lies “beyond (x2,y2)”.
76
+ Class
77
+
78
+ Extends `PerfectShape::Shape`
79
+
80
+ ![line](images/line.png)
81
+
82
+ - `::relative_counterclockwise(x1, y1, x2, y2, px, py)`: Returns an indicator of where the specified point (px,py) lies with respect to the line segment from (x1,y1) to (x2,y2). The return value can be either 1, -1, or 0 and indicates in which direction the specified line must pivot around its first end point, (x1,y1), in order to point at the specified point (px,py). A return value of 1 indicates that the line segment must turn in the direction that takes the positive X axis towards the negative Y axis. In the default coordinate system used by Java 2D, this direction is counterclockwise. A return value of -1 indicates that the line segment must turn in the direction that takes the positive X axis towards the positive Y axis. In the default coordinate system, this direction is clockwise. A return value of 0 indicates that the point lies exactly on the line segment. Note that an indicator value of 0 is rare and not useful for determining collinearity because of floating point rounding issues. If the point is colinear with the line segment, but not between the end points, then the value will be -1 if the point lies “beyond (x1,y1)” or 1 if the point lies “beyond (x2,y2)”.
83
+ - `::point_segment_distance_square(x1, y1, x2, y2, px, py)`: Returns the square of distance from a point to a line segment.
84
+ - `::point_segment_distance(x1, y1, x2, y2, px, py)`: Returns the distance from a point to a line segment.
85
+ - `::new(points: nil)`: constructs a polygon with `points` as `Array` of `Array`s of (x,y) pairs or flattened `Array` of alternating x and y values
86
+ - `#min_x`: min x
87
+ - `#min_y`: min y
88
+ - `#max_x`: max x
89
+ - `#max_y`: max y
90
+ - `#width`: width (from min x to max x)
91
+ - `#height`: height (from min y to max y)
92
+ - `#center_x`: center x
93
+ - `#center_y`: center y
94
+ - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
95
+ - `#contain?(x_or_point, y=nil, distance: 0)`: checks if point lies on line, with a distance tolerance (0 by default). Distance tolerance provides a fuzz factor that for example enables GUI users to mouse-click-select a line shape in a GUI more successfully.
96
+ - `#relative_counterclockwise(x_or_point, y=nil)`: Returns an indicator of where the specified point (px,py) lies with respect to the line segment from (x1,y1) to (x2,y2). The return value can be either 1, -1, or 0 and indicates in which direction the specified line must pivot around its first end point, (x1,y1), in order to point at the specified point (px,py). A return value of 1 indicates that the line segment must turn in the direction that takes the positive X axis towards the negative Y axis. In the default coordinate system used by Java 2D, this direction is counterclockwise. A return value of -1 indicates that the line segment must turn in the direction that takes the positive X axis towards the positive Y axis. In the default coordinate system, this direction is clockwise. A return value of 0 indicates that the point lies exactly on the line segment. Note that an indicator value of 0 is rare and not useful for determining collinearity because of floating point rounding issues. If the point is colinear with the line segment, but not between the end points, then the value will be -1 if the point lies “beyond (x1,y1)” or 1 if the point lies “beyond (x2,y2)”.
97
+ - `#point_segment_distance(x_or_point, y=nil)`: Returns the distance from a point to a line segment.
43
98
 
44
99
  ### `PerfectShape::Rectangle`
45
100
 
101
+ Class
102
+
103
+ Extends `PerfectShape::Shape`
104
+
46
105
  Includes `PerfectShape::RectangularShape`
47
106
 
48
107
  ![rectangle](images/rectangle.png)
@@ -54,10 +113,17 @@ Includes `PerfectShape::RectangularShape`
54
113
  - `#height`: height
55
114
  - `#center_x`: center x
56
115
  - `#center_y`: center y
116
+ - `#min_x`: min x
117
+ - `#min_y`: min y
118
+ - `#max_x`: max x
119
+ - `#max_y`: max y
120
+ - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
57
121
  - `#contain?(x_or_point, y=nil)`: checks if point is inside
58
122
 
59
123
  ### `PerfectShape::Square`
60
124
 
125
+ Class
126
+
61
127
  Extends `PerfectShape::Rectangle`
62
128
 
63
129
  ![square](images/square.png)
@@ -70,10 +136,19 @@ Extends `PerfectShape::Rectangle`
70
136
  - `#height`: height (equal to length)
71
137
  - `#center_x`: center x
72
138
  - `#center_y`: center y
139
+ - `#min_x`: min x
140
+ - `#min_y`: min y
141
+ - `#max_x`: max x
142
+ - `#max_y`: max y
143
+ - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
73
144
  - `#contain?(x_or_point, y=nil)`: checks if point is inside
74
145
 
75
146
  ### `PerfectShape::Arc`
76
147
 
148
+ Class
149
+
150
+ Extends `PerfectShape::Shape`
151
+
77
152
  Includes `PerfectShape::RectangularShape`
78
153
 
79
154
  Arcs can be of type `:open`, `:chord`, or `:pie`
@@ -84,29 +159,36 @@ Open Arc | Chord Arc | Pie Arc
84
159
 
85
160
  - `::new(type: :open, x: 0, y: 0, width: 1, height: 1, start: 0, extent: 360, center_x: nil, center_y: nil, radius_x: nil, radius_y: nil)`: constructs an arc of type `:open` (default), `:chord`, or `:pie`
86
161
  - `#type`: `:open`, `:chord`, or `:pie`
87
- - `#x`: top-left x of arc
88
- - `#y`: top-left y of arc
89
- - `#width`: width of arc
90
- - `#height`: height of arc
162
+ - `#x`: top-left x
163
+ - `#y`: top-left y
164
+ - `#width`: width
165
+ - `#height`: height
91
166
  - `#start`: start angle in degrees
92
167
  - `#extent`: extent angle in degrees
93
168
  - `#center_x`: center x
94
169
  - `#center_y`: center y
95
170
  - `#radius_x`: radius along the x-axis
96
171
  - `#radius_y`: radius along the y-axis
172
+ - `#min_x`: min x
173
+ - `#min_y`: min y
174
+ - `#max_x`: max x
175
+ - `#max_y`: max y
176
+ - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
97
177
  - `#contain?(x_or_point, y=nil)`: checks if point is inside
98
178
 
99
179
  ### `PerfectShape::Ellipse`
100
180
 
181
+ Class
182
+
101
183
  Extends `PerfectShape::Arc`
102
184
 
103
185
  ![ellipse](images/ellipse.png)
104
186
 
105
187
  - `::new(x: 0, y: 0, width: 1, height: 1, center_x: nil, center_y: nil, radius_x: nil, radius_y: nil)`: constructs an ellipse
106
- - `#x`: top-left x of arc
107
- - `#y`: top-left y of arc
108
- - `#width`: width of arc
109
- - `#height`: height of arc
188
+ - `#x`: top-left x
189
+ - `#y`: top-left y
190
+ - `#width`: width
191
+ - `#height`: height
110
192
  - `#center_x`: center x
111
193
  - `#center_y`: center y
112
194
  - `#radius_x`: radius along the x-axis
@@ -114,8 +196,62 @@ Extends `PerfectShape::Arc`
114
196
  - `#type`: always `:open`
115
197
  - `#start`: always `0`
116
198
  - `#extent`: always `360`
199
+ - `#min_x`: min x
200
+ - `#min_y`: min y
201
+ - `#max_x`: max x
202
+ - `#max_y`: max y
203
+ - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
204
+ - `#contain?(x_or_point, y=nil)`: checks if point is inside
205
+
206
+ ### `PerfectShape::Circle`
207
+
208
+ Class
209
+
210
+ Extends `PerfectShape::Ellipse`
211
+
212
+ ![circle](images/circle.png)
213
+
214
+ - `::new(x: 0, y: 0, diameter: 1, width: 1, height: 1, center_x: nil, center_y: nil, radius: nil, radius_x: nil, radius_y: nil)`: constructs a circle
215
+ - `#x`: top-left x
216
+ - `#y`: top-left y
217
+ - `#diameter`: diameter
218
+ - `#width`: width (equal to diameter)
219
+ - `#height`: height (equal to diameter)
220
+ - `#center_x`: center x
221
+ - `#center_y`: center y
222
+ - `#radius`: radius
223
+ - `#radius_x`: radius along the x-axis (equal to radius)
224
+ - `#radius_y`: radius along the y-axis (equal to radius)
225
+ - `#type`: always `:open`
226
+ - `#start`: always `0`
227
+ - `#extent`: always `360`
228
+ - `#min_x`: min x
229
+ - `#min_y`: min y
230
+ - `#max_x`: max x
231
+ - `#max_y`: max y
232
+ - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
117
233
  - `#contain?(x_or_point, y=nil)`: checks if point is inside
118
234
 
235
+ ### `PerfectShape::Polygon`
236
+
237
+ Class
238
+
239
+ Extends `PerfectShape::Shape`
240
+
241
+ ![polygon](images/polygon.png)
242
+
243
+ - `::new(points: nil)`: constructs a polygon with `points` as `Array` of `Array`s of (x,y) pairs or flattened `Array` of alternating x and y values
244
+ - `#min_x`: min x
245
+ - `#min_y`: min y
246
+ - `#max_x`: max x
247
+ - `#max_y`: max y
248
+ - `#width`: width (from min x to max x)
249
+ - `#height`: height (from min y to max y)
250
+ - `#center_x`: center x
251
+ - `#center_y`: center y
252
+ - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
253
+ - `#contain?(x_or_point, y=nil)`: checks if point is inside using the [Ray Casting Algorithm](https://en.wikipedia.org/wiki/Point_in_polygon) (aka [Even-Odd Rule](https://en.wikipedia.org/wiki/Even%E2%80%93odd_rule))
254
+
119
255
  ## Process
120
256
 
121
257
  [Glimmer Process](https://github.com/AndyObtiva/glimmer/blob/master/PROCESS.md)
@@ -123,7 +259,7 @@ Extends `PerfectShape::Arc`
123
259
  ## Resources
124
260
 
125
261
  - Rubydoc: https://www.rubydoc.info/gems/perfect-shape
126
- - AWT Geom JavaDoc: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/package-summary.html
262
+ - AWT Geom JavaDoc (inspiration): https://docs.oracle.com/javase/8/docs/api/java/awt/geom/package-summary.html
127
263
 
128
264
  ## TODO
129
265
 
@@ -133,7 +269,7 @@ Extends `PerfectShape::Arc`
133
269
 
134
270
  [CHANGELOG.md](CHANGELOG.md)
135
271
 
136
- ## Contributing to perfect-shape
272
+ ## Contributing
137
273
 
138
274
  - Check out the latest master to make sure the feature hasn't been
139
275
  implemented or the bug hasn't been fixed yet.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.5
1
+ 0.0.9
data/lib/perfect-shape.rb CHANGED
@@ -22,6 +22,7 @@
22
22
  $LOAD_PATH.unshift File.expand_path('.', __dir__)
23
23
 
24
24
  require 'bigdecimal'
25
+ require 'equalizer'
25
26
 
26
27
  # Perfect Shape algorithms are mostly ported from java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/package-summary.html
27
28
  module PerfectShape
@@ -27,6 +27,7 @@ module PerfectShape
27
27
  # Mostly ported from java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/Arc2D.html
28
28
  class Arc < Shape
29
29
  include RectangularShape
30
+ include Equalizer.new(:type, :x, :y, :width, :height, :start, :extent)
30
31
 
31
32
  TYPES = [:open, :chord, :pie]
32
33
  attr_accessor :type
@@ -177,8 +178,8 @@ module PerfectShape
177
178
  angle += Math.degrees_to_radians(-extent)
178
179
  x2 = Math.cos(angle)
179
180
  y2 = Math.sin(angle)
180
- inside = (Line.relative_ccw(x1, y1, x2, y2, 2*normx, 2*normy) *
181
- Line.relative_ccw(x1, y1, x2, y2, 0, 0) >= 0)
181
+ inside = (Line.relative_counterclockwise(x1, y1, x2, y2, 2*normx, 2*normy) *
182
+ Line.relative_counterclockwise(x1, y1, x2, y2, 0, 0) >= 0)
182
183
  inarc ? !inside : inside
183
184
  end
184
185
 
@@ -0,0 +1,91 @@
1
+ # Copyright (c) 2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'perfect_shape/ellipse'
23
+
24
+ module PerfectShape
25
+ class Circle < Ellipse
26
+ MESSAGE_WIDTH_AND_HEIGHT_AND_DIAMETER_NOT_EQUAL = 'Circle width, height, and diameter must all be equal if more than one is specified; or otherwise keep only one of them in arguments!'
27
+ MESSAGE_RADIUS_X_AND_RADIUS_Y_AND_RADIUS_NOT_EQUAL = 'Circle radius_x, radius_y, and radius must all be equal if more than one is specified; or otherwise keep only one of them in arguments!'
28
+
29
+ def initialize(x: 0, y: 0, width: nil, height: nil, diameter: nil, center_x: nil, center_y: nil, radius_x: nil, radius_y: nil, radius: nil)
30
+ raise MESSAGE_WIDTH_AND_HEIGHT_AND_DIAMETER_NOT_EQUAL if (diameter && width && diameter != width) || (diameter && height && diameter != height) || (width && height && width != height)
31
+ raise MESSAGE_RADIUS_X_AND_RADIUS_Y_AND_RADIUS_NOT_EQUAL if (radius && radius_x && radius != radius_x) || (radius && radius_y && radius != radius_y) || (radius_x && radius_y && radius_x != radius_y)
32
+ if center_x && center_y && (radius || radius_x || radius_y)
33
+ radius ||= radius_x || radius_y
34
+ self.radius = radius
35
+ super(center_x: center_x, center_y: center_y, radius_x: self.radius_x, radius_y: self.radius_y)
36
+ else
37
+ diameter ||= width || height || 1
38
+ self.diameter = diameter
39
+ super(x: x, y: y, width: self.width, height: self.height)
40
+ end
41
+ end
42
+
43
+ def diameter
44
+ @radius ? @radius * BigDecimal('2.0') : @diameter
45
+ end
46
+
47
+ def radius
48
+ @diameter ? @diameter / BigDecimal('2.0') : @radius
49
+ end
50
+
51
+ # Sets length, normalizing to BigDecimal
52
+ def diameter=(value)
53
+ @diameter = BigDecimal(value.to_s)
54
+ @radius = nil
55
+ self.width = value unless width == value
56
+ self.height = value unless height == value
57
+ end
58
+
59
+ # Sets radius, normalizing to BigDecimal
60
+ def radius=(value)
61
+ @radius = BigDecimal(value.to_s)
62
+ @diameter = nil
63
+ self.radius_x = value unless width == value
64
+ self.radius_y = value unless height == value
65
+ end
66
+
67
+ def width=(value)
68
+ super
69
+ self.diameter = value unless diameter == value
70
+ self.height = value unless height == value
71
+ end
72
+
73
+ def height=(value)
74
+ super
75
+ self.diameter = value unless diameter == value
76
+ self.width = value unless width == value
77
+ end
78
+
79
+ def radius_x=(value)
80
+ super
81
+ self.radius = value unless radius == value
82
+ self.radius_y = value unless radius_y == value
83
+ end
84
+
85
+ def radius_y=(value)
86
+ super
87
+ self.radius = value unless radius == value
88
+ self.radius_x = value unless radius_x == value
89
+ end
90
+ end
91
+ end
@@ -64,6 +64,7 @@ module PerfectShape
64
64
  # the ellipse, {@code false} if the point lies outside of the
65
65
  # ellipse's bounds.
66
66
  def contain?(x_or_point, y = nil)
67
+ # This is implemented again even though super would have just worked to have an optimized algorithm for Ellipse.
67
68
  x, y = normalize_point(x_or_point, y)
68
69
  return unless x && y
69
70
  ellw = self.width
@@ -20,6 +20,7 @@
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  require 'perfect_shape/shape'
23
+ require 'perfect_shape/multi_point'
23
24
 
24
25
  module PerfectShape
25
26
  # Mostly ported from java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/Line2D.html
@@ -52,7 +53,7 @@ module PerfectShape
52
53
  # @return an integer that indicates the position of the third specified
53
54
  # coordinates with respect to the line segment formed
54
55
  # by the first two specified coordinates.
55
- def relative_ccw(x1, y1, x2, y2, px, py)
56
+ def relative_counterclockwise(x1, y1, x2, y2, px, py)
56
57
  x2 -= x1;
57
58
  y2 -= y1;
58
59
  px -= x1;
@@ -82,6 +83,136 @@ module PerfectShape
82
83
  end
83
84
  (ccw < 0.0) ? -1 : ((ccw > 0.0) ? 1 : 0);
84
85
  end
86
+
87
+ # Returns the square of the distance from a point to a line segment.
88
+ # The distance measured is the distance between the specified
89
+ # point and the closest point between the specified end points.
90
+ # If the specified point intersects the line segment in between the
91
+ # end points, this method returns 0.0.
92
+ #
93
+ # @param x1 the X coordinate of the start point of the
94
+ # specified line segment
95
+ # @param y1 the Y coordinate of the start point of the
96
+ # specified line segment
97
+ # @param x2 the X coordinate of the end point of the
98
+ # specified line segment
99
+ # @param y2 the Y coordinate of the end point of the
100
+ # specified line segment
101
+ # @param px the X coordinate of the specified point being
102
+ # measured against the specified line segment
103
+ # @param py the Y coordinate of the specified point being
104
+ # measured against the specified line segment
105
+ # @return a double value that is the square of the distance from the
106
+ # specified point to the specified line segment.
107
+ def point_segment_distance_square(x1, y1,
108
+ x2, y2,
109
+ px, py)
110
+ x1 = BigDecimal(x1.to_s)
111
+ y1 = BigDecimal(y1.to_s)
112
+ x2 = BigDecimal(x2.to_s)
113
+ y2 = BigDecimal(y2.to_s)
114
+ px = BigDecimal(px.to_s)
115
+ py = BigDecimal(py.to_s)
116
+ # Adjust vectors relative to x1,y1
117
+ # x2,y2 becomes relative vector from x1,y1 to end of segment
118
+ x2 -= x1
119
+ y2 -= y1
120
+ # px,py becomes relative vector from x1,y1 to test point
121
+ px -= x1
122
+ py -= y1
123
+ dot_product = px * x2 + py * y2;
124
+ if dot_product <= 0.0
125
+ # px,py is on the side of x1,y1 away from x2,y2
126
+ # distance to segment is length of px,py vector
127
+ # "length of its (clipped) projection" is now 0.0
128
+ projected_length_square = BigDecimal('0.0');
129
+ else
130
+ # switch to backwards vectors relative to x2,y2
131
+ # x2,y2 are already the negative of x1,y1=>x2,y2
132
+ # to get px,py to be the negative of px,py=>x2,y2
133
+ # the dot product of two negated vectors is the same
134
+ # as the dot product of the two normal vectors
135
+ px = x2 - px
136
+ py = y2 - py
137
+ dot_product = px * x2 + py * y2
138
+ if dot_product <= 0.0
139
+ # px,py is on the side of x2,y2 away from x1,y1
140
+ # distance to segment is length of (backwards) px,py vector
141
+ # "length of its (clipped) projection" is now 0.0
142
+ projected_length_square = BigDecimal('0.0')
143
+ else
144
+ # px,py is between x1,y1 and x2,y2
145
+ # dot_product is the length of the px,py vector
146
+ # projected on the x2,y2=>x1,y1 vector times the
147
+ # length of the x2,y2=>x1,y1 vector
148
+ projected_length_square = dot_product * dot_product / (x2 * x2 + y2 * y2)
149
+ end
150
+ end
151
+ # Distance to line is now the length of the relative point
152
+ # vector minus the length of its projection onto the line
153
+ # (which is zero if the projection falls outside the range
154
+ # of the line segment).
155
+ length_square = px * px + py * py - projected_length_square
156
+ length_square = BigDecimal('0.0') if length_square < 0
157
+ length_square
158
+ end
159
+
160
+ # Returns the distance from a point to a line segment.
161
+ # The distance measured is the distance between the specified
162
+ # point and the closest point between the specified end points.
163
+ # If the specified point intersects the line segment in between the
164
+ # end points, this method returns 0.0.
165
+ #
166
+ # @param x1 the X coordinate of the start point of the
167
+ # specified line segment
168
+ # @param y1 the Y coordinate of the start point of the
169
+ # specified line segment
170
+ # @param x2 the X coordinate of the end point of the
171
+ # specified line segment
172
+ # @param y2 the Y coordinate of the end point of the
173
+ # specified line segment
174
+ # @param px the X coordinate of the specified point being
175
+ # measured against the specified line segment
176
+ # @param py the Y coordinate of the specified point being
177
+ # measured against the specified line segment
178
+ # @return a double value that is the distance from the specified point
179
+ # to the specified line segment.
180
+ def point_segment_distance(x1, y1,
181
+ x2, y2,
182
+ px, py)
183
+ BigDecimal(::Math.sqrt(point_segment_distance_square(x1, y1, x2, y2, px, py)).to_s)
184
+ end
185
+ end
186
+
187
+ include MultiPoint
188
+
189
+ # Checks if polygon contains point denoted by point (two-number Array or x, y args)
190
+ # using the Ray Casting Algorithm (aka Even-Odd Rule): https://en.wikipedia.org/wiki/Point_in_polygon
191
+ #
192
+ # @param x The X coordinate of the point to test.
193
+ # @param y The Y coordinate of the point to test.
194
+ #
195
+ # @return {@code true} if the point lies within the bound of
196
+ # the polygon, {@code false} if the point lies outside of the
197
+ # polygon's bounds.
198
+ def contain?(x_or_point, y = nil, distance: 0)
199
+ x, y = normalize_point(x_or_point, y)
200
+ return unless x && y
201
+ distance = BigDecimal(distance.to_s)
202
+ # TODO implement contain?(point) with a fuzz factor to enable successfully selecting a line in a GUI application
203
+ Line.point_segment_distance(points[0][0], points[0][1], points[1][0], points[1][1], x, y) <= distance
204
+ end
205
+
206
+ def point_segment_distance(x_or_point, y = nil)
207
+ x, y = normalize_point(x_or_point, y)
208
+ return unless x && y
209
+ Line.point_segment_distance(points[0][0], points[0][1], points[1][0], points[1][1], x, y)
210
+ end
211
+
212
+ def relative_counterclockwise(x_or_point, y = nil)
213
+ x, y = normalize_point(x_or_point, y)
214
+ return unless x && y
215
+ Line.relative_counterclockwise(points[0][0], points[0][1], points[1][0], points[1][1], x, y)
85
216
  end
86
217
  end
87
218
  end
@@ -6,33 +6,34 @@ module PerfectShape
6
6
  # Also includes standard Ruby ::Math utility methods
7
7
  module Math
8
8
  class << self
9
- # converts angle from radians to degrees
9
+ # converts angle from radians to degrees (normalizing to BigDecimal)
10
10
  def radians_to_degrees(radians)
11
- (180/Math::PI)*radians
11
+ (BigDecimal('180')/Math::PI)*BigDecimal(radians.to_s)
12
12
  end
13
13
 
14
- # converts angle from degrees to radians
14
+ # converts angle from degrees to radians (normalizing to BigDecimal)
15
15
  def degrees_to_radians(degrees)
16
- (Math::PI/180)*degrees
16
+ (Math::PI/BigDecimal('180'))*BigDecimal(degrees.to_s)
17
17
  end
18
18
 
19
19
  # Normalizes the specified angle into the range -180 to 180.
20
20
  def normalize_degrees(angle)
21
+ angle = BigDecimal(angle.to_s)
21
22
  if angle > 180.0
22
23
  if angle <= (180.0 + 360.0)
23
- angle = angle - 360.0
24
+ angle = angle - BigDecimal('360.0')
24
25
  else
25
26
  angle = Math.ieee_remainder(angle, 360.0)
26
27
  # IEEEremainder can return -180 here for some input values...
27
- angle = 180.0 if angle == -180.0
28
+ angle = BigDecimal('180.0') if angle == -180.0
28
29
  end
29
30
  elsif angle <= -180.0
30
31
  if angle > (-180.0 - 360.0)
31
- angle = angle + 360.0
32
+ angle = angle + BigDecimal('360.0')
32
33
  else
33
34
  angle = Math.ieee_remainder(angle, 360.0)
34
35
  # IEEEremainder can return -180 here for some input values...
35
- angle = 180.0 if angle == -180.0
36
+ angle = BigDecimal('180.0') if angle == -180.0
36
37
  end
37
38
  end
38
39
  angle
@@ -0,0 +1,67 @@
1
+ # Copyright (c) 2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'perfect_shape/shape'
23
+
24
+ module PerfectShape
25
+ # Represents multi-point shapes like Line, Polygon, and Polyline
26
+ module MultiPoint
27
+ attr_reader :points
28
+
29
+ def initialize(points: nil)
30
+ self.points = points || []
31
+ end
32
+
33
+ # Sets points, normalizing to an Array of Arrays of (x,y) pairs as BigDecimal
34
+ def points=(the_points)
35
+ unless the_points.first.is_a?(Array)
36
+ xs = the_points.each_with_index.select {|n, i| i.even?}.map(&:first)
37
+ ys = the_points.each_with_index.select {|n, i| i.odd?}.map(&:first)
38
+ the_points = xs.zip(ys)
39
+ end
40
+ @points = the_points.map {|pair| [BigDecimal(pair.first.to_s), BigDecimal(pair.last.to_s)]}
41
+ end
42
+
43
+ def min_x
44
+ points.map(&:first).min
45
+ end
46
+
47
+ def min_y
48
+ points.map(&:last).min
49
+ end
50
+
51
+ def max_x
52
+ points.map(&:first).max
53
+ end
54
+
55
+ def max_y
56
+ points.map(&:last).max
57
+ end
58
+
59
+ def width
60
+ max_x - min_x if min_x && max_x
61
+ end
62
+
63
+ def height
64
+ max_y - min_y if min_y && max_y
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,114 @@
1
+ # Copyright (c) 2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'perfect_shape/shape'
23
+ require 'perfect_shape/multi_point'
24
+
25
+ module PerfectShape
26
+ # Mostly ported from java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/Polygon.html
27
+ class Polygon < Shape
28
+ include MultiPoint
29
+
30
+ # Checks if polygon contains point denoted by point (two-number Array or x, y args)
31
+ # using the Ray Casting Algorithm (aka Even-Odd Rule): https://en.wikipedia.org/wiki/Point_in_polygon
32
+ #
33
+ # @param x The X coordinate of the point to test.
34
+ # @param y The Y coordinate of the point to test.
35
+ #
36
+ # @return {@code true} if the point lies within the bound of
37
+ # the polygon, {@code false} if the point lies outside of the
38
+ # polygon's bounds.
39
+ def contain?(x_or_point, y = nil)
40
+ x, y = normalize_point(x_or_point, y)
41
+ return unless x && y
42
+ npoints = points.count
43
+ xpoints = points.map(&:first)
44
+ ypoints = points.map(&:last)
45
+ return false if npoints <= 2 || !bounding_box.contain?(x, y)
46
+ hits = 0
47
+
48
+ lastx = xpoints[npoints - 1]
49
+ lasty = ypoints[npoints - 1]
50
+
51
+ # Walk the edges of the polygon
52
+ npoints.times do |i|
53
+ curx = xpoints[i]
54
+ cury = ypoints[i]
55
+
56
+ if cury == lasty
57
+ lastx = curx
58
+ lasty = cury
59
+ next
60
+ end
61
+
62
+ if curx < lastx
63
+ if x >= lastx
64
+ lastx = curx
65
+ lasty = cury
66
+ next
67
+ end
68
+ leftx = curx
69
+ else
70
+ if x >= curx
71
+ lastx = curx
72
+ lasty = cury
73
+ next
74
+ end
75
+ leftx = lastx
76
+ end
77
+
78
+ if cury < lasty
79
+ if y < cury || y >= lasty
80
+ lastx = curx
81
+ lasty = cury
82
+ next
83
+ end
84
+ if x < leftx
85
+ hits += 1
86
+ lastx = curx
87
+ lasty = cury
88
+ next
89
+ end
90
+ test1 = x - curx
91
+ test2 = y - cury
92
+ else
93
+ if y < lasty || y >= cury
94
+ lastx = curx
95
+ lasty = cury
96
+ next
97
+ end
98
+ if x < leftx
99
+ hits += 1
100
+ lastx = curx
101
+ lasty = cury
102
+ next
103
+ end
104
+ test1 = x - lastx
105
+ test2 = y - lasty
106
+ end
107
+
108
+ hits += 1 if (test1 < (test2 / (lasty - cury) * (lastx - curx)))
109
+ end
110
+
111
+ (hits & 1) != 0
112
+ end
113
+ end
114
+ end
@@ -26,7 +26,8 @@ module PerfectShape
26
26
  # Mostly ported from java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/Rectangle2D.html
27
27
  class Rectangle < Shape
28
28
  include RectangularShape
29
-
29
+ include Equalizer.new(:x, :y, :width, :height)
30
+
30
31
  # Checks if rectangle contains point denoted by point (two-number Array or x, y args)
31
32
  #
32
33
  # @param x The X coordinate of the point to test.
@@ -54,12 +54,20 @@ module PerfectShape
54
54
  @height = BigDecimal(value.to_s)
55
55
  end
56
56
 
57
- def center_x
58
- @x + (@width/BigDecimal('2.0')) if @x && @width
57
+ def min_x
58
+ @x
59
59
  end
60
60
 
61
- def center_y
62
- @y + (@height/BigDecimal('2.0')) if @y && @height
61
+ def min_y
62
+ @y
63
+ end
64
+
65
+ def max_x
66
+ @x + width if @x && width
67
+ end
68
+
69
+ def max_y
70
+ @y + height if @y && height
63
71
  end
64
72
  end
65
73
  end
@@ -20,7 +20,50 @@
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  module PerfectShape
23
+ # Superclass of all shapes
23
24
  class Shape
25
+ # Subclasses must implement
26
+ def min_x
27
+ end
28
+
29
+ # Subclasses must implement
30
+ def min_y
31
+ end
32
+
33
+ # Subclasses must implement
34
+ def max_x
35
+ end
36
+
37
+ # Subclasses must implement
38
+ def max_y
39
+ end
40
+
41
+ # Subclasses must implement
42
+ def width
43
+ end
44
+
45
+ # Subclasses must implement
46
+ def height
47
+ end
48
+
49
+ # center_x is min_x + width/2.0 by default
50
+ # Returns nil if min_x or width are nil
51
+ def center_x
52
+ min_x + width / BigDecimal('2.0') if min_x && width
53
+ end
54
+
55
+ # center_y is min_y + height/2.0 by default
56
+ # Returns nil if min_y or height are nil
57
+ def center_y
58
+ min_y + height / BigDecimal('2.0') if min_y && height
59
+ end
60
+
61
+ # Rectangle with x = self.min_x, y = self.min_y, width = self.width, height = self.height
62
+ def bounding_box
63
+ require 'perfect_shape/rectangle'
64
+ Rectangle.new(x: min_x, y: min_y, width: width, height: height)
65
+ end
66
+
24
67
  # Normalizes point args whether two-number Array or x, y args returning
25
68
  # normalized point array of two BigDecimal's
26
69
  #
@@ -2,17 +2,17 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: perfect-shape 0.0.5 ruby lib
5
+ # stub: perfect-shape 0.0.9 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "perfect-shape".freeze
9
- s.version = "0.0.5"
9
+ s.version = "0.0.9"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Andy Maleh".freeze]
14
- s.date = "2021-12-15"
15
- s.description = "Perfect Shape is a collection of pure Ruby geometric algorithms that are mostly useful for GUI manipulation like checking containment of a mouse click point in popular geometry shapes such as rectangle, square, arc (open, chord, and pie), ellipse, circle, polygon, polyline, polyquad, polycubic, and paths containing lines, bezier curves, and quadratic curves. Additionally, it contains some purely mathematical algorithms like IEEEremainder (also known as IEEE-754 remainder).".freeze
14
+ s.date = "2021-12-20"
15
+ s.description = "Perfect Shape is a collection of pure Ruby geometric algorithms that are mostly useful for GUI manipulation like checking containment of a mouse click point in popular geometry shapes such as rectangle, square, arc (open, chord, and pie), ellipse, circle, polygon (Ray Casting Algorithm aka Even-Odd Rule), polyline, polyquad, polycubic, and paths containing lines, bezier curves, and quadratic curves. Additionally, it contains some purely mathematical algorithms like IEEEremainder (also known as IEEE-754 remainder).".freeze
16
16
  s.email = "andy.am@gmail.com".freeze
17
17
  s.extra_rdoc_files = [
18
18
  "CHANGELOG.md",
@@ -26,9 +26,12 @@ Gem::Specification.new do |s|
26
26
  "VERSION",
27
27
  "lib/perfect-shape.rb",
28
28
  "lib/perfect_shape/arc.rb",
29
+ "lib/perfect_shape/circle.rb",
29
30
  "lib/perfect_shape/ellipse.rb",
30
31
  "lib/perfect_shape/line.rb",
31
32
  "lib/perfect_shape/math.rb",
33
+ "lib/perfect_shape/multi_point.rb",
34
+ "lib/perfect_shape/polygon.rb",
32
35
  "lib/perfect_shape/rectangle.rb",
33
36
  "lib/perfect_shape/rectangular_shape.rb",
34
37
  "lib/perfect_shape/shape.rb",
@@ -45,12 +48,14 @@ Gem::Specification.new do |s|
45
48
  end
46
49
 
47
50
  if s.respond_to? :add_runtime_dependency then
51
+ s.add_runtime_dependency(%q<equalizer>.freeze, ["= 0.0.11"])
48
52
  s.add_development_dependency(%q<rdoc>.freeze, ["~> 3.12"])
49
53
  s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
50
54
  s.add_development_dependency(%q<minitest>.freeze, ["~> 5.14.4"])
51
55
  s.add_development_dependency(%q<puts_debuggerer>.freeze, ["~> 0.13.1"])
52
56
  s.add_development_dependency(%q<rake-tui>.freeze, ["> 0"])
53
57
  else
58
+ s.add_dependency(%q<equalizer>.freeze, ["= 0.0.11"])
54
59
  s.add_dependency(%q<rdoc>.freeze, ["~> 3.12"])
55
60
  s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
56
61
  s.add_dependency(%q<minitest>.freeze, ["~> 5.14.4"])
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: perfect-shape
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-15 00:00:00.000000000 Z
11
+ date: 2021-12-20 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: equalizer
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.0.11
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.0.11
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rdoc
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -83,9 +97,10 @@ dependencies:
83
97
  description: Perfect Shape is a collection of pure Ruby geometric algorithms that
84
98
  are mostly useful for GUI manipulation like checking containment of a mouse click
85
99
  point in popular geometry shapes such as rectangle, square, arc (open, chord, and
86
- pie), ellipse, circle, polygon, polyline, polyquad, polycubic, and paths containing
87
- lines, bezier curves, and quadratic curves. Additionally, it contains some purely
88
- mathematical algorithms like IEEEremainder (also known as IEEE-754 remainder).
100
+ pie), ellipse, circle, polygon (Ray Casting Algorithm aka Even-Odd Rule), polyline,
101
+ polyquad, polycubic, and paths containing lines, bezier curves, and quadratic curves.
102
+ Additionally, it contains some purely mathematical algorithms like IEEEremainder
103
+ (also known as IEEE-754 remainder).
89
104
  email: andy.am@gmail.com
90
105
  executables: []
91
106
  extensions: []
@@ -100,9 +115,12 @@ files:
100
115
  - VERSION
101
116
  - lib/perfect-shape.rb
102
117
  - lib/perfect_shape/arc.rb
118
+ - lib/perfect_shape/circle.rb
103
119
  - lib/perfect_shape/ellipse.rb
104
120
  - lib/perfect_shape/line.rb
105
121
  - lib/perfect_shape/math.rb
122
+ - lib/perfect_shape/multi_point.rb
123
+ - lib/perfect_shape/polygon.rb
106
124
  - lib/perfect_shape/rectangle.rb
107
125
  - lib/perfect_shape/rectangular_shape.rb
108
126
  - lib/perfect_shape/shape.rb