perfect-shape 0.0.2 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -0
- data/README.md +123 -15
- data/VERSION +1 -1
- data/lib/perfect_shape/arc.rb +90 -26
- data/lib/perfect_shape/circle.rb +91 -0
- data/lib/perfect_shape/ellipse.rb +78 -0
- data/lib/perfect_shape/math.rb +22 -0
- data/lib/perfect_shape/rectangle.rb +1 -1
- data/lib/perfect_shape/rectangular_shape.rb +8 -0
- data/lib/perfect_shape/shape.rb +1 -0
- data/lib/perfect_shape/square.rb +56 -0
- data/perfect-shape.gemspec +7 -4
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 81aba6992bb9cb98b1ce01764cb4cd0ca8e88690c56066e8282b4c0876421925
|
4
|
+
data.tar.gz: c63f14e43dfa8adff2bd525a77ca25ded5d1381953abb4a728770b22dab43323
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ff01e388f4fadc337792f45ddfc598fecaeb3816fe13d04f5db4a13ecc78c7e978e2ef9683e33e32711bda62373c77dd1f5f4c07d69cfc23e4867bfbba9ccca
|
7
|
+
data.tar.gz: b32a4c65a4101ffa61cdab257fa1af05d59e7ee1f8e12ac8598dbad92a32dd33299bda92add7781e3f9028278051ebda36d9164cde116d79fc70f6d8b297f8ed
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,28 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.0.6
|
4
|
+
|
5
|
+
- `PerfectShape::Circle`
|
6
|
+
- `PerfectShape::Circle#contain?(x_or_point, y=nil)`
|
7
|
+
|
8
|
+
## 0.0.5
|
9
|
+
|
10
|
+
- `PerfectShape::Ellipse`
|
11
|
+
- `PerfectShape::Ellipse#contain?(x_or_point, y=nil)`
|
12
|
+
|
13
|
+
## 0.0.4
|
14
|
+
|
15
|
+
- `PerfectShape::Arc#center_x` / `PerfectShape::Arc#center_y` / `PerfectShape::Arc#radius_x` / `PerfectShape::Arc#radius_y`
|
16
|
+
- `PerfectShape::Rectangle#center_x` / `PerfectShape::Arc#center_y`
|
17
|
+
- `PerfectShape::Square#center_x` / `PerfectShape::Square#center_y`
|
18
|
+
- `PerfectShape::Arc#initialize(center_x: , center_y: , radius_x: , radius_y: )`
|
19
|
+
- `PerfectShape::Math::normalize_degrees(degrees)` extracted from `PerfectShape::Arc`
|
20
|
+
|
21
|
+
## 0.0.3
|
22
|
+
|
23
|
+
- `PerfectShape::Square`
|
24
|
+
- `PerfectShape::Square#contain?(x_or_point, y=nil)`
|
25
|
+
|
3
26
|
## 0.0.2
|
4
27
|
|
5
28
|
- `PerfectShape::Rectangle`
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
# Perfect Shape 0.0.
|
1
|
+
# Perfect Shape 0.0.6
|
2
2
|
## Geometric Algorithms
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/perfect-shape.svg)](http://badge.fury.io/rb/perfect-shape)
|
4
4
|
|
5
|
-
`PerfectShape` 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 in popular geometry shapes such as rectangle, square, arc, ellipse, circle, polygon, polyline,
|
5
|
+
`PerfectShape` 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 in popular geometry shapes such as rectangle, square, arc (open, chord, and pie), ellipse, circle, polygon, polyline, polyquad, polycubic, and paths containing lines, bezier curves, and quadratic curves.
|
6
6
|
|
7
7
|
Additionally, `PerfectShape::Math` contains some purely mathematical algorithms.
|
8
8
|
|
@@ -13,13 +13,13 @@ To ensure high accuracy, this library does all its mathematical operations with
|
|
13
13
|
Run:
|
14
14
|
|
15
15
|
```
|
16
|
-
gem install perfect-shape -v 0.0.
|
16
|
+
gem install perfect-shape -v 0.0.6
|
17
17
|
```
|
18
18
|
|
19
19
|
Or include in Bundler `Gemfile`:
|
20
20
|
|
21
21
|
```ruby
|
22
|
-
gem 'perfect-shape', '~> 0.0.
|
22
|
+
gem 'perfect-shape', '~> 0.0.6'
|
23
23
|
```
|
24
24
|
|
25
25
|
And, run:
|
@@ -32,33 +32,141 @@ bundle
|
|
32
32
|
|
33
33
|
### `PerfectShape::Math`
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
- `::
|
35
|
+
Module
|
36
|
+
|
37
|
+
- `::degrees_to_radians(angle)`: converts degrees to radians
|
38
|
+
- `::radians_to_degrees(angle)`: converts radians to degrees
|
39
|
+
- `::normalize_degrees(angle)`: normalizes the specified angle into the range -180 to 180.
|
40
|
+
- `::ieee_remainder(x, y)` (alias: `ieee754_remainder`): [IEEE 754-1985 Remainder](https://en.wikipedia.org/wiki/IEEE_754-1985) (different from standard % modulo operator as it operates on floats and could return a negative result)
|
41
|
+
|
42
|
+
### `PerfectShape::Shape`
|
43
|
+
|
44
|
+
Class
|
45
|
+
|
46
|
+
- `#normalize_point(x_or_point, y = nil)`: normalizes point into an `Array` of (x,y) coordinates
|
47
|
+
|
48
|
+
### `PerfectShape::RectangularShape`
|
49
|
+
|
50
|
+
Module
|
51
|
+
|
52
|
+
- `#initialize(x: 0, y: 0, width: 1, height: 1)`: initializes a rectangular shape
|
53
|
+
- `#x`: top-left x
|
54
|
+
- `#y`: top-left y
|
55
|
+
- `#width`: width
|
56
|
+
- `#height`: height
|
57
|
+
- `#center_x`: center x
|
58
|
+
- `#center_y`: center y
|
38
59
|
|
39
60
|
### `PerfectShape::Line`
|
40
61
|
|
62
|
+
Class
|
63
|
+
Extends `PerfectShape::Shape`
|
64
|
+
|
41
65
|
- `::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). 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)”.
|
42
66
|
|
43
67
|
### `PerfectShape::Rectangle`
|
44
68
|
|
69
|
+
Class
|
70
|
+
Extends `PerfectShape::Shape`
|
71
|
+
Includes `PerfectShape::RectangularShape`
|
72
|
+
|
73
|
+
![rectangle](images/rectangle.png)
|
74
|
+
|
45
75
|
- `::new(x: 0, y: 0, width: 1, height: 1)`: constructs a rectangle
|
46
76
|
- `#x`: top-left x
|
47
77
|
- `#y`: top-left y
|
48
78
|
- `#width`: width
|
49
79
|
- `#height`: height
|
50
|
-
- `#
|
80
|
+
- `#center_x`: center x
|
81
|
+
- `#center_y`: center y
|
82
|
+
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
83
|
+
|
84
|
+
### `PerfectShape::Square`
|
85
|
+
|
86
|
+
Class
|
87
|
+
Extends `PerfectShape::Rectangle`
|
88
|
+
|
89
|
+
![square](images/square.png)
|
90
|
+
|
91
|
+
- `::new(x: 0, y: 0, length: 1)`: constructs a square
|
92
|
+
- `#x`: top-left x
|
93
|
+
- `#y`: top-left y
|
94
|
+
- `#length`: length
|
95
|
+
- `#width`: width (equal to length)
|
96
|
+
- `#height`: height (equal to length)
|
97
|
+
- `#center_x`: center x
|
98
|
+
- `#center_y`: center y
|
99
|
+
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
51
100
|
|
52
101
|
### `PerfectShape::Arc`
|
53
102
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
103
|
+
Class
|
104
|
+
Extends `PerfectShape::Shape`
|
105
|
+
Includes `PerfectShape::RectangularShape`
|
106
|
+
|
107
|
+
Arcs can be of type `:open`, `:chord`, or `:pie`
|
108
|
+
|
109
|
+
Open Arc | Chord Arc | Pie Arc
|
110
|
+
---------|-----------|--------
|
111
|
+
![arc-open](images/arc-open.png) | ![arc-chord](images/arc-chord.png) | ![arc-pie](images/arc-pie.png)
|
112
|
+
|
113
|
+
- `::new(type: :open, x: 0, y: 0, width: 1, height: 1, start: 0, extent: 360, center_x: nil, center_y: nil, radius_x: nil, radius_y: nil)`: constructs an arc of type `:open` (default), `:chord`, or `:pie`
|
114
|
+
- `#type`: `:open`, `:chord`, or `:pie`
|
115
|
+
- `#x`: top-left x
|
116
|
+
- `#y`: top-left y
|
117
|
+
- `#width`: width
|
118
|
+
- `#height`: height
|
59
119
|
- `#start`: start angle in degrees
|
60
120
|
- `#extent`: extent angle in degrees
|
61
|
-
- `#
|
121
|
+
- `#center_x`: center x
|
122
|
+
- `#center_y`: center y
|
123
|
+
- `#radius_x`: radius along the x-axis
|
124
|
+
- `#radius_y`: radius along the y-axis
|
125
|
+
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
126
|
+
|
127
|
+
### `PerfectShape::Ellipse`
|
128
|
+
|
129
|
+
Class
|
130
|
+
Extends `PerfectShape::Arc`
|
131
|
+
|
132
|
+
![ellipse](images/ellipse.png)
|
133
|
+
|
134
|
+
- `::new(x: 0, y: 0, width: 1, height: 1, center_x: nil, center_y: nil, radius_x: nil, radius_y: nil)`: constructs an ellipse
|
135
|
+
- `#x`: top-left x
|
136
|
+
- `#y`: top-left y
|
137
|
+
- `#width`: width
|
138
|
+
- `#height`: height
|
139
|
+
- `#center_x`: center x
|
140
|
+
- `#center_y`: center y
|
141
|
+
- `#radius_x`: radius along the x-axis
|
142
|
+
- `#radius_y`: radius along the y-axis
|
143
|
+
- `#type`: always `:open`
|
144
|
+
- `#start`: always `0`
|
145
|
+
- `#extent`: always `360`
|
146
|
+
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
147
|
+
|
148
|
+
### `PerfectShape::Circle`
|
149
|
+
|
150
|
+
Class
|
151
|
+
Extends `PerfectShape::Ellipse`
|
152
|
+
|
153
|
+
![circle](images/circle.png)
|
154
|
+
|
155
|
+
- `::new(x: 0, y: 0, diameter: 1, width: 1, height: 1, center_x: nil, center_y: nil, radius: nil, radius_x: nil, radius_y: nil)`: constructs a circle
|
156
|
+
- `#x`: top-left x
|
157
|
+
- `#y`: top-left y
|
158
|
+
- `#diameter`: diameter
|
159
|
+
- `#width`: width (equal to diameter)
|
160
|
+
- `#height`: height (equal to diameter)
|
161
|
+
- `#center_x`: center x
|
162
|
+
- `#center_y`: center y
|
163
|
+
- `#radius`: radius
|
164
|
+
- `#radius_x`: radius along the x-axis (equal to radius)
|
165
|
+
- `#radius_y`: radius along the y-axis (equal to radius)
|
166
|
+
- `#type`: always `:open`
|
167
|
+
- `#start`: always `0`
|
168
|
+
- `#extent`: always `360`
|
169
|
+
- `#contain?(x_or_point, y=nil)`: checks if point is inside
|
62
170
|
|
63
171
|
## Process
|
64
172
|
|
@@ -77,7 +185,7 @@ bundle
|
|
77
185
|
|
78
186
|
[CHANGELOG.md](CHANGELOG.md)
|
79
187
|
|
80
|
-
## Contributing
|
188
|
+
## Contributing
|
81
189
|
|
82
190
|
- Check out the latest master to make sure the feature hasn't been
|
83
191
|
implemented or the bug hasn't been fixed yet.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.6
|
data/lib/perfect_shape/arc.rb
CHANGED
@@ -32,8 +32,15 @@ module PerfectShape
|
|
32
32
|
attr_accessor :type
|
33
33
|
attr_reader :start, :extent
|
34
34
|
|
35
|
-
def initialize(type: :open, x: 0, y: 0, width: 1, height: 1, start: 0, extent: 360)
|
36
|
-
|
35
|
+
def initialize(type: :open, x: 0, y: 0, width: 1, height: 1, start: 0, extent: 360, center_x: nil, center_y: nil, radius_x: nil, radius_y: nil)
|
36
|
+
if center_x && center_y && radius_x && radius_y
|
37
|
+
self.center_x = center_x
|
38
|
+
self.center_y = center_y
|
39
|
+
self.radius_x = radius_x
|
40
|
+
self.radius_y = radius_y
|
41
|
+
else
|
42
|
+
super(x: x, y: y, width: width, height: height)
|
43
|
+
end
|
37
44
|
@type = type
|
38
45
|
self.start = start
|
39
46
|
self.extent = extent
|
@@ -47,6 +54,86 @@ module PerfectShape
|
|
47
54
|
@extent = BigDecimal(value.to_s)
|
48
55
|
end
|
49
56
|
|
57
|
+
def x
|
58
|
+
@center_x && @radius_x ? @center_x - @radius_x : super
|
59
|
+
end
|
60
|
+
|
61
|
+
def y
|
62
|
+
@center_y && @radius_y ? @center_y - @radius_y : super
|
63
|
+
end
|
64
|
+
|
65
|
+
def width
|
66
|
+
@radius_x ? @radius_x * BigDecimal('2.0') : super
|
67
|
+
end
|
68
|
+
|
69
|
+
def height
|
70
|
+
@radius_y ? @radius_y * BigDecimal('2.0') : super
|
71
|
+
end
|
72
|
+
|
73
|
+
# Sets x, normalizing to BigDecimal
|
74
|
+
def x=(value)
|
75
|
+
super
|
76
|
+
@center_x = nil
|
77
|
+
self.width = width if @radius_x
|
78
|
+
end
|
79
|
+
|
80
|
+
# Sets y, normalizing to BigDecimal
|
81
|
+
def y=(value)
|
82
|
+
super
|
83
|
+
@center_y = nil
|
84
|
+
self.height = height if @radius_y
|
85
|
+
end
|
86
|
+
|
87
|
+
# Sets width, normalizing to BigDecimal
|
88
|
+
def width=(value)
|
89
|
+
super
|
90
|
+
@radius_x = nil
|
91
|
+
end
|
92
|
+
|
93
|
+
# Sets height, normalizing to BigDecimal
|
94
|
+
def height=(value)
|
95
|
+
super
|
96
|
+
@radius_y = nil
|
97
|
+
end
|
98
|
+
|
99
|
+
def center_x
|
100
|
+
super || @center_x
|
101
|
+
end
|
102
|
+
|
103
|
+
def center_y
|
104
|
+
super || @center_y
|
105
|
+
end
|
106
|
+
|
107
|
+
def radius_x
|
108
|
+
@width ? @width/BigDecimal('2.0') : @radius_x
|
109
|
+
end
|
110
|
+
|
111
|
+
def radius_y
|
112
|
+
@height ? @height/BigDecimal('2.0') : @radius_y
|
113
|
+
end
|
114
|
+
|
115
|
+
def center_x=(value)
|
116
|
+
@center_x = BigDecimal(value.to_s)
|
117
|
+
@x = nil
|
118
|
+
self.radius_x = radius_x if @width
|
119
|
+
end
|
120
|
+
|
121
|
+
def center_y=(value)
|
122
|
+
@center_y = BigDecimal(value.to_s)
|
123
|
+
@y = nil
|
124
|
+
self.radius_y = radius_y if @height
|
125
|
+
end
|
126
|
+
|
127
|
+
def radius_x=(value)
|
128
|
+
@radius_x = BigDecimal(value.to_s)
|
129
|
+
@width = nil
|
130
|
+
end
|
131
|
+
|
132
|
+
def radius_y=(value)
|
133
|
+
@radius_y = BigDecimal(value.to_s)
|
134
|
+
@height = nil
|
135
|
+
end
|
136
|
+
|
50
137
|
# Checks if arc contains point denoted by point (two-number Array or x, y args)
|
51
138
|
#
|
52
139
|
# @param x The X coordinate of the point to test.
|
@@ -103,34 +190,11 @@ module PerfectShape
|
|
103
190
|
ang_ext = -ang_ext if backwards
|
104
191
|
return true if ang_ext >= 360.0
|
105
192
|
|
106
|
-
angle = normalize_degrees(angle) - normalize_degrees(start)
|
193
|
+
angle = Math.normalize_degrees(angle) - Math.normalize_degrees(start)
|
107
194
|
angle = -angle if backwards
|
108
195
|
angle += 360.0 if angle < 0.0
|
109
196
|
|
110
197
|
(angle >= 0.0) && (angle < ang_ext)
|
111
198
|
end
|
112
|
-
|
113
|
-
# Normalizes the specified angle into the range -180 to 180.
|
114
|
-
# TODO refactor/extract to Math class
|
115
|
-
def normalize_degrees(angle)
|
116
|
-
if angle > 180.0
|
117
|
-
if angle <= (180.0 + 360.0)
|
118
|
-
angle = angle - 360.0
|
119
|
-
else
|
120
|
-
angle = Math.ieee_remainder(angle, 360.0)
|
121
|
-
# IEEEremainder can return -180 here for some input values...
|
122
|
-
angle = 180.0 if angle == -180.0
|
123
|
-
end
|
124
|
-
elsif angle <= -180.0
|
125
|
-
if angle > (-180.0 - 360.0)
|
126
|
-
angle = angle + 360.0
|
127
|
-
else
|
128
|
-
angle = Math.ieee_remainder(angle, 360.0)
|
129
|
-
# IEEEremainder can return -180 here for some input values...
|
130
|
-
angle = 180.0 if angle == -180.0
|
131
|
-
end
|
132
|
-
end
|
133
|
-
angle
|
134
|
-
end
|
135
199
|
end
|
136
200
|
end
|
@@ -0,0 +1,91 @@
|
|
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/ellipse'
|
23
|
+
|
24
|
+
module PerfectShape
|
25
|
+
class Circle < Ellipse
|
26
|
+
MESSAGE_WIDTH_AND_HEIGHT_AND_DIAMETER_NOT_EQUAL = 'Circle width, height, and diameter must all be equal if more than one is specified; or otherwise keep only one of them in arguments!'
|
27
|
+
MESSAGE_RADIUS_X_AND_RADIUS_Y_AND_RADIUS_NOT_EQUAL = 'Circle radius_x, radius_y, and radius must all be equal if more than one is specified; or otherwise keep only one of them in arguments!'
|
28
|
+
|
29
|
+
def initialize(x: 0, y: 0, width: nil, height: nil, diameter: nil, center_x: nil, center_y: nil, radius_x: nil, radius_y: nil, radius: nil)
|
30
|
+
raise MESSAGE_WIDTH_AND_HEIGHT_AND_DIAMETER_NOT_EQUAL if (diameter && width && diameter != width) || (diameter && height && diameter != height) || (width && height && width != height)
|
31
|
+
raise MESSAGE_RADIUS_X_AND_RADIUS_Y_AND_RADIUS_NOT_EQUAL if (radius && radius_x && radius != radius_x) || (radius && radius_y && radius != radius_y) || (radius_x && radius_y && radius_x != radius_y)
|
32
|
+
if center_x && center_y && (radius || radius_x || radius_y)
|
33
|
+
radius ||= radius_x || radius_y
|
34
|
+
self.radius = radius
|
35
|
+
super(center_x: center_x, center_y: center_y, radius_x: self.radius_x, radius_y: self.radius_y)
|
36
|
+
else
|
37
|
+
diameter ||= width || height || 1
|
38
|
+
self.diameter = diameter
|
39
|
+
super(x: x, y: y, width: self.width, height: self.height)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def diameter
|
44
|
+
@radius ? @radius * BigDecimal('2.0') : @diameter
|
45
|
+
end
|
46
|
+
|
47
|
+
def radius
|
48
|
+
@diameter ? @diameter / BigDecimal('2.0') : @radius
|
49
|
+
end
|
50
|
+
|
51
|
+
# Sets length, normalizing to BigDecimal
|
52
|
+
def diameter=(value)
|
53
|
+
@diameter = BigDecimal(value.to_s)
|
54
|
+
@radius = nil
|
55
|
+
self.width = value unless width == value
|
56
|
+
self.height = value unless height == value
|
57
|
+
end
|
58
|
+
|
59
|
+
# Sets radius, normalizing to BigDecimal
|
60
|
+
def radius=(value)
|
61
|
+
@radius = BigDecimal(value.to_s)
|
62
|
+
@diameter = nil
|
63
|
+
self.radius_x = value unless width == value
|
64
|
+
self.radius_y = value unless height == value
|
65
|
+
end
|
66
|
+
|
67
|
+
def width=(value)
|
68
|
+
super
|
69
|
+
self.diameter = value unless diameter == value
|
70
|
+
self.height = value unless height == value
|
71
|
+
end
|
72
|
+
|
73
|
+
def height=(value)
|
74
|
+
super
|
75
|
+
self.diameter = value unless diameter == value
|
76
|
+
self.width = value unless width == value
|
77
|
+
end
|
78
|
+
|
79
|
+
def radius_x=(value)
|
80
|
+
super
|
81
|
+
self.radius = value unless radius == value
|
82
|
+
self.radius_y = value unless radius_y == value
|
83
|
+
end
|
84
|
+
|
85
|
+
def radius_y=(value)
|
86
|
+
super
|
87
|
+
self.radius = value unless radius == value
|
88
|
+
self.radius_x = value unless radius_x == value
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,78 @@
|
|
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/arc'
|
23
|
+
|
24
|
+
module PerfectShape
|
25
|
+
# Mostly ported from java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/Ellipse2D.html
|
26
|
+
class Ellipse < Arc
|
27
|
+
MESSAGE_CANNOT_UPDATE_ATTRIUBTE = "Ellipse %s cannot be updated. If you want to update type, use Arc instead!"
|
28
|
+
|
29
|
+
def initialize(x: 0, y: 0, width: 1, height: 1, center_x: nil, center_y: nil, radius_x: nil, radius_y: nil)
|
30
|
+
super
|
31
|
+
@initialized = true
|
32
|
+
end
|
33
|
+
|
34
|
+
def type=(value)
|
35
|
+
if @initialized
|
36
|
+
raise MESSAGE_CANNOT_UPDATE_ATTRIUBTE % 'type'
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def start=(value)
|
43
|
+
if @initialized
|
44
|
+
raise MESSAGE_CANNOT_UPDATE_ATTRIUBTE % 'start'
|
45
|
+
else
|
46
|
+
super
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def extent=(value)
|
51
|
+
if @initialized
|
52
|
+
raise MESSAGE_CANNOT_UPDATE_ATTRIUBTE % 'extent'
|
53
|
+
else
|
54
|
+
super
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Checks if ellipse contains point denoted by point (two-number Array or x, y args)
|
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 {@code true} if the point lies within the bound of
|
64
|
+
# the ellipse, {@code false} if the point lies outside of the
|
65
|
+
# ellipse'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
|
+
ellw = self.width
|
70
|
+
return false if ellw <= 0.0
|
71
|
+
normx = (x - self.x) / ellw - 0.5
|
72
|
+
ellh = self.height
|
73
|
+
return false if ellh <= 0.0
|
74
|
+
normy = (y - self.y) / ellh - 0.5
|
75
|
+
(normx * normx + normy * normy) < 0.25
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/perfect_shape/math.rb
CHANGED
@@ -15,6 +15,28 @@ module PerfectShape
|
|
15
15
|
def degrees_to_radians(degrees)
|
16
16
|
(Math::PI/180)*degrees
|
17
17
|
end
|
18
|
+
|
19
|
+
# Normalizes the specified angle into the range -180 to 180.
|
20
|
+
def normalize_degrees(angle)
|
21
|
+
if angle > 180.0
|
22
|
+
if angle <= (180.0 + 360.0)
|
23
|
+
angle = angle - 360.0
|
24
|
+
else
|
25
|
+
angle = Math.ieee_remainder(angle, 360.0)
|
26
|
+
# IEEEremainder can return -180 here for some input values...
|
27
|
+
angle = 180.0 if angle == -180.0
|
28
|
+
end
|
29
|
+
elsif angle <= -180.0
|
30
|
+
if angle > (-180.0 - 360.0)
|
31
|
+
angle = angle + 360.0
|
32
|
+
else
|
33
|
+
angle = Math.ieee_remainder(angle, 360.0)
|
34
|
+
# IEEEremainder can return -180 here for some input values...
|
35
|
+
angle = 180.0 if angle == -180.0
|
36
|
+
end
|
37
|
+
end
|
38
|
+
angle
|
39
|
+
end
|
18
40
|
|
19
41
|
# Computes the remainder operation on two arguments as prescribed by the IEEE 754 standard.
|
20
42
|
#
|
@@ -38,7 +38,7 @@ module PerfectShape
|
|
38
38
|
def contain?(x_or_point, y = nil)
|
39
39
|
x, y = normalize_point(x_or_point, y)
|
40
40
|
return unless x && y
|
41
|
-
x.between?(
|
41
|
+
x.between?(self.x, self.x + self.width) && y.between?(self.y, self.y + self.height)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
@@ -53,5 +53,13 @@ module PerfectShape
|
|
53
53
|
def height=(value)
|
54
54
|
@height = BigDecimal(value.to_s)
|
55
55
|
end
|
56
|
+
|
57
|
+
def center_x
|
58
|
+
@x + (@width/BigDecimal('2.0')) if @x && @width
|
59
|
+
end
|
60
|
+
|
61
|
+
def center_y
|
62
|
+
@y + (@height/BigDecimal('2.0')) if @y && @height
|
63
|
+
end
|
56
64
|
end
|
57
65
|
end
|
data/lib/perfect_shape/shape.rb
CHANGED
@@ -20,6 +20,7 @@
|
|
20
20
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
21
|
|
22
22
|
module PerfectShape
|
23
|
+
# Superclass of all shapes
|
23
24
|
class Shape
|
24
25
|
# Normalizes point args whether two-number Array or x, y args returning
|
25
26
|
# normalized point array of two BigDecimal's
|
@@ -0,0 +1,56 @@
|
|
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/rectangle'
|
23
|
+
|
24
|
+
module PerfectShape
|
25
|
+
class Square < Rectangle
|
26
|
+
MESSAGE_WIDTH_AND_HEIGHT_AND_LENGTH_NOT_EQUAL = 'Square width, height, and length must all be equal if more than one is specified; or otherwise keep only one of them in arguments!'
|
27
|
+
|
28
|
+
attr_reader :length
|
29
|
+
|
30
|
+
# Constructs with x, y, length (optionally width or height can be passed as alias for length)
|
31
|
+
def initialize(x: 0, y: 0, length: nil, width: nil, height: nil)
|
32
|
+
raise MESSAGE_WIDTH_AND_HEIGHT_AND_LENGTH_NOT_EQUAL if (length && width && length != width) || (length && height && length != height) || (width && height && width != height)
|
33
|
+
length ||= width || height || 1
|
34
|
+
super(x: x, y: y, width: length, height: length)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Sets length, normalizing to BigDecimal
|
38
|
+
def length=(value)
|
39
|
+
@length = BigDecimal(value.to_s)
|
40
|
+
self.width = value unless width == value
|
41
|
+
self.height = value unless height == value
|
42
|
+
end
|
43
|
+
|
44
|
+
def width=(value)
|
45
|
+
super
|
46
|
+
self.length = value unless length == value
|
47
|
+
self.height = value unless height == value
|
48
|
+
end
|
49
|
+
|
50
|
+
def height=(value)
|
51
|
+
super
|
52
|
+
self.length = value unless length == value
|
53
|
+
self.width = value unless width == value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/perfect-shape.gemspec
CHANGED
@@ -2,17 +2,17 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: perfect-shape 0.0.
|
5
|
+
# stub: perfect-shape 0.0.6 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "perfect-shape".freeze
|
9
|
-
s.version = "0.0.
|
9
|
+
s.version = "0.0.6"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["Andy Maleh".freeze]
|
14
|
-
s.date = "2021-12-
|
15
|
-
s.description = "Perfect Shape is a collection of pure Ruby geometric algorithms that are mostly useful for GUI manipulation like checking containment of a mouse click point in popular geometry shapes such as rectangle, square, arc, ellipse, circle, polygon, polyline,
|
14
|
+
s.date = "2021-12-16"
|
15
|
+
s.description = "Perfect Shape is a collection of pure Ruby geometric algorithms that are mostly useful for GUI manipulation like checking containment of a mouse click point in popular geometry shapes such as rectangle, square, arc (open, chord, and pie), ellipse, circle, polygon, polyline, polyquad, polycubic, 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
16
|
s.email = "andy.am@gmail.com".freeze
|
17
17
|
s.extra_rdoc_files = [
|
18
18
|
"CHANGELOG.md",
|
@@ -26,11 +26,14 @@ Gem::Specification.new do |s|
|
|
26
26
|
"VERSION",
|
27
27
|
"lib/perfect-shape.rb",
|
28
28
|
"lib/perfect_shape/arc.rb",
|
29
|
+
"lib/perfect_shape/circle.rb",
|
30
|
+
"lib/perfect_shape/ellipse.rb",
|
29
31
|
"lib/perfect_shape/line.rb",
|
30
32
|
"lib/perfect_shape/math.rb",
|
31
33
|
"lib/perfect_shape/rectangle.rb",
|
32
34
|
"lib/perfect_shape/rectangular_shape.rb",
|
33
35
|
"lib/perfect_shape/shape.rb",
|
36
|
+
"lib/perfect_shape/square.rb",
|
34
37
|
"perfect-shape.gemspec"
|
35
38
|
]
|
36
39
|
s.homepage = "http://github.com/AndyObtiva/perfect-shape".freeze
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: perfect-shape
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Maleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-12-
|
11
|
+
date: 2021-12-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rdoc
|
@@ -82,10 +82,10 @@ dependencies:
|
|
82
82
|
version: '0'
|
83
83
|
description: Perfect Shape is a collection of pure Ruby geometric algorithms that
|
84
84
|
are mostly useful for GUI manipulation like checking containment of a mouse click
|
85
|
-
point in popular geometry shapes such as rectangle, square, arc,
|
86
|
-
|
87
|
-
curves. Additionally, it contains some purely
|
88
|
-
(also known as IEEE-754 remainder).
|
85
|
+
point in popular geometry shapes such as rectangle, square, arc (open, chord, and
|
86
|
+
pie), ellipse, circle, polygon, polyline, polyquad, polycubic, and paths containing
|
87
|
+
lines, bezier curves, and quadratic curves. Additionally, it contains some purely
|
88
|
+
mathematical algorithms like IEEEremainder (also known as IEEE-754 remainder).
|
89
89
|
email: andy.am@gmail.com
|
90
90
|
executables: []
|
91
91
|
extensions: []
|
@@ -100,11 +100,14 @@ files:
|
|
100
100
|
- VERSION
|
101
101
|
- lib/perfect-shape.rb
|
102
102
|
- lib/perfect_shape/arc.rb
|
103
|
+
- lib/perfect_shape/circle.rb
|
104
|
+
- lib/perfect_shape/ellipse.rb
|
103
105
|
- lib/perfect_shape/line.rb
|
104
106
|
- lib/perfect_shape/math.rb
|
105
107
|
- lib/perfect_shape/rectangle.rb
|
106
108
|
- lib/perfect_shape/rectangular_shape.rb
|
107
109
|
- lib/perfect_shape/shape.rb
|
110
|
+
- lib/perfect_shape/square.rb
|
108
111
|
- perfect-shape.gemspec
|
109
112
|
homepage: http://github.com/AndyObtiva/perfect-shape
|
110
113
|
licenses:
|