perfect-shape 0.0.7 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/README.md +87 -12
- data/VERSION +1 -1
- data/lib/perfect_shape/arc.rb +3 -3
- data/lib/perfect_shape/ellipse.rb +1 -1
- data/lib/perfect_shape/line.rb +132 -1
- data/lib/perfect_shape/math.rb +9 -8
- data/lib/perfect_shape/multi_point.rb +59 -0
- data/lib/perfect_shape/point.rb +82 -0
- data/lib/perfect_shape/point_location.rb +53 -0
- data/lib/perfect_shape/polygon.rb +4 -40
- data/lib/perfect_shape/rectangle.rb +1 -1
- data/lib/perfect_shape/rectangular_shape.rb +6 -22
- data/lib/perfect_shape/shape.rb +12 -3
- data/perfect-shape.gemspec +6 -3
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '097e027a1c0c026611e480d05bf6281c74db9228964f6f06c414637bb60af8b0'
|
4
|
+
data.tar.gz: e2bab91460ec298a74e4735a13a123bf7b5f0ae9d15f8b5544a77aa7733cd312
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a532c231809348346907d2f369df7380cbac384772c3fc6d9858bfa423f5da6f0fd15e9eda3f94404f5579ed3572cab87d3c8d99b19c50d776ae6e999a7ff5a1
|
7
|
+
data.tar.gz: 4d7b76430666115f21053b7417c290f6260fb99f278eb8397d53ddf1c5c4a84b48b1c87d49c977bdfc54cd513c801be49ca1e586d9c0d7501e4d2a5eb648f8e4
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,30 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.0.11
|
4
|
+
|
5
|
+
- `PerfectShape::Polygon#==`
|
6
|
+
- `PerfectShape::Line#==`
|
7
|
+
- `PerfectShape::Point#==`
|
8
|
+
|
9
|
+
## 0.0.10
|
10
|
+
|
11
|
+
- `PerfectShape::Point`
|
12
|
+
- `PerfectShape::Point#point_distance`
|
13
|
+
- `PerfectShape::Point#contain?(x_or_point, y=nil, distance: 0)`
|
14
|
+
- Refactor `PerfectShape::Point`,`PerfectShape::RectangularShape` to include shared `PerfectShape::PointLocation`
|
15
|
+
|
16
|
+
## 0.0.9
|
17
|
+
|
18
|
+
- `PerfectShape::Line#contain?(x_or_point, y=nil, distance: 0)` (add a distance tolerance fuzz factor option)
|
19
|
+
|
20
|
+
## 0.0.8
|
21
|
+
|
22
|
+
- `PerfectShape::Line`
|
23
|
+
- `PerfectShape::Line#contain?(x_or_point, y=nil)`
|
24
|
+
- `PerfectShape::Line#relative_counterclockwise`
|
25
|
+
- `PerfectShape::Line#point_segment_distance`
|
26
|
+
- Update `PerfectShape::Math::radians_to_degrees`, `PerfectShape::Math::degrees_to_radians`, and `PerfectShape::Math::normalize_degrees` to normalize numbers to `BigDecimal`
|
27
|
+
|
3
28
|
## 0.0.7
|
4
29
|
|
5
30
|
- `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`
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Perfect Shape 0.0.
|
1
|
+
# Perfect Shape 0.0.11
|
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
|
|
@@ -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.
|
16
|
+
gem install perfect-shape -v 0.0.11
|
17
17
|
```
|
18
18
|
|
19
19
|
Or include in Bundler `Gemfile`:
|
20
20
|
|
21
21
|
```ruby
|
22
|
-
gem 'perfect-shape', '~> 0.0.
|
22
|
+
gem 'perfect-shape', '~> 0.0.11'
|
23
23
|
```
|
24
24
|
|
25
25
|
And, run:
|
@@ -43,6 +43,8 @@ Module
|
|
43
43
|
|
44
44
|
Class
|
45
45
|
|
46
|
+
This is a base class for all shapes. It is not meant to be used directly. Subclasses implement/override its methods as needed.
|
47
|
+
|
46
48
|
- `#min_x`: min x
|
47
49
|
- `#min_y`: min y
|
48
50
|
- `#max_x`: max x
|
@@ -53,11 +55,24 @@ Class
|
|
53
55
|
- `#center_y`: center y
|
54
56
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height just as those of shape
|
55
57
|
- `#normalize_point(x_or_point, y = nil)`: normalizes point into an `Array` of (x,y) coordinates
|
58
|
+
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
59
|
+
|
60
|
+
### `PerfectShape::PointLocation`
|
61
|
+
|
62
|
+
Module
|
63
|
+
|
64
|
+
- `#initialize(x: 0, y: 0)`: initializes a point location, usually representing the top-left point in a shape
|
65
|
+
- `#x`: top-left x
|
66
|
+
- `#y`: top-left y
|
67
|
+
- `#min_x`: min x (x by default)
|
68
|
+
- `#min_y`: min y (y by default)
|
56
69
|
|
57
70
|
### `PerfectShape::RectangularShape`
|
58
71
|
|
59
72
|
Module
|
60
73
|
|
74
|
+
Includes `PerfectShape::PointLocation`
|
75
|
+
|
61
76
|
- `#initialize(x: 0, y: 0, width: 1, height: 1)`: initializes a rectangular shape
|
62
77
|
- `#x`: top-left x
|
63
78
|
- `#y`: top-left y
|
@@ -71,20 +86,68 @@ Module
|
|
71
86
|
- `#center_y`: center y
|
72
87
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
73
88
|
|
89
|
+
### `PerfectShape::Point`
|
90
|
+
|
91
|
+
Class
|
92
|
+
|
93
|
+
Extends `PerfectShape::Shape`
|
94
|
+
|
95
|
+
Includes `PerfectShape::PointLocation`
|
96
|
+
|
97
|
+
![point](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/point.png)
|
98
|
+
|
99
|
+
Points are simply represented by an `Array` of (x,y) coordinates when used within other shapes, but when needing point-specific operations like `point_distance`, the `PerfectShape::Point` class can come in handy.
|
100
|
+
|
101
|
+
- `::point_distance(x, y, px, py)`: Returns the distance from a point to another point
|
102
|
+
- `::new(x_or_point=nil, y_arg=nil, x: nil, y: nil)`: constructs a point with (x,y) pair (default: 0,0) whether specified as `Array` of (x,y) pair, flat `x,y` args, or `x:, y:` kwargs.
|
103
|
+
- `#min_x`: min x (always x)
|
104
|
+
- `#min_y`: min y (always y)
|
105
|
+
- `#max_x`: max x (always x)
|
106
|
+
- `#max_y`: max y (always y)
|
107
|
+
- `#width`: width (always 0)
|
108
|
+
- `#height`: height (always 0)
|
109
|
+
- `#center_x`: center x (always x)
|
110
|
+
- `#center_y`: center y (always y)
|
111
|
+
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
112
|
+
- `#contain?(x_or_point, y=nil, distance: 0)`: checks if point matches self, with a distance tolerance (0 by default). Distance tolerance provides a fuzz factor that for example enables GUI users to mouse-click-select a point shape in a GUI more successfully.
|
113
|
+
- `#point_distance(x_or_point, y=nil)`: Returns the distance from a point to another point
|
114
|
+
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
115
|
+
|
74
116
|
### `PerfectShape::Line`
|
75
117
|
|
76
118
|
Class
|
119
|
+
|
77
120
|
Extends `PerfectShape::Shape`
|
78
121
|
|
79
|
-
|
122
|
+
![line](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/line.png)
|
123
|
+
|
124
|
+
- `::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)”.
|
125
|
+
- `::point_segment_distance_square(x1, y1, x2, y2, px, py)`: Returns the square of distance from a point to a line segment.
|
126
|
+
- `::point_segment_distance(x1, y1, x2, y2, px, py)`: Returns the distance from a point to a line segment.
|
127
|
+
- `::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
|
128
|
+
- `#min_x`: min x
|
129
|
+
- `#min_y`: min y
|
130
|
+
- `#max_x`: max x
|
131
|
+
- `#max_y`: max y
|
132
|
+
- `#width`: width (from min x to max x)
|
133
|
+
- `#height`: height (from min y to max y)
|
134
|
+
- `#center_x`: center x
|
135
|
+
- `#center_y`: center y
|
136
|
+
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
137
|
+
- `#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.
|
138
|
+
- `#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)”.
|
139
|
+
- `#point_segment_distance(x_or_point, y=nil)`: Returns the distance from a point to a line segment.
|
140
|
+
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
80
141
|
|
81
142
|
### `PerfectShape::Rectangle`
|
82
143
|
|
83
144
|
Class
|
145
|
+
|
84
146
|
Extends `PerfectShape::Shape`
|
147
|
+
|
85
148
|
Includes `PerfectShape::RectangularShape`
|
86
149
|
|
87
|
-
![rectangle](images/rectangle.png)
|
150
|
+
![rectangle](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/rectangle.png)
|
88
151
|
|
89
152
|
- `::new(x: 0, y: 0, width: 1, height: 1)`: constructs a rectangle
|
90
153
|
- `#x`: top-left x
|
@@ -99,13 +162,15 @@ Includes `PerfectShape::RectangularShape`
|
|
99
162
|
- `#max_y`: max y
|
100
163
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
101
164
|
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
165
|
+
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
102
166
|
|
103
167
|
### `PerfectShape::Square`
|
104
168
|
|
105
169
|
Class
|
170
|
+
|
106
171
|
Extends `PerfectShape::Rectangle`
|
107
172
|
|
108
|
-
![square](images/square.png)
|
173
|
+
![square](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/square.png)
|
109
174
|
|
110
175
|
- `::new(x: 0, y: 0, length: 1)`: constructs a square
|
111
176
|
- `#x`: top-left x
|
@@ -121,18 +186,21 @@ Extends `PerfectShape::Rectangle`
|
|
121
186
|
- `#max_y`: max y
|
122
187
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
123
188
|
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
189
|
+
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
124
190
|
|
125
191
|
### `PerfectShape::Arc`
|
126
192
|
|
127
193
|
Class
|
194
|
+
|
128
195
|
Extends `PerfectShape::Shape`
|
196
|
+
|
129
197
|
Includes `PerfectShape::RectangularShape`
|
130
198
|
|
131
199
|
Arcs can be of type `:open`, `:chord`, or `:pie`
|
132
200
|
|
133
201
|
Open Arc | Chord Arc | Pie Arc
|
134
202
|
---------|-----------|--------
|
135
|
-
![arc-open](images/arc-open.png) | ![arc-chord](images/arc-chord.png) | ![arc-pie](images/arc-pie.png)
|
203
|
+
![arc-open](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/arc-open.png) | ![arc-chord](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/arc-chord.png) | ![arc-pie](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/arc-pie.png)
|
136
204
|
|
137
205
|
- `::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`
|
138
206
|
- `#type`: `:open`, `:chord`, or `:pie`
|
@@ -152,13 +220,15 @@ Open Arc | Chord Arc | Pie Arc
|
|
152
220
|
- `#max_y`: max y
|
153
221
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
154
222
|
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
223
|
+
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
155
224
|
|
156
225
|
### `PerfectShape::Ellipse`
|
157
226
|
|
158
227
|
Class
|
228
|
+
|
159
229
|
Extends `PerfectShape::Arc`
|
160
230
|
|
161
|
-
![ellipse](images/ellipse.png)
|
231
|
+
![ellipse](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/ellipse.png)
|
162
232
|
|
163
233
|
- `::new(x: 0, y: 0, width: 1, height: 1, center_x: nil, center_y: nil, radius_x: nil, radius_y: nil)`: constructs an ellipse
|
164
234
|
- `#x`: top-left x
|
@@ -178,13 +248,15 @@ Extends `PerfectShape::Arc`
|
|
178
248
|
- `#max_y`: max y
|
179
249
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
180
250
|
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
251
|
+
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
181
252
|
|
182
253
|
### `PerfectShape::Circle`
|
183
254
|
|
184
255
|
Class
|
256
|
+
|
185
257
|
Extends `PerfectShape::Ellipse`
|
186
258
|
|
187
|
-
![circle](images/circle.png)
|
259
|
+
![circle](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/circle.png)
|
188
260
|
|
189
261
|
- `::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
|
190
262
|
- `#x`: top-left x
|
@@ -206,15 +278,17 @@ Extends `PerfectShape::Ellipse`
|
|
206
278
|
- `#max_y`: max y
|
207
279
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
208
280
|
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
281
|
+
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
209
282
|
|
210
283
|
### `PerfectShape::Polygon`
|
211
284
|
|
212
285
|
Class
|
286
|
+
|
213
287
|
Extends `PerfectShape::Shape`
|
214
288
|
|
215
|
-
![polygon](images/polygon.png)
|
289
|
+
![polygon](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/polygon.png)
|
216
290
|
|
217
|
-
- `::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
|
291
|
+
- `::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
|
218
292
|
- `#min_x`: min x
|
219
293
|
- `#min_y`: min y
|
220
294
|
- `#max_x`: max x
|
@@ -225,6 +299,7 @@ Extends `PerfectShape::Shape`
|
|
225
299
|
- `#center_y`: center y
|
226
300
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
227
301
|
- `#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))
|
302
|
+
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
228
303
|
|
229
304
|
## Process
|
230
305
|
|
@@ -233,7 +308,7 @@ Extends `PerfectShape::Shape`
|
|
233
308
|
## Resources
|
234
309
|
|
235
310
|
- Rubydoc: https://www.rubydoc.info/gems/perfect-shape
|
236
|
-
- AWT Geom JavaDoc: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/package-summary.html
|
311
|
+
- AWT Geom JavaDoc (inspiration): https://docs.oracle.com/javase/8/docs/api/java/awt/geom/package-summary.html
|
237
312
|
|
238
313
|
## TODO
|
239
314
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.11
|
data/lib/perfect_shape/arc.rb
CHANGED
@@ -135,7 +135,7 @@ module PerfectShape
|
|
135
135
|
@height = nil
|
136
136
|
end
|
137
137
|
|
138
|
-
# Checks if arc contains point
|
138
|
+
# Checks if arc contains point (two-number Array or x, y args)
|
139
139
|
#
|
140
140
|
# @param x The X coordinate of the point to test.
|
141
141
|
# @param y The Y coordinate of the point to test.
|
@@ -178,8 +178,8 @@ module PerfectShape
|
|
178
178
|
angle += Math.degrees_to_radians(-extent)
|
179
179
|
x2 = Math.cos(angle)
|
180
180
|
y2 = Math.sin(angle)
|
181
|
-
inside = (Line.
|
182
|
-
Line.
|
181
|
+
inside = (Line.relative_counterclockwise(x1, y1, x2, y2, 2*normx, 2*normy) *
|
182
|
+
Line.relative_counterclockwise(x1, y1, x2, y2, 0, 0) >= 0)
|
183
183
|
inarc ? !inside : inside
|
184
184
|
end
|
185
185
|
|
@@ -55,7 +55,7 @@ module PerfectShape
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
-
# Checks if ellipse contains point
|
58
|
+
# Checks if ellipse contains point (two-number Array or x, y args)
|
59
59
|
#
|
60
60
|
# @param x The X coordinate of the point to test.
|
61
61
|
# @param y The Y coordinate of the point to test.
|
data/lib/perfect_shape/line.rb
CHANGED
@@ -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
|
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
|
+
include Equalizer.new(:points)
|
189
|
+
|
190
|
+
# Checks if line contains point (two-number Array or x, y args), with distance tolerance (0 by default)
|
191
|
+
#
|
192
|
+
# @param x The X coordinate of the point to test.
|
193
|
+
# @param y The Y coordinate of the point to test.
|
194
|
+
# @param distance The distance from line to tolerate (0 by default)
|
195
|
+
#
|
196
|
+
# @return {@code true} if the point lies within the bound of
|
197
|
+
# the line, {@code false} if the point lies outside of the
|
198
|
+
# line's bounds.
|
199
|
+
def contain?(x_or_point, y = nil, distance: 0)
|
200
|
+
x, y = normalize_point(x_or_point, y)
|
201
|
+
return unless x && y
|
202
|
+
distance = BigDecimal(distance.to_s)
|
203
|
+
point_segment_distance(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
|
data/lib/perfect_shape/math.rb
CHANGED
@@ -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,59 @@
|
|
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
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,82 @@
|
|
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/point_location'
|
24
|
+
|
25
|
+
module PerfectShape
|
26
|
+
class Point < Shape
|
27
|
+
class << self
|
28
|
+
def point_distance(x, y, px, py)
|
29
|
+
x = BigDecimal(x.to_s)
|
30
|
+
y = BigDecimal(y.to_s)
|
31
|
+
px = BigDecimal(px.to_s)
|
32
|
+
py = BigDecimal(py.to_s)
|
33
|
+
BigDecimal(Math.sqrt((px - x)**2 + (py - y)**2).to_s)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
include PointLocation
|
38
|
+
include Equalizer.new(:x, :y)
|
39
|
+
|
40
|
+
def initialize(x_or_point = nil, y_arg = nil, x: nil, y: nil)
|
41
|
+
if x_or_point.is_a?(Array)
|
42
|
+
x, y = x_or_point
|
43
|
+
super(x: x, y: y)
|
44
|
+
elsif x_or_point && y_arg
|
45
|
+
super(x: x_or_point, y: y_arg)
|
46
|
+
else
|
47
|
+
x ||= 0
|
48
|
+
y ||= 0
|
49
|
+
super(x: x, y: y)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def max_x
|
54
|
+
x
|
55
|
+
end
|
56
|
+
|
57
|
+
def max_y
|
58
|
+
y
|
59
|
+
end
|
60
|
+
|
61
|
+
# Checks if points match, with distance tolerance (0 by default)
|
62
|
+
#
|
63
|
+
# @param x The X coordinate of the point to test.
|
64
|
+
# @param y The Y coordinate of the point to test.
|
65
|
+
# @param distance The distance from point to tolerate (0 by default)
|
66
|
+
#
|
67
|
+
# @return {@code true} if the point is close enough within distance tolerance,
|
68
|
+
# {@code false} if the point is too far.
|
69
|
+
def contain?(x_or_point, y = nil, distance: 0)
|
70
|
+
x, y = normalize_point(x_or_point, y)
|
71
|
+
return unless x && y
|
72
|
+
distance = BigDecimal(distance.to_s)
|
73
|
+
point_distance(x, y) <= distance
|
74
|
+
end
|
75
|
+
|
76
|
+
def point_distance(x_or_point, y = nil)
|
77
|
+
x, y = normalize_point(x_or_point, y)
|
78
|
+
return unless x && y
|
79
|
+
Point.point_distance(self.x, self.y, x, y)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,53 @@
|
|
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
|
+
module PerfectShape
|
23
|
+
module PointLocation
|
24
|
+
attr_reader :x, :y
|
25
|
+
|
26
|
+
# Calls super before setting x,y (default: 0,0)
|
27
|
+
def initialize(x: 0, y: 0)
|
28
|
+
super()
|
29
|
+
self.x = x
|
30
|
+
self.y = y
|
31
|
+
end
|
32
|
+
|
33
|
+
# Sets x, normalizing to BigDecimal
|
34
|
+
def x=(value)
|
35
|
+
@x = BigDecimal(value.to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sets y, normalizing to BigDecimal
|
39
|
+
def y=(value)
|
40
|
+
@y = BigDecimal(value.to_s)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns x by default. Subclasses may override.
|
44
|
+
def min_x
|
45
|
+
x
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns y by default. Subclasses may override.
|
49
|
+
def min_y
|
50
|
+
y
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -20,51 +20,15 @@
|
|
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/Polygon.html
|
26
27
|
class Polygon < Shape
|
27
|
-
|
28
|
+
include MultiPoint
|
29
|
+
include Equalizer.new(:points)
|
28
30
|
|
29
|
-
|
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
|
-
|
67
|
-
# Checks if polygon contains point denoted by point (two-number Array or x, y args)
|
31
|
+
# Checks if polygon contains point (two-number Array or x, y args)
|
68
32
|
# using the Ray Casting Algorithm (aka Even-Odd Rule): https://en.wikipedia.org/wiki/Point_in_polygon
|
69
33
|
#
|
70
34
|
# @param x The X coordinate of the point to test.
|
@@ -28,7 +28,7 @@ module PerfectShape
|
|
28
28
|
include RectangularShape
|
29
29
|
include Equalizer.new(:x, :y, :width, :height)
|
30
30
|
|
31
|
-
# Checks if rectangle contains point
|
31
|
+
# Checks if rectangle contains point (two-number Array or x, y args)
|
32
32
|
#
|
33
33
|
# @param x The X coordinate of the point to test.
|
34
34
|
# @param y The Y coordinate of the point to test.
|
@@ -19,31 +19,23 @@
|
|
19
19
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
21
|
|
22
|
+
require 'perfect_shape/point_location'
|
23
|
+
|
22
24
|
module PerfectShape
|
23
25
|
# Mixin Module for Rectangular Shapes (having x, y, width, height)
|
24
26
|
# Can only be mixed into a class extending Shape or another module
|
25
27
|
module RectangularShape
|
26
|
-
|
28
|
+
include PointLocation
|
29
|
+
|
30
|
+
attr_reader :width, :height
|
27
31
|
|
28
32
|
# Calls super before setting x, y, width, height
|
29
33
|
def initialize(x: 0, y: 0, width: 1, height: 1)
|
30
|
-
super()
|
31
|
-
self.x = x
|
32
|
-
self.y = y
|
34
|
+
super(x: x, y: y)
|
33
35
|
self.width = width
|
34
36
|
self.height = height
|
35
37
|
end
|
36
38
|
|
37
|
-
# Sets x, normalizing to BigDecimal
|
38
|
-
def x=(value)
|
39
|
-
@x = BigDecimal(value.to_s)
|
40
|
-
end
|
41
|
-
|
42
|
-
# Sets y, normalizing to BigDecimal
|
43
|
-
def y=(value)
|
44
|
-
@y = BigDecimal(value.to_s)
|
45
|
-
end
|
46
|
-
|
47
39
|
# Sets width, normalizing to BigDecimal
|
48
40
|
def width=(value)
|
49
41
|
@width = BigDecimal(value.to_s)
|
@@ -54,14 +46,6 @@ module PerfectShape
|
|
54
46
|
@height = BigDecimal(value.to_s)
|
55
47
|
end
|
56
48
|
|
57
|
-
def min_x
|
58
|
-
@x
|
59
|
-
end
|
60
|
-
|
61
|
-
def min_y
|
62
|
-
@y
|
63
|
-
end
|
64
|
-
|
65
49
|
def max_x
|
66
50
|
@x + width if @x && width
|
67
51
|
end
|
data/lib/perfect_shape/shape.rb
CHANGED
@@ -20,7 +20,8 @@
|
|
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
|
+
# Superclass of all shapes. Not meant to be used directly.
|
24
|
+
# Subclasses must implement/override methods as needed.
|
24
25
|
class Shape
|
25
26
|
# Subclasses must implement
|
26
27
|
def min_x
|
@@ -38,12 +39,16 @@ module PerfectShape
|
|
38
39
|
def max_y
|
39
40
|
end
|
40
41
|
|
41
|
-
#
|
42
|
+
# Default implementation is max_x - min_x
|
43
|
+
# Subclasses can override
|
42
44
|
def width
|
45
|
+
max_x - min_x if max_x && min_x
|
43
46
|
end
|
44
47
|
|
45
|
-
#
|
48
|
+
# Default implementation is max_y - min_y
|
49
|
+
# Subclasses can override
|
46
50
|
def height
|
51
|
+
max_y - min_y if max_y && min_y
|
47
52
|
end
|
48
53
|
|
49
54
|
# center_x is min_x + width/2.0 by default
|
@@ -78,5 +83,9 @@ module PerfectShape
|
|
78
83
|
y = BigDecimal(y.to_s)
|
79
84
|
[x, y]
|
80
85
|
end
|
86
|
+
|
87
|
+
# Subclasses must implement
|
88
|
+
def ==(other)
|
89
|
+
end
|
81
90
|
end
|
82
91
|
end
|
data/perfect-shape.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
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
|
+
# stub: perfect-shape 0.0.11 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "perfect-shape".freeze
|
9
|
-
s.version = "0.0.
|
9
|
+
s.version = "0.0.11"
|
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-
|
14
|
+
s.date = "2021-12-20"
|
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 (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 = [
|
@@ -30,6 +30,9 @@ Gem::Specification.new do |s|
|
|
30
30
|
"lib/perfect_shape/ellipse.rb",
|
31
31
|
"lib/perfect_shape/line.rb",
|
32
32
|
"lib/perfect_shape/math.rb",
|
33
|
+
"lib/perfect_shape/multi_point.rb",
|
34
|
+
"lib/perfect_shape/point.rb",
|
35
|
+
"lib/perfect_shape/point_location.rb",
|
33
36
|
"lib/perfect_shape/polygon.rb",
|
34
37
|
"lib/perfect_shape/rectangle.rb",
|
35
38
|
"lib/perfect_shape/rectangular_shape.rb",
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: perfect-shape
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.11
|
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-
|
11
|
+
date: 2021-12-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: equalizer
|
@@ -119,6 +119,9 @@ files:
|
|
119
119
|
- lib/perfect_shape/ellipse.rb
|
120
120
|
- lib/perfect_shape/line.rb
|
121
121
|
- lib/perfect_shape/math.rb
|
122
|
+
- lib/perfect_shape/multi_point.rb
|
123
|
+
- lib/perfect_shape/point.rb
|
124
|
+
- lib/perfect_shape/point_location.rb
|
122
125
|
- lib/perfect_shape/polygon.rb
|
123
126
|
- lib/perfect_shape/rectangle.rb
|
124
127
|
- lib/perfect_shape/rectangular_shape.rb
|