perfect-shape 0.1.1 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -3
- data/README.md +300 -21
- data/VERSION +1 -1
- data/lib/perfect_shape/arc.rb +48 -34
- data/lib/perfect_shape/composite_shape.rb +72 -0
- data/lib/perfect_shape/cubic_bezier_curve.rb +120 -0
- data/lib/perfect_shape/ellipse.rb +12 -8
- data/lib/perfect_shape/line.rb +9 -9
- data/lib/perfect_shape/path.rb +74 -61
- data/lib/perfect_shape/point.rb +4 -4
- data/lib/perfect_shape/polygon.rb +68 -62
- data/lib/perfect_shape/quadratic_bezier_curve.rb +22 -14
- data/lib/perfect_shape/rectangle.rb +10 -2
- data/perfect-shape.gemspec +7 -5
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d9e16e850ee86ff6e5953dd10f2c1c753109906b671a85115ea1913386a3a736
|
4
|
+
data.tar.gz: a5b1fef3214f92dd3185c1ee1c6e3f0522ff278cda0a0f384341c9e4a5bad5f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19c2f3261ef17fa8e048c8e826e56a3f1d07b2ff39a3a8d18832a0b1e2113b63af8ce09aa8a978f6c81a931ffcb8154e0fdc77ed53b0f0937f7d8d6529250fd5
|
7
|
+
data.tar.gz: 681654b7cac1242f872225a0726f8f2fa5813a8c61d999342466fa06516ccaeb981be68f5fad003eeb0d86a4b58c698f264e94a356a4ee934361482f6a072726
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,31 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.3.1
|
4
|
+
|
5
|
+
- Check point containment in arc outline with distance tolerance (new method signature: `PerfectShape::Arc#contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)`)
|
6
|
+
- Check point containment in ellipse outline with distance tolerance (new method signature: `PerfectShape::Ellipse#contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)`)
|
7
|
+
- Check point containment in circle outline with distance tolerance (new method signature: `PerfectShape::Circle#contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)`)
|
8
|
+
|
9
|
+
## 0.3.0
|
10
|
+
|
11
|
+
- Refactoring: rename `distance` option for `#contain?` on `Point`/`Line` into `distance_tolerance`
|
12
|
+
- Check point containment in rectangle outline with distance tolerance (new method signature: `PerfectShape::Rectangle#contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)`)
|
13
|
+
- Check point containment in square outline with distance tolerance (new method signature: `PerfectShape::Square#contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)`)
|
14
|
+
- Check point containment in polygon outline with distance tolerance (new method signature: `PerfectShape::Polygon#contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)`)
|
15
|
+
|
16
|
+
## 0.2.0
|
17
|
+
|
18
|
+
- `PerfectShape::CompositeShape`: aggregate of multiple shapes
|
19
|
+
- `PerfectShape::CompositeShape#contain?(x_or_point, y=nil)`
|
20
|
+
- `PerfectShape::CompositeShape#==`
|
21
|
+
|
22
|
+
## 0.1.2
|
23
|
+
|
24
|
+
- `PerfectShape::CubicBezierCurve` (two end points and two control points)
|
25
|
+
- `PerfectShape::CubicBezierCurve#contain?(x_or_point, y=nil)`
|
26
|
+
- `PerfectShape::CubicBezierCurve#==`
|
27
|
+
- `PerfectShape::Path` having cubic bezier curves in addition to points, lines, and quadratic bezier curves
|
28
|
+
|
3
29
|
## 0.1.1
|
4
30
|
|
5
31
|
- `PerfectShape::QuadraticBezierCurve` (two end points and one control point)
|
@@ -10,7 +36,7 @@
|
|
10
36
|
## 0.1.0
|
11
37
|
|
12
38
|
- `PerfectShape::Path` (having points or lines)
|
13
|
-
- `PerfectShape::Path#contain?(x_or_point, y=nil,
|
39
|
+
- `PerfectShape::Path#contain?(x_or_point, y=nil, distance_tolerance: 0)`
|
14
40
|
- `PerfectShape::Path#point_crossings(x_or_point, y=nil)`
|
15
41
|
- `PerfectShape::Path#==`
|
16
42
|
|
@@ -24,12 +50,12 @@
|
|
24
50
|
|
25
51
|
- `PerfectShape::Point`
|
26
52
|
- `PerfectShape::Point#point_distance`
|
27
|
-
- `PerfectShape::Point#contain?(x_or_point, y=nil,
|
53
|
+
- `PerfectShape::Point#contain?(x_or_point, y=nil, distance_tolerance: 0)`
|
28
54
|
- Refactor `PerfectShape::Point`,`PerfectShape::RectangularShape` to include shared `PerfectShape::PointLocation`
|
29
55
|
|
30
56
|
## 0.0.9
|
31
57
|
|
32
|
-
- `PerfectShape::Line#contain?(x_or_point, y=nil,
|
58
|
+
- `PerfectShape::Line#contain?(x_or_point, y=nil, distance_tolerance: 0)` (add a distance tolerance fuzz factor option)
|
33
59
|
|
34
60
|
## 0.0.8
|
35
61
|
|
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
# Perfect Shape 0.
|
1
|
+
# Perfect Shape 0.3.1
|
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
|
[![Test](https://github.com/AndyObtiva/perfect-shape/actions/workflows/ruby.yml/badge.svg)](https://github.com/AndyObtiva/perfect-shape/actions/workflows/ruby.yml)
|
5
5
|
|
6
|
-
[`PerfectShape`](https://rubygems.org/gems/perfect-shape) 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](#perfectshapepoint) in popular geometry shapes such as [rectangle](#perfectshaperectangle), [square](#perfectshapesquare), [arc](#perfectshapearc) (open, chord, and pie), [ellipse](#perfectshapeellipse), [circle](#perfectshapecircle), [polygon](#perfectshapepolygon), and [paths](#perfectshapepath) containing [lines](#perfectshapeline), [quadratic bézier curves](#perfectshapequadraticbeziercurve), and cubic
|
6
|
+
[`PerfectShape`](https://rubygems.org/gems/perfect-shape) 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](#perfectshapepoint) in popular geometry shapes such as [rectangle](#perfectshaperectangle), [square](#perfectshapesquare), [arc](#perfectshapearc) (open, chord, and pie), [ellipse](#perfectshapeellipse), [circle](#perfectshapecircle), [polygon](#perfectshapepolygon), and [paths](#perfectshapepath) containing [lines](#perfectshapeline), [quadratic bézier curves](#perfectshapequadraticbeziercurve), and [cubic bezier curves](#perfectshapecubicbeziercurve) (including both [Ray Casting Algorithm](https://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm), aka [Even-odd Rule](https://en.wikipedia.org/wiki/Even%E2%80%93odd_rule), and [Winding Number Algorithm](https://en.wikipedia.org/wiki/Point_in_polygon#Winding_number_algorithm), aka [Nonzero Rule](https://en.wikipedia.org/wiki/Nonzero-rule)).
|
7
7
|
|
8
8
|
Additionally, [`PerfectShape::Math`](#perfectshapemath) contains some purely mathematical algorithms, like [IEEE 754-1985 Remainder](https://en.wikipedia.org/wiki/IEEE_754-1985).
|
9
9
|
|
@@ -14,13 +14,13 @@ To ensure high accuracy, this library does all its mathematical operations with
|
|
14
14
|
Run:
|
15
15
|
|
16
16
|
```
|
17
|
-
gem install perfect-shape -v 0.
|
17
|
+
gem install perfect-shape -v 0.3.1
|
18
18
|
```
|
19
19
|
|
20
20
|
Or include in Bundler `Gemfile`:
|
21
21
|
|
22
22
|
```ruby
|
23
|
-
gem 'perfect-shape', '~> 0.
|
23
|
+
gem 'perfect-shape', '~> 0.3.1'
|
24
24
|
```
|
25
25
|
|
26
26
|
And, run:
|
@@ -56,6 +56,7 @@ This is a base class for all shapes. It is not meant to be used directly. Subcla
|
|
56
56
|
- `#center_y`: center y
|
57
57
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height just as those of shape
|
58
58
|
- `#normalize_point(x_or_point, y = nil)`: normalizes point into an `Array` of `[x,y]` coordinates
|
59
|
+
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
59
60
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
60
61
|
|
61
62
|
### `PerfectShape::PointLocation`
|
@@ -110,14 +111,23 @@ Points are simply represented by an `Array` of `[x,y]` coordinates when used wit
|
|
110
111
|
- `#center_x`: center x (always x)
|
111
112
|
- `#center_y`: center y (always y)
|
112
113
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
113
|
-
- `#contain?(x_or_point, y=nil,
|
114
|
+
- `#contain?(x_or_point, y=nil, distance_tolerance: 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 more successfully.
|
114
115
|
- `#point_distance(x_or_point, y=nil)`: Returns the distance from a point to another point
|
115
116
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
116
117
|
|
117
118
|
Example:
|
118
119
|
|
119
120
|
```ruby
|
121
|
+
require 'perfect-shape'
|
122
|
+
|
120
123
|
shape = PerfectShape::Point.new(x: 200, y: 150)
|
124
|
+
|
125
|
+
shape.contain?(200, 150) # => true
|
126
|
+
shape.contain?([200, 150]) # => true
|
127
|
+
shape.contain?(200, 151) # => false
|
128
|
+
shape.contain?([200, 151]) # => false
|
129
|
+
shape.contain?(200, 151, distance_tolerance: 5) # => true
|
130
|
+
shape.contain?([200, 151], distance_tolerance: 5) # => true
|
121
131
|
```
|
122
132
|
|
123
133
|
### `PerfectShape::Line`
|
@@ -133,7 +143,7 @@ Includes `PerfectShape::MultiPoint`
|
|
133
143
|
- `::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)”.
|
134
144
|
- `::point_segment_distance_square(x1, y1, x2, y2, px, py)`: Returns the square of distance from a point to a line segment.
|
135
145
|
- `::point_segment_distance(x1, y1, x2, y2, px, py)`: Returns the distance from a point to a line segment.
|
136
|
-
- `::new(points: [])`: constructs a line with two `points` as `Array` of `Array`s of `[x,y]` pairs or flattened `Array` of alternating x and y
|
146
|
+
- `::new(points: [])`: constructs a line with two `points` as `Array` of `Array`s of `[x,y]` pairs or flattened `Array` of alternating x and y coordinates
|
137
147
|
- `#min_x`: min x
|
138
148
|
- `#min_y`: min y
|
139
149
|
- `#max_x`: max x
|
@@ -143,7 +153,7 @@ Includes `PerfectShape::MultiPoint`
|
|
143
153
|
- `#center_x`: center x
|
144
154
|
- `#center_y`: center y
|
145
155
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
146
|
-
- `#contain?(x_or_point, y=nil,
|
156
|
+
- `#contain?(x_or_point, y=nil, distance_tolerance: 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 more successfully.
|
147
157
|
- `#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)”.
|
148
158
|
- `#point_segment_distance(x_or_point, y=nil)`: Returns the distance from a point to a line segment.
|
149
159
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
@@ -151,7 +161,16 @@ Includes `PerfectShape::MultiPoint`
|
|
151
161
|
Example:
|
152
162
|
|
153
163
|
```ruby
|
154
|
-
shape
|
164
|
+
require 'perfect-shape'
|
165
|
+
|
166
|
+
shape = PerfectShape::Line.new(points: [[0, 0], [100, 100]]) # start point and end point
|
167
|
+
|
168
|
+
shape.contain?(50, 50) # => true
|
169
|
+
shape.contain?([50, 50]) # => true
|
170
|
+
shape.contain?(50, 51) # => false
|
171
|
+
shape.contain?([50, 51]) # => false
|
172
|
+
shape.contain?(50, 51, distance_tolerance: 5) # => true
|
173
|
+
shape.contain?([50, 51], distance_tolerance: 5) # => true
|
155
174
|
```
|
156
175
|
|
157
176
|
### `PerfectShape::QuadraticBezierCurve`
|
@@ -164,7 +183,8 @@ Includes `PerfectShape::MultiPoint`
|
|
164
183
|
|
165
184
|
![quadratic_bezier_curve](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/quadratic_bezier_curve.png)
|
166
185
|
|
167
|
-
- `::new(points: [])`: constructs a quadratic bézier curve with three `points` (
|
186
|
+
- `::new(points: [])`: constructs a quadratic bézier curve with three `points` (start point, control point, and end point) as `Array` of `Array`s of `[x,y]` pairs or flattened `Array` of alternating x and y coordinates
|
187
|
+
- `#points`: points (start point, control point, and end point)
|
168
188
|
- `#min_x`: min x
|
169
189
|
- `#min_y`: min y
|
170
190
|
- `#max_x`: max x
|
@@ -180,7 +200,47 @@ Includes `PerfectShape::MultiPoint`
|
|
180
200
|
Example:
|
181
201
|
|
182
202
|
```ruby
|
183
|
-
shape
|
203
|
+
require 'perfect-shape'
|
204
|
+
|
205
|
+
shape = PerfectShape::QuadraticBezierCurve.new(points: [[200, 150], [270, 320], [380, 150]]) # start point, control point, and end point
|
206
|
+
|
207
|
+
shape.contain?(270, 220) # => true
|
208
|
+
shape.contain?([270, 220]) # => true
|
209
|
+
```
|
210
|
+
|
211
|
+
### `PerfectShape::CubicBezierCurve`
|
212
|
+
|
213
|
+
Class
|
214
|
+
|
215
|
+
Extends `PerfectShape::Shape`
|
216
|
+
|
217
|
+
Includes `PerfectShape::MultiPoint`
|
218
|
+
|
219
|
+
![cubic_bezier_curve](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/cubic_bezier_curve.png)
|
220
|
+
|
221
|
+
- `::new(points: [])`: constructs a cubic bézier curve with four `points` (start point, two control points, and end point) as `Array` of `Array`s of `[x,y]` pairs or flattened `Array` of alternating x and y coordinates
|
222
|
+
- `#points`: points (start point, two control points, and end point)
|
223
|
+
- `#min_x`: min x
|
224
|
+
- `#min_y`: min y
|
225
|
+
- `#max_x`: max x
|
226
|
+
- `#max_y`: max y
|
227
|
+
- `#width`: width (from min x to max x)
|
228
|
+
- `#height`: height (from min y to max y)
|
229
|
+
- `#center_x`: center x
|
230
|
+
- `#center_y`: center y
|
231
|
+
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape (bounding box only guarantees that the shape is within it, but it might be bigger than the shape)
|
232
|
+
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
233
|
+
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
234
|
+
|
235
|
+
Example:
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
require 'perfect-shape'
|
239
|
+
|
240
|
+
shape = PerfectShape::CubicBezierCurve.new(points: [[200, 150], [235, 235], [270, 320], [380, 150]]) # start point, two control points, and end point
|
241
|
+
|
242
|
+
shape.contain?(270, 220) # => true
|
243
|
+
shape.contain?([270, 220]) # => true
|
184
244
|
```
|
185
245
|
|
186
246
|
### `PerfectShape::Rectangle`
|
@@ -205,13 +265,26 @@ Includes `PerfectShape::RectangularShape`
|
|
205
265
|
- `#max_x`: max x
|
206
266
|
- `#max_y`: max y
|
207
267
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
208
|
-
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
268
|
+
- `#contain?(x_or_point, y=nil, outline: false, distance_tolerance: 0)`: checks if point is inside when `outline` is `false` or if point is on the outline when `outline` is `true`. `distance_tolerance` can be used as a fuzz factor when `outline` is `true`, for example, to help GUI users mouse-click-select a rectangle shape from its outline more successfully
|
209
269
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
210
270
|
|
211
271
|
Example:
|
212
272
|
|
213
273
|
```ruby
|
274
|
+
require 'perfect-shape'
|
275
|
+
|
214
276
|
shape = PerfectShape::Rectangle.new(x: 15, y: 30, width: 200, height: 100)
|
277
|
+
|
278
|
+
shape.contain?(115, 80) # => true
|
279
|
+
shape.contain?([115, 80]) # => true
|
280
|
+
shape.contain?(115, 80, outline: true) # => false
|
281
|
+
shape.contain?([115, 80], outline: true) # => false
|
282
|
+
shape.contain?(115, 30, outline: true) # => true
|
283
|
+
shape.contain?([115, 30], outline: true) # => true
|
284
|
+
shape.contain?(115, 31, outline: true) # => false
|
285
|
+
shape.contain?([115, 31], outline: true) # => false
|
286
|
+
shape.contain?(115, 31, outline: true, distance_tolerance: 1) # => true
|
287
|
+
shape.contain?([115, 31], outline: true, distance_tolerance: 1) # => true
|
215
288
|
```
|
216
289
|
|
217
290
|
### `PerfectShape::Square`
|
@@ -235,13 +308,26 @@ Extends `PerfectShape::Rectangle`
|
|
235
308
|
- `#max_x`: max x
|
236
309
|
- `#max_y`: max y
|
237
310
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
238
|
-
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
311
|
+
- `#contain?(x_or_point, y=nil, outline: false, distance_tolerance: 0)`: checks if point is inside when `outline` is `false` or if point is on the outline when `outline` is `true`. `distance_tolerance` can be used as a fuzz factor when `outline` is `true`, for example, to help GUI users mouse-click-select a square shape from its outline more successfully
|
239
312
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
240
313
|
|
241
314
|
Example:
|
242
315
|
|
243
316
|
```ruby
|
317
|
+
require 'perfect-shape'
|
318
|
+
|
244
319
|
shape = PerfectShape::Square.new(x: 15, y: 30, length: 200)
|
320
|
+
|
321
|
+
shape.contain?(115, 130) # => true
|
322
|
+
shape.contain?([115, 130]) # => true
|
323
|
+
shape.contain?(115, 130, outline: true) # => false
|
324
|
+
shape.contain?([115, 130], outline: true) # => false
|
325
|
+
shape.contain?(115, 30, outline: true) # => true
|
326
|
+
shape.contain?([115, 30], outline: true) # => true
|
327
|
+
shape.contain?(115, 31, outline: true) # => false
|
328
|
+
shape.contain?([115, 31], outline: true) # => false
|
329
|
+
shape.contain?(115, 31, outline: true, distance_tolerance: 1) # => true
|
330
|
+
shape.contain?([115, 31], outline: true, distance_tolerance: 1) # => true
|
245
331
|
```
|
246
332
|
|
247
333
|
### `PerfectShape::Arc`
|
@@ -275,14 +361,101 @@ Open Arc | Chord Arc | Pie Arc
|
|
275
361
|
- `#max_x`: max x
|
276
362
|
- `#max_y`: max y
|
277
363
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
278
|
-
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
364
|
+
- `#contain?(x_or_point, y=nil, outline: false, distance_tolerance: 0)`: checks if point is inside when `outline` is `false` or if point is on the outline when `outline` is `true`. `distance_tolerance` can be used as a fuzz factor when `outline` is `true`, for example, to help GUI users mouse-click-select an arc shape from its outline more successfully
|
279
365
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
280
366
|
|
281
367
|
Example:
|
282
368
|
|
283
369
|
```ruby
|
284
|
-
shape
|
285
|
-
|
370
|
+
require 'perfect-shape'
|
371
|
+
|
372
|
+
shape = PerfectShape::Arc.new(type: :open, x: 2, y: 3, width: 50, height: 60, start: 45, extent: 270)
|
373
|
+
shape2 = PerfectShape::Arc.new(type: :open, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start: 45, extent: 270)
|
374
|
+
|
375
|
+
shape.contain?(39.5, 33.0) # => true
|
376
|
+
shape.contain?([39.5, 33.0]) # => true
|
377
|
+
shape2.contain?(39.5, 33.0) # => true
|
378
|
+
shape2.contain?([39.5, 33.0]) # => true
|
379
|
+
shape.contain?(39.5, 33.0, outline: true) # => false
|
380
|
+
shape.contain?([39.5, 33.0], outline: true) # => false
|
381
|
+
shape2.contain?(39.5, 33.0, outline: true) # => false
|
382
|
+
shape2.contain?([39.5, 33.0], outline: true) # => false
|
383
|
+
shape.contain?(2.0, 33.0, outline: true) # => true
|
384
|
+
shape.contain?([2.0, 33.0], outline: true) # => true
|
385
|
+
shape2.contain?(2.0, 33.0, outline: true) # => true
|
386
|
+
shape2.contain?([2.0, 33.0], outline: true) # => true
|
387
|
+
shape.contain?(3.0, 33.0, outline: true) # => false
|
388
|
+
shape.contain?([3.0, 33.0], outline: true) # => false
|
389
|
+
shape2.contain?(3.0, 33.0, outline: true) # => false
|
390
|
+
shape2.contain?([3.0, 33.0], outline: true) # => false
|
391
|
+
shape.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
|
392
|
+
shape.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
|
393
|
+
shape2.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
|
394
|
+
shape2.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
|
395
|
+
shape.contain?(shape.center_x, shape.center_y, outline: true) # => false
|
396
|
+
shape.contain?([shape.center_x, shape.center_y], outline: true) # => false
|
397
|
+
shape2.contain?(shape2.center_x, shape2.center_y, outline: true) # => false
|
398
|
+
shape2.contain?([shape2.center_x, shape2.center_y], outline: true) # => false
|
399
|
+
|
400
|
+
shape3 = PerfectShape::Arc.new(type: :chord, x: 2, y: 3, width: 50, height: 60, start: 45, extent: 270)
|
401
|
+
shape4 = PerfectShape::Arc.new(type: :chord, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start: 45, extent: 270)
|
402
|
+
|
403
|
+
shape3.contain?(39.5, 33.0) # => true
|
404
|
+
shape3.contain?([39.5, 33.0]) # => true
|
405
|
+
shape4.contain?(39.5, 33.0) # => true
|
406
|
+
shape4.contain?([39.5, 33.0]) # => true
|
407
|
+
shape3.contain?(39.5, 33.0, outline: true) # => false
|
408
|
+
shape3.contain?([39.5, 33.0], outline: true) # => false
|
409
|
+
shape4.contain?(39.5, 33.0, outline: true) # => false
|
410
|
+
shape4.contain?([39.5, 33.0], outline: true) # => false
|
411
|
+
shape3.contain?(2.0, 33.0, outline: true) # => true
|
412
|
+
shape3.contain?([2.0, 33.0], outline: true) # => true
|
413
|
+
shape4.contain?(2.0, 33.0, outline: true) # => true
|
414
|
+
shape4.contain?([2.0, 33.0], outline: true) # => true
|
415
|
+
shape3.contain?(3.0, 33.0, outline: true) # => false
|
416
|
+
shape3.contain?([3.0, 33.0], outline: true) # => false
|
417
|
+
shape4.contain?(3.0, 33.0, outline: true) # => false
|
418
|
+
shape4.contain?([3.0, 33.0], outline: true) # => false
|
419
|
+
shape3.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
|
420
|
+
shape3.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
|
421
|
+
shape4.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
|
422
|
+
shape4.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
|
423
|
+
shape3.contain?(shape3.center_x, shape3.center_y, outline: true) # => false
|
424
|
+
shape3.contain?([shape3.center_x, shape3.center_y], outline: true) # => false
|
425
|
+
shape4.contain?(shape4.center_x, shape4.center_y, outline: true) # => false
|
426
|
+
shape4.contain?([shape4.center_x, shape4.center_y], outline: true) # => false
|
427
|
+
|
428
|
+
shape5 = PerfectShape::Arc.new(type: :pie, x: 2, y: 3, width: 50, height: 60, start: 45, extent: 270)
|
429
|
+
shape6 = PerfectShape::Arc.new(type: :pie, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start: 45, extent: 270)
|
430
|
+
|
431
|
+
shape5.contain?(39.5, 33.0) # => false
|
432
|
+
shape5.contain?([39.5, 33.0]) # => false
|
433
|
+
shape6.contain?(39.5, 33.0) # => false
|
434
|
+
shape6.contain?([39.5, 33.0]) # => false
|
435
|
+
shape5.contain?(9.5, 33.0) # => true
|
436
|
+
shape5.contain?([9.5, 33.0]) # => true
|
437
|
+
shape6.contain?(9.5, 33.0) # => true
|
438
|
+
shape6.contain?([9.5, 33.0]) # => true
|
439
|
+
shape5.contain?(39.5, 33.0, outline: true) # => false
|
440
|
+
shape5.contain?([39.5, 33.0], outline: true) # => false
|
441
|
+
shape6.contain?(39.5, 33.0, outline: true) # => false
|
442
|
+
shape6.contain?([39.5, 33.0], outline: true) # => false
|
443
|
+
shape5.contain?(2.0, 33.0, outline: true) # => true
|
444
|
+
shape5.contain?([2.0, 33.0], outline: true) # => true
|
445
|
+
shape6.contain?(2.0, 33.0, outline: true) # => true
|
446
|
+
shape6.contain?([2.0, 33.0], outline: true) # => true
|
447
|
+
shape5.contain?(3.0, 33.0, outline: true) # => false
|
448
|
+
shape5.contain?([3.0, 33.0], outline: true) # => false
|
449
|
+
shape6.contain?(3.0, 33.0, outline: true) # => false
|
450
|
+
shape6.contain?([3.0, 33.0], outline: true) # => false
|
451
|
+
shape5.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
|
452
|
+
shape5.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
|
453
|
+
shape6.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
|
454
|
+
shape6.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
|
455
|
+
shape5.contain?(shape5.center_x, shape5.center_y, outline: true) # => true
|
456
|
+
shape5.contain?([shape5.center_x, shape5.center_y], outline: true) # => true
|
457
|
+
shape6.contain?(shape6.center_x, shape6.center_y, outline: true) # => true
|
458
|
+
shape6.contain?([shape6.center_x, shape6.center_y], outline: true) # => true
|
286
459
|
```
|
287
460
|
|
288
461
|
### `PerfectShape::Ellipse`
|
@@ -310,14 +483,37 @@ Extends `PerfectShape::Arc`
|
|
310
483
|
- `#max_x`: max x
|
311
484
|
- `#max_y`: max y
|
312
485
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
313
|
-
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
486
|
+
- `#contain?(x_or_point, y=nil, outline: false, distance_tolerance: 0)`: checks if point is inside when `outline` is `false` or if point is on the outline when `outline` is `true`. `distance_tolerance` can be used as a fuzz factor when `outline` is `true`, for example, to help GUI users mouse-click-select an ellipse shape from its outline more successfully
|
314
487
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
315
488
|
|
316
489
|
Example:
|
317
490
|
|
318
491
|
```ruby
|
492
|
+
require 'perfect-shape'
|
493
|
+
|
319
494
|
shape = PerfectShape::Ellipse.new(x: 2, y: 3, width: 50, height: 60)
|
320
495
|
shape2 = PerfectShape::Ellipse.new(center_x: 27, center_y: 33, radius_x: 25, radius_y: 30)
|
496
|
+
|
497
|
+
shape.contain?(27, 33) # => true
|
498
|
+
shape.contain?([27, 33]) # => true
|
499
|
+
shape2.contain?(27, 33) # => true
|
500
|
+
shape2.contain?([27, 33]) # => true
|
501
|
+
shape.contain?(27, 33, outline: true) # => false
|
502
|
+
shape.contain?([27, 33], outline: true) # => false
|
503
|
+
shape2.contain?(27, 33, outline: true) # => false
|
504
|
+
shape2.contain?([27, 33], outline: true) # => false
|
505
|
+
shape.contain?(2, 33, outline: true) # => true
|
506
|
+
shape.contain?([2, 33], outline: true) # => true
|
507
|
+
shape2.contain?(2, 33, outline: true) # => true
|
508
|
+
shape2.contain?([2, 33], outline: true) # => true
|
509
|
+
shape.contain?(1, 33, outline: true) # => false
|
510
|
+
shape.contain?([1, 33], outline: true) # => false
|
511
|
+
shape2.contain?(1, 33, outline: true) # => false
|
512
|
+
shape2.contain?([1, 33], outline: true) # => false
|
513
|
+
shape.contain?(1, 33, outline: true, distance_tolerance: 1) # => true
|
514
|
+
shape.contain?([1, 33], outline: true, distance_tolerance: 1) # => true
|
515
|
+
shape2.contain?(1, 33, outline: true, distance_tolerance: 1) # => true
|
516
|
+
shape2.contain?([1, 33], outline: true, distance_tolerance: 1) # => true
|
321
517
|
```
|
322
518
|
|
323
519
|
### `PerfectShape::Circle`
|
@@ -347,14 +543,37 @@ Extends `PerfectShape::Ellipse`
|
|
347
543
|
- `#max_x`: max x
|
348
544
|
- `#max_y`: max y
|
349
545
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
350
|
-
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
546
|
+
- `#contain?(x_or_point, y=nil, outline: false, distance_tolerance: 0)`: checks if point is inside when `outline` is `false` or if point is on the outline when `outline` is `true`. `distance_tolerance` can be used as a fuzz factor when `outline` is `true`, for example, to help GUI users mouse-click-select a circle shape from its outline more successfully
|
351
547
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
352
548
|
|
353
549
|
Example:
|
354
550
|
|
355
551
|
```ruby
|
552
|
+
require 'perfect-shape'
|
553
|
+
|
356
554
|
shape = PerfectShape::Circle.new(x: 2, y: 3, diameter: 60)
|
357
555
|
shape2 = PerfectShape::Circle.new(center_x: 2 + 30, center_y: 3 + 30, radius: 30)
|
556
|
+
|
557
|
+
shape.contain?(32, 33) # => true
|
558
|
+
shape.contain?([32, 33]) # => true
|
559
|
+
shape2.contain?(32, 33) # => true
|
560
|
+
shape2.contain?([32, 33]) # => true
|
561
|
+
shape.contain?(32, 33, outline: true) # => false
|
562
|
+
shape.contain?([32, 33], outline: true) # => false
|
563
|
+
shape2.contain?(32, 33, outline: true) # => false
|
564
|
+
shape2.contain?([32, 33], outline: true) # => false
|
565
|
+
shape.contain?(2, 33, outline: true) # => true
|
566
|
+
shape.contain?([2, 33], outline: true) # => true
|
567
|
+
shape2.contain?(2, 33, outline: true) # => true
|
568
|
+
shape2.contain?([2, 33], outline: true) # => true
|
569
|
+
shape.contain?(1, 33, outline: true) # => false
|
570
|
+
shape.contain?([1, 33], outline: true) # => false
|
571
|
+
shape2.contain?(1, 33, outline: true) # => false
|
572
|
+
shape2.contain?([1, 33], outline: true) # => false
|
573
|
+
shape.contain?(1, 33, outline: true, distance_tolerance: 1) # => true
|
574
|
+
shape.contain?([1, 33], outline: true, distance_tolerance: 1) # => true
|
575
|
+
shape2.contain?(1, 33, outline: true, distance_tolerance: 1) # => true
|
576
|
+
shape2.contain?([1, 33], outline: true, distance_tolerance: 1) # => true
|
358
577
|
```
|
359
578
|
|
360
579
|
### `PerfectShape::Polygon`
|
@@ -369,7 +588,7 @@ A polygon can be thought of as a special case of [path](#perfectshapepath) that
|
|
369
588
|
|
370
589
|
![polygon](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/polygon.png)
|
371
590
|
|
372
|
-
- `::new(points: [])`: constructs a polygon with `points` as `Array` of `Array`s of `[x,y]` pairs or flattened `Array` of alternating x and y
|
591
|
+
- `::new(points: [])`: constructs a polygon with `points` as `Array` of `Array`s of `[x,y]` pairs or flattened `Array` of alternating x and y coordinates
|
373
592
|
- `#min_x`: min x
|
374
593
|
- `#min_y`: min y
|
375
594
|
- `#max_x`: max x
|
@@ -379,13 +598,26 @@ A polygon can be thought of as a special case of [path](#perfectshapepath) that
|
|
379
598
|
- `#center_x`: center x
|
380
599
|
- `#center_y`: center y
|
381
600
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
382
|
-
- `#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))
|
601
|
+
- `#contain?(x_or_point, y=nil, outline: false, distance_tolerance: 0)`: When `outline` is `false`, it 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)). Otherwise, when `outline` is `true`, it checks if point is on the outline. `distance_tolerance` can be used as a fuzz factor when `outline` is `true`, for example, to help GUI users mouse-click-select a polygon shape from its outline more successfully
|
383
602
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
384
603
|
|
385
604
|
Example:
|
386
605
|
|
387
606
|
```ruby
|
607
|
+
require 'perfect-shape'
|
608
|
+
|
388
609
|
shape = PerfectShape::Polygon.new(points: [[200, 150], [270, 170], [250, 220], [220, 190], [200, 200], [180, 170]])
|
610
|
+
|
611
|
+
shape.contain?(225, 185) # => true
|
612
|
+
shape.contain?([225, 185]) # => true
|
613
|
+
shape.contain?(225, 185, outline: true) # => false
|
614
|
+
shape.contain?([225, 185], outline: true) # => false
|
615
|
+
shape.contain?(200, 150, outline: true) # => true
|
616
|
+
shape.contain?([200, 150], outline: true) # => true
|
617
|
+
shape.contain?(200, 151, outline: true) # => false
|
618
|
+
shape.contain?([200, 151], outline: true) # => false
|
619
|
+
shape.contain?(200, 151, outline: true, distance_tolerance: 1) # => true
|
620
|
+
shape.contain?([200, 151], outline: true, distance_tolerance: 1) # => true
|
389
621
|
```
|
390
622
|
|
391
623
|
### `PerfectShape::Path`
|
@@ -398,7 +630,7 @@ Includes `PerfectShape::MultiPoint`
|
|
398
630
|
|
399
631
|
![path](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/path.png)
|
400
632
|
|
401
|
-
- `::new(shapes: [], closed: false, winding_rule: :wind_non_zero)`: constructs a path with `shapes` as `Array` of shape objects, which can be `PerfectShape::Point` (or `Array` of `[x, y]` coordinates), `PerfectShape::Line`, or `PerfectShape::
|
633
|
+
- `::new(shapes: [], closed: false, winding_rule: :wind_non_zero)`: constructs a path with `shapes` as `Array` of shape objects, which can be `PerfectShape::Point` (or `Array` of `[x, y]` coordinates), `PerfectShape::Line`, `PerfectShape::QuadraticBezierCurve`, or `PerfectShape::CubicBezierCurve`. If a path is closed, its last point is automatically connected to its first point with a line segment. The winding rule can be `:wind_non_zero` (default) or `:wind_even_odd`.
|
402
634
|
- `#shapes`: the shapes that the path is composed of (must always start with `PerfectShape::Point` or Array of [x,y] coordinates representing start point)
|
403
635
|
- `#closed?`: returns `true` if closed and `false` otherwise
|
404
636
|
- `#winding_rule`: returns winding rule (`:wind_non_zero` or `:wind_even_odd`)
|
@@ -419,12 +651,59 @@ Includes `PerfectShape::MultiPoint`
|
|
419
651
|
Example:
|
420
652
|
|
421
653
|
```ruby
|
654
|
+
require 'perfect-shape'
|
655
|
+
|
422
656
|
path_shapes = []
|
423
657
|
path_shapes << PerfectShape::Point.new(x: 200, y: 150)
|
424
658
|
path_shapes << PerfectShape::Line.new(points: [250, 170]) # no need for start point, just end point
|
425
659
|
path_shapes << PerfectShape::QuadraticBezierCurve.new(points: [[300, 185], [350, 150]]) # no need for start point, just control point and end point
|
660
|
+
path_shapes << PerfectShape::CubicBezierCurve.new(points: [[370, 50], [430, 220], [480, 170]]) # no need for start point, just two control points and end point
|
426
661
|
|
427
662
|
shape = PerfectShape::Path.new(shapes: path_shapes, closed: false, winding_rule: :wind_even_odd)
|
663
|
+
|
664
|
+
shape.contain?(225, 160) # => true
|
665
|
+
shape.contain?([225, 160]) # => true
|
666
|
+
```
|
667
|
+
|
668
|
+
### `PerfectShape::CompositeShape`
|
669
|
+
|
670
|
+
Class
|
671
|
+
|
672
|
+
Extends `PerfectShape::Shape`
|
673
|
+
|
674
|
+
A composite shape is simply an aggregate of multiple shapes (e.g. square and polygon)
|
675
|
+
|
676
|
+
![composite shape](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/composite-shape.png)
|
677
|
+
|
678
|
+
- `::new(shapes: [])`: constructs a composite shape with `shapes` as `Array` of `PerfectShape::Shape` objects
|
679
|
+
- `#shapes`: the shapes that the composite shape is composed of
|
680
|
+
- `#min_x`: min x
|
681
|
+
- `#min_y`: min y
|
682
|
+
- `#max_x`: max x
|
683
|
+
- `#max_y`: max y
|
684
|
+
- `#width`: width (from min x to max x)
|
685
|
+
- `#height`: height (from min y to max y)
|
686
|
+
- `#center_x`: center x
|
687
|
+
- `#center_y`: center y
|
688
|
+
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape (bounding box only guarantees that the shape is within it, but it might be bigger than the shape)
|
689
|
+
- `#contain?(x_or_point, y=nil)`: checks if point is inside any of the shapes owned by the composite shape
|
690
|
+
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
691
|
+
|
692
|
+
Example:
|
693
|
+
|
694
|
+
```ruby
|
695
|
+
require 'perfect-shape'
|
696
|
+
|
697
|
+
shapes = []
|
698
|
+
shapes << PerfectShape::Square.new(x: 120, y: 215, length: 100)
|
699
|
+
shapes << PerfectShape::Polygon.new(points: [[120, 215], [170, 165], [220, 215]])
|
700
|
+
|
701
|
+
shape = PerfectShape::CompositeShape.new(shapes: shapes)
|
702
|
+
|
703
|
+
shape.contain?(170, 265) # => true
|
704
|
+
shape.contain?([170, 265]) # => true
|
705
|
+
shape.contain?(170, 190) # => true
|
706
|
+
shape.contain?([170, 190]) # => true
|
428
707
|
```
|
429
708
|
|
430
709
|
## Process
|
@@ -434,7 +713,7 @@ shape = PerfectShape::Path.new(shapes: path_shapes, closed: false, winding_rule:
|
|
434
713
|
## Resources
|
435
714
|
|
436
715
|
- Rubydoc: https://www.rubydoc.info/gems/perfect-shape
|
437
|
-
- AWT Geom
|
716
|
+
- AWT Geom Javadoc (inspiration): https://docs.oracle.com/javase/8/docs/api/java/awt/geom/package-summary.html
|
438
717
|
|
439
718
|
## TODO
|
440
719
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.1
|