perfect-shape 0.3.3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -3
- data/README.md +138 -32
- data/VERSION +1 -1
- data/lib/perfect-shape.rb +1 -1
- data/lib/perfect_shape/affine_transform.rb +235 -0
- data/lib/perfect_shape/arc.rb +4 -4
- data/lib/perfect_shape/composite_shape.rb +4 -3
- data/lib/perfect_shape/cubic_bezier_curve.rb +42 -37
- data/lib/perfect_shape/ellipse.rb +1 -2
- data/lib/perfect_shape/line.rb +37 -12
- data/lib/perfect_shape/multi_point.rb +20 -5
- data/lib/perfect_shape/path.rb +64 -15
- data/lib/perfect_shape/point.rb +23 -7
- data/lib/perfect_shape/polygon.rb +2 -2
- data/lib/perfect_shape/quadratic_bezier_curve.rb +14 -13
- data/lib/perfect_shape/rectangle.rb +42 -2
- data/lib/perfect_shape/shape.rb +6 -15
- data/perfect-shape.gemspec +5 -4
- metadata +11 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea11a066d120228aeb5889877909e17d93022e44e9c46899088bf97e52a328a6
|
4
|
+
data.tar.gz: 2d9f3031bd0edfa073d20c6c8f8d56541aa473ef92335f6606b88fbe0a9b39f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8025f51a536223a337d10b8a3b0bb9cd66aefe37543d24108aa002d719c4eb123a5b19d89b00157a85054bd614f92bdaf944562efbbecf14a93ab6d9c0a6481c
|
7
|
+
data.tar.gz: 06701161fbe42ec6afe1f19d9ece0db38d44db679702ea0db420fc260fa19526a2d2b4475c5aaa0d6f0500260ad48ad7267b4da1ec626153749d70175c50e523
|
data/CHANGELOG.md
CHANGED
@@ -1,11 +1,45 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.5.0
|
4
|
+
|
5
|
+
- `Line#intersect?(rectangle)`
|
6
|
+
- `Rectangle#out_state(x_or_point, y = nil)`
|
7
|
+
|
8
|
+
## 0.4.0
|
9
|
+
|
10
|
+
- `PerfectShape::AffineTransform#new`
|
11
|
+
- `PerfectShape::AffineTransform#==`
|
12
|
+
- `PerfectShape::AffineTransform#transform_point`
|
13
|
+
- `PerfectShape::AffineTransform#transform_points`
|
14
|
+
- `PerfectShape::AffineTransform#identity!` (alias: `reset!`)
|
15
|
+
- `PerfectShape::AffineTransform#invert!`
|
16
|
+
- `PerfectShape::AffineTransform#invertible?`
|
17
|
+
- `PerfectShape::AffineTransform#multiply!`
|
18
|
+
- `PerfectShape::AffineTransform#translate!`
|
19
|
+
- `PerfectShape::AffineTransform#scale!`
|
20
|
+
- `PerfectShape::AffineTransform#rotate!`
|
21
|
+
- `PerfectShape::AffineTransform#shear!` (alias: `skew!`)
|
22
|
+
- `PerfectShape::AffineTransform#clone`
|
23
|
+
- `PerfectShape::AffineTransform#inverse_transform_point`
|
24
|
+
- `PerfectShape::AffineTransform#inverse_transform_points`
|
25
|
+
|
26
|
+
## 0.3.5
|
27
|
+
|
28
|
+
- 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)`)
|
29
|
+
|
30
|
+
## 0.3.4
|
31
|
+
|
32
|
+
- 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)`)
|
33
|
+
- `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.
|
34
|
+
- `Shape#center_point` as `[center_x, center_y]`
|
35
|
+
- Rename `#point_segment_distance` to `#point_distance` everywhere
|
36
|
+
|
3
37
|
## 0.3.3
|
4
38
|
|
5
39
|
- 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
40
|
- `PerfectShape::QuadraticBezierCurve#curve_center_point`, `PerfectShape::QuadraticBezierCurve#curve_center_x`, `PerfectShape::QuadraticBezierCurve#curve_center_y`
|
7
41
|
- `PerfectShape::QuadraticBezierCurve#subdivisions(level=1)`
|
8
|
-
- `PerfectShape::QuadraticBezierCurve#
|
42
|
+
- `PerfectShape::QuadraticBezierCurve#point_distance(x_or_point, y = nil, minimum_distance_threshold: OUTLINE_MINIMUM_DISTANCE_THRESHOLD)`
|
9
43
|
- `PerfectShape::Polygon#edges` returns edges of polygon as `PerfectShape::Line` objects
|
10
44
|
- `PerfectShape::Rectangle#edges` returns edges of rectangle as `PerfectShape::Line` objects
|
11
45
|
- `PerfectShape::Square#edges` returns edges of square as `PerfectShape::Line` objects
|
@@ -16,7 +50,7 @@
|
|
16
50
|
- 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
51
|
- `PerfectShape::CubicBezierCurve#curve_center_point`, `PerfectShape::CubicBezierCurve#curve_center_x`, `PerfectShape::CubicBezierCurve#curve_center_y`
|
18
52
|
- `PerfectShape::CubicBezierCurve#subdivisions(level=1)`
|
19
|
-
- `PerfectShape::CubicBezierCurve#
|
53
|
+
- `PerfectShape::CubicBezierCurve#point_distance(x_or_point, y = nil, minimum_distance_threshold: OUTLINE_MINIMUM_DISTANCE_THRESHOLD)`
|
20
54
|
|
21
55
|
## 0.3.1
|
22
56
|
|
@@ -80,7 +114,7 @@
|
|
80
114
|
- `PerfectShape::Line`
|
81
115
|
- `PerfectShape::Line#contain?(x_or_point, y=nil)`
|
82
116
|
- `PerfectShape::Line#relative_counterclockwise`
|
83
|
-
- `PerfectShape::Line#
|
117
|
+
- `PerfectShape::Line#point_distance`
|
84
118
|
- Update `PerfectShape::Math::radians_to_degrees`, `PerfectShape::Math::degrees_to_radians`, and `PerfectShape::Math::normalize_degrees` to normalize numbers to `BigDecimal`
|
85
119
|
|
86
120
|
## 0.0.7
|
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
# Perfect Shape 0.
|
1
|
+
# Perfect Shape 0.5.0
|
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 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)).
|
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 viewport rectangle intersection or 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), potentially with [affine transforms](#perfectshapeaffinetransform) applied like translation, scale, rotation, shear/skew, and inversion (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.5.0
|
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.5.0'
|
24
24
|
```
|
25
25
|
|
26
26
|
And, run:
|
@@ -52,11 +52,11 @@ 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
59
|
- `#==(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
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`
|
@@ -84,9 +84,71 @@ Includes `PerfectShape::PointLocation`
|
|
84
84
|
- `#min_y`: min y
|
85
85
|
- `#max_x`: max x
|
86
86
|
- `#max_y`: max y
|
87
|
-
|
88
|
-
|
89
|
-
|
87
|
+
|
88
|
+
### `PerfectShape::AffineTransform`
|
89
|
+
|
90
|
+
Class
|
91
|
+
|
92
|
+
Affine transforms have the following matrix:
|
93
|
+
|
94
|
+
[ xxp xyp xt ]<br>
|
95
|
+
[ yxp yyp yt ]
|
96
|
+
|
97
|
+
The matrix is used to transform (x,y) point coordinates as follows:
|
98
|
+
|
99
|
+
[ xxp xyp xt ] * [x] = [ xxp * x + xyp * y + xt ]<br>
|
100
|
+
[ yxp yyp yt ] * [y] = [ yxp * x + yyp * y + yt ]
|
101
|
+
|
102
|
+
`xxp` is the x coordinate x product (`m11`)<br>
|
103
|
+
`xyp` is the x coordinate y product (`m12`)<br>
|
104
|
+
`yxp` is the y coordinate x product (`m21`)<br>
|
105
|
+
`yyp` is the y coordinate y product (`m22`)<br>
|
106
|
+
`xt` is the x coordinate translation (`m13`)<br>
|
107
|
+
`yt` is the y coordinate translation (`m23`)
|
108
|
+
|
109
|
+
Affine transform mutation operations ending with `!` can be chained as they all return `self`.
|
110
|
+
|
111
|
+
- `::new(xxp_element = nil, xyp_element = nil, yxp_element = nil, yyp_element = nil, xt_element = nil, yt_element = nil,
|
112
|
+
xxp: nil, xyp: nil, yxp: nil, yyp: nil, xt: nil, yt: nil,
|
113
|
+
m11: nil, m12: nil, m21: nil, m22: nil, m13: nil, m23: nil)`:
|
114
|
+
The constructor accepts either the (x,y)-operation related argument/kwarg names or traditional matrix element kwarg names. If no arguments are supplied, it constructs an identity matrix (i.e. like calling `::new(xxp: 1, xyp: 0, yxp: 0, yyp: 1, xt: 0, yt: 0)`).
|
115
|
+
- `#matrix_3d`: Returns Ruby `Matrix` object representing affine transform in 3D (used internally for performing multiplication)
|
116
|
+
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
117
|
+
- `#identity!` (alias: `reset!`): Resets to identity matrix (i.e. like calling `::new(xxp: 1, xyp: 0, yxp: 0, yyp: 1, xt: 0, yt: 0)`)
|
118
|
+
- `#invertible?` Returns `true` if matrix is invertible and `false` otherwise
|
119
|
+
- `#invert!`: Inverts affine transform matrix if invertible or raises an error otherwise
|
120
|
+
- `#multiply!(other)`: Multiplies affine transform with another affine transform, storing resulting changes in matrix elements
|
121
|
+
- `#translate!(x_or_point, y=nil)`: Translates affine transform with (x, y) translation values
|
122
|
+
- `#scale!(x_or_point, y=nil)`: Scales affine transform with (x, y) scale values
|
123
|
+
- `#rotate!(degrees)`: Rotates by angle degrees counter-clockwise if angle value is positive or clockwise if angle value is negative. Note that it returns very close approximate results for rotations that are 90/180/270 degrees (good enough for inverse-transform GUI point containment checks needed when checking if mouse-click-point is inside a transformed shape).
|
124
|
+
- `#shear!(x_or_point, y=nil)`: Shears by x and y factors
|
125
|
+
- `#clone`: Returns a new AffineTransform with the same matrix elements
|
126
|
+
- `#transform_point(x_or_point, y=nil)`: returns `[xxp * x + xyp * y + xt, yxp * x + yyp * y + yt]`. Note that result is a close approximation, but should be good enough for GUI mouse-click-point containment checks.
|
127
|
+
- `#transform_points(*xy_coordinates_or_points)`: returns `Array` of (x,y) pair `Array`s transformed with `#transform_point` method
|
128
|
+
- `#inverse_transform_point(x_or_point, y=nil)`: returns inverse transform of a point (x,y) coordinates (clones self and inverts clone, and then transforms point). Note that result is a close approximation, but should be good enough for GUI mouse-click-point containment checks.
|
129
|
+
- `#inverse_transform_points(*xy_coordinates_or_points)`: returns inverse transforms of a point `Array` of (x,y) coordinates
|
130
|
+
|
131
|
+
Example:
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
xxp = 2
|
135
|
+
xyp = 3
|
136
|
+
yxp = 4
|
137
|
+
yyp = 5
|
138
|
+
xt = 6
|
139
|
+
yt = 7
|
140
|
+
affine_transform1 = PerfectShape::AffineTransform.new(xxp: xxp, xyp: xyp, yxp: yxp, yyp: yyp, xt: xt, yt: yt) # (x,y)-operation kwarg names
|
141
|
+
affine_transform2 = PerfectShape::AffineTransform.new(m11: xxp, m12: xyp, m21: yxp, m22: yyp, m13: xt, m23: yt) # traditional matrix element kwarg names
|
142
|
+
affine_transform3 = PerfectShape::AffineTransform.new(xxp, xyp, yxp, yyp, xt, yt) # standard arguments
|
143
|
+
|
144
|
+
affine_transform2.matrix_3d == affine_transform1.matrix_3d # => true
|
145
|
+
affine_transform3.matrix_3d == affine_transform1.matrix_3d # => true
|
146
|
+
|
147
|
+
affine_transform = PerfectShape::AffineTransform.new.translate!(30, 20).scale!(2, 3)
|
148
|
+
|
149
|
+
affine_transform.transform_point(10, 10) # => approximately [50, 50]
|
150
|
+
affine_transform.inverse_transform_point(50, 50) # => approximately [10, 10]
|
151
|
+
```
|
90
152
|
|
91
153
|
### `PerfectShape::Point`
|
92
154
|
|
@@ -101,6 +163,7 @@ Includes `PerfectShape::PointLocation`
|
|
101
163
|
Points are simply represented by an `Array` of `[x,y]` coordinates when used within other shapes, but when needing point-specific operations like `point_distance`, the `PerfectShape::Point` class can come in handy.
|
102
164
|
|
103
165
|
- `::point_distance(x, y, px, py)`: Returns the distance from a point to another point
|
166
|
+
- `::normalize_point(x_or_point, y = nil)`: Normalizes point args whether two-number `point` `Array` or `x`, `y` args, returning normalized point `Array` of two `BigDecimal`'s
|
104
167
|
- `::new(x_or_point=nil, y_arg=nil, x: nil, y: nil)`: constructs a point with (x,y) pair (default: 0,0) whether specified as `Array` of (x,y) pair, flat `x,y` args, or `x:, y:` kwargs.
|
105
168
|
- `#min_x`: min x (always x)
|
106
169
|
- `#min_y`: min y (always y)
|
@@ -108,11 +171,12 @@ Points are simply represented by an `Array` of `[x,y]` coordinates when used wit
|
|
108
171
|
- `#max_y`: max y (always y)
|
109
172
|
- `#width`: width (always 0)
|
110
173
|
- `#height`: height (always 0)
|
174
|
+
- `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
|
111
175
|
- `#center_x`: center x (always x)
|
112
176
|
- `#center_y`: center y (always y)
|
113
177
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
114
178
|
- `#==(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.
|
179
|
+
- `#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
|
116
180
|
- `#point_distance(x_or_point, y=nil)`: Returns the distance from a point to another point
|
117
181
|
|
118
182
|
Example:
|
@@ -140,9 +204,9 @@ Includes `PerfectShape::MultiPoint`
|
|
140
204
|
|
141
205
|
![line](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/line.png)
|
142
206
|
|
143
|
-
- `::relative_counterclockwise(x1, y1, x2, y2, px, py)`: Returns an indicator of where the specified point (px,py) lies with respect to the line segment from (x1,y1) to (x2,y2). The return value can be either 1, -1, or 0 and indicates in which direction the specified line must pivot around its first end point, (x1,y1), in order to point at the specified point (px,py). A return value of 1 indicates that the line segment must turn in the direction that takes the positive X axis towards the negative Y axis. In the default coordinate system
|
144
|
-
- `::
|
145
|
-
- `::
|
207
|
+
- `::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, 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)”.
|
208
|
+
- `::point_distance_square(x1, y1, x2, y2, px, py)`: Returns the square of distance from a point to a line segment.
|
209
|
+
- `::point_distance(x1, y1, x2, y2, px, py)`: Returns the distance from a point to a line segment.
|
146
210
|
- `::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
211
|
- `#min_x`: min x
|
148
212
|
- `#min_y`: min y
|
@@ -150,13 +214,15 @@ Includes `PerfectShape::MultiPoint`
|
|
150
214
|
- `#max_y`: max y
|
151
215
|
- `#width`: width (from min x to max x)
|
152
216
|
- `#height`: height (from min y to max y)
|
217
|
+
- `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
|
153
218
|
- `#center_x`: center x
|
154
219
|
- `#center_y`: center y
|
155
220
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
156
221
|
- `#==(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.
|
158
|
-
- `#
|
159
|
-
- `#
|
222
|
+
- `#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
|
223
|
+
- `#intersect?(rectangle)`: Returns `true` if intersecting with interior of rectangle or `false` otherwise. This is useful for GUI optimization checks of whether a shape appears in a GUI viewport rectangle and needs redrawing
|
224
|
+
- `#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, 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)”.
|
225
|
+
- `#point_distance(x_or_point, y=nil)`: Returns the distance from a point to a line segment.
|
160
226
|
|
161
227
|
Example:
|
162
228
|
|
@@ -191,16 +257,17 @@ Includes `PerfectShape::MultiPoint`
|
|
191
257
|
- `#max_y`: max y
|
192
258
|
- `#width`: width (from min x to max x)
|
193
259
|
- `#height`: height (from min y to max y)
|
260
|
+
- `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
|
194
261
|
- `#center_x`: center x
|
195
262
|
- `#center_y`: center y
|
196
263
|
- `#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
264
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
198
265
|
- `#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`)
|
266
|
+
- `#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`)
|
267
|
+
- `#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`)
|
268
|
+
- `#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`)
|
202
269
|
- `#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
|
-
- `#
|
270
|
+
- `#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.
|
204
271
|
|
205
272
|
Example:
|
206
273
|
|
@@ -239,16 +306,17 @@ Includes `PerfectShape::MultiPoint`
|
|
239
306
|
- `#max_y`: max y
|
240
307
|
- `#width`: width (from min x to max x)
|
241
308
|
- `#height`: height (from min y to max y)
|
309
|
+
- `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
|
242
310
|
- `#center_x`: center x
|
243
311
|
- `#center_y`: center y
|
244
312
|
- `#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)
|
245
313
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
246
314
|
- `#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`)
|
315
|
+
- `#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`)
|
316
|
+
- `#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`)
|
317
|
+
- `#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`)
|
250
318
|
- `#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
|
-
- `#
|
319
|
+
- `#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.
|
252
320
|
|
253
321
|
Example:
|
254
322
|
|
@@ -284,6 +352,7 @@ Includes `PerfectShape::RectangularShape`
|
|
284
352
|
- `#y`: top-left y
|
285
353
|
- `#width`: width
|
286
354
|
- `#height`: height
|
355
|
+
- `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
|
287
356
|
- `#center_x`: center x
|
288
357
|
- `#center_y`: center y
|
289
358
|
- `#min_x`: min x
|
@@ -294,6 +363,7 @@ Includes `PerfectShape::RectangularShape`
|
|
294
363
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
295
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 a rectangle shape from its outline more successfully
|
296
365
|
- `#edges`: edges of rectangle as `PerfectShape::Line` objects
|
366
|
+
- `#out_state(x_or_point, y = nil)`: Returns "out state" of specified point (x,y) (whether it lies to the left, right, top, bottom of rectangle). If point is outside rectangle, it returns a bit mask combination of `Rectangle::OUT_LEFT`, `Rectangle::OUT_RIGHT`, `Rectangle::OUT_TOP`, or `Rectangle::OUT_BOTTOM`. Otherwise, it returns `0` if point is inside the rectangle.
|
297
367
|
|
298
368
|
Example:
|
299
369
|
|
@@ -328,6 +398,7 @@ Extends `PerfectShape::Rectangle`
|
|
328
398
|
- `#length`: length
|
329
399
|
- `#width`: width (equal to length)
|
330
400
|
- `#height`: height (equal to length)
|
401
|
+
- `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
|
331
402
|
- `#center_x`: center x
|
332
403
|
- `#center_y`: center y
|
333
404
|
- `#min_x`: min x
|
@@ -380,6 +451,7 @@ Open Arc | Chord Arc | Pie Arc
|
|
380
451
|
- `#height`: height
|
381
452
|
- `#start`: start angle in degrees
|
382
453
|
- `#extent`: extent angle in degrees
|
454
|
+
- `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
|
383
455
|
- `#center_x`: center x
|
384
456
|
- `#center_y`: center y
|
385
457
|
- `#radius_x`: radius along the x-axis
|
@@ -499,6 +571,7 @@ Extends `PerfectShape::Arc`
|
|
499
571
|
- `#y`: top-left y
|
500
572
|
- `#width`: width
|
501
573
|
- `#height`: height
|
574
|
+
- `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
|
502
575
|
- `#center_x`: center x
|
503
576
|
- `#center_y`: center y
|
504
577
|
- `#radius_x`: radius along the x-axis
|
@@ -558,6 +631,7 @@ Extends `PerfectShape::Ellipse`
|
|
558
631
|
- `#diameter`: diameter
|
559
632
|
- `#width`: width (equal to diameter)
|
560
633
|
- `#height`: height (equal to diameter)
|
634
|
+
- `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
|
561
635
|
- `#center_x`: center x
|
562
636
|
- `#center_y`: center y
|
563
637
|
- `#radius`: radius
|
@@ -623,6 +697,7 @@ A polygon can be thought of as a special case of [path](#perfectshapepath) that
|
|
623
697
|
- `#max_y`: max y
|
624
698
|
- `#width`: width (from min x to max x)
|
625
699
|
- `#height`: height (from min y to max y)
|
700
|
+
- `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
|
626
701
|
- `#center_x`: center x
|
627
702
|
- `#center_y`: center y
|
628
703
|
- `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
|
@@ -670,12 +745,14 @@ Includes `PerfectShape::MultiPoint`
|
|
670
745
|
- `#max_y`: max y
|
671
746
|
- `#width`: width (from min x to max x)
|
672
747
|
- `#height`: height (from min y to max y)
|
748
|
+
- `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
|
673
749
|
- `#center_x`: center x
|
674
750
|
- `#center_y`: center y
|
675
751
|
- `#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
752
|
- `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
|
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))
|
753
|
+
- `#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
|
678
754
|
- `#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)
|
755
|
+
- `#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
|
679
756
|
|
680
757
|
Example:
|
681
758
|
|
@@ -690,8 +767,16 @@ path_shapes << PerfectShape::CubicBezierCurve.new(points: [[370, 50], [430, 220]
|
|
690
767
|
|
691
768
|
shape = PerfectShape::Path.new(shapes: path_shapes, closed: false, winding_rule: :wind_even_odd)
|
692
769
|
|
693
|
-
shape.contain?(
|
694
|
-
shape.contain?([
|
770
|
+
shape.contain?(275, 165) # => true
|
771
|
+
shape.contain?([275, 165]) # => true
|
772
|
+
shape.contain?(275, 165, outline: true) # => false
|
773
|
+
shape.contain?([275, 165], outline: true) # => false
|
774
|
+
shape.contain?(shape.disconnected_shapes[1].curve_center_x, shape.disconnected_shapes[1].curve_center_y, outline: true) # => true
|
775
|
+
shape.contain?([shape.disconnected_shapes[1].curve_center_x, shape.disconnected_shapes[1].curve_center_y], outline: true) # => true
|
776
|
+
shape.contain?(shape.disconnected_shapes[1].curve_center_x + 1, shape.disconnected_shapes[1].curve_center_y, outline: true) # => false
|
777
|
+
shape.contain?([shape.disconnected_shapes[1].curve_center_x + 1, shape.disconnected_shapes[1].curve_center_y], outline: true) # => false
|
778
|
+
shape.contain?(shape.disconnected_shapes[1].curve_center_x + 1, shape.disconnected_shapes[1].curve_center_y, outline: true, distance_tolerance: 1) # => true
|
779
|
+
shape.contain?([shape.disconnected_shapes[1].curve_center_x + 1, shape.disconnected_shapes[1].curve_center_y], outline: true, distance_tolerance: 1) # => true
|
695
780
|
```
|
696
781
|
|
697
782
|
### `PerfectShape::CompositeShape`
|
@@ -712,11 +797,12 @@ A composite shape is simply an aggregate of multiple shapes (e.g. square and pol
|
|
712
797
|
- `#max_y`: max y
|
713
798
|
- `#width`: width (from min x to max x)
|
714
799
|
- `#height`: height (from min y to max y)
|
800
|
+
- `#center_point`: center point as `Array` of `[center_x, center_y]` coordinates
|
715
801
|
- `#center_x`: center x
|
716
802
|
- `#center_y`: center y
|
717
803
|
- `#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)
|
718
804
|
- `#==(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
|
805
|
+
- `#contain?(x_or_point, y=nil, outline: false, distance_tolerance: 0)`: 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
|
720
806
|
|
721
807
|
Example:
|
722
808
|
|
@@ -729,10 +815,27 @@ shapes << PerfectShape::Polygon.new(points: [[120, 215], [170, 165], [220, 215]]
|
|
729
815
|
|
730
816
|
shape = PerfectShape::CompositeShape.new(shapes: shapes)
|
731
817
|
|
732
|
-
shape.contain?(170, 265) # => true
|
733
|
-
shape.contain?([170, 265]) # => true
|
734
|
-
shape.contain?(170,
|
735
|
-
shape.contain?([170,
|
818
|
+
shape.contain?(170, 265) # => true inside square
|
819
|
+
shape.contain?([170, 265]) # => true inside square
|
820
|
+
shape.contain?(170, 265, outline: true) # => false
|
821
|
+
shape.contain?([170, 265], outline: true) # => false
|
822
|
+
shape.contain?(170, 315, outline: true) # => true
|
823
|
+
shape.contain?([170, 315], outline: true) # => true
|
824
|
+
shape.contain?(170, 316, outline: true) # => false
|
825
|
+
shape.contain?([170, 316], outline: true) # => false
|
826
|
+
shape.contain?(170, 316, outline: true, distance_tolerance: 1) # => true
|
827
|
+
shape.contain?([170, 316], outline: true, distance_tolerance: 1) # => true
|
828
|
+
|
829
|
+
shape.contain?(170, 190) # => true inside polygon
|
830
|
+
shape.contain?([170, 190]) # => true inside polygon
|
831
|
+
shape.contain?(170, 190, outline: true) # => false
|
832
|
+
shape.contain?([170, 190], outline: true) # => false
|
833
|
+
shape.contain?(145, 190, outline: true) # => true
|
834
|
+
shape.contain?([145, 190], outline: true) # => true
|
835
|
+
shape.contain?(145, 189, outline: true) # => false
|
836
|
+
shape.contain?([145, 189], outline: true) # => false
|
837
|
+
shape.contain?(145, 189, outline: true, distance_tolerance: 1) # => true
|
838
|
+
shape.contain?([145, 189], outline: true, distance_tolerance: 1) # => true
|
736
839
|
```
|
737
840
|
|
738
841
|
## Process
|
@@ -742,7 +845,10 @@ shape.contain?([170, 190]) # => true
|
|
742
845
|
## Resources
|
743
846
|
|
744
847
|
- Rubydoc: https://www.rubydoc.info/gems/perfect-shape
|
745
|
-
-
|
848
|
+
- Point in Polygon: https://en.wikipedia.org/wiki/Point_in_polygon
|
849
|
+
- Even-Odd Rule: https://en.wikipedia.org/wiki/Even%E2%80%93odd_rule
|
850
|
+
- Nonzero Rule: https://en.wikipedia.org/wiki/Nonzero-rule
|
851
|
+
- IEEE 754-1985 Remainder: https://en.wikipedia.org/wiki/IEEE_754-1985
|
746
852
|
|
747
853
|
## TODO
|
748
854
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
data/lib/perfect-shape.rb
CHANGED
@@ -23,8 +23,8 @@ $LOAD_PATH.unshift File.expand_path('.', __dir__)
|
|
23
23
|
|
24
24
|
require 'bigdecimal'
|
25
25
|
require 'equalizer'
|
26
|
+
require 'matrix'
|
26
27
|
|
27
|
-
# Perfect Shape algorithms are mostly ported from java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/package-summary.html
|
28
28
|
module PerfectShape
|
29
29
|
end
|
30
30
|
|