perfect-shape 0.3.1 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9e16e850ee86ff6e5953dd10f2c1c753109906b671a85115ea1913386a3a736
4
- data.tar.gz: a5b1fef3214f92dd3185c1ee1c6e3f0522ff278cda0a0f384341c9e4a5bad5f7
3
+ metadata.gz: ab1f48d0551ed07a6a2b5f2d51abba6c6596eef82e156e68df57489319104e4c
4
+ data.tar.gz: bb67e137bbf23df2ed291a574ef45ae97113dd140d0a17269c9b60c27b7261e9
5
5
  SHA512:
6
- metadata.gz: 19c2f3261ef17fa8e048c8e826e56a3f1d07b2ff39a3a8d18832a0b1e2113b63af8ce09aa8a978f6c81a931ffcb8154e0fdc77ed53b0f0937f7d8d6529250fd5
7
- data.tar.gz: 681654b7cac1242f872225a0726f8f2fa5813a8c61d999342466fa06516ccaeb981be68f5fad003eeb0d86a4b58c698f264e94a356a4ee934361482f6a072726
6
+ metadata.gz: 6d365e47635c7daa094f02849df24696784b667c55bffa479bdbaacc41abf1b9ad1114084a4ed7bd0145e6dd18ad233f9b09e6bad8792828417aca49db2076c4
7
+ data.tar.gz: d8440bb0d35e1539dcf5c8cad79e2930d74e10e32a408391085027d773c0ea7e4272a6011a7244677c035c8f6a7954606065611282ae8cd14aab71cbbe6b2bba
data/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.3.5
4
+
5
+ - Check point containment in composite shape outline with distance tolerance (new method signature: `PerfectShape::CompositeShape#contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)`)
6
+
7
+ ## 0.3.4
8
+
9
+ - Check point containment in path outline with distance tolerance (new method signature: `PerfectShape::Path#contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)`)
10
+ - `PerfectShape::Path#disconnected_shapes`: Disconnected shapes have their start point filled in so that each shape does not depend on the previous shape to determine its start point.
11
+ - `Shape#center_point` as `[center_x, center_y]`
12
+ - Rename `#point_segment_distance` to `#point_distance` everywhere
13
+
14
+ ## 0.3.3
15
+
16
+ - 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)`)
17
+ - `PerfectShape::QuadraticBezierCurve#curve_center_point`, `PerfectShape::QuadraticBezierCurve#curve_center_x`, `PerfectShape::QuadraticBezierCurve#curve_center_y`
18
+ - `PerfectShape::QuadraticBezierCurve#subdivisions(level=1)`
19
+ - `PerfectShape::QuadraticBezierCurve#point_distance(x_or_point, y = nil, minimum_distance_threshold: OUTLINE_MINIMUM_DISTANCE_THRESHOLD)`
20
+ - `PerfectShape::Polygon#edges` returns edges of polygon as `PerfectShape::Line` objects
21
+ - `PerfectShape::Rectangle#edges` returns edges of rectangle as `PerfectShape::Line` objects
22
+ - `PerfectShape::Square#edges` returns edges of square as `PerfectShape::Line` objects
23
+ - Rename `number` arg to `level` in `CubicBezierCurve#subdivisions(level=1)`, making it signify the level of subdivision recursion to perform.
24
+
25
+ ## 0.3.2
26
+
27
+ - 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)`)
28
+ - `PerfectShape::CubicBezierCurve#curve_center_point`, `PerfectShape::CubicBezierCurve#curve_center_x`, `PerfectShape::CubicBezierCurve#curve_center_y`
29
+ - `PerfectShape::CubicBezierCurve#subdivisions(level=1)`
30
+ - `PerfectShape::CubicBezierCurve#point_distance(x_or_point, y = nil, minimum_distance_threshold: OUTLINE_MINIMUM_DISTANCE_THRESHOLD)`
31
+
3
32
  ## 0.3.1
4
33
 
5
34
  - 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)`)
@@ -62,7 +91,7 @@
62
91
  - `PerfectShape::Line`
63
92
  - `PerfectShape::Line#contain?(x_or_point, y=nil)`
64
93
  - `PerfectShape::Line#relative_counterclockwise`
65
- - `PerfectShape::Line#point_segment_distance`
94
+ - `PerfectShape::Line#point_distance`
66
95
  - Update `PerfectShape::Math::radians_to_degrees`, `PerfectShape::Math::degrees_to_radians`, and `PerfectShape::Math::normalize_degrees` to normalize numbers to `BigDecimal`
67
96
 
68
97
  ## 0.0.7
data/LICENSE.txt 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
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Perfect Shape 0.3.1
1
+ # Perfect Shape 0.3.5
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)
@@ -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.3.1
17
+ gem install perfect-shape -v 0.3.5
18
18
  ```
19
19
 
20
20
  Or include in Bundler `Gemfile`:
21
21
 
22
22
  ```ruby
23
- gem 'perfect-shape', '~> 0.3.1'
23
+ gem 'perfect-shape', '~> 0.3.5'
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 % modulo operator as it operates on floats and could return a negative result)
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
 
@@ -52,12 +52,13 @@ This is a base class for all shapes. It is not meant to be used directly. Subcla
52
52
  - `#max_y`: max y
53
53
  - `#width`: width
54
54
  - `#height`: height
55
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
55
56
  - `#center_x`: center x
56
57
  - `#center_y`: center y
57
58
  - `#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
59
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
60
+ - `#normalize_point(x_or_point, y = nil)`: normalizes point into an `Array` of `[x,y]` coordinates
61
+ - `#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
62
 
62
63
  ### `PerfectShape::PointLocation`
63
64
 
@@ -84,9 +85,6 @@ Includes `PerfectShape::PointLocation`
84
85
  - `#min_y`: min y
85
86
  - `#max_x`: max x
86
87
  - `#max_y`: max y
87
- - `#center_x`: center x
88
- - `#center_y`: center y
89
- - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
90
88
 
91
89
  ### `PerfectShape::Point`
92
90
 
@@ -108,12 +106,13 @@ Points are simply represented by an `Array` of `[x,y]` coordinates when used wit
108
106
  - `#max_y`: max y (always y)
109
107
  - `#width`: width (always 0)
110
108
  - `#height`: height (always 0)
109
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
111
110
  - `#center_x`: center x (always x)
112
111
  - `#center_y`: center y (always y)
113
112
  - `#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_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.
115
- - `#point_distance(x_or_point, y=nil)`: Returns the distance from a point to another point
116
113
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
114
+ - `#contain?(x_or_point, y=nil, outline: true, 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. `outline` option makes no difference on point
115
+ - `#point_distance(x_or_point, y=nil)`: Returns the distance from a point to another point
117
116
 
118
117
  Example:
119
118
 
@@ -141,8 +140,8 @@ Includes `PerfectShape::MultiPoint`
141
140
  ![line](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/line.png)
142
141
 
143
142
  - `::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)”.
144
- - `::point_segment_distance_square(x1, y1, x2, y2, px, py)`: Returns the square of distance from a point to a line segment.
145
- - `::point_segment_distance(x1, y1, x2, y2, px, py)`: Returns the distance from a point to a line segment.
143
+ - `::point_distance_square(x1, y1, x2, y2, px, py)`: Returns the square of distance from a point to a line segment.
144
+ - `::point_distance(x1, y1, x2, y2, px, py)`: Returns the distance from a point to a line segment.
146
145
  - `::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
147
146
  - `#min_x`: min x
148
147
  - `#min_y`: min y
@@ -150,13 +149,14 @@ Includes `PerfectShape::MultiPoint`
150
149
  - `#max_y`: max y
151
150
  - `#width`: width (from min x to max x)
152
151
  - `#height`: height (from min y to max y)
152
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
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
- - `#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
- - `#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
- - `#point_segment_distance(x_or_point, y=nil)`: Returns the distance from a point to a line segment.
159
156
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
157
+ - `#contain?(x_or_point, y=nil, outline: true, 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. `outline` option makes no difference on line
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)”.
159
+ - `#point_distance(x_or_point, y=nil)`: Returns the distance from a point to a line segment.
160
160
 
161
161
  Example:
162
162
 
@@ -191,11 +191,17 @@ Includes `PerfectShape::MultiPoint`
191
191
  - `#max_y`: max y
192
192
  - `#width`: width (from min x to max x)
193
193
  - `#height`: height (from min y to max y)
194
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
194
195
  - `#center_x`: center x
195
196
  - `#center_y`: center y
196
197
  - `#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
198
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
199
+ - `#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
200
+ - `#curve_center_point`: point at the center of the curve outline (not the center of the bounding box area like `center_x` and `center_y`)
201
+ - `#curve_center_x`: point x coordinate at the center of the curve outline (not the center of the bounding box area like `center_x` and `center_y`)
202
+ - `#curve_center_y`: point y coordinate at the center of the curve outline (not the center of the bounding box area like `center_x` and `center_y`)
203
+ - `#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`.
204
+ - `#point_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
205
 
200
206
  Example:
201
207
 
@@ -206,6 +212,14 @@ shape = PerfectShape::QuadraticBezierCurve.new(points: [[200, 150], [270, 320],
206
212
 
207
213
  shape.contain?(270, 220) # => true
208
214
  shape.contain?([270, 220]) # => true
215
+ shape.contain?(270, 220, outline: true) # => false
216
+ shape.contain?([270, 220], outline: true) # => false
217
+ shape.contain?(280, 235, outline: true) # => true
218
+ shape.contain?([280, 235], outline: true) # => true
219
+ shape.contain?(281, 235, outline: true) # => false
220
+ shape.contain?([281, 235], outline: true) # => false
221
+ shape.contain?(281, 235, outline: true, distance_tolerance: 1) # => true
222
+ shape.contain?([281, 235], outline: true, distance_tolerance: 1) # => true
209
223
  ```
210
224
 
211
225
  ### `PerfectShape::CubicBezierCurve`
@@ -226,11 +240,17 @@ Includes `PerfectShape::MultiPoint`
226
240
  - `#max_y`: max y
227
241
  - `#width`: width (from min x to max x)
228
242
  - `#height`: height (from min y to max y)
243
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
229
244
  - `#center_x`: center x
230
245
  - `#center_y`: center y
231
246
  - `#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
247
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
248
+ - `#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
249
+ - `#curve_center_point`: point at the center of the curve outline (not the center of the bounding box area like `center_x` and `center_y`)
250
+ - `#curve_center_x`: point x coordinate at the center of the curve outline (not the center of the bounding box area like `center_x` and `center_y`)
251
+ - `#curve_center_y`: point y coordinate at the center of the curve outline (not the center of the bounding box area like `center_x` and `center_y`)
252
+ - `#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`.
253
+ - `#point_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
254
 
235
255
  Example:
236
256
 
@@ -241,6 +261,14 @@ shape = PerfectShape::CubicBezierCurve.new(points: [[200, 150], [235, 235], [270
241
261
 
242
262
  shape.contain?(270, 220) # => true
243
263
  shape.contain?([270, 220]) # => true
264
+ shape.contain?(270, 220, outline: true) # => false
265
+ shape.contain?([270, 220], outline: true) # => false
266
+ shape.contain?(261.875, 245.625, outline: true) # => true
267
+ shape.contain?([261.875, 245.625], outline: true) # => true
268
+ shape.contain?(261.875, 246.625, outline: true) # => false
269
+ shape.contain?([261.875, 246.625], outline: true) # => false
270
+ shape.contain?(261.875, 246.625, outline: true, distance_tolerance: 1) # => true
271
+ shape.contain?([261.875, 246.625], outline: true, distance_tolerance: 1) # => true
244
272
  ```
245
273
 
246
274
  ### `PerfectShape::Rectangle`
@@ -258,6 +286,7 @@ Includes `PerfectShape::RectangularShape`
258
286
  - `#y`: top-left y
259
287
  - `#width`: width
260
288
  - `#height`: height
289
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
261
290
  - `#center_x`: center x
262
291
  - `#center_y`: center y
263
292
  - `#min_x`: min x
@@ -265,8 +294,9 @@ Includes `PerfectShape::RectangularShape`
265
294
  - `#max_x`: max x
266
295
  - `#max_y`: max y
267
296
  - `#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, 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
269
297
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
298
+ - `#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
299
+ - `#edges`: edges of rectangle as `PerfectShape::Line` objects
270
300
 
271
301
  Example:
272
302
 
@@ -301,6 +331,7 @@ Extends `PerfectShape::Rectangle`
301
331
  - `#length`: length
302
332
  - `#width`: width (equal to length)
303
333
  - `#height`: height (equal to length)
334
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
304
335
  - `#center_x`: center x
305
336
  - `#center_y`: center y
306
337
  - `#min_x`: min x
@@ -308,8 +339,9 @@ Extends `PerfectShape::Rectangle`
308
339
  - `#max_x`: max x
309
340
  - `#max_y`: max y
310
341
  - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
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
312
342
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
343
+ - `#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
344
+ - `#edges`: edges of square as `PerfectShape::Line` objects
313
345
 
314
346
  Example:
315
347
 
@@ -352,6 +384,7 @@ Open Arc | Chord Arc | Pie Arc
352
384
  - `#height`: height
353
385
  - `#start`: start angle in degrees
354
386
  - `#extent`: extent angle in degrees
387
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
355
388
  - `#center_x`: center x
356
389
  - `#center_y`: center y
357
390
  - `#radius_x`: radius along the x-axis
@@ -361,8 +394,8 @@ Open Arc | Chord Arc | Pie Arc
361
394
  - `#max_x`: max x
362
395
  - `#max_y`: max y
363
396
  - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
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
365
397
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
398
+ - `#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
366
399
 
367
400
  Example:
368
401
 
@@ -471,6 +504,7 @@ Extends `PerfectShape::Arc`
471
504
  - `#y`: top-left y
472
505
  - `#width`: width
473
506
  - `#height`: height
507
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
474
508
  - `#center_x`: center x
475
509
  - `#center_y`: center y
476
510
  - `#radius_x`: radius along the x-axis
@@ -483,8 +517,8 @@ Extends `PerfectShape::Arc`
483
517
  - `#max_x`: max x
484
518
  - `#max_y`: max y
485
519
  - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
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
487
520
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
521
+ - `#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
488
522
 
489
523
  Example:
490
524
 
@@ -530,6 +564,7 @@ Extends `PerfectShape::Ellipse`
530
564
  - `#diameter`: diameter
531
565
  - `#width`: width (equal to diameter)
532
566
  - `#height`: height (equal to diameter)
567
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
533
568
  - `#center_x`: center x
534
569
  - `#center_y`: center y
535
570
  - `#radius`: radius
@@ -543,8 +578,8 @@ Extends `PerfectShape::Ellipse`
543
578
  - `#max_x`: max x
544
579
  - `#max_y`: max y
545
580
  - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
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
547
581
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
582
+ - `#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
548
583
 
549
584
  Example:
550
585
 
@@ -595,11 +630,13 @@ A polygon can be thought of as a special case of [path](#perfectshapepath) that
595
630
  - `#max_y`: max y
596
631
  - `#width`: width (from min x to max x)
597
632
  - `#height`: height (from min y to max y)
633
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
598
634
  - `#center_x`: center x
599
635
  - `#center_y`: center y
600
636
  - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
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
602
637
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
638
+ - `#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
639
+ - `#edges`: edges of polygon as `PerfectShape::Line` objects
603
640
 
604
641
  Example:
605
642
 
@@ -641,12 +678,14 @@ Includes `PerfectShape::MultiPoint`
641
678
  - `#max_y`: max y
642
679
  - `#width`: width (from min x to max x)
643
680
  - `#height`: height (from min y to max y)
681
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
644
682
  - `#center_x`: center x
645
683
  - `#center_y`: center y
646
684
  - `#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)
647
- - `#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))
648
- - `#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)
649
685
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
686
+ - `#contain?(x_or_point, y=nil, outline: false, distance_tolerance: 0)`: When `outline` is `false`, it 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)). 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 path shape from its outline more successfully
687
+ - `#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)
688
+ - `#disconnected_shapes`: Disconnected shapes have their start point filled in so that each shape does not depend on the previous shape to determine its start point. Also, if a point is followed by a non-point shape, it is removed since it is augmented to the following shape as its start point. Lastly, if the path is closed, an extra shape is added to represent the line connecting the last point to the first
650
689
 
651
690
  Example:
652
691
 
@@ -661,8 +700,16 @@ path_shapes << PerfectShape::CubicBezierCurve.new(points: [[370, 50], [430, 220]
661
700
 
662
701
  shape = PerfectShape::Path.new(shapes: path_shapes, closed: false, winding_rule: :wind_even_odd)
663
702
 
664
- shape.contain?(225, 160) # => true
665
- shape.contain?([225, 160]) # => true
703
+ shape.contain?(275, 165) # => true
704
+ shape.contain?([275, 165]) # => true
705
+ shape.contain?(275, 165, outline: true) # => false
706
+ shape.contain?([275, 165], outline: true) # => false
707
+ shape.contain?(shape.disconnected_shapes[1].curve_center_x, shape.disconnected_shapes[1].curve_center_y, outline: true) # => true
708
+ shape.contain?([shape.disconnected_shapes[1].curve_center_x, shape.disconnected_shapes[1].curve_center_y], outline: true) # => true
709
+ shape.contain?(shape.disconnected_shapes[1].curve_center_x + 1, shape.disconnected_shapes[1].curve_center_y, outline: true) # => false
710
+ shape.contain?([shape.disconnected_shapes[1].curve_center_x + 1, shape.disconnected_shapes[1].curve_center_y], outline: true) # => false
711
+ shape.contain?(shape.disconnected_shapes[1].curve_center_x + 1, shape.disconnected_shapes[1].curve_center_y, outline: true, distance_tolerance: 1) # => true
712
+ shape.contain?([shape.disconnected_shapes[1].curve_center_x + 1, shape.disconnected_shapes[1].curve_center_y], outline: true, distance_tolerance: 1) # => true
666
713
  ```
667
714
 
668
715
  ### `PerfectShape::CompositeShape`
@@ -683,11 +730,12 @@ A composite shape is simply an aggregate of multiple shapes (e.g. square and pol
683
730
  - `#max_y`: max y
684
731
  - `#width`: width (from min x to max x)
685
732
  - `#height`: height (from min y to max y)
733
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
686
734
  - `#center_x`: center x
687
735
  - `#center_y`: center y
688
736
  - `#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
737
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
738
+ - `#contain?(x_or_point, y=nil)`: When `outline` is `false`, it checks if point is inside any of the shapes owned by the composite shape. Otherwise, when `outline` is `true`, it checks if point is on the outline of any of the shapes owned by the composite shape. `distance_tolerance` can be used as a fuzz factor when `outline` is `true`, for example, to help GUI users mouse-click-select a composite shape from its outline more successfully
691
739
 
692
740
  Example:
693
741
 
@@ -700,10 +748,27 @@ shapes << PerfectShape::Polygon.new(points: [[120, 215], [170, 165], [220, 215]]
700
748
 
701
749
  shape = PerfectShape::CompositeShape.new(shapes: shapes)
702
750
 
703
- shape.contain?(170, 265) # => true
704
- shape.contain?([170, 265]) # => true
705
- shape.contain?(170, 190) # => true
706
- shape.contain?([170, 190]) # => true
751
+ shape.contain?(170, 265) # => true inside square
752
+ shape.contain?([170, 265]) # => true inside square
753
+ shape.contain?(170, 265, outline: true) # => false
754
+ shape.contain?([170, 265], outline: true) # => false
755
+ shape.contain?(170, 315, outline: true) # => true
756
+ shape.contain?([170, 315], outline: true) # => true
757
+ shape.contain?(170, 316, outline: true) # => false
758
+ shape.contain?([170, 316], outline: true) # => false
759
+ shape.contain?(170, 316, outline: true, distance_tolerance: 1) # => true
760
+ shape.contain?([170, 316], outline: true, distance_tolerance: 1) # => true
761
+
762
+ shape.contain?(170, 190) # => true inside polygon
763
+ shape.contain?([170, 190]) # => true inside polygon
764
+ shape.contain?(170, 190, outline: true) # => false
765
+ shape.contain?([170, 190], outline: true) # => false
766
+ shape.contain?(145, 190, outline: true) # => true
767
+ shape.contain?([145, 190], outline: true) # => true
768
+ shape.contain?(145, 189, outline: true) # => false
769
+ shape.contain?([145, 189], outline: true) # => false
770
+ shape.contain?(145, 189, outline: true, distance_tolerance: 1) # => true
771
+ shape.contain?([145, 189], outline: true, distance_tolerance: 1) # => true
707
772
  ```
708
773
 
709
774
  ## Process
@@ -743,5 +808,5 @@ shape.contain?([170, 190]) # => true
743
808
 
744
809
  [MIT](LICENSE.txt)
745
810
 
746
- Copyright (c) 2021 Andy Maleh. See
811
+ Copyright (c) 2021-2022 Andy Maleh. See
747
812
  [LICENSE.txt](LICENSE.txt) for further details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.1
1
+ 0.3.5
data/lib/perfect-shape.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
@@ -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
 
@@ -151,8 +152,8 @@ module PerfectShape
151
152
  true
152
153
  else
153
154
  distance_tolerance = BigDecimal(distance_tolerance.to_s)
154
- outside_inside_radius_difference = BigDecimal('0.001') + distance_tolerance * 2.0
155
- outside_radius_difference = inside_radius_difference = outside_inside_radius_difference / 2.0
155
+ outside_inside_radius_difference = DEFAULT_OUTLINE_RADIUS + distance_tolerance * 2
156
+ outside_radius_difference = inside_radius_difference = outside_inside_radius_difference / 2
156
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)
157
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)
158
159
  outside_shape.contain?(x, y, outline: false) and
@@ -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
@@ -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
@@ -63,10 +63,11 @@ module PerfectShape
63
63
  # @return true if the point lies within the bound of
64
64
  # the composite shape or false if the point lies outside of the
65
65
  # path's bounds.
66
- def contain?(x_or_point, y = nil)
66
+ def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
67
67
  x, y = normalize_point(x_or_point, y)
68
68
  return unless x && y
69
- shapes.any? {|shape| shape.contain?(x, y) }
69
+
70
+ shapes.any? { |shape| shape.contain?(x, y, outline: outline, distance_tolerance: distance_tolerance) }
70
71
  end
71
72
  end
72
73
  end