perfect-shape 0.1.2 → 0.2.0

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: cfa0c67a9d8a4b23b53bdd7895121fa51ee3da17e1d22fa9bb56c0890a47844b
4
- data.tar.gz: 81cd3fe5766f023fc250951cab6541142d80ff70b8af3d8cbbfcefcb56154cba
3
+ metadata.gz: 78cfa9540ff6b960663bc61054940f3699a2fc12d4a9c45a84811eeea2db8872
4
+ data.tar.gz: e8a7d1536b02fe49e6e32ef64baced2ca74f612a944ef1fcc452441b9e351ca2
5
5
  SHA512:
6
- metadata.gz: ea4539cd6122363483988ef9de8b712ebd981c2e7102d5510e94f9d093bad1c02d17cd0e1c4eebe5ebc3e4211a67d22ca7ee2630cdf561404a9adc36031de220
7
- data.tar.gz: 4a9fcfa3accf7ca519c403e24076bfc76b1bd9e2b47fda886e0012af610d3e142257d09c6508a04deccf665b536c41b60cc68047ba2c76f06a667be2625b0c40
6
+ metadata.gz: 26ec5bc87ec612ea07a1ec6c38277d92ed4e2ff08f08d7e091d4254bdfc70b7da0704fc5a93f68063a14faa67fee32c57bac54cc0496df34f098aac8d85b4770
7
+ data.tar.gz: 9334103aa1ea12b3f6d2d5f611bdcb36a1f532061e1e22b3dd4783498527665a9851c6294778720d21fa221113526bdfbbe0e8d6208462568680a17df026b2b5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.2.0
4
+
5
+ - `PerfectShape::CompositeShape`: aggregate of multiple shapes
6
+ - `PerfectShape::CompositeShape#contain?(x_or_point, y=nil)`
7
+ - `PerfectShape::CompositeShape#==`
8
+
3
9
  ## 0.1.2
4
10
 
5
11
  - `PerfectShape::CubicBezierCurve` (two end points and two control points)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Perfect Shape 0.1.2
1
+ # Perfect Shape 0.2.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)
@@ -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.1.2
17
+ gem install perfect-shape -v 0.2.0
18
18
  ```
19
19
 
20
20
  Or include in Bundler `Gemfile`:
21
21
 
22
22
  ```ruby
23
- gem 'perfect-shape', '~> 0.1.2'
23
+ gem 'perfect-shape', '~> 0.2.0'
24
24
  ```
25
25
 
26
26
  And, run:
@@ -56,6 +56,7 @@ This is a base class for all shapes. It is not meant to be used directly. Subcla
56
56
  - `#center_y`: center y
57
57
  - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height just as those of shape
58
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
59
60
  - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
60
61
 
61
62
  ### `PerfectShape::PointLocation`
@@ -117,7 +118,16 @@ Points are simply represented by an `Array` of `[x,y]` coordinates when used wit
117
118
  Example:
118
119
 
119
120
  ```ruby
121
+ require 'perfect-shape'
122
+
120
123
  shape = PerfectShape::Point.new(x: 200, y: 150)
124
+
125
+ shape.contain?(200, 150) # => true
126
+ shape.contain?([200, 150]) # => true
127
+ shape.contain?(200, 151) # => false
128
+ shape.contain?([200, 151]) # => false
129
+ shape.contain?(200, 151, distance: 5) # => false
130
+ shape.contain?([200, 151], distance: 5) # => false
121
131
  ```
122
132
 
123
133
  ### `PerfectShape::Line`
@@ -151,7 +161,16 @@ Includes `PerfectShape::MultiPoint`
151
161
  Example:
152
162
 
153
163
  ```ruby
154
- shape = PerfectShape::Line.new(points: [[200, 150], [270, 220]]) # start point and end point
164
+ require 'perfect-shape'
165
+
166
+ shape = PerfectShape::Line.new(points: [[0, 0], [100, 100]]) # start point and end point
167
+
168
+ shape.contain?(50, 50) # => true
169
+ shape.contain?([50, 50]) # => true
170
+ shape.contain?(50, 51) # => false
171
+ shape.contain?([50, 51]) # => false
172
+ shape.contain?(50, 51, distance: 5) # => true
173
+ shape.contain?([50, 51], distance: 5) # => true
155
174
  ```
156
175
 
157
176
  ### `PerfectShape::QuadraticBezierCurve`
@@ -181,7 +200,12 @@ Includes `PerfectShape::MultiPoint`
181
200
  Example:
182
201
 
183
202
  ```ruby
184
- shape = PerfectShape::QuadraticBezierCurve.new(points: [[200, 150], [270, 220], [180, 170]]) # start point, control point, and end point
203
+ require 'perfect-shape'
204
+
205
+ shape = PerfectShape::QuadraticBezierCurve.new(points: [[200, 150], [270, 320], [380, 150]]) # start point, control point, and end point
206
+
207
+ shape.contain?(270, 220) # => true
208
+ shape.contain?([270, 220]) # => true
185
209
  ```
186
210
 
187
211
  ### `PerfectShape::CubicBezierCurve`
@@ -211,7 +235,12 @@ Includes `PerfectShape::MultiPoint`
211
235
  Example:
212
236
 
213
237
  ```ruby
214
- shape = PerfectShape::CubicBezierCurve.new(points: [[200, 150], [230, 50], [270, 220], [180, 170]]) # start point, two control points, and end point
238
+ require 'perfect-shape'
239
+
240
+ shape = PerfectShape::CubicBezierCurve.new(points: [[200, 150], [235, 235], [270, 320], [380, 150]]) # start point, two control points, and end point
241
+
242
+ shape.contain?(270, 220) # => true
243
+ shape.contain?([270, 220]) # => true
215
244
  ```
216
245
 
217
246
  ### `PerfectShape::Rectangle`
@@ -242,7 +271,12 @@ Includes `PerfectShape::RectangularShape`
242
271
  Example:
243
272
 
244
273
  ```ruby
274
+ require 'perfect-shape'
275
+
245
276
  shape = PerfectShape::Rectangle.new(x: 15, y: 30, width: 200, height: 100)
277
+
278
+ shape.contain?(115, 80) # => true
279
+ shape.contain?([115, 80]) # => true
246
280
  ```
247
281
 
248
282
  ### `PerfectShape::Square`
@@ -272,7 +306,12 @@ Extends `PerfectShape::Rectangle`
272
306
  Example:
273
307
 
274
308
  ```ruby
309
+ require 'perfect-shape'
310
+
275
311
  shape = PerfectShape::Square.new(x: 15, y: 30, length: 200)
312
+
313
+ shape.contain?(115, 130) # => true
314
+ shape.contain?([115, 130]) # => true
276
315
  ```
277
316
 
278
317
  ### `PerfectShape::Arc`
@@ -312,8 +351,35 @@ Open Arc | Chord Arc | Pie Arc
312
351
  Example:
313
352
 
314
353
  ```ruby
315
- shape = PerfectShape::Arc.new(type: :chord, x: 2, y: 3, width: 50, height: 60, start: 30, extent: 90)
316
- shape2 = PerfectShape::Arc.new(type: :chord, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start: 30, extent: 90)
354
+ require 'perfect-shape'
355
+
356
+ shape = PerfectShape::Arc.new(type: :open, x: 2, y: 3, width: 50, height: 60, start: 45, extent: 270)
357
+ shape2 = PerfectShape::Arc.new(type: :open, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start: 30, extent: 90)
358
+
359
+ shape.contain?(39.5, 33.0) # => true
360
+ shape.contain?([39.5, 33.0]) # => true
361
+ shape2.contain?(39.5, 33.0) # => true
362
+ shape2.contain?([39.5, 33.0]) # => true
363
+
364
+ shape3 = PerfectShape::Arc.new(type: :chord, x: 2, y: 3, width: 50, height: 60, start: 45, extent: 270)
365
+ shape4 = PerfectShape::Arc.new(type: :chord, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start: 30, extent: 90)
366
+
367
+ shape3.contain?(39.5, 33.0) # => true
368
+ shape3.contain?([39.5, 33.0]) # => true
369
+ shape4.contain?(39.5, 33.0) # => true
370
+ shape4.contain?([39.5, 33.0]) # => true
371
+
372
+ shape5 = PerfectShape::Arc.new(type: :pie, x: 2, y: 3, width: 50, height: 60, start: 45, extent: 270)
373
+ shape6 = PerfectShape::Arc.new(type: :pie, center_x: 2 + 25, center_y: 3 + 30, radius_x: 25, radius_y: 30, start: 30, extent: 90)
374
+
375
+ shape5.contain?(39.5, 33.0) # => false
376
+ shape5.contain?([39.5, 33.0]) # => false
377
+ shape6.contain?(39.5, 33.0) # => false
378
+ shape6.contain?([39.5, 33.0]) # => false
379
+ shape5.contain?(9.5, 33.0) # => true
380
+ shape5.contain?([9.5, 33.0]) # => true
381
+ shape6.contain?(9.5, 33.0) # => true
382
+ shape6.contain?([9.5, 33.0]) # => true
317
383
  ```
318
384
 
319
385
  ### `PerfectShape::Ellipse`
@@ -347,8 +413,15 @@ Extends `PerfectShape::Arc`
347
413
  Example:
348
414
 
349
415
  ```ruby
416
+ require 'perfect-shape'
417
+
350
418
  shape = PerfectShape::Ellipse.new(x: 2, y: 3, width: 50, height: 60)
351
419
  shape2 = PerfectShape::Ellipse.new(center_x: 27, center_y: 33, radius_x: 25, radius_y: 30)
420
+
421
+ shape.contain?(27, 33) # => true
422
+ shape.contain?([27, 33]) # => true
423
+ shape2.contain?(27, 33) # => true
424
+ shape2.contain?([27, 33]) # => true
352
425
  ```
353
426
 
354
427
  ### `PerfectShape::Circle`
@@ -384,8 +457,15 @@ Extends `PerfectShape::Ellipse`
384
457
  Example:
385
458
 
386
459
  ```ruby
460
+ require 'perfect-shape'
461
+
387
462
  shape = PerfectShape::Circle.new(x: 2, y: 3, diameter: 60)
388
463
  shape2 = PerfectShape::Circle.new(center_x: 2 + 30, center_y: 3 + 30, radius: 30)
464
+
465
+ shape.contain?(32, 33) # => true
466
+ shape.contain?([32, 33]) # => true
467
+ shape2.contain?(32, 33) # => true
468
+ shape2.contain?([32, 33]) # => true
389
469
  ```
390
470
 
391
471
  ### `PerfectShape::Polygon`
@@ -416,7 +496,12 @@ A polygon can be thought of as a special case of [path](#perfectshapepath) that
416
496
  Example:
417
497
 
418
498
  ```ruby
499
+ require 'perfect-shape'
500
+
419
501
  shape = PerfectShape::Polygon.new(points: [[200, 150], [270, 170], [250, 220], [220, 190], [200, 200], [180, 170]])
502
+
503
+ shape.contain?(225, 185) # => true
504
+ shape.contain?([225, 185]) # => true
420
505
  ```
421
506
 
422
507
  ### `PerfectShape::Path`
@@ -450,6 +535,8 @@ Includes `PerfectShape::MultiPoint`
450
535
  Example:
451
536
 
452
537
  ```ruby
538
+ require 'perfect-shape'
539
+
453
540
  path_shapes = []
454
541
  path_shapes << PerfectShape::Point.new(x: 200, y: 150)
455
542
  path_shapes << PerfectShape::Line.new(points: [250, 170]) # no need for start point, just end point
@@ -457,6 +544,48 @@ path_shapes << PerfectShape::QuadraticBezierCurve.new(points: [[300, 185], [350,
457
544
  path_shapes << PerfectShape::CubicBezierCurve.new(points: [[370, 50], [430, 220], [480, 170]]) # no need for start point, just two control points and end point
458
545
 
459
546
  shape = PerfectShape::Path.new(shapes: path_shapes, closed: false, winding_rule: :wind_even_odd)
547
+
548
+ shape.contain?(225, 160) # => true
549
+ shape.contain?([225, 160]) # => true
550
+ ```
551
+
552
+ ### `PerfectShape::CompositeShape`
553
+
554
+ Class
555
+
556
+ Extends `PerfectShape::Shape`
557
+
558
+ ![composite shape](https://raw.githubusercontent.com/AndyObtiva/perfect-shape/master/images/composite-shape.png)
559
+
560
+ - `::new(shapes: [])`: constructs a composite shape with `shapes` as `Array` of `PerfectShape::Shape` objects
561
+ - `#shapes`: the shapes that the path is composed of
562
+ - `#min_x`: min x
563
+ - `#min_y`: min y
564
+ - `#max_x`: max x
565
+ - `#max_y`: max y
566
+ - `#width`: width (from min x to max x)
567
+ - `#height`: height (from min y to max y)
568
+ - `#center_x`: center x
569
+ - `#center_y`: center y
570
+ - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape (bounding box only guarantees that the shape is within it, but it might be bigger than the shape)
571
+ - `#contain?(x_or_point, y=nil)`: checks if point is inside any of the shapes owned by the composite shape
572
+ - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
573
+
574
+ Example:
575
+
576
+ ```ruby
577
+ require 'perfect-shape'
578
+
579
+ shapes = []
580
+ shapes << PerfectShape::Square.new(x: 120, y: 215, length: 100)
581
+ shapes << PerfectShape::Polygon.new(points: [[120, 215], [170, 165], [220, 215]])
582
+
583
+ shape = PerfectShape::CompositeShape.new(shapes: shapes)
584
+
585
+ shape.contain?(170, 265) # => true
586
+ shape.contain?([170, 265]) # => true
587
+ shape.contain?(170, 190) # => true
588
+ shape.contain?([170, 190]) # => true
460
589
  ```
461
590
 
462
591
  ## Process
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.2
1
+ 0.2.0
@@ -0,0 +1,72 @@
1
+ # Copyright (c) 2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'perfect_shape/shape'
23
+ require 'perfect_shape/point'
24
+ require 'perfect_shape/line'
25
+ require 'perfect_shape/quadratic_bezier_curve'
26
+ require 'perfect_shape/cubic_bezier_curve'
27
+ require 'perfect_shape/multi_point'
28
+
29
+ module PerfectShape
30
+ # A composite of multiple shapes
31
+ class CompositeShape < Shape
32
+ include Equalizer.new(:shapes)
33
+
34
+ attr_accessor :shapes
35
+
36
+ # Constructs from multiple shapes
37
+ def initialize(shapes: [])
38
+ self.shapes = shapes
39
+ end
40
+
41
+ def min_x
42
+ shapes.map(&:min_x).min
43
+ end
44
+
45
+ def min_y
46
+ shapes.map(&:min_y).min
47
+ end
48
+
49
+ def max_x
50
+ shapes.map(&:max_x).max
51
+ end
52
+
53
+ def max_y
54
+ shapes.map(&:max_y).max
55
+ end
56
+
57
+ # Checks if composite shape contains point (two-number Array or x, y args)
58
+ # by comparing against all shapes it consists of
59
+ #
60
+ # @param x The X coordinate of the point to test.
61
+ # @param y The Y coordinate of the point to test.
62
+ #
63
+ # @return true if the point lies within the bound of
64
+ # the composite shape or false if the point lies outside of the
65
+ # path's bounds.
66
+ def contain?(x_or_point, y = nil)
67
+ x, y = normalize_point(x_or_point, y)
68
+ return unless x && y
69
+ shapes.any? {|shape| shape.contain?(x, y) }
70
+ end
71
+ end
72
+ end
@@ -43,8 +43,9 @@ module PerfectShape
43
43
  alias closed? closed
44
44
 
45
45
  # Constructs Path with winding rule, closed status, and shapes (must always start with PerfectShape::Point or Array of [x,y] coordinates)
46
- # Shape class types can be any of SHAPE_TYPES: Array (x,y coordinates), PerfectShape::Point, or PerfectShape::Line
46
+ # Shape class types can be any of SHAPE_TYPES: Array (x,y coordinates), PerfectShape::Point, PerfectShape::Line, PerfectShape::QuadraticBezierCurve, or PerfectShape::CubicBezierCurve
47
47
  # winding_rule can be any of WINDING_RULES: :wind_non_zero (default) or :wind_even_odd
48
+ # closed can be true or false
48
49
  def initialize(shapes: [], closed: false, winding_rule: :wind_non_zero)
49
50
  self.closed = closed
50
51
  self.winding_rule = winding_rule
@@ -110,8 +111,8 @@ module PerfectShape
110
111
  # @param x The X coordinate of the point to test.
111
112
  # @param y The Y coordinate of the point to test.
112
113
  #
113
- # @return {@code true} if the point lies within the bound of
114
- # the path, {@code false} if the point lies outside of the
114
+ # @return true if the point lies within the bound of
115
+ # the path or false if the point lies outside of the
115
116
  # path's bounds.
116
117
  def contain?(x_or_point, y = nil)
117
118
  x, y = 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.1.2 ruby lib
5
+ # stub: perfect-shape 0.2.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "perfect-shape".freeze
9
- s.version = "0.1.2"
9
+ s.version = "0.2.0"
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 = "2021-12-22"
14
+ s.date = "2022-01-07"
15
15
  s.description = "Perfect Shape is a collection of pure Ruby geometric algorithms that are mostly useful for GUI manipulation like checking 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 b\u00E9zier curves (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 = [
@@ -27,6 +27,7 @@ Gem::Specification.new do |s|
27
27
  "lib/perfect-shape.rb",
28
28
  "lib/perfect_shape/arc.rb",
29
29
  "lib/perfect_shape/circle.rb",
30
+ "lib/perfect_shape/composite_shape.rb",
30
31
  "lib/perfect_shape/cubic_bezier_curve.rb",
31
32
  "lib/perfect_shape/ellipse.rb",
32
33
  "lib/perfect_shape/line.rb",
@@ -45,7 +46,7 @@ Gem::Specification.new do |s|
45
46
  ]
46
47
  s.homepage = "http://github.com/AndyObtiva/perfect-shape".freeze
47
48
  s.licenses = ["MIT".freeze]
48
- s.rubygems_version = "3.2.31".freeze
49
+ s.rubygems_version = "3.3.1".freeze
49
50
  s.summary = "Perfect Shape - Geometric Algorithms".freeze
50
51
 
51
52
  if s.respond_to? :specification_version then
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.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-22 00:00:00.000000000 Z
11
+ date: 2022-01-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: equalizer
@@ -116,6 +116,7 @@ files:
116
116
  - lib/perfect-shape.rb
117
117
  - lib/perfect_shape/arc.rb
118
118
  - lib/perfect_shape/circle.rb
119
+ - lib/perfect_shape/composite_shape.rb
119
120
  - lib/perfect_shape/cubic_bezier_curve.rb
120
121
  - lib/perfect_shape/ellipse.rb
121
122
  - lib/perfect_shape/line.rb
@@ -150,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
150
151
  - !ruby/object:Gem::Version
151
152
  version: '0'
152
153
  requirements: []
153
- rubygems_version: 3.2.31
154
+ rubygems_version: 3.3.1
154
155
  signing_key:
155
156
  specification_version: 4
156
157
  summary: Perfect Shape - Geometric Algorithms