perfect-shape 0.3.0 → 0.3.4

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: 4e7efc4e2333e43b5e4d034e7fa3a3dddbdc9c6fd392e9d2ae1a409995122eb5
4
- data.tar.gz: 07f098098f6e42d6d71bdab33b44d3250c05c79ee48b2304b60784fba7de3f75
3
+ metadata.gz: 4dc25ad218f1860bfff0fccc88b81ea4b6ef71795c737b740b998385f4c7bf7d
4
+ data.tar.gz: 31b52e8b765db80edde7ede8487f579a5de33b51fa1939bc1601dbad093741b1
5
5
  SHA512:
6
- metadata.gz: a69ec76140e777f303724c2db8d7dd904b2f679b11ce6380629c27ae0f3feb1cc0be0d615b79c085cd276e9a96b9a5842fbf8eb4d810398ae1329a3598b37592
7
- data.tar.gz: d87a97b80e155939e380024653b01fe7a230660c0b58957d96f6d3d9392b113996f051bf55dbbc3b935c7d44a763cd686716dc9cb568d7ff85580aa1ca955c9d
6
+ metadata.gz: 949d9a223e08cf4b9154f63f575908707c257620e18fdcd4a2820855c305400f418038b5ec53b56c4a4baea7c38065df8da71158dc29c76a5a19a809bf38da33
7
+ data.tar.gz: '049d0fb36f1b633ebb20cf9fae4731688a19197a4bcbf29cbf55a7f837a5e4a9916b9b3a396a2f859387d3f80fcc7d8ebb9c6a23757dc3b389b0c70e79c7b845'
data/CHANGELOG.md CHANGED
@@ -1,11 +1,42 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.3.4
4
+
5
+ - 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)`)
6
+ - `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.
7
+ - `Shape#center_point` as `[center_x, center_y]`
8
+ - Rename `#point_segment_distance` to `#point_distance` everywhere
9
+
10
+ ## 0.3.3
11
+
12
+ - 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)`)
13
+ - `PerfectShape::QuadraticBezierCurve#curve_center_point`, `PerfectShape::QuadraticBezierCurve#curve_center_x`, `PerfectShape::QuadraticBezierCurve#curve_center_y`
14
+ - `PerfectShape::QuadraticBezierCurve#subdivisions(level=1)`
15
+ - `PerfectShape::QuadraticBezierCurve#point_distance(x_or_point, y = nil, minimum_distance_threshold: OUTLINE_MINIMUM_DISTANCE_THRESHOLD)`
16
+ - `PerfectShape::Polygon#edges` returns edges of polygon as `PerfectShape::Line` objects
17
+ - `PerfectShape::Rectangle#edges` returns edges of rectangle as `PerfectShape::Line` objects
18
+ - `PerfectShape::Square#edges` returns edges of square as `PerfectShape::Line` objects
19
+ - Rename `number` arg to `level` in `CubicBezierCurve#subdivisions(level=1)`, making it signify the level of subdivision recursion to perform.
20
+
21
+ ## 0.3.2
22
+
23
+ - 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)`)
24
+ - `PerfectShape::CubicBezierCurve#curve_center_point`, `PerfectShape::CubicBezierCurve#curve_center_x`, `PerfectShape::CubicBezierCurve#curve_center_y`
25
+ - `PerfectShape::CubicBezierCurve#subdivisions(level=1)`
26
+ - `PerfectShape::CubicBezierCurve#point_distance(x_or_point, y = nil, minimum_distance_threshold: OUTLINE_MINIMUM_DISTANCE_THRESHOLD)`
27
+
28
+ ## 0.3.1
29
+
30
+ - 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)`)
31
+ - 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)`)
32
+ - 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)`)
33
+
3
34
  ## 0.3.0
4
35
 
5
36
  - Refactoring: rename `distance` option for `#contain?` on `Point`/`Line` into `distance_tolerance`
6
- - Check point containment in rectangle outline with distance tolerance: `PerfectShape::Rectangle#contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)`
7
- - Check point containment in square outline with distance tolerance: `PerfectShape::Square#contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)`
8
- - Check point containment in polygon outline with distance tolerance: `PerfectShape::Polygon#contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)`
37
+ - 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)`)
38
+ - 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)`)
39
+ - 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)`)
9
40
 
10
41
  ## 0.2.0
11
42
 
@@ -56,7 +87,7 @@
56
87
  - `PerfectShape::Line`
57
88
  - `PerfectShape::Line#contain?(x_or_point, y=nil)`
58
89
  - `PerfectShape::Line#relative_counterclockwise`
59
- - `PerfectShape::Line#point_segment_distance`
90
+ - `PerfectShape::Line#point_distance`
60
91
  - Update `PerfectShape::Math::radians_to_degrees`, `PerfectShape::Math::degrees_to_radians`, and `PerfectShape::Math::normalize_degrees` to normalize numbers to `BigDecimal`
61
92
 
62
93
  ## 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.0
1
+ # Perfect Shape 0.3.4
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.0
17
+ gem install perfect-shape -v 0.3.4
18
18
  ```
19
19
 
20
20
  Or include in Bundler `Gemfile`:
21
21
 
22
22
  ```ruby
23
- gem 'perfect-shape', '~> 0.3.0'
23
+ gem 'perfect-shape', '~> 0.3.4'
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 in a GUI 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 in a GUI 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 users mouse-click-select a rectangle shape from its edges in a GUI 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 users mouse-click-select a square shape from its edges in a GUI 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)`: checks if point is inside
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
 
@@ -370,23 +403,63 @@ Example:
370
403
  require 'perfect-shape'
371
404
 
372
405
  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: 30, extent: 90)
406
+ 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
407
 
375
408
  shape.contain?(39.5, 33.0) # => true
376
409
  shape.contain?([39.5, 33.0]) # => true
377
410
  shape2.contain?(39.5, 33.0) # => true
378
411
  shape2.contain?([39.5, 33.0]) # => true
412
+ shape.contain?(39.5, 33.0, outline: true) # => false
413
+ shape.contain?([39.5, 33.0], outline: true) # => false
414
+ shape2.contain?(39.5, 33.0, outline: true) # => false
415
+ shape2.contain?([39.5, 33.0], outline: true) # => false
416
+ shape.contain?(2.0, 33.0, outline: true) # => true
417
+ shape.contain?([2.0, 33.0], outline: true) # => true
418
+ shape2.contain?(2.0, 33.0, outline: true) # => true
419
+ shape2.contain?([2.0, 33.0], outline: true) # => true
420
+ shape.contain?(3.0, 33.0, outline: true) # => false
421
+ shape.contain?([3.0, 33.0], outline: true) # => false
422
+ shape2.contain?(3.0, 33.0, outline: true) # => false
423
+ shape2.contain?([3.0, 33.0], outline: true) # => false
424
+ shape.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
425
+ shape.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
426
+ shape2.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
427
+ shape2.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
428
+ shape.contain?(shape.center_x, shape.center_y, outline: true) # => false
429
+ shape.contain?([shape.center_x, shape.center_y], outline: true) # => false
430
+ shape2.contain?(shape2.center_x, shape2.center_y, outline: true) # => false
431
+ shape2.contain?([shape2.center_x, shape2.center_y], outline: true) # => false
379
432
 
380
433
  shape3 = PerfectShape::Arc.new(type: :chord, x: 2, y: 3, width: 50, height: 60, start: 45, extent: 270)
381
- shape4 = PerfectShape::Arc.new(type: :chord, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start: 30, extent: 90)
434
+ shape4 = PerfectShape::Arc.new(type: :chord, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start: 45, extent: 270)
382
435
 
383
436
  shape3.contain?(39.5, 33.0) # => true
384
437
  shape3.contain?([39.5, 33.0]) # => true
385
438
  shape4.contain?(39.5, 33.0) # => true
386
439
  shape4.contain?([39.5, 33.0]) # => true
440
+ shape3.contain?(39.5, 33.0, outline: true) # => false
441
+ shape3.contain?([39.5, 33.0], outline: true) # => false
442
+ shape4.contain?(39.5, 33.0, outline: true) # => false
443
+ shape4.contain?([39.5, 33.0], outline: true) # => false
444
+ shape3.contain?(2.0, 33.0, outline: true) # => true
445
+ shape3.contain?([2.0, 33.0], outline: true) # => true
446
+ shape4.contain?(2.0, 33.0, outline: true) # => true
447
+ shape4.contain?([2.0, 33.0], outline: true) # => true
448
+ shape3.contain?(3.0, 33.0, outline: true) # => false
449
+ shape3.contain?([3.0, 33.0], outline: true) # => false
450
+ shape4.contain?(3.0, 33.0, outline: true) # => false
451
+ shape4.contain?([3.0, 33.0], outline: true) # => false
452
+ shape3.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
453
+ shape3.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
454
+ shape4.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
455
+ shape4.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
456
+ shape3.contain?(shape3.center_x, shape3.center_y, outline: true) # => false
457
+ shape3.contain?([shape3.center_x, shape3.center_y], outline: true) # => false
458
+ shape4.contain?(shape4.center_x, shape4.center_y, outline: true) # => false
459
+ shape4.contain?([shape4.center_x, shape4.center_y], outline: true) # => false
387
460
 
388
461
  shape5 = PerfectShape::Arc.new(type: :pie, x: 2, y: 3, width: 50, height: 60, start: 45, extent: 270)
389
- shape6 = PerfectShape::Arc.new(type: :pie, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start: 30, extent: 90)
462
+ shape6 = PerfectShape::Arc.new(type: :pie, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start: 45, extent: 270)
390
463
 
391
464
  shape5.contain?(39.5, 33.0) # => false
392
465
  shape5.contain?([39.5, 33.0]) # => false
@@ -396,6 +469,26 @@ shape5.contain?(9.5, 33.0) # => true
396
469
  shape5.contain?([9.5, 33.0]) # => true
397
470
  shape6.contain?(9.5, 33.0) # => true
398
471
  shape6.contain?([9.5, 33.0]) # => true
472
+ shape5.contain?(39.5, 33.0, outline: true) # => false
473
+ shape5.contain?([39.5, 33.0], outline: true) # => false
474
+ shape6.contain?(39.5, 33.0, outline: true) # => false
475
+ shape6.contain?([39.5, 33.0], outline: true) # => false
476
+ shape5.contain?(2.0, 33.0, outline: true) # => true
477
+ shape5.contain?([2.0, 33.0], outline: true) # => true
478
+ shape6.contain?(2.0, 33.0, outline: true) # => true
479
+ shape6.contain?([2.0, 33.0], outline: true) # => true
480
+ shape5.contain?(3.0, 33.0, outline: true) # => false
481
+ shape5.contain?([3.0, 33.0], outline: true) # => false
482
+ shape6.contain?(3.0, 33.0, outline: true) # => false
483
+ shape6.contain?([3.0, 33.0], outline: true) # => false
484
+ shape5.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
485
+ shape5.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
486
+ shape6.contain?(3.0, 33.0, outline: true, distance_tolerance: 1.0) # => true
487
+ shape6.contain?([3.0, 33.0], outline: true, distance_tolerance: 1.0) # => true
488
+ shape5.contain?(shape5.center_x, shape5.center_y, outline: true) # => true
489
+ shape5.contain?([shape5.center_x, shape5.center_y], outline: true) # => true
490
+ shape6.contain?(shape6.center_x, shape6.center_y, outline: true) # => true
491
+ shape6.contain?([shape6.center_x, shape6.center_y], outline: true) # => true
399
492
  ```
400
493
 
401
494
  ### `PerfectShape::Ellipse`
@@ -411,6 +504,7 @@ Extends `PerfectShape::Arc`
411
504
  - `#y`: top-left y
412
505
  - `#width`: width
413
506
  - `#height`: height
507
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
414
508
  - `#center_x`: center x
415
509
  - `#center_y`: center y
416
510
  - `#radius_x`: radius along the x-axis
@@ -423,8 +517,8 @@ Extends `PerfectShape::Arc`
423
517
  - `#max_x`: max x
424
518
  - `#max_y`: max y
425
519
  - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
426
- - `#contain?(x_or_point, y=nil)`: checks if point is inside
427
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
428
522
 
429
523
  Example:
430
524
 
@@ -438,6 +532,22 @@ shape.contain?(27, 33) # => true
438
532
  shape.contain?([27, 33]) # => true
439
533
  shape2.contain?(27, 33) # => true
440
534
  shape2.contain?([27, 33]) # => true
535
+ shape.contain?(27, 33, outline: true) # => false
536
+ shape.contain?([27, 33], outline: true) # => false
537
+ shape2.contain?(27, 33, outline: true) # => false
538
+ shape2.contain?([27, 33], outline: true) # => false
539
+ shape.contain?(2, 33, outline: true) # => true
540
+ shape.contain?([2, 33], outline: true) # => true
541
+ shape2.contain?(2, 33, outline: true) # => true
542
+ shape2.contain?([2, 33], outline: true) # => true
543
+ shape.contain?(1, 33, outline: true) # => false
544
+ shape.contain?([1, 33], outline: true) # => false
545
+ shape2.contain?(1, 33, outline: true) # => false
546
+ shape2.contain?([1, 33], outline: true) # => false
547
+ shape.contain?(1, 33, outline: true, distance_tolerance: 1) # => true
548
+ shape.contain?([1, 33], outline: true, distance_tolerance: 1) # => true
549
+ shape2.contain?(1, 33, outline: true, distance_tolerance: 1) # => true
550
+ shape2.contain?([1, 33], outline: true, distance_tolerance: 1) # => true
441
551
  ```
442
552
 
443
553
  ### `PerfectShape::Circle`
@@ -454,6 +564,7 @@ Extends `PerfectShape::Ellipse`
454
564
  - `#diameter`: diameter
455
565
  - `#width`: width (equal to diameter)
456
566
  - `#height`: height (equal to diameter)
567
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
457
568
  - `#center_x`: center x
458
569
  - `#center_y`: center y
459
570
  - `#radius`: radius
@@ -467,8 +578,8 @@ Extends `PerfectShape::Ellipse`
467
578
  - `#max_x`: max x
468
579
  - `#max_y`: max y
469
580
  - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
470
- - `#contain?(x_or_point, y=nil)`: checks if point is inside
471
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
472
583
 
473
584
  Example:
474
585
 
@@ -482,6 +593,22 @@ shape.contain?(32, 33) # => true
482
593
  shape.contain?([32, 33]) # => true
483
594
  shape2.contain?(32, 33) # => true
484
595
  shape2.contain?([32, 33]) # => true
596
+ shape.contain?(32, 33, outline: true) # => false
597
+ shape.contain?([32, 33], outline: true) # => false
598
+ shape2.contain?(32, 33, outline: true) # => false
599
+ shape2.contain?([32, 33], outline: true) # => false
600
+ shape.contain?(2, 33, outline: true) # => true
601
+ shape.contain?([2, 33], outline: true) # => true
602
+ shape2.contain?(2, 33, outline: true) # => true
603
+ shape2.contain?([2, 33], outline: true) # => true
604
+ shape.contain?(1, 33, outline: true) # => false
605
+ shape.contain?([1, 33], outline: true) # => false
606
+ shape2.contain?(1, 33, outline: true) # => false
607
+ shape2.contain?([1, 33], outline: true) # => false
608
+ shape.contain?(1, 33, outline: true, distance_tolerance: 1) # => true
609
+ shape.contain?([1, 33], outline: true, distance_tolerance: 1) # => true
610
+ shape2.contain?(1, 33, outline: true, distance_tolerance: 1) # => true
611
+ shape2.contain?([1, 33], outline: true, distance_tolerance: 1) # => true
485
612
  ```
486
613
 
487
614
  ### `PerfectShape::Polygon`
@@ -503,11 +630,13 @@ A polygon can be thought of as a special case of [path](#perfectshapepath) that
503
630
  - `#max_y`: max y
504
631
  - `#width`: width (from min x to max x)
505
632
  - `#height`: height (from min y to max y)
633
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
506
634
  - `#center_x`: center x
507
635
  - `#center_y`: center y
508
636
  - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
509
- - `#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 users mouse-click-select a polygon shape from its edges in a GUI more successfully
510
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
511
640
 
512
641
  Example:
513
642
 
@@ -549,12 +678,14 @@ Includes `PerfectShape::MultiPoint`
549
678
  - `#max_y`: max y
550
679
  - `#width`: width (from min x to max x)
551
680
  - `#height`: height (from min y to max y)
681
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
552
682
  - `#center_x`: center x
553
683
  - `#center_y`: center y
554
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)
555
- - `#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))
556
- - `#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)
557
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
558
689
 
559
690
  Example:
560
691
 
@@ -571,6 +702,14 @@ shape = PerfectShape::Path.new(shapes: path_shapes, closed: false, winding_rule:
571
702
 
572
703
  shape.contain?(225, 160) # => true
573
704
  shape.contain?([225, 160]) # => true
705
+ shape.contain?(225, 160, outline: true) # => false
706
+ shape.contain?([225, 160], 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
574
713
  ```
575
714
 
576
715
  ### `PerfectShape::CompositeShape`
@@ -591,11 +730,12 @@ A composite shape is simply an aggregate of multiple shapes (e.g. square and pol
591
730
  - `#max_y`: max y
592
731
  - `#width`: width (from min x to max x)
593
732
  - `#height`: height (from min y to max y)
733
+ - `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
594
734
  - `#center_x`: center x
595
735
  - `#center_y`: center y
596
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)
597
- - `#contain?(x_or_point, y=nil)`: checks if point is inside any of the shapes owned by the composite shape
598
737
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
738
+ - `#contain?(x_or_point, y=nil)`: checks if point is inside any of the shapes owned by the composite shape
599
739
 
600
740
  Example:
601
741
 
@@ -621,7 +761,7 @@ shape.contain?([170, 190]) # => true
621
761
  ## Resources
622
762
 
623
763
  - Rubydoc: https://www.rubydoc.info/gems/perfect-shape
624
- - AWT Geom JavaDoc (inspiration): https://docs.oracle.com/javase/8/docs/api/java/awt/geom/package-summary.html
764
+ - AWT Geom Javadoc (inspiration): https://docs.oracle.com/javase/8/docs/api/java/awt/geom/package-summary.html
625
765
 
626
766
  ## TODO
627
767
 
@@ -651,5 +791,5 @@ shape.contain?([170, 190]) # => true
651
791
 
652
792
  [MIT](LICENSE.txt)
653
793
 
654
- Copyright (c) 2021 Andy Maleh. See
794
+ Copyright (c) 2021-2022 Andy Maleh. See
655
795
  [LICENSE.txt](LICENSE.txt) for further details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.3.4
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