perfect-shape 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +30 -3
 - data/README.md +294 -14
 - data/VERSION +1 -1
 - data/lib/perfect_shape/composite_shape.rb +72 -0
 - data/lib/perfect_shape/cubic_bezier_curve.rb +120 -0
 - data/lib/perfect_shape/line.rb +4 -4
 - data/lib/perfect_shape/multi_point.rb +2 -2
 - data/lib/perfect_shape/path.rb +84 -63
 - data/lib/perfect_shape/point.rb +4 -4
 - data/lib/perfect_shape/polygon.rb +68 -62
 - data/lib/perfect_shape/quadratic_bezier_curve.rb +190 -0
 - data/lib/perfect_shape/rectangle.rb +10 -2
 - data/perfect-shape.gemspec +8 -5
 - metadata +10 -8
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 4e7efc4e2333e43b5e4d034e7fa3a3dddbdc9c6fd392e9d2ae1a409995122eb5
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 07f098098f6e42d6d71bdab33b44d3250c05c79ee48b2304b60784fba7de3f75
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: a69ec76140e777f303724c2db8d7dd904b2f679b11ce6380629c27ae0f3feb1cc0be0d615b79c085cd276e9a96b9a5842fbf8eb4d810398ae1329a3598b37592
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: d87a97b80e155939e380024653b01fe7a230660c0b58957d96f6d3d9392b113996f051bf55dbbc3b935c7d44a763cd686716dc9cb568d7ff85580aa1ca955c9d
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,9 +1,36 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Change Log
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            ## 0.3.0
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            - 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)`
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            ## 0.2.0
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            - `PerfectShape::CompositeShape`: aggregate of multiple shapes
         
     | 
| 
      
 13 
     | 
    
         
            +
            - `PerfectShape::CompositeShape#contain?(x_or_point, y=nil)`
         
     | 
| 
      
 14 
     | 
    
         
            +
            - `PerfectShape::CompositeShape#==`
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            ## 0.1.2
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            - `PerfectShape::CubicBezierCurve` (two end points and two control points)
         
     | 
| 
      
 19 
     | 
    
         
            +
            - `PerfectShape::CubicBezierCurve#contain?(x_or_point, y=nil)`
         
     | 
| 
      
 20 
     | 
    
         
            +
            - `PerfectShape::CubicBezierCurve#==`
         
     | 
| 
      
 21 
     | 
    
         
            +
            - `PerfectShape::Path` having cubic bezier curves in addition to points, lines, and quadratic bezier curves
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            ## 0.1.1
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            - `PerfectShape::QuadraticBezierCurve` (two end points and one control point)
         
     | 
| 
      
 26 
     | 
    
         
            +
            - `PerfectShape::QuadraticBezierCurve#contain?(x_or_point, y=nil)`
         
     | 
| 
      
 27 
     | 
    
         
            +
            - `PerfectShape::QuadraticBezierCurve#==`
         
     | 
| 
      
 28 
     | 
    
         
            +
            - `PerfectShape::Path` having quadratic bezier curves in addition to points and lines
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
       3 
30 
     | 
    
         
             
            ## 0.1.0
         
     | 
| 
       4 
31 
     | 
    
         | 
| 
       5 
32 
     | 
    
         
             
            - `PerfectShape::Path` (having points or lines)
         
     | 
| 
       6 
     | 
    
         
            -
            - `PerfectShape::Path#contain?(x_or_point, y=nil,  
     | 
| 
      
 33 
     | 
    
         
            +
            - `PerfectShape::Path#contain?(x_or_point, y=nil, distance_tolerance: 0)`
         
     | 
| 
       7 
34 
     | 
    
         
             
            - `PerfectShape::Path#point_crossings(x_or_point, y=nil)`
         
     | 
| 
       8 
35 
     | 
    
         
             
            - `PerfectShape::Path#==`
         
     | 
| 
       9 
36 
     | 
    
         | 
| 
         @@ -17,12 +44,12 @@ 
     | 
|
| 
       17 
44 
     | 
    
         | 
| 
       18 
45 
     | 
    
         
             
            - `PerfectShape::Point`
         
     | 
| 
       19 
46 
     | 
    
         
             
            - `PerfectShape::Point#point_distance`
         
     | 
| 
       20 
     | 
    
         
            -
            - `PerfectShape::Point#contain?(x_or_point, y=nil,  
     | 
| 
      
 47 
     | 
    
         
            +
            - `PerfectShape::Point#contain?(x_or_point, y=nil, distance_tolerance: 0)`
         
     | 
| 
       21 
48 
     | 
    
         
             
            - Refactor `PerfectShape::Point`,`PerfectShape::RectangularShape` to include shared `PerfectShape::PointLocation`
         
     | 
| 
       22 
49 
     | 
    
         | 
| 
       23 
50 
     | 
    
         
             
            ## 0.0.9
         
     | 
| 
       24 
51 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
            - `PerfectShape::Line#contain?(x_or_point, y=nil,  
     | 
| 
      
 52 
     | 
    
         
            +
            - `PerfectShape::Line#contain?(x_or_point, y=nil, distance_tolerance: 0)` (add a distance tolerance fuzz factor option)
         
     | 
| 
       26 
53 
     | 
    
         | 
| 
       27 
54 
     | 
    
         
             
            ## 0.0.8
         
     | 
| 
       28 
55 
     | 
    
         | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,9 +1,9 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # Perfect Shape 0. 
     | 
| 
      
 1 
     | 
    
         
            +
            # Perfect Shape 0.3.0
         
     | 
| 
       2 
2 
     | 
    
         
             
            ## Geometric Algorithms
         
     | 
| 
       3 
3 
     | 
    
         
             
            [](http://badge.fury.io/rb/perfect-shape)
         
     | 
| 
       4 
4 
     | 
    
         
             
            [](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),  
     | 
| 
      
 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)).
         
     | 
| 
       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.3.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.3.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`
         
     | 
| 
         @@ -110,10 +111,25 @@ Points are simply represented by an `Array` of `[x,y]` coordinates when used wit 
     | 
|
| 
       110 
111 
     | 
    
         
             
            - `#center_x`: center x (always x)
         
     | 
| 
       111 
112 
     | 
    
         
             
            - `#center_y`: center y (always y)
         
     | 
| 
       112 
113 
     | 
    
         
             
            - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
         
     | 
| 
       113 
     | 
    
         
            -
            - `#contain?(x_or_point, y=nil,  
     | 
| 
      
 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.
         
     | 
| 
       114 
115 
     | 
    
         
             
            - `#point_distance(x_or_point, y=nil)`: Returns the distance from a point to another point
         
     | 
| 
       115 
116 
     | 
    
         
             
            - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
         
     | 
| 
       116 
117 
     | 
    
         | 
| 
      
 118 
     | 
    
         
            +
            Example:
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 121 
     | 
    
         
            +
            require 'perfect-shape'
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 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_tolerance: 5) # => true
         
     | 
| 
      
 130 
     | 
    
         
            +
            shape.contain?([200, 151], distance_tolerance: 5) # => true
         
     | 
| 
      
 131 
     | 
    
         
            +
            ```
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
       117 
133 
     | 
    
         
             
            ### `PerfectShape::Line`
         
     | 
| 
       118 
134 
     | 
    
         | 
| 
       119 
135 
     | 
    
         
             
            Class
         
     | 
| 
         @@ -127,7 +143,7 @@ Includes `PerfectShape::MultiPoint` 
     | 
|
| 
       127 
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 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)”.
         
     | 
| 
       128 
144 
     | 
    
         
             
            - `::point_segment_distance_square(x1, y1, x2, y2, px, py)`: Returns the square of distance from a point to a line segment.
         
     | 
| 
       129 
145 
     | 
    
         
             
            - `::point_segment_distance(x1, y1, x2, y2, px, py)`: Returns the distance from a point to a line segment.
         
     | 
| 
       130 
     | 
    
         
            -
            - `::new(points:  
     | 
| 
      
 146 
     | 
    
         
            +
            - `::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
         
     | 
| 
       131 
147 
     | 
    
         
             
            - `#min_x`: min x
         
     | 
| 
       132 
148 
     | 
    
         
             
            - `#min_y`: min y
         
     | 
| 
       133 
149 
     | 
    
         
             
            - `#max_x`: max x
         
     | 
| 
         @@ -137,11 +153,96 @@ Includes `PerfectShape::MultiPoint` 
     | 
|
| 
       137 
153 
     | 
    
         
             
            - `#center_x`: center x
         
     | 
| 
       138 
154 
     | 
    
         
             
            - `#center_y`: center y
         
     | 
| 
       139 
155 
     | 
    
         
             
            - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
         
     | 
| 
       140 
     | 
    
         
            -
            - `#contain?(x_or_point, y=nil,  
     | 
| 
      
 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.
         
     | 
| 
       141 
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)”.
         
     | 
| 
       142 
158 
     | 
    
         
             
            - `#point_segment_distance(x_or_point, y=nil)`: Returns the distance from a point to a line segment.
         
     | 
| 
       143 
159 
     | 
    
         
             
            - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
         
     | 
| 
       144 
160 
     | 
    
         | 
| 
      
 161 
     | 
    
         
            +
            Example:
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 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_tolerance: 5) # => true
         
     | 
| 
      
 173 
     | 
    
         
            +
            shape.contain?([50, 51], distance_tolerance: 5) # => true
         
     | 
| 
      
 174 
     | 
    
         
            +
            ```
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
            ### `PerfectShape::QuadraticBezierCurve`
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
            Class
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
      
 180 
     | 
    
         
            +
            Extends `PerfectShape::Shape`
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
            Includes `PerfectShape::MultiPoint`
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
            
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
            - `::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
         
     | 
| 
      
 187 
     | 
    
         
            +
            - `#points`: points (start point, control point, and end point)
         
     | 
| 
      
 188 
     | 
    
         
            +
            - `#min_x`: min x
         
     | 
| 
      
 189 
     | 
    
         
            +
            - `#min_y`: min y
         
     | 
| 
      
 190 
     | 
    
         
            +
            - `#max_x`: max x
         
     | 
| 
      
 191 
     | 
    
         
            +
            - `#max_y`: max y
         
     | 
| 
      
 192 
     | 
    
         
            +
            - `#width`: width (from min x to max x)
         
     | 
| 
      
 193 
     | 
    
         
            +
            - `#height`: height (from min y to max y)
         
     | 
| 
      
 194 
     | 
    
         
            +
            - `#center_x`: center x
         
     | 
| 
      
 195 
     | 
    
         
            +
            - `#center_y`: center y
         
     | 
| 
      
 196 
     | 
    
         
            +
            - `#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 
     | 
    
         
            +
            - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
      
 200 
     | 
    
         
            +
            Example:
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 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
         
     | 
| 
      
 209 
     | 
    
         
            +
            ```
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
            ### `PerfectShape::CubicBezierCurve`
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
            Class
         
     | 
| 
      
 214 
     | 
    
         
            +
             
     | 
| 
      
 215 
     | 
    
         
            +
            Extends `PerfectShape::Shape`
         
     | 
| 
      
 216 
     | 
    
         
            +
             
     | 
| 
      
 217 
     | 
    
         
            +
            Includes `PerfectShape::MultiPoint`
         
     | 
| 
      
 218 
     | 
    
         
            +
             
     | 
| 
      
 219 
     | 
    
         
            +
            
         
     | 
| 
      
 220 
     | 
    
         
            +
             
     | 
| 
      
 221 
     | 
    
         
            +
            - `::new(points: [])`: constructs a cubic bézier curve with four `points` (start point, two control points, and end point) as `Array` of `Array`s of `[x,y]` pairs or flattened `Array` of alternating x and y coordinates
         
     | 
| 
      
 222 
     | 
    
         
            +
            - `#points`: points (start point, two control points, and end point)
         
     | 
| 
      
 223 
     | 
    
         
            +
            - `#min_x`: min x
         
     | 
| 
      
 224 
     | 
    
         
            +
            - `#min_y`: min y
         
     | 
| 
      
 225 
     | 
    
         
            +
            - `#max_x`: max x
         
     | 
| 
      
 226 
     | 
    
         
            +
            - `#max_y`: max y
         
     | 
| 
      
 227 
     | 
    
         
            +
            - `#width`: width (from min x to max x)
         
     | 
| 
      
 228 
     | 
    
         
            +
            - `#height`: height (from min y to max y)
         
     | 
| 
      
 229 
     | 
    
         
            +
            - `#center_x`: center x
         
     | 
| 
      
 230 
     | 
    
         
            +
            - `#center_y`: center y
         
     | 
| 
      
 231 
     | 
    
         
            +
            - `#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 
     | 
    
         
            +
            - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
         
     | 
| 
      
 234 
     | 
    
         
            +
             
     | 
| 
      
 235 
     | 
    
         
            +
            Example:
         
     | 
| 
      
 236 
     | 
    
         
            +
             
     | 
| 
      
 237 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 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
         
     | 
| 
      
 244 
     | 
    
         
            +
            ```
         
     | 
| 
      
 245 
     | 
    
         
            +
             
     | 
| 
       145 
246 
     | 
    
         
             
            ### `PerfectShape::Rectangle`
         
     | 
| 
       146 
247 
     | 
    
         | 
| 
       147 
248 
     | 
    
         
             
            Class
         
     | 
| 
         @@ -164,9 +265,28 @@ Includes `PerfectShape::RectangularShape` 
     | 
|
| 
       164 
265 
     | 
    
         
             
            - `#max_x`: max x
         
     | 
| 
       165 
266 
     | 
    
         
             
            - `#max_y`: max y
         
     | 
| 
       166 
267 
     | 
    
         
             
            - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
         
     | 
| 
       167 
     | 
    
         
            -
            - `#contain?(x_or_point, y=nil)`: checks if point is inside
         
     | 
| 
      
 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
         
     | 
| 
       168 
269 
     | 
    
         
             
            - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
         
     | 
| 
       169 
270 
     | 
    
         | 
| 
      
 271 
     | 
    
         
            +
            Example:
         
     | 
| 
      
 272 
     | 
    
         
            +
             
     | 
| 
      
 273 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 274 
     | 
    
         
            +
            require 'perfect-shape'
         
     | 
| 
      
 275 
     | 
    
         
            +
             
     | 
| 
      
 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
         
     | 
| 
      
 280 
     | 
    
         
            +
            shape.contain?(115, 80, outline: true) # => false
         
     | 
| 
      
 281 
     | 
    
         
            +
            shape.contain?([115, 80], outline: true) # => false
         
     | 
| 
      
 282 
     | 
    
         
            +
            shape.contain?(115, 30, outline: true) # => true
         
     | 
| 
      
 283 
     | 
    
         
            +
            shape.contain?([115, 30], outline: true) # => true
         
     | 
| 
      
 284 
     | 
    
         
            +
            shape.contain?(115, 31, outline: true) # => false
         
     | 
| 
      
 285 
     | 
    
         
            +
            shape.contain?([115, 31], outline: true) # => false
         
     | 
| 
      
 286 
     | 
    
         
            +
            shape.contain?(115, 31, outline: true, distance_tolerance: 1) # => true
         
     | 
| 
      
 287 
     | 
    
         
            +
            shape.contain?([115, 31], outline: true, distance_tolerance: 1) # => true
         
     | 
| 
      
 288 
     | 
    
         
            +
            ```
         
     | 
| 
      
 289 
     | 
    
         
            +
             
     | 
| 
       170 
290 
     | 
    
         
             
            ### `PerfectShape::Square`
         
     | 
| 
       171 
291 
     | 
    
         | 
| 
       172 
292 
     | 
    
         
             
            Class
         
     | 
| 
         @@ -188,9 +308,28 @@ Extends `PerfectShape::Rectangle` 
     | 
|
| 
       188 
308 
     | 
    
         
             
            - `#max_x`: max x
         
     | 
| 
       189 
309 
     | 
    
         
             
            - `#max_y`: max y
         
     | 
| 
       190 
310 
     | 
    
         
             
            - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
         
     | 
| 
       191 
     | 
    
         
            -
            - `#contain?(x_or_point, y=nil)`: checks if point is inside
         
     | 
| 
      
 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
         
     | 
| 
       192 
312 
     | 
    
         
             
            - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
         
     | 
| 
       193 
313 
     | 
    
         | 
| 
      
 314 
     | 
    
         
            +
            Example:
         
     | 
| 
      
 315 
     | 
    
         
            +
             
     | 
| 
      
 316 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 317 
     | 
    
         
            +
            require 'perfect-shape'
         
     | 
| 
      
 318 
     | 
    
         
            +
             
     | 
| 
      
 319 
     | 
    
         
            +
            shape = PerfectShape::Square.new(x: 15, y: 30, length: 200)
         
     | 
| 
      
 320 
     | 
    
         
            +
             
     | 
| 
      
 321 
     | 
    
         
            +
            shape.contain?(115, 130) # => true
         
     | 
| 
      
 322 
     | 
    
         
            +
            shape.contain?([115, 130]) # => true
         
     | 
| 
      
 323 
     | 
    
         
            +
            shape.contain?(115, 130, outline: true) # => false
         
     | 
| 
      
 324 
     | 
    
         
            +
            shape.contain?([115, 130], outline: true) # => false
         
     | 
| 
      
 325 
     | 
    
         
            +
            shape.contain?(115, 30, outline: true) # => true
         
     | 
| 
      
 326 
     | 
    
         
            +
            shape.contain?([115, 30], outline: true) # => true
         
     | 
| 
      
 327 
     | 
    
         
            +
            shape.contain?(115, 31, outline: true) # => false
         
     | 
| 
      
 328 
     | 
    
         
            +
            shape.contain?([115, 31], outline: true) # => false
         
     | 
| 
      
 329 
     | 
    
         
            +
            shape.contain?(115, 31, outline: true, distance_tolerance: 1) # => true
         
     | 
| 
      
 330 
     | 
    
         
            +
            shape.contain?([115, 31], outline: true, distance_tolerance: 1) # => true
         
     | 
| 
      
 331 
     | 
    
         
            +
            ```
         
     | 
| 
      
 332 
     | 
    
         
            +
             
     | 
| 
       194 
333 
     | 
    
         
             
            ### `PerfectShape::Arc`
         
     | 
| 
       195 
334 
     | 
    
         | 
| 
       196 
335 
     | 
    
         
             
            Class
         
     | 
| 
         @@ -225,6 +364,40 @@ Open Arc | Chord Arc | Pie Arc 
     | 
|
| 
       225 
364 
     | 
    
         
             
            - `#contain?(x_or_point, y=nil)`: checks if point is inside
         
     | 
| 
       226 
365 
     | 
    
         
             
            - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
         
     | 
| 
       227 
366 
     | 
    
         | 
| 
      
 367 
     | 
    
         
            +
            Example:
         
     | 
| 
      
 368 
     | 
    
         
            +
             
     | 
| 
      
 369 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 370 
     | 
    
         
            +
            require 'perfect-shape'
         
     | 
| 
      
 371 
     | 
    
         
            +
             
     | 
| 
      
 372 
     | 
    
         
            +
            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)
         
     | 
| 
      
 374 
     | 
    
         
            +
             
     | 
| 
      
 375 
     | 
    
         
            +
            shape.contain?(39.5, 33.0) # => true
         
     | 
| 
      
 376 
     | 
    
         
            +
            shape.contain?([39.5, 33.0]) # => true
         
     | 
| 
      
 377 
     | 
    
         
            +
            shape2.contain?(39.5, 33.0) # => true
         
     | 
| 
      
 378 
     | 
    
         
            +
            shape2.contain?([39.5, 33.0]) # => true
         
     | 
| 
      
 379 
     | 
    
         
            +
             
     | 
| 
      
 380 
     | 
    
         
            +
            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)
         
     | 
| 
      
 382 
     | 
    
         
            +
             
     | 
| 
      
 383 
     | 
    
         
            +
            shape3.contain?(39.5, 33.0) # => true
         
     | 
| 
      
 384 
     | 
    
         
            +
            shape3.contain?([39.5, 33.0]) # => true
         
     | 
| 
      
 385 
     | 
    
         
            +
            shape4.contain?(39.5, 33.0) # => true
         
     | 
| 
      
 386 
     | 
    
         
            +
            shape4.contain?([39.5, 33.0]) # => true
         
     | 
| 
      
 387 
     | 
    
         
            +
             
     | 
| 
      
 388 
     | 
    
         
            +
            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)
         
     | 
| 
      
 390 
     | 
    
         
            +
             
     | 
| 
      
 391 
     | 
    
         
            +
            shape5.contain?(39.5, 33.0) # => false
         
     | 
| 
      
 392 
     | 
    
         
            +
            shape5.contain?([39.5, 33.0]) # => false
         
     | 
| 
      
 393 
     | 
    
         
            +
            shape6.contain?(39.5, 33.0) # => false
         
     | 
| 
      
 394 
     | 
    
         
            +
            shape6.contain?([39.5, 33.0]) # => false
         
     | 
| 
      
 395 
     | 
    
         
            +
            shape5.contain?(9.5, 33.0) # => true
         
     | 
| 
      
 396 
     | 
    
         
            +
            shape5.contain?([9.5, 33.0]) # => true
         
     | 
| 
      
 397 
     | 
    
         
            +
            shape6.contain?(9.5, 33.0) # => true
         
     | 
| 
      
 398 
     | 
    
         
            +
            shape6.contain?([9.5, 33.0]) # => true
         
     | 
| 
      
 399 
     | 
    
         
            +
            ```
         
     | 
| 
      
 400 
     | 
    
         
            +
             
     | 
| 
       228 
401 
     | 
    
         
             
            ### `PerfectShape::Ellipse`
         
     | 
| 
       229 
402 
     | 
    
         | 
| 
       230 
403 
     | 
    
         
             
            Class
         
     | 
| 
         @@ -253,6 +426,20 @@ Extends `PerfectShape::Arc` 
     | 
|
| 
       253 
426 
     | 
    
         
             
            - `#contain?(x_or_point, y=nil)`: checks if point is inside
         
     | 
| 
       254 
427 
     | 
    
         
             
            - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
         
     | 
| 
       255 
428 
     | 
    
         | 
| 
      
 429 
     | 
    
         
            +
            Example:
         
     | 
| 
      
 430 
     | 
    
         
            +
             
     | 
| 
      
 431 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 432 
     | 
    
         
            +
            require 'perfect-shape'
         
     | 
| 
      
 433 
     | 
    
         
            +
             
     | 
| 
      
 434 
     | 
    
         
            +
            shape = PerfectShape::Ellipse.new(x: 2, y: 3, width: 50, height: 60)
         
     | 
| 
      
 435 
     | 
    
         
            +
            shape2 = PerfectShape::Ellipse.new(center_x: 27, center_y: 33, radius_x: 25, radius_y: 30)
         
     | 
| 
      
 436 
     | 
    
         
            +
             
     | 
| 
      
 437 
     | 
    
         
            +
            shape.contain?(27, 33) # => true
         
     | 
| 
      
 438 
     | 
    
         
            +
            shape.contain?([27, 33]) # => true
         
     | 
| 
      
 439 
     | 
    
         
            +
            shape2.contain?(27, 33) # => true
         
     | 
| 
      
 440 
     | 
    
         
            +
            shape2.contain?([27, 33]) # => true
         
     | 
| 
      
 441 
     | 
    
         
            +
            ```
         
     | 
| 
      
 442 
     | 
    
         
            +
             
     | 
| 
       256 
443 
     | 
    
         
             
            ### `PerfectShape::Circle`
         
     | 
| 
       257 
444 
     | 
    
         | 
| 
       258 
445 
     | 
    
         
             
            Class
         
     | 
| 
         @@ -283,6 +470,20 @@ Extends `PerfectShape::Ellipse` 
     | 
|
| 
       283 
470 
     | 
    
         
             
            - `#contain?(x_or_point, y=nil)`: checks if point is inside
         
     | 
| 
       284 
471 
     | 
    
         
             
            - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
         
     | 
| 
       285 
472 
     | 
    
         | 
| 
      
 473 
     | 
    
         
            +
            Example:
         
     | 
| 
      
 474 
     | 
    
         
            +
             
     | 
| 
      
 475 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 476 
     | 
    
         
            +
            require 'perfect-shape'
         
     | 
| 
      
 477 
     | 
    
         
            +
             
     | 
| 
      
 478 
     | 
    
         
            +
            shape = PerfectShape::Circle.new(x: 2, y: 3, diameter: 60)
         
     | 
| 
      
 479 
     | 
    
         
            +
            shape2 = PerfectShape::Circle.new(center_x: 2 + 30, center_y: 3 + 30, radius: 30)
         
     | 
| 
      
 480 
     | 
    
         
            +
             
     | 
| 
      
 481 
     | 
    
         
            +
            shape.contain?(32, 33) # => true
         
     | 
| 
      
 482 
     | 
    
         
            +
            shape.contain?([32, 33]) # => true
         
     | 
| 
      
 483 
     | 
    
         
            +
            shape2.contain?(32, 33) # => true
         
     | 
| 
      
 484 
     | 
    
         
            +
            shape2.contain?([32, 33]) # => true
         
     | 
| 
      
 485 
     | 
    
         
            +
            ```
         
     | 
| 
      
 486 
     | 
    
         
            +
             
     | 
| 
       286 
487 
     | 
    
         
             
            ### `PerfectShape::Polygon`
         
     | 
| 
       287 
488 
     | 
    
         | 
| 
       288 
489 
     | 
    
         
             
            Class
         
     | 
| 
         @@ -291,9 +492,11 @@ Extends `PerfectShape::Shape` 
     | 
|
| 
       291 
492 
     | 
    
         | 
| 
       292 
493 
     | 
    
         
             
            Includes `PerfectShape::MultiPoint`
         
     | 
| 
       293 
494 
     | 
    
         | 
| 
      
 495 
     | 
    
         
            +
            A polygon can be thought of as a special case of [path](#perfectshapepath) that is closed, has the [Even-Odd](https://en.wikipedia.org/wiki/Even%E2%80%93odd_rule) winding rule, and consists of lines only.
         
     | 
| 
      
 496 
     | 
    
         
            +
             
     | 
| 
       294 
497 
     | 
    
         
             
            
         
     | 
| 
       295 
498 
     | 
    
         | 
| 
       296 
     | 
    
         
            -
            - `::new(points:  
     | 
| 
      
 499 
     | 
    
         
            +
            - `::new(points: [])`: constructs a polygon with `points` as `Array` of `Array`s of `[x,y]` pairs or flattened `Array` of alternating x and y coordinates
         
     | 
| 
       297 
500 
     | 
    
         
             
            - `#min_x`: min x
         
     | 
| 
       298 
501 
     | 
    
         
             
            - `#min_y`: min y
         
     | 
| 
       299 
502 
     | 
    
         
             
            - `#max_x`: max x
         
     | 
| 
         @@ -303,9 +506,28 @@ Includes `PerfectShape::MultiPoint` 
     | 
|
| 
       303 
506 
     | 
    
         
             
            - `#center_x`: center x
         
     | 
| 
       304 
507 
     | 
    
         
             
            - `#center_y`: center y
         
     | 
| 
       305 
508 
     | 
    
         
             
            - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
         
     | 
| 
       306 
     | 
    
         
            -
            - `#contain?(x_or_point, y=nil)`: 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))
         
     | 
| 
      
 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
         
     | 
| 
       307 
510 
     | 
    
         
             
            - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
         
     | 
| 
       308 
511 
     | 
    
         | 
| 
      
 512 
     | 
    
         
            +
            Example:
         
     | 
| 
      
 513 
     | 
    
         
            +
             
     | 
| 
      
 514 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 515 
     | 
    
         
            +
            require 'perfect-shape'
         
     | 
| 
      
 516 
     | 
    
         
            +
             
     | 
| 
      
 517 
     | 
    
         
            +
            shape = PerfectShape::Polygon.new(points: [[200, 150], [270, 170], [250, 220], [220, 190], [200, 200], [180, 170]])
         
     | 
| 
      
 518 
     | 
    
         
            +
             
     | 
| 
      
 519 
     | 
    
         
            +
            shape.contain?(225, 185) # => true
         
     | 
| 
      
 520 
     | 
    
         
            +
            shape.contain?([225, 185]) # => true
         
     | 
| 
      
 521 
     | 
    
         
            +
            shape.contain?(225, 185, outline: true) # => false
         
     | 
| 
      
 522 
     | 
    
         
            +
            shape.contain?([225, 185], outline: true) # => false
         
     | 
| 
      
 523 
     | 
    
         
            +
            shape.contain?(200, 150, outline: true) # => true
         
     | 
| 
      
 524 
     | 
    
         
            +
            shape.contain?([200, 150], outline: true) # => true
         
     | 
| 
      
 525 
     | 
    
         
            +
            shape.contain?(200, 151, outline: true) # => false
         
     | 
| 
      
 526 
     | 
    
         
            +
            shape.contain?([200, 151], outline: true) # => false
         
     | 
| 
      
 527 
     | 
    
         
            +
            shape.contain?(200, 151, outline: true, distance_tolerance: 1) # => true
         
     | 
| 
      
 528 
     | 
    
         
            +
            shape.contain?([200, 151], outline: true, distance_tolerance: 1) # => true
         
     | 
| 
      
 529 
     | 
    
         
            +
            ```
         
     | 
| 
      
 530 
     | 
    
         
            +
             
     | 
| 
       309 
531 
     | 
    
         
             
            ### `PerfectShape::Path`
         
     | 
| 
       310 
532 
     | 
    
         | 
| 
       311 
533 
     | 
    
         
             
            Class
         
     | 
| 
         @@ -316,8 +538,8 @@ Includes `PerfectShape::MultiPoint` 
     | 
|
| 
       316 
538 
     | 
    
         | 
| 
       317 
539 
     | 
    
         
             
            
         
     | 
| 
       318 
540 
     | 
    
         | 
| 
       319 
     | 
    
         
            -
            - `::new(shapes:  
     | 
| 
       320 
     | 
    
         
            -
            - `#shapes`: the shapes that the path is composed of
         
     | 
| 
      
 541 
     | 
    
         
            +
            - `::new(shapes: [], closed: false, winding_rule: :wind_non_zero)`: constructs a path with `shapes` as `Array` of shape objects, which can be `PerfectShape::Point` (or `Array` of `[x, y]` coordinates), `PerfectShape::Line`, `PerfectShape::QuadraticBezierCurve`, or `PerfectShape::CubicBezierCurve`. If a path is closed, its last point is automatically connected to its first point with a line segment. The winding rule can be `:wind_non_zero` (default) or `:wind_even_odd`.
         
     | 
| 
      
 542 
     | 
    
         
            +
            - `#shapes`: the shapes that the path is composed of (must always start with `PerfectShape::Point` or Array of [x,y] coordinates representing start point)
         
     | 
| 
       321 
543 
     | 
    
         
             
            - `#closed?`: returns `true` if closed and `false` otherwise
         
     | 
| 
       322 
544 
     | 
    
         
             
            - `#winding_rule`: returns winding rule (`:wind_non_zero` or `:wind_even_odd`)
         
     | 
| 
       323 
545 
     | 
    
         
             
            - `#points`: path points calculated (derived) from shapes
         
     | 
| 
         @@ -329,11 +551,69 @@ Includes `PerfectShape::MultiPoint` 
     | 
|
| 
       329 
551 
     | 
    
         
             
            - `#height`: height (from min y to max y)
         
     | 
| 
       330 
552 
     | 
    
         
             
            - `#center_x`: center x
         
     | 
| 
       331 
553 
     | 
    
         
             
            - `#center_y`: center y
         
     | 
| 
       332 
     | 
    
         
            -
            - `#bounding_box`: bounding box is a rectangle with x = min x, y = min y, and width/height of shape
         
     | 
| 
      
 554 
     | 
    
         
            +
            - `#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)
         
     | 
| 
       333 
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))
         
     | 
| 
       334 
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)
         
     | 
| 
       335 
557 
     | 
    
         
             
            - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
         
     | 
| 
       336 
558 
     | 
    
         | 
| 
      
 559 
     | 
    
         
            +
            Example:
         
     | 
| 
      
 560 
     | 
    
         
            +
             
     | 
| 
      
 561 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 562 
     | 
    
         
            +
            require 'perfect-shape'
         
     | 
| 
      
 563 
     | 
    
         
            +
             
     | 
| 
      
 564 
     | 
    
         
            +
            path_shapes = []
         
     | 
| 
      
 565 
     | 
    
         
            +
            path_shapes << PerfectShape::Point.new(x: 200, y: 150)
         
     | 
| 
      
 566 
     | 
    
         
            +
            path_shapes << PerfectShape::Line.new(points: [250, 170]) # no need for start point, just end point
         
     | 
| 
      
 567 
     | 
    
         
            +
            path_shapes << PerfectShape::QuadraticBezierCurve.new(points: [[300, 185], [350, 150]]) # no need for start point, just control point and end point
         
     | 
| 
      
 568 
     | 
    
         
            +
            path_shapes << PerfectShape::CubicBezierCurve.new(points: [[370, 50], [430, 220], [480, 170]]) # no need for start point, just two control points and end point
         
     | 
| 
      
 569 
     | 
    
         
            +
             
     | 
| 
      
 570 
     | 
    
         
            +
            shape = PerfectShape::Path.new(shapes: path_shapes, closed: false, winding_rule: :wind_even_odd)
         
     | 
| 
      
 571 
     | 
    
         
            +
             
     | 
| 
      
 572 
     | 
    
         
            +
            shape.contain?(225, 160) # => true
         
     | 
| 
      
 573 
     | 
    
         
            +
            shape.contain?([225, 160]) # => true
         
     | 
| 
      
 574 
     | 
    
         
            +
            ```
         
     | 
| 
      
 575 
     | 
    
         
            +
             
     | 
| 
      
 576 
     | 
    
         
            +
            ### `PerfectShape::CompositeShape`
         
     | 
| 
      
 577 
     | 
    
         
            +
             
     | 
| 
      
 578 
     | 
    
         
            +
            Class
         
     | 
| 
      
 579 
     | 
    
         
            +
             
     | 
| 
      
 580 
     | 
    
         
            +
            Extends `PerfectShape::Shape`
         
     | 
| 
      
 581 
     | 
    
         
            +
             
     | 
| 
      
 582 
     | 
    
         
            +
            A composite shape is simply an aggregate of multiple shapes (e.g. square and polygon)
         
     | 
| 
      
 583 
     | 
    
         
            +
             
     | 
| 
      
 584 
     | 
    
         
            +
            
         
     | 
| 
      
 585 
     | 
    
         
            +
             
     | 
| 
      
 586 
     | 
    
         
            +
            - `::new(shapes: [])`: constructs a composite shape with `shapes` as `Array` of `PerfectShape::Shape` objects
         
     | 
| 
      
 587 
     | 
    
         
            +
            - `#shapes`: the shapes that the composite shape is composed of
         
     | 
| 
      
 588 
     | 
    
         
            +
            - `#min_x`: min x
         
     | 
| 
      
 589 
     | 
    
         
            +
            - `#min_y`: min y
         
     | 
| 
      
 590 
     | 
    
         
            +
            - `#max_x`: max x
         
     | 
| 
      
 591 
     | 
    
         
            +
            - `#max_y`: max y
         
     | 
| 
      
 592 
     | 
    
         
            +
            - `#width`: width (from min x to max x)
         
     | 
| 
      
 593 
     | 
    
         
            +
            - `#height`: height (from min y to max y)
         
     | 
| 
      
 594 
     | 
    
         
            +
            - `#center_x`: center x
         
     | 
| 
      
 595 
     | 
    
         
            +
            - `#center_y`: center y
         
     | 
| 
      
 596 
     | 
    
         
            +
            - `#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 
     | 
    
         
            +
            - `#==(other)`: Returns `true` if equal to `other` or `false` otherwise
         
     | 
| 
      
 599 
     | 
    
         
            +
             
     | 
| 
      
 600 
     | 
    
         
            +
            Example:
         
     | 
| 
      
 601 
     | 
    
         
            +
             
     | 
| 
      
 602 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 603 
     | 
    
         
            +
            require 'perfect-shape'
         
     | 
| 
      
 604 
     | 
    
         
            +
             
     | 
| 
      
 605 
     | 
    
         
            +
            shapes = []
         
     | 
| 
      
 606 
     | 
    
         
            +
            shapes << PerfectShape::Square.new(x: 120, y: 215, length: 100)
         
     | 
| 
      
 607 
     | 
    
         
            +
            shapes << PerfectShape::Polygon.new(points: [[120, 215], [170, 165], [220, 215]])
         
     | 
| 
      
 608 
     | 
    
         
            +
             
     | 
| 
      
 609 
     | 
    
         
            +
            shape = PerfectShape::CompositeShape.new(shapes: shapes)
         
     | 
| 
      
 610 
     | 
    
         
            +
             
     | 
| 
      
 611 
     | 
    
         
            +
            shape.contain?(170, 265) # => true
         
     | 
| 
      
 612 
     | 
    
         
            +
            shape.contain?([170, 265]) # => true
         
     | 
| 
      
 613 
     | 
    
         
            +
            shape.contain?(170, 190) # => true
         
     | 
| 
      
 614 
     | 
    
         
            +
            shape.contain?([170, 190]) # => true
         
     | 
| 
      
 615 
     | 
    
         
            +
            ```
         
     | 
| 
      
 616 
     | 
    
         
            +
             
     | 
| 
       337 
617 
     | 
    
         
             
            ## Process
         
     | 
| 
       338 
618 
     | 
    
         | 
| 
       339 
619 
     | 
    
         
             
            [Glimmer Process](https://github.com/AndyObtiva/glimmer/blob/master/PROCESS.md)
         
     | 
    
        data/VERSION
    CHANGED
    
    | 
         @@ -1 +1 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            0. 
     | 
| 
      
 1 
     | 
    
         
            +
            0.3.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
         
     |