perfect-shape 0.2.0 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -3
- data/LICENSE.txt +1 -1
- data/README.md +178 -31
- data/VERSION +1 -1
- data/lib/perfect-shape.rb +1 -1
- data/lib/perfect_shape/arc.rb +50 -35
- data/lib/perfect_shape/circle.rb +1 -1
- data/lib/perfect_shape/composite_shape.rb +1 -1
- data/lib/perfect_shape/cubic_bezier_curve.rb +106 -16
- data/lib/perfect_shape/ellipse.rb +13 -9
- data/lib/perfect_shape/line.rb +5 -5
- data/lib/perfect_shape/math.rb +21 -0
- data/lib/perfect_shape/multi_point.rb +1 -1
- data/lib/perfect_shape/path.rb +1 -1
- data/lib/perfect_shape/point.rb +5 -5
- data/lib/perfect_shape/point_location.rb +1 -1
- data/lib/perfect_shape/polygon.rb +74 -64
- data/lib/perfect_shape/quadratic_bezier_curve.rb +172 -86
- data/lib/perfect_shape/rectangle.rb +17 -3
- data/lib/perfect_shape/rectangular_shape.rb +1 -1
- data/lib/perfect_shape/shape.rb +5 -1
- data/lib/perfect_shape/square.rb +1 -1
- data/perfect-shape.gemspec +4 -4
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 42bff3742b2697349882d865b7c5da9c06ce4a259850996bb92f291e8a74130e
|
4
|
+
data.tar.gz: 5a2f007129f1e0f483a48ea607a7ee3e44cbdf01b400a656f406b412dc8e9524
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9bf87bebe682dfbab8a4b082caa321ac0b7051510d86e946e3f214337e818a18da7cee51bc12432510567deaa68f65a69cb3c7da10b7c9153045803e1d25f0b7
|
7
|
+
data.tar.gz: fa32aa79d500055b81b572e17ded38429503a3757430bba1c0fea021853e4377d7e025071cf52ed8c2f965c8bc8ecd35601276f2a1bcda847202c6f49c8422a0
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,36 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.3.3
|
4
|
+
|
5
|
+
- Check point containment in quadratic bezier curve outline with distance tolerance (new method signature: `PerfectShape::QuadraticBezierCurve#contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)`)
|
6
|
+
- `PerfectShape::QuadraticBezierCurve#curve_center_point`, `PerfectShape::QuadraticBezierCurve#curve_center_x`, `PerfectShape::QuadraticBezierCurve#curve_center_y`
|
7
|
+
- `PerfectShape::QuadraticBezierCurve#subdivisions(level=1)`
|
8
|
+
- `PerfectShape::QuadraticBezierCurve#point_segment_distance(x_or_point, y = nil, minimum_distance_threshold: OUTLINE_MINIMUM_DISTANCE_THRESHOLD)`
|
9
|
+
- `PerfectShape::Polygon#edges` returns edges of polygon as `PerfectShape::Line` objects
|
10
|
+
- `PerfectShape::Rectangle#edges` returns edges of rectangle as `PerfectShape::Line` objects
|
11
|
+
- `PerfectShape::Square#edges` returns edges of square as `PerfectShape::Line` objects
|
12
|
+
- Rename `number` arg to `level` in `CubicBezierCurve#subdivisions(level=1)`, making it signify the level of subdivision recursion to perform.
|
13
|
+
|
14
|
+
## 0.3.2
|
15
|
+
|
16
|
+
- Check point containment in cubic bezier curve outline with distance tolerance (new method signature: `PerfectShape::CubicBezierCurve#contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)`)
|
17
|
+
- `PerfectShape::CubicBezierCurve#curve_center_point`, `PerfectShape::CubicBezierCurve#curve_center_x`, `PerfectShape::CubicBezierCurve#curve_center_y`
|
18
|
+
- `PerfectShape::CubicBezierCurve#subdivisions(level=1)`
|
19
|
+
- `PerfectShape::CubicBezierCurve#point_segment_distance(x_or_point, y = nil, minimum_distance_threshold: OUTLINE_MINIMUM_DISTANCE_THRESHOLD)`
|
20
|
+
|
21
|
+
## 0.3.1
|
22
|
+
|
23
|
+
- 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)`)
|
24
|
+
- 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)`)
|
25
|
+
- 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)`)
|
26
|
+
|
27
|
+
## 0.3.0
|
28
|
+
|
29
|
+
- Refactoring: rename `distance` option for `#contain?` on `Point`/`Line` into `distance_tolerance`
|
30
|
+
- 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)`)
|
31
|
+
- 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)`)
|
32
|
+
- 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)`)
|
33
|
+
|
3
34
|
## 0.2.0
|
4
35
|
|
5
36
|
- `PerfectShape::CompositeShape`: aggregate of multiple shapes
|
@@ -23,7 +54,7 @@
|
|
23
54
|
## 0.1.0
|
24
55
|
|
25
56
|
- `PerfectShape::Path` (having points or lines)
|
26
|
-
- `PerfectShape::Path#contain?(x_or_point, y=nil,
|
57
|
+
- `PerfectShape::Path#contain?(x_or_point, y=nil, distance_tolerance: 0)`
|
27
58
|
- `PerfectShape::Path#point_crossings(x_or_point, y=nil)`
|
28
59
|
- `PerfectShape::Path#==`
|
29
60
|
|
@@ -37,12 +68,12 @@
|
|
37
68
|
|
38
69
|
- `PerfectShape::Point`
|
39
70
|
- `PerfectShape::Point#point_distance`
|
40
|
-
- `PerfectShape::Point#contain?(x_or_point, y=nil,
|
71
|
+
- `PerfectShape::Point#contain?(x_or_point, y=nil, distance_tolerance: 0)`
|
41
72
|
- Refactor `PerfectShape::Point`,`PerfectShape::RectangularShape` to include shared `PerfectShape::PointLocation`
|
42
73
|
|
43
74
|
## 0.0.9
|
44
75
|
|
45
|
-
- `PerfectShape::Line#contain?(x_or_point, y=nil,
|
76
|
+
- `PerfectShape::Line#contain?(x_or_point, y=nil, distance_tolerance: 0)` (add a distance tolerance fuzz factor option)
|
46
77
|
|
47
78
|
## 0.0.8
|
48
79
|
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
# Perfect Shape 0.
|
1
|
+
# Perfect Shape 0.3.3
|
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.3
|
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.3'
|
24
24
|
```
|
25
25
|
|
26
26
|
And, run:
|
@@ -38,7 +38,7 @@ Module
|
|
38
38
|
- `::degrees_to_radians(angle)`: converts degrees to radians
|
39
39
|
- `::radians_to_degrees(angle)`: converts radians to degrees
|
40
40
|
- `::normalize_degrees(angle)`: normalizes the specified angle into the range -180 to 180.
|
41
|
-
- `::ieee_remainder(x, y)` (alias: `ieee754_remainder`): [IEEE 754-1985 Remainder](https://en.wikipedia.org/wiki/IEEE_754-1985) (different from standard
|
41
|
+
- `::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)
|
42
42
|
|
43
43
|
### `PerfectShape::Shape`
|
44
44
|
|
@@ -55,9 +55,9 @@ This is a base class for all shapes. It is not meant to be used directly. Subcla
|
|
55
55
|
- `#center_x`: center x
|
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
|
-
- `#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
|
60
58
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
59
|
+
- `#normalize_point(x_or_point, y = nil)`: normalizes point into an `Array` of `[x,y]` coordinates
|
60
|
+
- `#contain?(x_or_point, y=nil, outline: false, distance_tolerance: 0)`: checks if point is inside if `outline` is `false` or if point is on the outline if `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 shape from its outline more successfully
|
61
61
|
|
62
62
|
### `PerfectShape::PointLocation`
|
63
63
|
|
@@ -111,9 +111,9 @@ Points are simply represented by an `Array` of `[x,y]` coordinates when used wit
|
|
111
111
|
- `#center_x`: center x (always x)
|
112
112
|
- `#center_y`: center y (always y)
|
113
113
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
114
|
-
- `#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.
|
115
|
-
- `#point_distance(x_or_point, y=nil)`: Returns the distance from a point to another point
|
116
114
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
115
|
+
- `#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.
|
116
|
+
- `#point_distance(x_or_point, y=nil)`: Returns the distance from a point to another point
|
117
117
|
|
118
118
|
Example:
|
119
119
|
|
@@ -126,8 +126,8 @@ shape.contain?(200, 150) # => true
|
|
126
126
|
shape.contain?([200, 150]) # => true
|
127
127
|
shape.contain?(200, 151) # => false
|
128
128
|
shape.contain?([200, 151]) # => false
|
129
|
-
shape.contain?(200, 151,
|
130
|
-
shape.contain?([200, 151],
|
129
|
+
shape.contain?(200, 151, distance_tolerance: 5) # => true
|
130
|
+
shape.contain?([200, 151], distance_tolerance: 5) # => true
|
131
131
|
```
|
132
132
|
|
133
133
|
### `PerfectShape::Line`
|
@@ -153,10 +153,10 @@ Includes `PerfectShape::MultiPoint`
|
|
153
153
|
- `#center_x`: center x
|
154
154
|
- `#center_y`: center y
|
155
155
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
156
|
-
-
|
156
|
+
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
157
|
+
- `#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.
|
157
158
|
- `#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)”.
|
158
159
|
- `#point_segment_distance(x_or_point, y=nil)`: Returns the distance from a point to a line segment.
|
159
|
-
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
160
160
|
|
161
161
|
Example:
|
162
162
|
|
@@ -169,8 +169,8 @@ shape.contain?(50, 50) # => true
|
|
169
169
|
shape.contain?([50, 50]) # => true
|
170
170
|
shape.contain?(50, 51) # => false
|
171
171
|
shape.contain?([50, 51]) # => false
|
172
|
-
shape.contain?(50, 51,
|
173
|
-
shape.contain?([50, 51],
|
172
|
+
shape.contain?(50, 51, distance_tolerance: 5) # => true
|
173
|
+
shape.contain?([50, 51], distance_tolerance: 5) # => true
|
174
174
|
```
|
175
175
|
|
176
176
|
### `PerfectShape::QuadraticBezierCurve`
|
@@ -194,8 +194,13 @@ Includes `PerfectShape::MultiPoint`
|
|
194
194
|
- `#center_x`: center x
|
195
195
|
- `#center_y`: center y
|
196
196
|
- `#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)
|
197
|
-
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
198
197
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
198
|
+
- `#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 quadratic bezier curve shape from its outline more successfully
|
199
|
+
- `#curve_center_point`: point at the center of the curve (not the center of the bounding box area like `center_x` and `center_y`)
|
200
|
+
- `#curve_center_x`: point x coordinate at the center of the curve (not the center of the bounding box area like `center_x` and `center_y`)
|
201
|
+
- `#curve_center_y`: point y coordinate at the center of the curve (not the center of the bounding box area like `center_x` and `center_y`)
|
202
|
+
- `#subdivisions(level=1)`: subdivides quadratic bezier curve at its center into into 2 quadratic bezier curves by default, or more if `level` of recursion is specified. The resulting number of subdivisions is `2` to the power of `level`.
|
203
|
+
- `#point_segment_distance(x_or_point, y=nil, minimum_distance_threshold: OUTLINE_MINIMUM_DISTANCE_THRESHOLD)`: calculates distance from point to curve segment. It does so by subdividing curve into smaller curves and checking against the curve center points until the distance is less than `minimum_distance_threshold`, to avoid being an overly costly operation.
|
199
204
|
|
200
205
|
Example:
|
201
206
|
|
@@ -206,6 +211,14 @@ shape = PerfectShape::QuadraticBezierCurve.new(points: [[200, 150], [270, 320],
|
|
206
211
|
|
207
212
|
shape.contain?(270, 220) # => true
|
208
213
|
shape.contain?([270, 220]) # => true
|
214
|
+
shape.contain?(270, 220, outline: true) # => false
|
215
|
+
shape.contain?([270, 220], outline: true) # => false
|
216
|
+
shape.contain?(280, 235, outline: true) # => true
|
217
|
+
shape.contain?([280, 235], outline: true) # => true
|
218
|
+
shape.contain?(281, 235, outline: true) # => false
|
219
|
+
shape.contain?([281, 235], outline: true) # => false
|
220
|
+
shape.contain?(281, 235, outline: true, distance_tolerance: 1) # => true
|
221
|
+
shape.contain?([281, 235], outline: true, distance_tolerance: 1) # => true
|
209
222
|
```
|
210
223
|
|
211
224
|
### `PerfectShape::CubicBezierCurve`
|
@@ -229,8 +242,13 @@ Includes `PerfectShape::MultiPoint`
|
|
229
242
|
- `#center_x`: center x
|
230
243
|
- `#center_y`: center y
|
231
244
|
- `#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
245
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
246
|
+
- `#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 cubic bezier curve shape from its outline more successfully
|
247
|
+
- `#curve_center_point`: point at the center of the curve (not the center of the bounding box area like `center_x` and `center_y`)
|
248
|
+
- `#curve_center_x`: point x coordinate at the center of the curve (not the center of the bounding box area like `center_x` and `center_y`)
|
249
|
+
- `#curve_center_y`: point y coordinate at the center of the curve (not the center of the bounding box area like `center_x` and `center_y`)
|
250
|
+
- `#subdivisions(level=1)`: subdivides cubic bezier curve at its center into into 2 cubic bezier curves by default, or more if `level` of recursion is specified. The resulting number of subdivisions is `2` to the power of `level`.
|
251
|
+
- `#point_segment_distance(x_or_point, y=nil, minimum_distance_threshold: OUTLINE_MINIMUM_DISTANCE_THRESHOLD)`: calculates distance from point to curve segment. It does so by subdividing curve into smaller curves and checking against the curve center points until the distance is less than `minimum_distance_threshold`, to avoid being an overly costly operation.
|
234
252
|
|
235
253
|
Example:
|
236
254
|
|
@@ -241,6 +259,14 @@ shape = PerfectShape::CubicBezierCurve.new(points: [[200, 150], [235, 235], [270
|
|
241
259
|
|
242
260
|
shape.contain?(270, 220) # => true
|
243
261
|
shape.contain?([270, 220]) # => true
|
262
|
+
shape.contain?(270, 220, outline: true) # => false
|
263
|
+
shape.contain?([270, 220], outline: true) # => false
|
264
|
+
shape.contain?(261.875, 245.625, outline: true) # => true
|
265
|
+
shape.contain?([261.875, 245.625], outline: true) # => true
|
266
|
+
shape.contain?(261.875, 246.625, outline: true) # => false
|
267
|
+
shape.contain?([261.875, 246.625], outline: true) # => false
|
268
|
+
shape.contain?(261.875, 246.625, outline: true, distance_tolerance: 1) # => true
|
269
|
+
shape.contain?([261.875, 246.625], outline: true, distance_tolerance: 1) # => true
|
244
270
|
```
|
245
271
|
|
246
272
|
### `PerfectShape::Rectangle`
|
@@ -265,8 +291,9 @@ Includes `PerfectShape::RectangularShape`
|
|
265
291
|
- `#max_x`: max x
|
266
292
|
- `#max_y`: max y
|
267
293
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
268
|
-
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
269
294
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
295
|
+
- `#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
|
296
|
+
- `#edges`: edges of rectangle as `PerfectShape::Line` objects
|
270
297
|
|
271
298
|
Example:
|
272
299
|
|
@@ -277,6 +304,14 @@ shape = PerfectShape::Rectangle.new(x: 15, y: 30, width: 200, height: 100)
|
|
277
304
|
|
278
305
|
shape.contain?(115, 80) # => true
|
279
306
|
shape.contain?([115, 80]) # => true
|
307
|
+
shape.contain?(115, 80, outline: true) # => false
|
308
|
+
shape.contain?([115, 80], outline: true) # => false
|
309
|
+
shape.contain?(115, 30, outline: true) # => true
|
310
|
+
shape.contain?([115, 30], outline: true) # => true
|
311
|
+
shape.contain?(115, 31, outline: true) # => false
|
312
|
+
shape.contain?([115, 31], outline: true) # => false
|
313
|
+
shape.contain?(115, 31, outline: true, distance_tolerance: 1) # => true
|
314
|
+
shape.contain?([115, 31], outline: true, distance_tolerance: 1) # => true
|
280
315
|
```
|
281
316
|
|
282
317
|
### `PerfectShape::Square`
|
@@ -300,8 +335,9 @@ Extends `PerfectShape::Rectangle`
|
|
300
335
|
- `#max_x`: max x
|
301
336
|
- `#max_y`: max y
|
302
337
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
303
|
-
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
304
338
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
339
|
+
- `#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
|
340
|
+
- `#edges`: edges of square as `PerfectShape::Line` objects
|
305
341
|
|
306
342
|
Example:
|
307
343
|
|
@@ -312,6 +348,14 @@ shape = PerfectShape::Square.new(x: 15, y: 30, length: 200)
|
|
312
348
|
|
313
349
|
shape.contain?(115, 130) # => true
|
314
350
|
shape.contain?([115, 130]) # => true
|
351
|
+
shape.contain?(115, 130, outline: true) # => false
|
352
|
+
shape.contain?([115, 130], outline: true) # => false
|
353
|
+
shape.contain?(115, 30, outline: true) # => true
|
354
|
+
shape.contain?([115, 30], outline: true) # => true
|
355
|
+
shape.contain?(115, 31, outline: true) # => false
|
356
|
+
shape.contain?([115, 31], outline: true) # => false
|
357
|
+
shape.contain?(115, 31, outline: true, distance_tolerance: 1) # => true
|
358
|
+
shape.contain?([115, 31], outline: true, distance_tolerance: 1) # => true
|
315
359
|
```
|
316
360
|
|
317
361
|
### `PerfectShape::Arc`
|
@@ -345,8 +389,8 @@ Open Arc | Chord Arc | Pie Arc
|
|
345
389
|
- `#max_x`: max x
|
346
390
|
- `#max_y`: max y
|
347
391
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
348
|
-
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
349
392
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
393
|
+
- `#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
|
350
394
|
|
351
395
|
Example:
|
352
396
|
|
@@ -354,23 +398,63 @@ Example:
|
|
354
398
|
require 'perfect-shape'
|
355
399
|
|
356
400
|
shape = PerfectShape::Arc.new(type: :open, x: 2, y: 3, width: 50, height: 60, start: 45, extent: 270)
|
357
|
-
shape2 = PerfectShape::Arc.new(type: :open, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start:
|
401
|
+
shape2 = PerfectShape::Arc.new(type: :open, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start: 45, extent: 270)
|
358
402
|
|
359
403
|
shape.contain?(39.5, 33.0) # => true
|
360
404
|
shape.contain?([39.5, 33.0]) # => true
|
361
405
|
shape2.contain?(39.5, 33.0) # => true
|
362
406
|
shape2.contain?([39.5, 33.0]) # => true
|
407
|
+
shape.contain?(39.5, 33.0, outline: true) # => false
|
408
|
+
shape.contain?([39.5, 33.0], outline: true) # => false
|
409
|
+
shape2.contain?(39.5, 33.0, outline: true) # => false
|
410
|
+
shape2.contain?([39.5, 33.0], outline: true) # => false
|
411
|
+
shape.contain?(2.0, 33.0, outline: true) # => true
|
412
|
+
shape.contain?([2.0, 33.0], outline: true) # => true
|
413
|
+
shape2.contain?(2.0, 33.0, outline: true) # => true
|
414
|
+
shape2.contain?([2.0, 33.0], outline: true) # => true
|
415
|
+
shape.contain?(3.0, 33.0, outline: true) # => false
|
416
|
+
shape.contain?([3.0, 33.0], outline: true) # => false
|
417
|
+
shape2.contain?(3.0, 33.0, outline: true) # => false
|
418
|
+
shape2.contain?([3.0, 33.0], outline: true) # => false
|
419
|
+
shape.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
|
420
|
+
shape.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
|
421
|
+
shape2.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
|
422
|
+
shape2.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
|
423
|
+
shape.contain?(shape.center_x, shape.center_y, outline: true) # => false
|
424
|
+
shape.contain?([shape.center_x, shape.center_y], outline: true) # => false
|
425
|
+
shape2.contain?(shape2.center_x, shape2.center_y, outline: true) # => false
|
426
|
+
shape2.contain?([shape2.center_x, shape2.center_y], outline: true) # => false
|
363
427
|
|
364
428
|
shape3 = PerfectShape::Arc.new(type: :chord, x: 2, y: 3, width: 50, height: 60, start: 45, extent: 270)
|
365
|
-
shape4 = PerfectShape::Arc.new(type: :chord, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start:
|
429
|
+
shape4 = PerfectShape::Arc.new(type: :chord, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start: 45, extent: 270)
|
366
430
|
|
367
431
|
shape3.contain?(39.5, 33.0) # => true
|
368
432
|
shape3.contain?([39.5, 33.0]) # => true
|
369
433
|
shape4.contain?(39.5, 33.0) # => true
|
370
434
|
shape4.contain?([39.5, 33.0]) # => true
|
435
|
+
shape3.contain?(39.5, 33.0, outline: true) # => false
|
436
|
+
shape3.contain?([39.5, 33.0], outline: true) # => false
|
437
|
+
shape4.contain?(39.5, 33.0, outline: true) # => false
|
438
|
+
shape4.contain?([39.5, 33.0], outline: true) # => false
|
439
|
+
shape3.contain?(2.0, 33.0, outline: true) # => true
|
440
|
+
shape3.contain?([2.0, 33.0], outline: true) # => true
|
441
|
+
shape4.contain?(2.0, 33.0, outline: true) # => true
|
442
|
+
shape4.contain?([2.0, 33.0], outline: true) # => true
|
443
|
+
shape3.contain?(3.0, 33.0, outline: true) # => false
|
444
|
+
shape3.contain?([3.0, 33.0], outline: true) # => false
|
445
|
+
shape4.contain?(3.0, 33.0, outline: true) # => false
|
446
|
+
shape4.contain?([3.0, 33.0], outline: true) # => false
|
447
|
+
shape3.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
|
448
|
+
shape3.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
|
449
|
+
shape4.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
|
450
|
+
shape4.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
|
451
|
+
shape3.contain?(shape3.center_x, shape3.center_y, outline: true) # => false
|
452
|
+
shape3.contain?([shape3.center_x, shape3.center_y], outline: true) # => false
|
453
|
+
shape4.contain?(shape4.center_x, shape4.center_y, outline: true) # => false
|
454
|
+
shape4.contain?([shape4.center_x, shape4.center_y], outline: true) # => false
|
371
455
|
|
372
456
|
shape5 = PerfectShape::Arc.new(type: :pie, x: 2, y: 3, width: 50, height: 60, start: 45, extent: 270)
|
373
|
-
shape6 = PerfectShape::Arc.new(type: :pie, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start:
|
457
|
+
shape6 = PerfectShape::Arc.new(type: :pie, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start: 45, extent: 270)
|
374
458
|
|
375
459
|
shape5.contain?(39.5, 33.0) # => false
|
376
460
|
shape5.contain?([39.5, 33.0]) # => false
|
@@ -380,6 +464,26 @@ shape5.contain?(9.5, 33.0) # => true
|
|
380
464
|
shape5.contain?([9.5, 33.0]) # => true
|
381
465
|
shape6.contain?(9.5, 33.0) # => true
|
382
466
|
shape6.contain?([9.5, 33.0]) # => true
|
467
|
+
shape5.contain?(39.5, 33.0, outline: true) # => false
|
468
|
+
shape5.contain?([39.5, 33.0], outline: true) # => false
|
469
|
+
shape6.contain?(39.5, 33.0, outline: true) # => false
|
470
|
+
shape6.contain?([39.5, 33.0], outline: true) # => false
|
471
|
+
shape5.contain?(2.0, 33.0, outline: true) # => true
|
472
|
+
shape5.contain?([2.0, 33.0], outline: true) # => true
|
473
|
+
shape6.contain?(2.0, 33.0, outline: true) # => true
|
474
|
+
shape6.contain?([2.0, 33.0], outline: true) # => true
|
475
|
+
shape5.contain?(3.0, 33.0, outline: true) # => false
|
476
|
+
shape5.contain?([3.0, 33.0], outline: true) # => false
|
477
|
+
shape6.contain?(3.0, 33.0, outline: true) # => false
|
478
|
+
shape6.contain?([3.0, 33.0], outline: true) # => false
|
479
|
+
shape5.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
|
480
|
+
shape5.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
|
481
|
+
shape6.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
|
482
|
+
shape6.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
|
483
|
+
shape5.contain?(shape5.center_x, shape5.center_y, outline: true) # => true
|
484
|
+
shape5.contain?([shape5.center_x, shape5.center_y], outline: true) # => true
|
485
|
+
shape6.contain?(shape6.center_x, shape6.center_y, outline: true) # => true
|
486
|
+
shape6.contain?([shape6.center_x, shape6.center_y], outline: true) # => true
|
383
487
|
```
|
384
488
|
|
385
489
|
### `PerfectShape::Ellipse`
|
@@ -407,8 +511,8 @@ Extends `PerfectShape::Arc`
|
|
407
511
|
- `#max_x`: max x
|
408
512
|
- `#max_y`: max y
|
409
513
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
410
|
-
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
411
514
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
515
|
+
- `#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
|
412
516
|
|
413
517
|
Example:
|
414
518
|
|
@@ -422,6 +526,22 @@ shape.contain?(27, 33) # => true
|
|
422
526
|
shape.contain?([27, 33]) # => true
|
423
527
|
shape2.contain?(27, 33) # => true
|
424
528
|
shape2.contain?([27, 33]) # => true
|
529
|
+
shape.contain?(27, 33, outline: true) # => false
|
530
|
+
shape.contain?([27, 33], outline: true) # => false
|
531
|
+
shape2.contain?(27, 33, outline: true) # => false
|
532
|
+
shape2.contain?([27, 33], outline: true) # => false
|
533
|
+
shape.contain?(2, 33, outline: true) # => true
|
534
|
+
shape.contain?([2, 33], outline: true) # => true
|
535
|
+
shape2.contain?(2, 33, outline: true) # => true
|
536
|
+
shape2.contain?([2, 33], outline: true) # => true
|
537
|
+
shape.contain?(1, 33, outline: true) # => false
|
538
|
+
shape.contain?([1, 33], outline: true) # => false
|
539
|
+
shape2.contain?(1, 33, outline: true) # => false
|
540
|
+
shape2.contain?([1, 33], outline: true) # => false
|
541
|
+
shape.contain?(1, 33, outline: true, distance_tolerance: 1) # => true
|
542
|
+
shape.contain?([1, 33], outline: true, distance_tolerance: 1) # => true
|
543
|
+
shape2.contain?(1, 33, outline: true, distance_tolerance: 1) # => true
|
544
|
+
shape2.contain?([1, 33], outline: true, distance_tolerance: 1) # => true
|
425
545
|
```
|
426
546
|
|
427
547
|
### `PerfectShape::Circle`
|
@@ -451,8 +571,8 @@ Extends `PerfectShape::Ellipse`
|
|
451
571
|
- `#max_x`: max x
|
452
572
|
- `#max_y`: max y
|
453
573
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
454
|
-
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
455
574
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
575
|
+
- `#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
|
456
576
|
|
457
577
|
Example:
|
458
578
|
|
@@ -466,6 +586,22 @@ shape.contain?(32, 33) # => true
|
|
466
586
|
shape.contain?([32, 33]) # => true
|
467
587
|
shape2.contain?(32, 33) # => true
|
468
588
|
shape2.contain?([32, 33]) # => true
|
589
|
+
shape.contain?(32, 33, outline: true) # => false
|
590
|
+
shape.contain?([32, 33], outline: true) # => false
|
591
|
+
shape2.contain?(32, 33, outline: true) # => false
|
592
|
+
shape2.contain?([32, 33], outline: true) # => false
|
593
|
+
shape.contain?(2, 33, outline: true) # => true
|
594
|
+
shape.contain?([2, 33], outline: true) # => true
|
595
|
+
shape2.contain?(2, 33, outline: true) # => true
|
596
|
+
shape2.contain?([2, 33], outline: true) # => true
|
597
|
+
shape.contain?(1, 33, outline: true) # => false
|
598
|
+
shape.contain?([1, 33], outline: true) # => false
|
599
|
+
shape2.contain?(1, 33, outline: true) # => false
|
600
|
+
shape2.contain?([1, 33], outline: true) # => false
|
601
|
+
shape.contain?(1, 33, outline: true, distance_tolerance: 1) # => true
|
602
|
+
shape.contain?([1, 33], outline: true, distance_tolerance: 1) # => true
|
603
|
+
shape2.contain?(1, 33, outline: true, distance_tolerance: 1) # => true
|
604
|
+
shape2.contain?([1, 33], outline: true, distance_tolerance: 1) # => true
|
469
605
|
```
|
470
606
|
|
471
607
|
### `PerfectShape::Polygon`
|
@@ -490,8 +626,9 @@ A polygon can be thought of as a special case of [path](#perfectshapepath) that
|
|
490
626
|
- `#center_x`: center x
|
491
627
|
- `#center_y`: center y
|
492
628
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
493
|
-
- `#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))
|
494
629
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
630
|
+
- `#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
|
631
|
+
- `#edges`: edges of polygon as `PerfectShape::Line` objects
|
495
632
|
|
496
633
|
Example:
|
497
634
|
|
@@ -502,6 +639,14 @@ shape = PerfectShape::Polygon.new(points: [[200, 150], [270, 170], [250, 220], [
|
|
502
639
|
|
503
640
|
shape.contain?(225, 185) # => true
|
504
641
|
shape.contain?([225, 185]) # => true
|
642
|
+
shape.contain?(225, 185, outline: true) # => false
|
643
|
+
shape.contain?([225, 185], outline: true) # => false
|
644
|
+
shape.contain?(200, 150, outline: true) # => true
|
645
|
+
shape.contain?([200, 150], outline: true) # => true
|
646
|
+
shape.contain?(200, 151, outline: true) # => false
|
647
|
+
shape.contain?([200, 151], outline: true) # => false
|
648
|
+
shape.contain?(200, 151, outline: true, distance_tolerance: 1) # => true
|
649
|
+
shape.contain?([200, 151], outline: true, distance_tolerance: 1) # => true
|
505
650
|
```
|
506
651
|
|
507
652
|
### `PerfectShape::Path`
|
@@ -528,9 +673,9 @@ Includes `PerfectShape::MultiPoint`
|
|
528
673
|
- `#center_x`: center x
|
529
674
|
- `#center_y`: center y
|
530
675
|
- `#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)
|
676
|
+
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
531
677
|
- `#contain?(x_or_point, y=nil)`: checks if point is inside path utilizing the configured winding rule, which can be the [Nonzero-Rule](https://en.wikipedia.org/wiki/Nonzero-rule) (aka [Winding Number Algorithm](https://en.wikipedia.org/wiki/Point_in_polygon#Winding_number_algorithm)) or the [Even-Odd Rule](https://en.wikipedia.org/wiki/Even%E2%80%93odd_rule) (aka [Ray Casting Algorithm](https://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm))
|
532
678
|
- `#point_crossings(x_or_point, y=nil)`: calculates the number of times the given path crosses the ray extending to the right from (x,y)
|
533
|
-
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
534
679
|
|
535
680
|
Example:
|
536
681
|
|
@@ -555,10 +700,12 @@ Class
|
|
555
700
|
|
556
701
|
Extends `PerfectShape::Shape`
|
557
702
|
|
703
|
+
A composite shape is simply an aggregate of multiple shapes (e.g. square and polygon)
|
704
|
+
|
558
705
|
![composite shape](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/composite-shape.png)
|
559
706
|
|
560
707
|
- `::new(shapes: [])`: constructs a composite shape with `shapes` as `Array` of `PerfectShape::Shape` objects
|
561
|
-
- `#shapes`: the shapes that the
|
708
|
+
- `#shapes`: the shapes that the composite shape is composed of
|
562
709
|
- `#min_x`: min x
|
563
710
|
- `#min_y`: min y
|
564
711
|
- `#max_x`: max x
|
@@ -568,8 +715,8 @@ Extends `PerfectShape::Shape`
|
|
568
715
|
- `#center_x`: center x
|
569
716
|
- `#center_y`: center y
|
570
717
|
- `#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)
|
571
|
-
- `#contain?(x_or_point, y=nil)`: checks if point is inside any of the shapes owned by the composite shape
|
572
718
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
719
|
+
- `#contain?(x_or_point, y=nil)`: checks if point is inside any of the shapes owned by the composite shape
|
573
720
|
|
574
721
|
Example:
|
575
722
|
|
@@ -595,7 +742,7 @@ shape.contain?([170, 190]) # => true
|
|
595
742
|
## Resources
|
596
743
|
|
597
744
|
- Rubydoc: https://www.rubydoc.info/gems/perfect-shape
|
598
|
-
- AWT Geom
|
745
|
+
- AWT Geom Javadoc (inspiration): https://docs.oracle.com/javase/8/docs/api/java/awt/geom/package-summary.html
|
599
746
|
|
600
747
|
## TODO
|
601
748
|
|
@@ -625,5 +772,5 @@ shape.contain?([170, 190]) # => true
|
|
625
772
|
|
626
773
|
[MIT](LICENSE.txt)
|
627
774
|
|
628
|
-
Copyright (c) 2021 Andy Maleh. See
|
775
|
+
Copyright (c) 2021-2022 Andy Maleh. See
|
629
776
|
[LICENSE.txt](LICENSE.txt) for further details.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.3
|
data/lib/perfect-shape.rb
CHANGED
data/lib/perfect_shape/arc.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2021 Andy Maleh
|
1
|
+
# Copyright (c) 2021-2022 Andy Maleh
|
2
2
|
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
# a copy of this software and associated documentation files (the
|
@@ -30,6 +30,7 @@ module PerfectShape
|
|
30
30
|
include Equalizer.new(:type, :x, :y, :width, :height, :start, :extent)
|
31
31
|
|
32
32
|
TYPES = [:open, :chord, :pie]
|
33
|
+
DEFAULT_OUTLINE_RADIUS = BigDecimal('0.001')
|
33
34
|
attr_accessor :type
|
34
35
|
attr_reader :start, :extent
|
35
36
|
|
@@ -143,44 +144,58 @@ module PerfectShape
|
|
143
144
|
# @return {@code true} if the point lies within the bound of
|
144
145
|
# the arc, {@code false} if the point lies outside of the
|
145
146
|
# arc's bounds.
|
146
|
-
def contain?(x_or_point, y = nil)
|
147
|
+
def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
|
147
148
|
x, y = normalize_point(x_or_point, y)
|
148
149
|
return unless x && y
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
inarc = contain_angle?(-1*Math.radians_to_degrees(Math.atan2(normy, normx)))
|
162
|
-
|
163
|
-
return inarc if type == :pie
|
164
|
-
# CHORD and OPEN behave the same way
|
165
|
-
if inarc
|
166
|
-
return true if ang_ext >= 180.0
|
167
|
-
# point must be outside the "pie triangle"
|
150
|
+
if outline
|
151
|
+
if type == :pie && x == center_x && y == center_y
|
152
|
+
true
|
153
|
+
else
|
154
|
+
distance_tolerance = BigDecimal(distance_tolerance.to_s)
|
155
|
+
outside_inside_radius_difference = DEFAULT_OUTLINE_RADIUS + distance_tolerance * 2.0
|
156
|
+
outside_radius_difference = inside_radius_difference = outside_inside_radius_difference / 2.0
|
157
|
+
outside_shape = Arc.new(type: type, center_x: center_x, center_y: center_y, radius_x: radius_x + outside_radius_difference, radius_y: radius_y + outside_radius_difference, start: start, extent: extent)
|
158
|
+
inside_shape = Arc.new(type: type, center_x: center_x, center_y: center_y, radius_x: radius_x - inside_radius_difference, radius_y: radius_y - inside_radius_difference, start: start, extent: extent)
|
159
|
+
outside_shape.contain?(x, y, outline: false) and
|
160
|
+
!inside_shape.contain?(x, y, outline: false)
|
161
|
+
end
|
168
162
|
else
|
169
|
-
|
170
|
-
#
|
163
|
+
# Normalize the coordinates compared to the ellipse
|
164
|
+
# having a center at 0,0 and a radius of 0.5.
|
165
|
+
ellw = width
|
166
|
+
return false if (ellw <= 0.0)
|
167
|
+
normx = (x - self.x) / ellw - 0.5
|
168
|
+
ellh = height
|
169
|
+
return false if (ellh <= 0.0)
|
170
|
+
normy = (y - self.y) / ellh - 0.5
|
171
|
+
dist_sq = (normx * normx) + (normy * normy)
|
172
|
+
return false if (dist_sq >= 0.25)
|
173
|
+
ang_ext = self.extent.abs
|
174
|
+
return true if (ang_ext >= 360.0)
|
175
|
+
inarc = contain_angle?(-1*Math.radians_to_degrees(Math.atan2(normy, normx)))
|
176
|
+
|
177
|
+
return inarc if type == :pie
|
178
|
+
# CHORD and OPEN behave the same way
|
179
|
+
if inarc
|
180
|
+
return true if ang_ext >= 180.0
|
181
|
+
# point must be outside the "pie triangle"
|
182
|
+
else
|
183
|
+
return false if ang_ext <= 180.0
|
184
|
+
# point must be inside the "pie triangle"
|
185
|
+
end
|
186
|
+
|
187
|
+
# The point is inside the pie triangle iff it is on the same
|
188
|
+
# side of the line connecting the ends of the arc as the center.
|
189
|
+
angle = Math.degrees_to_radians(-start)
|
190
|
+
x1 = Math.cos(angle)
|
191
|
+
y1 = Math.sin(angle)
|
192
|
+
angle += Math.degrees_to_radians(-extent)
|
193
|
+
x2 = Math.cos(angle)
|
194
|
+
y2 = Math.sin(angle)
|
195
|
+
inside = (Line.relative_counterclockwise(x1, y1, x2, y2, 2*normx, 2*normy) *
|
196
|
+
Line.relative_counterclockwise(x1, y1, x2, y2, 0, 0) >= 0)
|
197
|
+
inarc ? !inside : inside
|
171
198
|
end
|
172
|
-
|
173
|
-
# The point is inside the pie triangle iff it is on the same
|
174
|
-
# side of the line connecting the ends of the arc as the center.
|
175
|
-
angle = Math.degrees_to_radians(-start)
|
176
|
-
x1 = Math.cos(angle)
|
177
|
-
y1 = Math.sin(angle)
|
178
|
-
angle += Math.degrees_to_radians(-extent)
|
179
|
-
x2 = Math.cos(angle)
|
180
|
-
y2 = Math.sin(angle)
|
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
|
-
inarc ? !inside : inside
|
184
199
|
end
|
185
200
|
|
186
201
|
# Determines whether or not the specified angle is within the
|