perfect-shape 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +102 -0
- data/LICENSE.txt +20 -0
- data/README.md +82 -0
- data/Rakefile +46 -0
- data/TODO.md +31 -0
- data/VERSION +1 -0
- data/lib/perfect-shape.rb +28 -0
- data/lib/perfect_shape/arc.rb +142 -0
- data/lib/perfect_shape/line.rb +98 -0
- data/lib/perfect_shape/math.rb +63 -0
- data/perfect-shape.gemspec +65 -0
- data/test/lib/perfect_shape/test_arc.rb +184 -0
- data/test/lib/perfect_shape/test_math.rb +76 -0
- metadata +137 -0
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
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: []
|