perfect-shape 0.5.4 → 0.5.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: 98226e1aac4fbac06b162630c2dbaa5ca2f749d24590cc0bd0cca9924f760635
4
- data.tar.gz: 3d7d5be2905838db9ff1b3f2876e8437e0df90d9ac6b2316c1347aa4f58db626
3
+ metadata.gz: f32696a2e0ecd7c1aceb16d9211b38e977b4dd15c7ba57acb8ac99a769d75036
4
+ data.tar.gz: 50c458cfcf134ce86bc11e0b22f1650b2faf2146c79c288d7b2501a2d7d9264c
5
5
  SHA512:
6
- metadata.gz: 686a5054b13bd3adf1176e779e80d2ef758c4cf9cc25625633151628a724c1b802c03847301a447fb14155b570ee0fe0fdf8fe4d357b92186bdd7df83d6de240
7
- data.tar.gz: e05c4a2eb8d2efa7feef615dc5dd3d522778d8aa98b60845726de0d3a0e68bf84ffa173984d0a907401838a16432b725ed1e0ac5ac1d4239ed66596181dac324
6
+ metadata.gz: 1e15378328df2eaad3769ce087118f7990fcb834963cbc56b738ea1cc292a92df0f86d77506e1a2f94985bc03211b5436b9965e8b6b5c97c80f9d7ad59f6cb20
7
+ data.tar.gz: cb64f0b3951ca4628f96e265070a88684060be6ffa38b5a45deea611bce0b2d20b72e43ef6c5d70b969925a31fe0017f4ec84407927f23a1b7824e0352bc9732
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.5.5
4
+
5
+ - `PerfectShape::Arc#intersect?(rectangle)`
6
+ - `PerfectShape::Ellipse#intersect?(rectangle)`
7
+ - `PerfectShape::Circle#intersect?(rectangle)`
8
+
3
9
  ## 0.5.4
4
10
 
5
11
  - `PerfectShape::Rectangle#intersect?(rectangle)`
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Perfect Shape 0.5.4
1
+ # Perfect Shape 0.5.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.5.4
17
+ gem install perfect-shape -v 0.5.5
18
18
  ```
19
19
 
20
20
  Or include in Bundler `Gemfile`:
21
21
 
22
22
  ```ruby
23
- gem 'perfect-shape', '~> 0.5.4'
23
+ gem 'perfect-shape', '~> 0.5.5'
24
24
  ```
25
25
 
26
26
  And, run:
@@ -253,7 +253,7 @@ Includes `PerfectShape::MultiPoint`
253
253
 
254
254
  - `::tag(coord, low, high)`: Determine where coord lies with respect to the range from low to high. It is assumed that low < high. The return value is one of the 5 values BELOW, LOWEDGE, INSIDE, HIGHEDGE, or ABOVE.
255
255
  - `::eqn(val, c1, cp, c2)`: Fill an array with the coefficients of the parametric equation in t, ready for solving against val with solve_quadratic. We currently have: val = Py(t) = C1*(1-t)^2 + 2*CP*t*(1-t) + C2*t^2 = C1 - 2*C1*t + C1*t^2 + 2*CP*t - 2*CP*t^2 + C2*t^2 = C1 + (2*CP - 2*C1)*t + (C1 - 2*CP + C2)*t^2; 0 = (C1 - val) + (2*CP - 2*C1)*t + (C1 - 2*CP + C2)*t^2; 0 = C + Bt + At^2; C = C1 - val; B = 2*CP - 2*C1; A = C1 - 2*CP + C2
256
- - `::solve_quadratic(eqn)`: Solves the quadratic whose coefficients are in the eqn array and places the non-complex roots into the res array, returning the number of roots. The quadratic solved is represented by the equation: <pre>eqn = {C, B, A}; ax^2 + bx + c = 0</pre> A return value of {@code -1} is used to distinguish a constant equation, which might be always 0 or never 0, from an equation that has no zeroes.
256
+ - `::solve_quadratic(eqn)`: Solves the quadratic whose coefficients are in the eqn array and places the non-complex roots into the res array, returning the number of roots. The quadratic solved is represented by the equation: <pre>eqn = {C, B, A}; ax^2 + bx + c = 0</pre> A return value of `-1` is used to distinguish a constant equation, which might be always 0 or never 0, from an equation that has no zeroes.
257
257
  - `::eval_quadratic(vals, num, include0, include1, inflect, c1, ctrl, c2)`: Evaluate the t values in the first num slots of the vals[] array and place the evaluated values back into the same array. Only evaluate t values that are within the range <, >, including the 0 and 1 ends of the range iff the include0 or include1 booleans are true. If an "inflection" equation is handed in, then any points which represent a point of inflection for that quadratic equation are also ignored.
258
258
  - `::new(points: [])`: constructs a quadratic bézier curve with three `points` (start point, control point, and end point) as `Array` of `Array`s of `[x,y]` pairs or flattened `Array` of alternating x and y coordinates
259
259
  - `#points`: points (start point, control point, and end point)
@@ -477,6 +477,8 @@ Open Arc | Chord Arc | Pie Arc
477
477
  - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
478
478
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
479
479
  - `#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
480
+ - `#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
481
+ - `#contain_angle?(angle)`: returns `true` if the angle is within the angular extents of the arc and `false` otherwise
480
482
 
481
483
  Example:
482
484
 
@@ -600,6 +602,7 @@ Extends `PerfectShape::Arc`
600
602
  - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
601
603
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
602
604
  - `#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
605
+ - `#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
603
606
 
604
607
  Example:
605
608
 
@@ -661,6 +664,7 @@ Extends `PerfectShape::Ellipse`
661
664
  - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
662
665
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
663
666
  - `#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
667
+ - `#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
664
668
 
665
669
  Example:
666
670
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.4
1
+ 0.5.5
@@ -141,8 +141,8 @@ module PerfectShape
141
141
  # @param x The X coordinate of the point to test.
142
142
  # @param y The Y coordinate of the point to test.
143
143
  #
144
- # @return {@code true} if the point lies within the bound of
145
- # the arc, {@code false} if the point lies outside of the
144
+ # @return true if the point lies within the bound of
145
+ # the arc, false if the point lies outside of the
146
146
  # arc's bounds.
147
147
  def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
148
148
  x, y = Point.normalize_point(x_or_point, y)
@@ -212,5 +212,105 @@ module PerfectShape
212
212
 
213
213
  (angle >= 0.0) && (angle < ang_ext)
214
214
  end
215
+
216
+ def intersect?(rectangle)
217
+ x = rectangle.x
218
+ y = rectangle.y
219
+ w = rectangle.width
220
+ h = rectangle.height
221
+ aw = self.width
222
+ ah = self.height
223
+
224
+ return false if w <= 0 || h <= 0 || aw <= 0 || ah <= 0
225
+ ext = self.extent
226
+ return false if ext == 0
227
+
228
+ ax = self.x
229
+ ay = self.y
230
+ axw = ax + aw
231
+ ayh = ay + ah
232
+ xw = x + w
233
+ yh = y + h
234
+
235
+ # check bbox
236
+ return false if x >= axw || y >= ayh || xw <= ax || yh <= ay
237
+
238
+ # extract necessary data
239
+ axc = self.center_x
240
+ ayc = self.center_y
241
+ sx, sy = self.start_point
242
+ ex, ey = self.end_point
243
+
244
+ # Try to catch rectangles that intersect arc in areas
245
+ # outside of rectagle with left top corner coordinates
246
+ # (min(center x, start point x, end point x),
247
+ # min(center y, start point y, end point y))
248
+ # and rigth bottom corner coordinates
249
+ # (max(center x, start point x, end point x),
250
+ # max(center y, start point y, end point y)).
251
+ # So we'll check axis segments outside of rectangle above.
252
+ if ayc >= y && ayc <= yh # 0 and 180
253
+ return true if (sx < xw && ex < xw && axc < xw &&
254
+ axw > x && contain_angle?(0)) ||
255
+ (sx > x && ex > x && axc > x &&
256
+ ax < xw && contain_angle?(180))
257
+ end
258
+ if axc >= x && axc <= xw # 90 and 270
259
+ return true if (sy > y && ey > y && ayc > y &&
260
+ ay < yh && contain_angle?(90)) ||
261
+ (sy < yh && ey < yh && ayc < yh &&
262
+ ayh > y && contain_angle?(270))
263
+ end
264
+
265
+ # For PIE we should check intersection with pie slices
266
+ # also we should do the same for arcs with extent is greater
267
+ # than 180, because we should cover case of rectangle, which
268
+ # situated between center of arc and chord, but does not
269
+ # intersect the chord.
270
+ rect = PerfectShape::Rectangle.new(x: x, y: y, width: w, height: h)
271
+ if type == :pie || ext.abs > 180
272
+ # for PIE: try to find intersections with pie slices
273
+ line1 = PerfectShape::Line.new(points: [[axc, ayc], [sx, sy]])
274
+ line2 = PerfectShape::Line.new(points: [[axc, ayc], [ex, ey]])
275
+ return true if line1.intersect?(rect) || line2.intersect?(rect)
276
+ else
277
+ # for CHORD and OPEN: try to find intersections with chord
278
+ line = PerfectShape::Line.new(points: [[sx, sy], [ex, ey]])
279
+ return true if line.intersect?(rect)
280
+ end
281
+
282
+ # finally check the rectangle corners inside the arc
283
+ return true if contain?(x, y) || contain?(x + w, y) ||
284
+ contain?(x, y + h) || contain?(x + w, y + h)
285
+
286
+ false
287
+ end
288
+
289
+ # Returns the starting point of the arc. This point is the
290
+ # intersection of the ray from the center defined by the
291
+ # starting angle and the elliptical boundary of the arc.
292
+ #
293
+ # @return An (x,y) pair Array object representing the
294
+ # x,y coordinates of the starting point of the arc.
295
+ def start_point
296
+ angle = Math.degrees_to_radians(-self.start)
297
+ x = self.x + (Math.cos(angle) * 0.5 + 0.5) * self.width
298
+ y = self.y + (Math.sin(angle) * 0.5 + 0.5) * self.height
299
+ [x, y]
300
+ end
301
+
302
+ # Returns the ending point of the arc. This point is the
303
+ # intersection of the ray from the center defined by the
304
+ # starting angle plus the angular extent of the arc and the
305
+ # elliptical boundary of the arc.
306
+ #
307
+ # @return An (x,y) pair Array object representing the
308
+ # x,y coordinates of the ending point of the arc.
309
+ def end_point
310
+ angle = Math.degrees_to_radians(-self.start - self.extent)
311
+ x = self.x + (Math.cos(angle) * 0.5 + 0.5) * self.width
312
+ y = self.y + (Math.sin(angle) * 0.5 + 0.5) * self.height
313
+ [x, y]
314
+ end
215
315
  end
216
316
  end
@@ -82,8 +82,8 @@ module PerfectShape
82
82
  # @param x The X coordinate of the point to test.
83
83
  # @param y The Y coordinate of the point to test.
84
84
  #
85
- # @return {@code true} if the point lies within the bound of
86
- # the cubic bézier curve, {@code false} if the point lies outside of the
85
+ # @return true if the point lies within the bound of
86
+ # the cubic bézier curve, false if the point lies outside of the
87
87
  # cubic bézier curve's bounds.
88
88
  def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
89
89
  x, y = Point.normalize_point(x_or_point, y)
@@ -59,8 +59,8 @@ module PerfectShape
59
59
  # @param x The X coordinate of the point to test.
60
60
  # @param y The Y coordinate of the point to test.
61
61
  #
62
- # @return {@code true} if the point lies within the bound of
63
- # the ellipse, {@code false} if the point lies outside of the
62
+ # @return true if the point lies within the bound of
63
+ # the ellipse, false if the point lies outside of the
64
64
  # ellipse's bounds.
65
65
  def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
66
66
  # This is implemented again even though super would have just worked to have an optimized algorithm for Ellipse.
@@ -209,8 +209,8 @@ module PerfectShape
209
209
  # @param y The Y coordinate of the point to test.
210
210
  # @param distance_tolerance The distance from line to tolerate (0 by default)
211
211
  #
212
- # @return {@code true} if the point lies within the bound of
213
- # the line, {@code false} if the point lies outside of the
212
+ # @return true if the point lies within the bound of
213
+ # the line, false if the point lies outside of the
214
214
  # line's bounds.
215
215
  def contain?(x_or_point, y = nil, outline: true, distance_tolerance: 0)
216
216
  x, y = Point.normalize_point(x_or_point, y)
@@ -80,8 +80,8 @@ module PerfectShape
80
80
  # @param y The Y coordinate of the point to test.
81
81
  # @param distance_tolerance The distance from point to tolerate (0 by default)
82
82
  #
83
- # @return {@code true} if the point is close enough within distance tolerance,
84
- # {@code false} if the point is too far.
83
+ # @return true if the point is close enough within distance tolerance,
84
+ # false if the point is too far.
85
85
  def contain?(x_or_point, y = nil, outline: true, distance_tolerance: 0)
86
86
  x, y = Point.normalize_point(x_or_point, y)
87
87
  return unless x && y
@@ -34,8 +34,8 @@ module PerfectShape
34
34
  # @param x The X coordinate of the point to test.
35
35
  # @param y The Y coordinate of the point to test.
36
36
  #
37
- # @return {@code true} if the point lies within the bound of
38
- # the polygon, {@code false} if the point lies outside of the
37
+ # @return true if the point lies within the bound of
38
+ # the polygon, false if the point lies outside of the
39
39
  # polygon's bounds.
40
40
  def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
41
41
  x, y = Point.normalize_point(x_or_point, y)
@@ -94,22 +94,22 @@ module PerfectShape
94
94
  ]
95
95
  end
96
96
 
97
- # Solves the quadratic whose coefficients are in the {@code eqn}
98
- # array and places the non-complex roots into the {@code res}
97
+ # Solves the quadratic whose coefficients are in the eqn
98
+ # array and places the non-complex roots into the res
99
99
  # array, returning the number of roots.
100
100
  # The quadratic solved is represented by the equation:
101
101
  # <pre>
102
102
  # eqn = {C, B, A}
103
103
  # ax^2 + bx + c = 0
104
104
  # </pre>
105
- # A return value of {@code -1} is used to distinguish a constant
105
+ # A return value of -1 is used to distinguish a constant
106
106
  # equation, which might be always 0 or never 0, from an equation that
107
107
  # has no zeroes.
108
108
  # @param eqn the specified array of coefficients to use to solve
109
109
  # the quadratic equation
110
110
  # @param res the array that contains the non-complex roots
111
111
  # resulting from the solution of the quadratic equation
112
- # @return the number of roots, or {@code -1} if the equation is
112
+ # @return the number of roots, or -1 if the equation is
113
113
  # a constant.
114
114
  def solve_quadratic(eqn, res)
115
115
  a = eqn[2]
@@ -187,8 +187,8 @@ module PerfectShape
187
187
  # @param x The X coordinate of the point to test.
188
188
  # @param y The Y coordinate of the point to test.
189
189
  #
190
- # @return {@code true} if the point lies within the bound of
191
- # the quadratic bézier curve, {@code false} if the point lies outside of the
190
+ # @return true if the point lies within the bound of
191
+ # the quadratic bézier curve, false if the point lies outside of the
192
192
  # quadratic bézier curve's bounds.
193
193
  def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
194
194
  x, y = Point.normalize_point(x_or_point, y)
@@ -48,8 +48,8 @@ module PerfectShape
48
48
  # @param x The X coordinate of the point to test.
49
49
  # @param y The Y coordinate of the point to test.
50
50
  #
51
- # @return {@code true} if the point lies within the bound of
52
- # the rectangle, {@code false} if the point lies outside of the
51
+ # @return true if the point lies within the bound of
52
+ # the rectangle, false if the point lies outside of the
53
53
  # rectangle's bounds.
54
54
  def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
55
55
  x, y = Point.normalize_point(x_or_point, y)
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: perfect-shape 0.5.4 ruby lib
5
+ # stub: perfect-shape 0.5.5 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "perfect-shape".freeze
9
- s.version = "0.5.4"
9
+ s.version = "0.5.5"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Andy Maleh".freeze]
14
- s.date = "2022-01-21"
14
+ s.date = "2022-01-22"
15
15
  s.description = "Perfect Shape is a collection of pure Ruby geometric algorithms that are mostly useful for GUI manipulation like checking viewport rectangle intersection or containment of a mouse click point in popular geometry shapes such as rectangle, square, arc (open, chord, and pie), ellipse, circle, polygon, and paths containing lines, quadratic b\u00E9zier curves, and cubic bezier curves, potentially with affine transforms applied like translation, scale, rotation, shear/skew, and inversion (including both Ray Casting Algorithm, aka Even-odd Rule, and Winding Number Algorithm, aka Nonzero Rule). Additionally, it contains some purely mathematical algorithms like IEEEremainder (also known as IEEE-754 remainder).".freeze
16
16
  s.email = "andy.am@gmail.com".freeze
17
17
  s.extra_rdoc_files = [
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: perfect-shape
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.4
4
+ version: 0.5.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-21 00:00:00.000000000 Z
11
+ date: 2022-01-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: equalizer