perfect-shape 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 00c1add66582b68b1c6ec2d15b91c9af7840daf97b7e40662210683690d1a082
4
+ data.tar.gz: 2b789617e71a321cd7b736abfcfd59d7c46dbbf91fa0b4e2e5f581704e28527a
5
+ SHA512:
6
+ metadata.gz: 6c7b3efef675a42f6d33aae8d2d208e7224c44f9d8e64064dc9f18b0a3594b4cf2b3addb360376e5837f9478c5e61b18d85b989a16c122ed1d762f88c849075a
7
+ data.tar.gz: 6b87d053a77eab1900c67ffb1a5800a878f9f06de7e9713d72f9ecedf5fc6e463e2cb885d1f6e1cfd7eca9eeb08b1321e6e97009564d6c4082a7a542db2d8ea2
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ perfect-shape
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-3.0.2
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Change Log
2
+
3
+ ## 0.0.1
4
+
5
+ - `PerfectShape::Math`
6
+ - `PerfectShape::Math::ieee_remainder` (alias: `ieee754_remainder`)
7
+ - `PerfectShape::Math::degrees_to_radians(angle)`
8
+ - `PerfectShape::Math::radians_to_degrees(angle)`
9
+ - `PerfectShape::Line`
10
+ - `PerfectShape::Line::relative_ccw(x1, y1, x2, y2, px, py)`
11
+ - `PerfectShape::Arc`
12
+ - `PerfectShape::Arc#contain?(x_or_point, y=nil)`
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "https://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "rdoc", "~> 3.12"
10
+ gem "juwelier", "~> 2.1.0"
11
+ gem 'minitest', '~> 5.14.4'
12
+ gem 'puts_debuggerer', '~> 0.13.1'
13
+ gem 'rake-tui', '> 0'
14
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,102 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.8.0)
5
+ public_suffix (>= 2.0.2, < 5.0)
6
+ awesome_print (1.9.2)
7
+ builder (3.2.4)
8
+ descendants_tracker (0.0.4)
9
+ thread_safe (~> 0.3, >= 0.3.1)
10
+ faraday (1.8.0)
11
+ faraday-em_http (~> 1.0)
12
+ faraday-em_synchrony (~> 1.0)
13
+ faraday-excon (~> 1.1)
14
+ faraday-httpclient (~> 1.0.1)
15
+ faraday-net_http (~> 1.0)
16
+ faraday-net_http_persistent (~> 1.1)
17
+ faraday-patron (~> 1.0)
18
+ faraday-rack (~> 1.0)
19
+ multipart-post (>= 1.2, < 3)
20
+ ruby2_keywords (>= 0.0.4)
21
+ faraday-em_http (1.0.0)
22
+ faraday-em_synchrony (1.0.0)
23
+ faraday-excon (1.1.0)
24
+ faraday-httpclient (1.0.1)
25
+ faraday-net_http (1.0.1)
26
+ faraday-net_http_persistent (1.2.0)
27
+ faraday-patron (1.0.0)
28
+ faraday-rack (1.0.0)
29
+ git (1.9.1)
30
+ rchardet (~> 1.8)
31
+ github_api (0.19.0)
32
+ addressable (~> 2.4)
33
+ descendants_tracker (~> 0.0.4)
34
+ faraday (>= 0.8, < 2)
35
+ hashie (~> 3.5, >= 3.5.2)
36
+ oauth2 (~> 1.0)
37
+ hashie (3.6.0)
38
+ highline (2.0.3)
39
+ json (1.8.6)
40
+ juwelier (2.1.3)
41
+ builder
42
+ bundler (>= 1.13)
43
+ git (>= 1.2.5)
44
+ github_api
45
+ highline (>= 1.6.15)
46
+ nokogiri (>= 1.5.10)
47
+ rake
48
+ rdoc
49
+ semver
50
+ jwt (2.3.0)
51
+ minitest (5.14.4)
52
+ multi_json (1.15.0)
53
+ multi_xml (0.6.0)
54
+ multipart-post (2.1.1)
55
+ nokogiri (1.12.5-x86_64-darwin)
56
+ racc (~> 1.4)
57
+ oauth2 (1.4.7)
58
+ faraday (>= 0.8, < 2.0)
59
+ jwt (>= 1.0, < 3.0)
60
+ multi_json (~> 1.3)
61
+ multi_xml (~> 0.5)
62
+ rack (>= 1.2, < 3)
63
+ pastel (0.8.0)
64
+ tty-color (~> 0.5)
65
+ public_suffix (4.0.6)
66
+ puts_debuggerer (0.13.1)
67
+ awesome_print (~> 1.9.2)
68
+ racc (1.6.0)
69
+ rack (2.2.3)
70
+ rake (13.0.6)
71
+ rake-tui (0.2.3)
72
+ tty-prompt
73
+ rchardet (1.8.0)
74
+ rdoc (3.12.2)
75
+ json (~> 1.4)
76
+ ruby2_keywords (0.0.5)
77
+ semver (1.0.1)
78
+ thread_safe (0.3.6)
79
+ tty-color (0.6.0)
80
+ tty-cursor (0.7.1)
81
+ tty-prompt (0.23.1)
82
+ pastel (~> 0.8)
83
+ tty-reader (~> 0.8)
84
+ tty-reader (0.9.0)
85
+ tty-cursor (~> 0.7)
86
+ tty-screen (~> 0.8)
87
+ wisper (~> 2.0)
88
+ tty-screen (0.8.1)
89
+ wisper (2.0.1)
90
+
91
+ PLATFORMS
92
+ x86_64-darwin-19
93
+
94
+ DEPENDENCIES
95
+ juwelier (~> 2.1.0)
96
+ minitest (~> 5.14.4)
97
+ puts_debuggerer (~> 0.13.1)
98
+ rake-tui (> 0)
99
+ rdoc (~> 3.12)
100
+
101
+ BUNDLED WITH
102
+ 2.2.31
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
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.
data/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # Perfect Shape 0.0.1
2
+ ## Geometric Algorithms
3
+
4
+ `PerfectShape` is a collection of pure Ruby geometric algorithms that are mostly useful for GUI (Graphical User Interface) manipulation like checking containment of a point in popular geometric shapes such as rectangle, square, arc, circle, polygon, polyline, and paths containing lines, bezier curves, and quadratic curves.
5
+
6
+ Additionally, `PerfectShape::Math` contains some purely mathematical algorithms.
7
+
8
+ To ensure high accuracy, this library does all its mathematical operations with `BigDecimal` numbers.
9
+
10
+ ## Setup
11
+
12
+ Run:
13
+
14
+ ```
15
+ gem install perfect-shape -v 0.0.1
16
+ ```
17
+
18
+ Or include in Bundler `Gemfile`:
19
+
20
+ ```ruby
21
+ gem 'perfect-shape', '~> 0.0.1'
22
+ ```
23
+
24
+ And, run:
25
+
26
+ ```
27
+ bundle
28
+ ```
29
+
30
+ ## API
31
+
32
+ ### `PerfectShape::Math`
33
+
34
+ - `::degrees_to_radians(angle)`
35
+ - `::radians_to_degrees(angle)`
36
+ - `::ieee_remainder(x, y)` (alias: `ieee754_remainder`)
37
+
38
+ ### `PerfectShape::Line`
39
+
40
+ - `::relative_ccw(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
41
+
42
+ ### `PerfectShape::Arc`
43
+
44
+ - `::new(type: :open, x: 0, y: 0, width: 1, height: 1, start: 0, extent: 360)`: constructs an arc of type `:open` (default), `:chord`, or `:pie`
45
+ - `#x`: top-left x of arc
46
+ - `#y`: top-left y of arc
47
+ - `#width`: width of arc
48
+ - `#height`: height of arc
49
+ - `#start`: start angle in degrees
50
+ - `#extent`: extent angle in degrees
51
+ - `#contain?(x_or_point, y=0)`: checks if point is in arc
52
+
53
+ ## TODO
54
+
55
+ [TODO.md](TODO.md)
56
+
57
+ ## Change Log
58
+
59
+ [CHANGELOG.md](CHANGELOG.md)
60
+
61
+ ## Contributing to perfect-shape
62
+
63
+ - Check out the latest master to make sure the feature hasn't been
64
+ implemented or the bug hasn't been fixed yet.
65
+ - Check out the issue tracker to make sure someone already hasn't
66
+ requested it and/or contributed it.
67
+ - Fork the project.
68
+ - Start a feature/bugfix branch.
69
+ - Commit and push until you are happy with your contribution.
70
+ - Make sure to add tests for it. This is important so I don't break it
71
+ in a future version unintentionally.
72
+ - Please try not to mess with the Rakefile, version, or history. If
73
+ you want to have your own version, or is otherwise necessary, that
74
+ is fine, but please isolate to its own commit so I can cherry-pick
75
+ around it.
76
+
77
+ ## Copyright
78
+
79
+ [MIT](LICENSE.txt)
80
+
81
+ Copyright (c) 2021 Andy Maleh. See
82
+ [LICENSE.txt](LICENSE.txt) for further details.
data/Rakefile ADDED
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+ require 'juwelier'
14
+ Juwelier::Tasks.new do |gem|
15
+ # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
16
+ gem.name = "perfect-shape"
17
+ gem.homepage = "http://github.com/AndyObtiva/perfect-shape"
18
+ gem.license = "MIT"
19
+ gem.summary = %Q{Perfect Shape - Geometric Algorithms}
20
+ gem.description = %Q{Perfect Shape is a collection of pure Ruby geometric algorithms that are mostly useful for GUI manipulation like checking containment of a point in popular geometric shapes such as rectangle, square, arc, circle, polygon, polyline, and paths containing lines, bezier curves, and quadratic curves. Additionally, it contains some purely mathematical algorithms like IEEEremainder (also known as IEEE-754 remainder).}
21
+ gem.email = "andy.am@gmail.com"
22
+ gem.authors = ["Andy Maleh"]
23
+
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Juwelier::RubygemsDotOrgTasks.new
27
+
28
+ require "rake/testtask"
29
+
30
+ Rake::TestTask.new(:test) do |t|
31
+ t.libs << "test"
32
+ t.libs << "lib"
33
+ t.test_files = FileList["test/**/test_*.rb"]
34
+ end
35
+
36
+ task :default => :test
37
+
38
+ require 'rdoc/task'
39
+ Rake::RDocTask.new do |rdoc|
40
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
41
+
42
+ rdoc.rdoc_dir = 'rdoc'
43
+ rdoc.title = "perfect-shape #{version}"
44
+ rdoc.rdoc_files.include('README*')
45
+ rdoc.rdoc_files.include('lib/**/*.rb')
46
+ end
data/TODO.md ADDED
@@ -0,0 +1,31 @@
1
+ # TODO
2
+
3
+ ## Geometry
4
+
5
+ Mostly inspired by java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/package-summary.html
6
+
7
+ - Polygon
8
+ - CubicCurve
9
+ - QuadCurve
10
+ - Line
11
+ - Path: flexible shape which represents an arbitrary geometric path
12
+ - GeneralPath: represents a geometric path constructed from straight lines, and quadratic and cubic (Bézier) curves. It can contain multiple subpaths.
13
+ - Rectangle
14
+ - RoundRectangle
15
+ - Point
16
+ - Ellipse
17
+ - Polybezier
18
+ - Polyline
19
+ - Area: aggregate of multiple shapes
20
+ - AffineTransform: represents a 2D affine transform that performs a linear mapping from 2D coordinates to other 2D coordinates that preserves the "straightness" and "parallelness" of lines.
21
+
22
+ ## Miscellaneous
23
+
24
+ - Include screenshots of the supported shapes in README
25
+
26
+ ## Maybe
27
+
28
+ - Consider the idea of having tests run in JRuby and check against java.awt.geom and compare result with perfect-shape
29
+ - Consider contributing IEEEremainder to Ruby
30
+ - Contribute this type of expectation: _(arc).must_be :contain?, *point to Minitest Expectations
31
+ - Maybe contribute xit to minitest expectations
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,28 @@
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 'bigdecimal'
23
+
24
+ # Perfect Shape algorithms are mostly ported from java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/package-summary.html
25
+ module PerfectShape
26
+ end
27
+
28
+ Dir[File.expand_path('./perfect_shape/*.rb', __dir__)].each {|f| require f}
@@ -0,0 +1,142 @@
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_relative 'line'
23
+
24
+ module PerfectShape
25
+ # Mostly ported from java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/Arc2D.html
26
+ class Arc
27
+ TYPES = [:open, :chord, :pie]
28
+ attr_accessor :type, :x, :y, :width, :height, :start, :extent
29
+
30
+ def initialize(type: :open, x: 0, y: 0, width: 1, height: 1, start: 0, extent: 360)
31
+ @type = type
32
+ self.x = x
33
+ self.y = y
34
+ self.width = width
35
+ self.height = height
36
+ self.start = start
37
+ self.extent = extent
38
+ end
39
+
40
+ def x=(value)
41
+ @x = BigDecimal(value.to_s)
42
+ end
43
+
44
+ def y=(value)
45
+ @y = BigDecimal(value.to_s)
46
+ end
47
+
48
+ def width=(value)
49
+ @width = BigDecimal(value.to_s)
50
+ end
51
+
52
+ def height=(value)
53
+ @height = BigDecimal(value.to_s)
54
+ end
55
+
56
+ def start=(value)
57
+ @start = BigDecimal(value.to_s)
58
+ end
59
+
60
+ def extent=(value)
61
+ @extent = BigDecimal(value.to_s)
62
+ end
63
+
64
+ # Checks if arc contains point denoted by point (two-number Array or x, y args)
65
+ def contain?(x_or_point, y = nil)
66
+ x = x_or_point
67
+ x, y = x if y.nil? && x_or_point.is_a?(Array) && x_or_point.size == 2
68
+ x = BigDecimal(x.to_s)
69
+ y = BigDecimal(y.to_s)
70
+ return unless x && y
71
+ # Normalize the coordinates compared to the ellipse
72
+ # having a center at 0,0 and a radius of 0.5.
73
+ ellw = width
74
+ return false if (ellw <= 0.0)
75
+ normx = (x - self.x) / ellw - 0.5
76
+ ellh = height
77
+ return false if (ellh <= 0.0)
78
+ normy = (y - self.y) / ellh - 0.5
79
+ dist_sq = (normx * normx) + (normy * normy)
80
+ return false if (dist_sq >= 0.25)
81
+ ang_ext = self.extent.abs
82
+ return true if (ang_ext >= 360.0)
83
+ inarc = contain_angle?(-1*Math.radians_to_degrees(Math.atan2(normy, normx)))
84
+
85
+ return inarc if type == :pie
86
+ # CHORD and OPEN behave the same way
87
+ if inarc
88
+ return true if ang_ext >= 180.0
89
+ # point must be outside the "pie triangle"
90
+ else
91
+ return false if ang_ext <= 180.0
92
+ # point must be inside the "pie triangle"
93
+ end
94
+
95
+ # The point is inside the pie triangle iff it is on the same
96
+ # side of the line connecting the ends of the arc as the center.
97
+ angle = Math.degrees_to_radians(-start)
98
+ x1 = Math.cos(angle)
99
+ y1 = Math.sin(angle)
100
+ angle += Math.degrees_to_radians(-extent)
101
+ x2 = Math.cos(angle)
102
+ y2 = Math.sin(angle)
103
+ inside = (Line.relative_ccw(x1, y1, x2, y2, 2*normx, 2*normy) *
104
+ Line.relative_ccw(x1, y1, x2, y2, 0, 0) >= 0)
105
+ inarc ? !inside : inside
106
+ end
107
+
108
+ def contain_angle?(angle)
109
+ ang_ext = self.extent
110
+ backwards = ang_ext < 0.0
111
+ ang_ext = -ang_ext if backwards
112
+ return true if ang_ext >= 360.0
113
+
114
+ angle = normalize_degrees(angle) - normalize_degrees(start)
115
+ angle = -angle if backwards
116
+ angle += 360.0 if angle < 0.0
117
+
118
+ (angle >= 0.0) && (angle < ang_ext)
119
+ end
120
+
121
+ def normalize_degrees(angle)
122
+ if angle > 180.0
123
+ if angle <= (180.0 + 360.0)
124
+ angle = angle - 360.0
125
+ else
126
+ angle = Math.ieee_remainder(angle, 360.0)
127
+ # IEEEremainder can return -180 here for some input values...
128
+ angle = 180.0 if angle == -180.0
129
+ end
130
+ elsif angle <= -180.0
131
+ if angle > (-180.0 - 360.0)
132
+ angle = angle + 360.0
133
+ else
134
+ angle = Math.ieee_remainder(angle, 360.0)
135
+ # IEEEremainder can return -180 here for some input values...
136
+ angle = 180.0 if angle == -180.0
137
+ end
138
+ end
139
+ angle
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,98 @@
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
+ module PerfectShape
23
+ # Mostly ported from java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/Line2D.html
24
+ class Line
25
+ class << self
26
+ # Returns an indicator of where the specified point
27
+ # {@code (px,py)} lies with respect to the line segment from
28
+ # {@code (x1,y1)} to {@code (x2,y2)}.
29
+ # The return value can be either 1, -1, or 0 and indicates
30
+ # in which direction the specified line must pivot around its
31
+ # first end point, {@code (x1,y1)}, in order to point at the
32
+ # specified point {@code (px,py)}.
33
+ # <p>A return value of 1 indicates that the line segment must
34
+ # turn in the direction that takes the positive X axis towards
35
+ # the negative Y axis. In the default coordinate system used by
36
+ # Java 2D, this direction is counterclockwise.
37
+ # <p>A return value of -1 indicates that the line segment must
38
+ # turn in the direction that takes the positive X axis towards
39
+ # the positive Y axis. In the default coordinate system, this
40
+ # direction is clockwise.
41
+ # <p>A return value of 0 indicates that the point lies
42
+ # exactly on the line segment. Note that an indicator value
43
+ # of 0 is rare and not useful for determining collinearity
44
+ # because of floating point rounding issues.
45
+ # <p>If the point is colinear with the line segment, but
46
+ # not between the end points, then the value will be -1 if the point
47
+ # lies "beyond {@code (x1,y1)}" or 1 if the point lies
48
+ # "beyond {@code (x2,y2)}".
49
+ #
50
+ # @param x1 the X coordinate of the start point of the
51
+ # specified line segment
52
+ # @param y1 the Y coordinate of the start point of the
53
+ # specified line segment
54
+ # @param x2 the X coordinate of the end point of the
55
+ # specified line segment
56
+ # @param y2 the Y coordinate of the end point of the
57
+ # specified line segment
58
+ # @param px the X coordinate of the specified point to be
59
+ # compared with the specified line segment
60
+ # @param py the Y coordinate of the specified point to be
61
+ # compared with the specified line segment
62
+ # @return an integer that indicates the position of the third specified
63
+ # coordinates with respect to the line segment formed
64
+ # by the first two specified coordinates.
65
+ # @since 1.2
66
+ def relative_ccw(x1, y1, x2, y2, px, py)
67
+ x2 -= x1;
68
+ y2 -= y1;
69
+ px -= x1;
70
+ py -= y1;
71
+ ccw = px * y2 - py * x2;
72
+ if ccw == 0.0
73
+ # The point is colinear, classify based on which side of
74
+ # the segment the point falls on. We can calculate a
75
+ # relative value using the projection of px,py onto the
76
+ # segment - a negative value indicates the point projects
77
+ # outside of the segment in the direction of the particular
78
+ # endpoint used as the origin for the projection.
79
+ ccw = px * x2 + py * y2;
80
+ if ccw > 0.0
81
+ # Reverse the projection to be relative to the original x2,y2
82
+ # x2 and y2 are simply negated.
83
+ # px and py need to have (x2 - x1) or (y2 - y1) subtracted
84
+ # from them (based on the original values)
85
+ # Since we really want to get a positive answer when the
86
+ # point is "beyond (x2,y2)", then we want to calculate
87
+ # the inverse anyway - thus we leave x2 & y2 negated.
88
+ px -= x2;
89
+ py -= y2;
90
+ ccw = px * x2 + py * y2;
91
+ ccw = 0.0 if ccw < 0.0
92
+ end
93
+ end
94
+ (ccw < 0.0) ? -1 : ((ccw > 0.0) ? 1 : 0);
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,63 @@
1
+ module PerfectShape
2
+ # Perfect Shape Math utility methods
3
+ # Mostly ported from java.lang.Math: https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html
4
+ # Also includes standard Ruby ::Math utility methods
5
+ module Math
6
+ class << self
7
+ # converts angle from radians to degrees
8
+ def radians_to_degrees(radians)
9
+ (180/Math::PI)*radians
10
+ end
11
+
12
+ # converts angle from degrees to radians
13
+ def degrees_to_radians(degrees)
14
+ (Math::PI/180)*degrees
15
+ end
16
+
17
+ # Computes the remainder operation on two arguments as prescribed by the IEEE 754 standard.
18
+ # Algorithm: x – (round(x/y)*y).
19
+ # The `round` part rounds to the nearest even number when it is a halfway between n & y (integer + 0.5 number)
20
+ # The remainder value is mathematically equal to x - y × n, where n is the mathematical integer closest to the exact mathematical value of the quotient x/y, and if two mathematical integers are equally close to x/y, then n is the integer that is even. If the remainder is zero, its sign is the same as the sign of the first argument. Special cases:
21
+ # If either argument is NaN, or the first argument is infinite, or the second argument is positive zero or negative zero, then the result is NaN.
22
+ # If the first argument is finite and the second argument is infinite, then the result is the same as the first argument.
23
+ # Parameters:
24
+ # x - the dividend.
25
+ # y - the divisor.
26
+ # Returns:
27
+ # the remainder when x is divided by y.
28
+ def ieee754_remainder(x, y)
29
+ x = BigDecimal(x.to_s)
30
+ y = BigDecimal(y.to_s)
31
+ return BigDecimal::NAN if x.nan? || y.nan? || x.infinite? || y.zero?
32
+ return x if x.finite? && y.infinite?
33
+ division = x / y
34
+ rounded_division_low = BigDecimal(division.floor)
35
+ rounded_division_high = BigDecimal(division.ceil)
36
+ rounded_division_half = rounded_division_low + 0.5
37
+ rounded_division = if division == rounded_division_half
38
+ rounded_division_low.to_i.even? ? rounded_division_low : rounded_division_high
39
+ else
40
+ BigDecimal(division.round)
41
+ end
42
+ (x - (rounded_division * y))
43
+ end
44
+ alias ieee_remainder ieee754_remainder
45
+
46
+ def respond_to?(method_name, include_private = false)
47
+ super || ::Math.respond_to?(method_name, include_private)
48
+ end
49
+
50
+ def method_missing(method_name, *args, **kwargs, &block)
51
+ if ::Math.respond_to?(method_name, true)
52
+ ::Math.send(method_name, *args, **kwargs, &block)
53
+ else
54
+ super
55
+ end
56
+ end
57
+
58
+ def const_missing(constant)
59
+ ::Math::const_get(constant)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,65 @@
1
+ # Generated by juwelier
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: perfect-shape 0.0.1 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "perfect-shape".freeze
9
+ s.version = "0.0.1"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib".freeze]
13
+ s.authors = ["Andy Maleh".freeze]
14
+ s.date = "2021-12-06"
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 point in popular geometric shapes such as rectangle, square, arc, circle, polygon, polyline, and paths containing lines, bezier curves, and quadratic curves. Additionally, it contains some purely mathematical algorithms like IEEEremainder (also known as IEEE-754 remainder).".freeze
16
+ s.email = "andy.am@gmail.com".freeze
17
+ s.extra_rdoc_files = [
18
+ "CHANGELOG.md",
19
+ "LICENSE.txt",
20
+ "README.md"
21
+ ]
22
+ s.files = [
23
+ ".document",
24
+ ".ruby-gemset",
25
+ ".ruby-version",
26
+ "CHANGELOG.md",
27
+ "Gemfile",
28
+ "Gemfile.lock",
29
+ "LICENSE.txt",
30
+ "README.md",
31
+ "Rakefile",
32
+ "TODO.md",
33
+ "VERSION",
34
+ "lib/perfect-shape.rb",
35
+ "lib/perfect_shape/arc.rb",
36
+ "lib/perfect_shape/line.rb",
37
+ "lib/perfect_shape/math.rb",
38
+ "perfect-shape.gemspec",
39
+ "test/lib/perfect_shape/test_arc.rb",
40
+ "test/lib/perfect_shape/test_math.rb"
41
+ ]
42
+ s.homepage = "http://github.com/AndyObtiva/perfect-shape".freeze
43
+ s.licenses = ["MIT".freeze]
44
+ s.rubygems_version = "3.2.31".freeze
45
+ s.summary = "Perfect Shape - Geometric Algorithms".freeze
46
+
47
+ if s.respond_to? :specification_version then
48
+ s.specification_version = 4
49
+ end
50
+
51
+ if s.respond_to? :add_runtime_dependency then
52
+ s.add_development_dependency(%q<rdoc>.freeze, ["~> 3.12"])
53
+ s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
54
+ s.add_development_dependency(%q<minitest>.freeze, ["~> 5.14.4"])
55
+ s.add_development_dependency(%q<puts_debuggerer>.freeze, ["~> 0.13.1"])
56
+ s.add_development_dependency(%q<rake-tui>.freeze, ["> 0"])
57
+ else
58
+ s.add_dependency(%q<rdoc>.freeze, ["~> 3.12"])
59
+ s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
60
+ s.add_dependency(%q<minitest>.freeze, ["~> 5.14.4"])
61
+ s.add_dependency(%q<puts_debuggerer>.freeze, ["~> 0.13.1"])
62
+ s.add_dependency(%q<rake-tui>.freeze, ["> 0"])
63
+ end
64
+ end
65
+
@@ -0,0 +1,184 @@
1
+ require 'puts_debuggerer'
2
+ require 'minitest/autorun'
3
+
4
+ require_relative '../../../lib/perfect-shape'
5
+
6
+ describe PerfectShape do
7
+ describe PerfectShape::Arc do
8
+ it 'constructs with chord type and dimensions' do
9
+ arc = PerfectShape::Arc.new(type: :chord, x: 2, y: 3, width: 50, height: 60, start: 30, extent: 90)
10
+
11
+ _(arc.type).must_equal :chord
12
+ _(arc.y).must_equal 3
13
+ _(arc.y).must_equal 3
14
+ _(arc.width).must_equal 50
15
+ _(arc.height).must_equal 60
16
+ _(arc.start).must_equal 30
17
+ _(arc.extent).must_equal 90
18
+ end
19
+
20
+ it 'constructs with open type defaults' do
21
+ arc = PerfectShape::Arc.new
22
+
23
+ _(arc.type).must_equal :open
24
+ _(arc.x).must_equal 0
25
+ _(arc.y).must_equal 0
26
+ _(arc.width).must_equal 1
27
+ _(arc.height).must_equal 1
28
+ _(arc.start).must_equal 0
29
+ _(arc.extent).must_equal 360
30
+ end
31
+
32
+ it 'updates attributes' do
33
+ arc = PerfectShape::Arc.new
34
+ arc.type = :chord
35
+ arc.x = 2
36
+ arc.y = 3
37
+ arc.width = 50
38
+ arc.height = 60
39
+ arc.start = 30
40
+ arc.extent = 90
41
+
42
+ _(arc.type).must_equal :chord
43
+ _(arc.y).must_equal 3
44
+ _(arc.y).must_equal 3
45
+ _(arc.width).must_equal 50
46
+ _(arc.height).must_equal 60
47
+ _(arc.start).must_equal 30
48
+ _(arc.extent).must_equal 90
49
+ end
50
+
51
+ it 'contains point with chord type circle' do
52
+ arc = PerfectShape::Arc.new(type: :chord, x: 2, y: 3, width: 50.5, height: 60.75, start: 0, extent: 360)
53
+ point = [arc.x + arc.width / 2.0, arc.y + arc.height / 2.0]
54
+
55
+ _(arc).must_be :contain?, point
56
+ _(arc.contain?(point)).must_equal arc.contain?(*point)
57
+ end
58
+
59
+ it 'contains point with chord type arc' do
60
+ arc = PerfectShape::Arc.new(type: :chord, x: 2, y: 3, width: 50.5, height: 60.75, start: 45, extent: 270)
61
+ point = [arc.x + arc.width*3/4.0, arc.y + (arc.height / 2.0)]
62
+
63
+ _(arc).must_be :contain?, point
64
+ _(arc.contain?(point)).must_equal arc.contain?(*point)
65
+ end
66
+
67
+ it 'does not contain point within bounding box with chord type arc' do
68
+ arc = PerfectShape::Arc.new(type: :chord, x: 2, y: 3, width: 67, height: 46, start: 45, extent: 270)
69
+ point = [arc.x + arc.width*(3.9/4.0), arc.y + (arc.height / 2.0)]
70
+
71
+ _(arc.contain?(point)).must_equal false
72
+ end
73
+
74
+ it 'does not contain point in center of chord type arc' do
75
+ arc = PerfectShape::Arc.new(type: :chord, x: 2, y: 3, width: 50.5, height: 60.75, start: 0, extent: 145)
76
+ point = [arc.x + arc.width / 2.0, arc.y + arc.height / 2.0]
77
+
78
+ _(arc.contain?(point)).must_equal false
79
+ end
80
+
81
+ it 'does not contain point within bounding box with chord type circle' do
82
+ arc = PerfectShape::Arc.new(type: :chord, x: 2, y: 3, width: 50.5, height: 60.75, start: 0, extent: 360)
83
+ point = [3.0, 4.0]
84
+
85
+ _(arc.contain?(point)).must_equal false
86
+ end
87
+
88
+ it 'does not contain point outside bounding box with chord type circle' do
89
+ arc = PerfectShape::Arc.new(type: :chord, x: 2, y: 3, width: 50.5, height: 60.75, start: 0, extent: 360)
90
+ point = [1.0, 2.0]
91
+
92
+ _(arc.contain?(point)).must_equal false
93
+ end
94
+
95
+ it 'contains point with open type circle' do
96
+ arc = PerfectShape::Arc.new(type: :open, x: 2, y: 3, width: 50.5, height: 60.75, start: 0, extent: 360)
97
+ point = [arc.x + arc.width / 2.0, arc.y + arc.height / 2.0]
98
+
99
+ _(arc).must_be :contain?, point
100
+ _(arc.contain?(point)).must_equal arc.contain?(*point)
101
+ end
102
+
103
+ it 'contains point with open type arc' do
104
+ arc = PerfectShape::Arc.new(type: :open, x: 2, y: 3, width: 50.5, height: 60.75, start: 45, extent: 270)
105
+ point = [arc.x + arc.width*3/4.0, arc.y + (arc.height / 2.0)]
106
+
107
+ _(arc).must_be :contain?, point
108
+ _(arc.contain?(point)).must_equal arc.contain?(*point)
109
+ end
110
+
111
+ it 'does not contain point within bounding box with open type arc' do
112
+ arc = PerfectShape::Arc.new(type: :open, x: 2, y: 3, width: 67, height: 46, start: 45, extent: 270)
113
+ point = [arc.x + arc.width*(3.9/4.0), arc.y + (arc.height / 2.0)]
114
+
115
+ _(arc.contain?(point)).must_equal false
116
+ end
117
+
118
+ it 'does not contain point in center of open type arc' do
119
+ arc = PerfectShape::Arc.new(type: :open, x: 2, y: 3, width: 50.5, height: 60.75, start: 0, extent: 145)
120
+ point = [arc.x + arc.width / 2.0, arc.y + arc.height / 2.0]
121
+
122
+ _(arc.contain?(point)).must_equal false
123
+ end
124
+
125
+ it 'does not contain point within bounding box with open type circle' do
126
+ arc = PerfectShape::Arc.new(type: :open, x: 2, y: 3, width: 50.5, height: 60.75, start: 0, extent: 360)
127
+ point = [3.0, 4.0]
128
+
129
+ _(arc.contain?(point)).must_equal false
130
+ end
131
+
132
+ it 'does not contain point outside bounding box with open type circle' do
133
+ arc = PerfectShape::Arc.new(type: :open, x: 2, y: 3, width: 50.5, height: 60.75, start: 0, extent: 360)
134
+ point = [1.0, 2.0]
135
+
136
+ _(arc.contain?(point)).must_equal false
137
+ end
138
+
139
+ it 'contains point with pie type circle' do
140
+ arc = PerfectShape::Arc.new(type: :pie, x: 2, y: 3, width: 50.5, height: 60.75, start: 0, extent: 360)
141
+ point = [arc.x + arc.width / 2.0, arc.y + arc.height / 2.0]
142
+
143
+ _(arc).must_be :contain?, point
144
+ _(arc.contain?(point)).must_equal arc.contain?(*point)
145
+ end
146
+
147
+ it 'contains point in center of pie type arc' do
148
+ arc = PerfectShape::Arc.new(type: :pie, x: 2, y: 3, width: 50.5, height: 60.75, start: 0, extent: 145)
149
+ point = [arc.x + arc.width / 2.0, arc.y + arc.height / 2.0]
150
+
151
+ _(arc).must_be :contain?, point
152
+ _(arc.contain?(point)).must_equal arc.contain?(*point)
153
+ end
154
+
155
+ it 'contains point with pie type arc' do
156
+ arc = PerfectShape::Arc.new(type: :pie, x: 2, y: 3, width: 50.5, height: 60.75, start: 0, extent: 145)
157
+ point = [arc.x + arc.width / 2.0 + (arc.width / 4.0), arc.y + (arc.height / 2.0) - (arc.height / 4.0)]
158
+
159
+ _(arc).must_be :contain?, point
160
+ _(arc.contain?(point)).must_equal arc.contain?(*point)
161
+ end
162
+
163
+ it 'does not contain point within bounding box with pie type arc' do
164
+ arc = PerfectShape::Arc.new(type: :pie, x: 2, y: 3, width: 50.5, height: 60.75, start: 0, extent: 145)
165
+ point = [arc.x + (arc.width / 2.0) - (arc.width / 4.0), arc.y + arc.height / 2.0]
166
+
167
+ _(arc.contain?(point)).must_equal false
168
+ end
169
+
170
+ it 'does not contain point within bounding box with pie type circle' do
171
+ arc = PerfectShape::Arc.new(type: :pie, x: 2, y: 3, width: 50.5, height: 60.75, start: 0, extent: 360)
172
+ point = [3.0, 4.0]
173
+
174
+ _(arc.contain?(point)).must_equal false
175
+ end
176
+
177
+ it 'does not contain point outside bounding box with pie type circle' do
178
+ arc = PerfectShape::Arc.new(type: :pie, x: 2, y: 3, width: 50.5, height: 60.75, start: 0, extent: 360)
179
+ point = [1.0, 2.0]
180
+
181
+ _(arc.contain?(point)).must_equal false
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,76 @@
1
+ require 'puts_debuggerer'
2
+ require 'minitest/autorun'
3
+
4
+ require_relative '../../../lib/perfect-shape'
5
+
6
+ describe PerfectShape do
7
+ describe PerfectShape::Math do
8
+ describe '#ieee754_remainder' do
9
+ it 'returns a negative number rounding ceiling' do
10
+ result = PerfectShape::Math.ieee754_remainder(7.5, 4.5)
11
+
12
+ _(result).must_equal -1.5
13
+ end
14
+
15
+ it 'returns a positive number rounding floor' do
16
+ result = PerfectShape::Math.ieee754_remainder(7.5, 3.5)
17
+
18
+ _(result).must_equal 0.5
19
+ end
20
+
21
+ it 'returns a positive number rounding halfway to floor even number' do
22
+ result = PerfectShape::Math.ieee754_remainder(5, 10)
23
+
24
+ _(result).must_equal 5
25
+ end
26
+
27
+ it 'returns a negative number rounding halfway to ceiling even number' do
28
+ result = PerfectShape::Math.ieee754_remainder(15, 10)
29
+
30
+ _(result).must_equal -5
31
+ end
32
+
33
+ it 'returns NaN for x=NaN' do
34
+ result = PerfectShape::Math.ieee754_remainder(Float::NAN, 10)
35
+
36
+ _(result).must_be :nan?
37
+ end
38
+
39
+ it 'returns NaN for x=Infinity' do
40
+ result = PerfectShape::Math.ieee754_remainder(Float::INFINITY, 10)
41
+
42
+ _(result).must_be :nan?
43
+ end
44
+
45
+ it 'returns NaN for y=NaN' do
46
+ result = PerfectShape::Math.ieee754_remainder(10, Float::NAN)
47
+
48
+ _(result).must_be :nan?
49
+ end
50
+
51
+ it 'returns NaN for y=0' do
52
+ result = PerfectShape::Math.ieee754_remainder(10, 0)
53
+
54
+ _(result).must_be :nan?
55
+ end
56
+
57
+ it 'returns NaN for y=+0.0' do
58
+ result = PerfectShape::Math.ieee754_remainder(10, 0.0)
59
+
60
+ _(result).must_be :nan?
61
+ end
62
+
63
+ it 'returns NaN for y=-0.0' do
64
+ result = PerfectShape::Math.ieee754_remainder(10, -0.0)
65
+
66
+ _(result).must_be :nan?
67
+ end
68
+
69
+ it 'returns x for y=Infinity' do
70
+ result = PerfectShape::Math.ieee754_remainder(10, Float::INFINITY)
71
+
72
+ _(result).must_equal 10
73
+ end
74
+ end
75
+ end
76
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: perfect-shape
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Andy Maleh
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-12-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rdoc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: juwelier
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.1.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.1.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 5.14.4
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 5.14.4
55
+ - !ruby/object:Gem::Dependency
56
+ name: puts_debuggerer
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.13.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.13.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake-tui
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">"
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">"
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Perfect Shape is a collection of pure Ruby geometric algorithms that
84
+ are mostly useful for GUI manipulation like checking containment of a point in popular
85
+ geometric shapes such as rectangle, square, arc, circle, polygon, polyline, and
86
+ paths containing lines, bezier curves, and quadratic curves. Additionally, it contains
87
+ some purely mathematical algorithms like IEEEremainder (also known as IEEE-754 remainder).
88
+ email: andy.am@gmail.com
89
+ executables: []
90
+ extensions: []
91
+ extra_rdoc_files:
92
+ - CHANGELOG.md
93
+ - LICENSE.txt
94
+ - README.md
95
+ files:
96
+ - ".document"
97
+ - ".ruby-gemset"
98
+ - ".ruby-version"
99
+ - CHANGELOG.md
100
+ - Gemfile
101
+ - Gemfile.lock
102
+ - LICENSE.txt
103
+ - README.md
104
+ - Rakefile
105
+ - TODO.md
106
+ - VERSION
107
+ - lib/perfect-shape.rb
108
+ - lib/perfect_shape/arc.rb
109
+ - lib/perfect_shape/line.rb
110
+ - lib/perfect_shape/math.rb
111
+ - perfect-shape.gemspec
112
+ - test/lib/perfect_shape/test_arc.rb
113
+ - test/lib/perfect_shape/test_math.rb
114
+ homepage: http://github.com/AndyObtiva/perfect-shape
115
+ licenses:
116
+ - MIT
117
+ metadata: {}
118
+ post_install_message:
119
+ rdoc_options: []
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubygems_version: 3.2.31
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: Perfect Shape - Geometric Algorithms
137
+ test_files: []