perfect-shape 0.3.3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -3
- data/README.md +138 -32
- data/VERSION +1 -1
- data/lib/perfect-shape.rb +1 -1
- data/lib/perfect_shape/affine_transform.rb +235 -0
- data/lib/perfect_shape/arc.rb +4 -4
- data/lib/perfect_shape/composite_shape.rb +4 -3
- data/lib/perfect_shape/cubic_bezier_curve.rb +42 -37
- data/lib/perfect_shape/ellipse.rb +1 -2
- data/lib/perfect_shape/line.rb +37 -12
- data/lib/perfect_shape/multi_point.rb +20 -5
- data/lib/perfect_shape/path.rb +64 -15
- data/lib/perfect_shape/point.rb +23 -7
- data/lib/perfect_shape/polygon.rb +2 -2
- data/lib/perfect_shape/quadratic_bezier_curve.rb +14 -13
- data/lib/perfect_shape/rectangle.rb +42 -2
- data/lib/perfect_shape/shape.rb +6 -15
- data/perfect-shape.gemspec +5 -4
- metadata +11 -8
data/lib/perfect_shape/path.rb
CHANGED
@@ -27,7 +27,6 @@ require 'perfect_shape/cubic_bezier_curve'
|
|
27
27
|
require 'perfect_shape/multi_point'
|
28
28
|
|
29
29
|
module PerfectShape
|
30
|
-
# Mostly ported from java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/Path2D.html
|
31
30
|
class Path < Shape
|
32
31
|
include MultiPoint
|
33
32
|
include Equalizer.new(:shapes, :closed, :winding_rule)
|
@@ -114,21 +113,26 @@ module PerfectShape
|
|
114
113
|
# @return true if the point lies within the bound of
|
115
114
|
# the path or false if the point lies outside of the
|
116
115
|
# path's bounds.
|
117
|
-
def contain?(x_or_point, y = nil)
|
118
|
-
x, y = normalize_point(x_or_point, y)
|
116
|
+
def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
|
117
|
+
x, y = Point.normalize_point(x_or_point, y)
|
119
118
|
return unless x && y
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
return false if shapes.count < 2
|
124
|
-
mask = winding_rule == :wind_non_zero ? -1 : 1
|
125
|
-
(point_crossings(x, y) & mask) != 0
|
119
|
+
|
120
|
+
if outline
|
121
|
+
disconnected_shapes.any? {|shape| shape.contain?(x, y, outline: true, distance_tolerance: distance_tolerance) }
|
126
122
|
else
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
123
|
+
if (x * 0.0 + y * 0.0) == 0.0
|
124
|
+
# N * 0.0 is 0.0 only if N is finite.
|
125
|
+
# Here we know that both x and y are finite.
|
126
|
+
return false if shapes.count < 2
|
127
|
+
mask = winding_rule == :wind_non_zero ? -1 : 1
|
128
|
+
(point_crossings(x, y) & mask) != 0
|
129
|
+
else
|
130
|
+
# Either x or y was infinite or NaN.
|
131
|
+
# A NaN always produces a negative response to any test
|
132
|
+
# and Infinity values cannot be "inside" any path so
|
133
|
+
# they should return false as well.
|
134
|
+
false
|
135
|
+
end
|
132
136
|
end
|
133
137
|
end
|
134
138
|
|
@@ -144,7 +148,7 @@ module PerfectShape
|
|
144
148
|
# The caller must check for NaN values.
|
145
149
|
# The caller may also reject infinite values as well.
|
146
150
|
def point_crossings(x_or_point, y = nil)
|
147
|
-
x, y = normalize_point(x_or_point, y)
|
151
|
+
x, y = Point.normalize_point(x_or_point, y)
|
148
152
|
return unless x && y
|
149
153
|
return 0 if shapes.count == 0
|
150
154
|
movx = movy = curx = cury = endx = endy = 0
|
@@ -218,5 +222,50 @@ module PerfectShape
|
|
218
222
|
end
|
219
223
|
crossings
|
220
224
|
end
|
225
|
+
|
226
|
+
# Disconnected shapes have their start point filled in
|
227
|
+
# so that each shape does not depend on the previous shape
|
228
|
+
# to determine its start point.
|
229
|
+
#
|
230
|
+
# Also, if a point is followed by a non-point shape, it is removed
|
231
|
+
# since it is augmented to the following shape as its start point.
|
232
|
+
#
|
233
|
+
# Lastly, if the path is closed, an extra shape is
|
234
|
+
# added to represent the line connecting the last point to the first
|
235
|
+
def disconnected_shapes
|
236
|
+
initial_point = start_point = @shapes.first.to_a.map {|n| BigDecimal(n.to_s)}
|
237
|
+
final_point = nil
|
238
|
+
the_disconnected_shapes = @shapes.drop(1).map do |shape|
|
239
|
+
case shape
|
240
|
+
when Point
|
241
|
+
disconnected_shape = Point.new(*shape.to_a)
|
242
|
+
start_point = shape.to_a
|
243
|
+
final_point = disconnected_shape.to_a
|
244
|
+
nil
|
245
|
+
when Array
|
246
|
+
disconnected_shape = Point.new(*shape.map {|n| BigDecimal(n.to_s)})
|
247
|
+
start_point = shape.map {|n| BigDecimal(n.to_s)}
|
248
|
+
final_point = disconnected_shape.to_a
|
249
|
+
nil
|
250
|
+
when Line
|
251
|
+
disconnected_shape = Line.new(points: [start_point.to_a, shape.points.last])
|
252
|
+
start_point = shape.points.last.to_a
|
253
|
+
final_point = disconnected_shape.points.last.to_a
|
254
|
+
disconnected_shape
|
255
|
+
when QuadraticBezierCurve
|
256
|
+
disconnected_shape = QuadraticBezierCurve.new(points: [start_point.to_a] + shape.points)
|
257
|
+
start_point = shape.points.last.to_a
|
258
|
+
final_point = disconnected_shape.points.last.to_a
|
259
|
+
disconnected_shape
|
260
|
+
when CubicBezierCurve
|
261
|
+
disconnected_shape = CubicBezierCurve.new(points: [start_point.to_a] + shape.points)
|
262
|
+
start_point = shape.points.last.to_a
|
263
|
+
final_point = disconnected_shape.points.last.to_a
|
264
|
+
disconnected_shape
|
265
|
+
end
|
266
|
+
end
|
267
|
+
the_disconnected_shapes << Line.new(points: [final_point, initial_point]) if closed?
|
268
|
+
the_disconnected_shapes.compact
|
269
|
+
end
|
221
270
|
end
|
222
271
|
end
|
data/lib/perfect_shape/point.rb
CHANGED
@@ -27,12 +27,27 @@ module PerfectShape
|
|
27
27
|
class Point < Shape
|
28
28
|
class << self
|
29
29
|
def point_distance(x, y, px, py)
|
30
|
-
x = BigDecimal(x.to_s)
|
31
|
-
y = BigDecimal(y.to_s)
|
32
|
-
px = BigDecimal(px.to_s)
|
33
|
-
py = BigDecimal(py.to_s)
|
30
|
+
x = x.is_a?(BigDecimal) ? x : BigDecimal(x.to_s)
|
31
|
+
y = y.is_a?(BigDecimal) ? y : BigDecimal(y.to_s)
|
32
|
+
px = px.is_a?(BigDecimal) ? px : BigDecimal(px.to_s)
|
33
|
+
py = py.is_a?(BigDecimal) ? py : BigDecimal(py.to_s)
|
34
34
|
BigDecimal(Math.sqrt((px - x)**2 + (py - y)**2).to_s)
|
35
35
|
end
|
36
|
+
|
37
|
+
# Normalizes point args whether two-number Array or x, y args returning
|
38
|
+
# normalized point array of two BigDecimal's
|
39
|
+
#
|
40
|
+
# @param x_or_point The point or X coordinate of the point to test.
|
41
|
+
# @param y The Y coordinate of the point to test.
|
42
|
+
#
|
43
|
+
# @return Array of x and y BigDecimal's representing point
|
44
|
+
def normalize_point(x_or_point, y = nil)
|
45
|
+
x = x_or_point
|
46
|
+
x, y = x if y.nil? && x_or_point.is_a?(Array) && x_or_point.size == 2
|
47
|
+
x = x.is_a?(BigDecimal) ? x : BigDecimal(x.to_s)
|
48
|
+
y = y.is_a?(BigDecimal) ? y : BigDecimal(y.to_s)
|
49
|
+
[x, y]
|
50
|
+
end
|
36
51
|
end
|
37
52
|
|
38
53
|
include PointLocation
|
@@ -67,16 +82,17 @@ module PerfectShape
|
|
67
82
|
#
|
68
83
|
# @return {@code true} if the point is close enough within distance tolerance,
|
69
84
|
# {@code false} if the point is too far.
|
70
|
-
def contain?(x_or_point, y = nil, outline:
|
71
|
-
x, y = normalize_point(x_or_point, y)
|
85
|
+
def contain?(x_or_point, y = nil, outline: true, distance_tolerance: 0)
|
86
|
+
x, y = Point.normalize_point(x_or_point, y)
|
72
87
|
return unless x && y
|
73
88
|
distance_tolerance = BigDecimal(distance_tolerance.to_s)
|
74
89
|
point_distance(x, y) <= distance_tolerance
|
75
90
|
end
|
76
91
|
|
77
92
|
def point_distance(x_or_point, y = nil)
|
78
|
-
x, y = normalize_point(x_or_point, y)
|
93
|
+
x, y = Point.normalize_point(x_or_point, y)
|
79
94
|
return unless x && y
|
95
|
+
|
80
96
|
Point.point_distance(self.x, self.y, x, y)
|
81
97
|
end
|
82
98
|
|
@@ -20,10 +20,10 @@
|
|
20
20
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
21
|
|
22
22
|
require 'perfect_shape/shape'
|
23
|
+
require 'perfect_shape/point'
|
23
24
|
require 'perfect_shape/multi_point'
|
24
25
|
|
25
26
|
module PerfectShape
|
26
|
-
# Mostly ported from java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/Polygon.html
|
27
27
|
class Polygon < Shape
|
28
28
|
include MultiPoint
|
29
29
|
include Equalizer.new(:points)
|
@@ -38,7 +38,7 @@ module PerfectShape
|
|
38
38
|
# the polygon, {@code false} if the point lies outside of the
|
39
39
|
# polygon's bounds.
|
40
40
|
def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
|
41
|
-
x, y = normalize_point(x_or_point, y)
|
41
|
+
x, y = Point.normalize_point(x_or_point, y)
|
42
42
|
return unless x && y
|
43
43
|
if outline
|
44
44
|
edges.any? { |edge| edge.contain?(x, y, distance_tolerance: distance_tolerance) }
|
@@ -20,10 +20,10 @@
|
|
20
20
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
21
|
|
22
22
|
require 'perfect_shape/shape'
|
23
|
+
require 'perfect_shape/point'
|
23
24
|
require 'perfect_shape/multi_point'
|
24
25
|
|
25
26
|
module PerfectShape
|
26
|
-
# Mostly ported from java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/QuadCurve2D.html
|
27
27
|
class QuadraticBezierCurve < Shape
|
28
28
|
class << self
|
29
29
|
# Calculates the number of times the quadratic bézier curve from (x1,y1) to (x2,y2)
|
@@ -80,7 +80,7 @@ module PerfectShape
|
|
80
80
|
# the quadratic bézier curve, {@code false} if the point lies outside of the
|
81
81
|
# quadratic bézier curve's bounds.
|
82
82
|
def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
|
83
|
-
x, y = normalize_point(x_or_point, y)
|
83
|
+
x, y = Point.normalize_point(x_or_point, y)
|
84
84
|
return unless x && y
|
85
85
|
|
86
86
|
x1 = points[0][0]
|
@@ -91,8 +91,9 @@ module PerfectShape
|
|
91
91
|
y2 = points[2][1]
|
92
92
|
|
93
93
|
if outline
|
94
|
+
distance_tolerance = BigDecimal(distance_tolerance.to_s)
|
94
95
|
minimum_distance_threshold = OUTLINE_MINIMUM_DISTANCE_THRESHOLD + distance_tolerance
|
95
|
-
|
96
|
+
point_distance(x, y, minimum_distance_threshold: minimum_distance_threshold) < minimum_distance_threshold
|
96
97
|
else
|
97
98
|
# We have a convex shape bounded by quad curve Pc(t)
|
98
99
|
# and ine Pl(t).
|
@@ -189,13 +190,13 @@ module PerfectShape
|
|
189
190
|
# +1 is added for each crossing where the Y coordinate is increasing
|
190
191
|
# -1 is added for each crossing where the Y coordinate is decreasing
|
191
192
|
def point_crossings(x_or_point, y = nil, level = 0)
|
192
|
-
x, y = normalize_point(x_or_point, y)
|
193
|
+
x, y = Point.normalize_point(x_or_point, y)
|
193
194
|
return unless x && y
|
194
195
|
QuadraticBezierCurve.point_crossings(points[0][0], points[0][1], points[1][0], points[1][1], points[2][0], points[2][1], x, y, level)
|
195
196
|
end
|
196
197
|
|
197
|
-
|
198
198
|
# The center point on the outline of the curve
|
199
|
+
# in Array format as pair of (x, y) coordinates
|
199
200
|
def curve_center_point
|
200
201
|
subdivisions.last.points[0]
|
201
202
|
end
|
@@ -226,12 +227,12 @@ module PerfectShape
|
|
226
227
|
ctrly = points[1][1]
|
227
228
|
x2 = points[2][0]
|
228
229
|
y2 = points[2][1]
|
229
|
-
ctrlx1 = (x1 + ctrlx) / 2
|
230
|
-
ctrly1 = (y1 + ctrly) / 2
|
231
|
-
ctrlx2 = (x2 + ctrlx) / 2
|
232
|
-
ctrly2 = (y2 + ctrly) / 2
|
233
|
-
centerx = (ctrlx1 + ctrlx2) / 2
|
234
|
-
centery = (ctrly1 + ctrly2) / 2
|
230
|
+
ctrlx1 = BigDecimal((x1 + ctrlx).to_s) / 2
|
231
|
+
ctrly1 = BigDecimal((y1 + ctrly).to_s) / 2
|
232
|
+
ctrlx2 = BigDecimal((x2 + ctrlx).to_s) / 2
|
233
|
+
ctrly2 = BigDecimal((y2 + ctrly).to_s) / 2
|
234
|
+
centerx = BigDecimal((ctrlx1 + ctrlx2).to_s) / 2
|
235
|
+
centery = BigDecimal((ctrly1 + ctrly2).to_s) / 2
|
235
236
|
|
236
237
|
default_subdivisions = [
|
237
238
|
QuadraticBezierCurve.new(points: [x1, y1, ctrlx1, ctrly1, centerx, centery]),
|
@@ -245,8 +246,8 @@ module PerfectShape
|
|
245
246
|
end
|
246
247
|
end
|
247
248
|
|
248
|
-
def
|
249
|
-
x, y = normalize_point(x_or_point, y)
|
249
|
+
def point_distance(x_or_point, y = nil, minimum_distance_threshold: OUTLINE_MINIMUM_DISTANCE_THRESHOLD)
|
250
|
+
x, y = Point.normalize_point(x_or_point, y)
|
250
251
|
return unless x && y
|
251
252
|
|
252
253
|
point = Point.new(x, y)
|
@@ -21,13 +21,25 @@
|
|
21
21
|
|
22
22
|
require 'perfect_shape/shape'
|
23
23
|
require 'perfect_shape/rectangular_shape'
|
24
|
+
require 'perfect_shape/point'
|
24
25
|
require 'perfect_shape/line'
|
25
26
|
|
26
27
|
module PerfectShape
|
27
|
-
# Mostly ported from java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/Rectangle2D.html
|
28
28
|
class Rectangle < Shape
|
29
29
|
include RectangularShape
|
30
30
|
include Equalizer.new(:x, :y, :width, :height)
|
31
|
+
|
32
|
+
# bitmask indicating a point lies to the left
|
33
|
+
OUT_LEFT = 1
|
34
|
+
|
35
|
+
# bitmask indicating a point lies above
|
36
|
+
OUT_TOP = 2
|
37
|
+
|
38
|
+
# bitmask indicating a point lies to the right
|
39
|
+
OUT_RIGHT = 4
|
40
|
+
|
41
|
+
# bitmask indicating a point lies below
|
42
|
+
OUT_BOTTOM = 8
|
31
43
|
|
32
44
|
# Checks if rectangle contains point (two-number Array or x, y args)
|
33
45
|
#
|
@@ -38,8 +50,9 @@ module PerfectShape
|
|
38
50
|
# the rectangle, {@code false} if the point lies outside of the
|
39
51
|
# rectangle's bounds.
|
40
52
|
def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
|
41
|
-
x, y = normalize_point(x_or_point, y)
|
53
|
+
x, y = Point.normalize_point(x_or_point, y)
|
42
54
|
return unless x && y
|
55
|
+
|
43
56
|
if outline
|
44
57
|
edges.any? { |edge| edge.contain?(x, y, distance_tolerance: distance_tolerance) }
|
45
58
|
else
|
@@ -55,5 +68,32 @@ module PerfectShape
|
|
55
68
|
Line.new(points: [[self.x, self.y + height], [self.x, self.y]])
|
56
69
|
]
|
57
70
|
end
|
71
|
+
|
72
|
+
# Returns out state for specified point (x,y): (left, right, top, bottom)
|
73
|
+
#
|
74
|
+
# It can be 0 meaning not outside the rectangle,
|
75
|
+
# or if outside the rectangle, then a bit mask
|
76
|
+
# combination of OUT_LEFT, OUT_RIGHT, OUT_TOP, or OUT_BOTTOM
|
77
|
+
def out_state(x_or_point, y = nil)
|
78
|
+
x, y = Point.normalize_point(x_or_point, y)
|
79
|
+
return unless x && y
|
80
|
+
|
81
|
+
out = 0
|
82
|
+
if self.width <= 0
|
83
|
+
out |= OUT_LEFT | OUT_RIGHT
|
84
|
+
elsif x < self.x
|
85
|
+
out |= OUT_LEFT
|
86
|
+
elsif x > self.x + self.width
|
87
|
+
out |= OUT_RIGHT
|
88
|
+
end
|
89
|
+
if self.height <= 0
|
90
|
+
out |= OUT_TOP | OUT_BOTTOM
|
91
|
+
elsif y < self.y
|
92
|
+
out |= OUT_TOP
|
93
|
+
elsif y > self.y + self.height
|
94
|
+
out |= OUT_BOTTOM
|
95
|
+
end
|
96
|
+
out
|
97
|
+
end
|
58
98
|
end
|
59
99
|
end
|
data/lib/perfect_shape/shape.rb
CHANGED
@@ -51,6 +51,12 @@ module PerfectShape
|
|
51
51
|
max_y - min_y if max_y && min_y
|
52
52
|
end
|
53
53
|
|
54
|
+
# Center point is `[center_x, center_y]`
|
55
|
+
# Returns `nil` if either center_x or center_y are `nil`
|
56
|
+
def center_point
|
57
|
+
[center_x, center_y] unless center_x.nil? || center_y.nil?
|
58
|
+
end
|
59
|
+
|
54
60
|
# center_x is min_x + width/2.0 by default
|
55
61
|
# Returns nil if min_x or width are nil
|
56
62
|
def center_x
|
@@ -69,21 +75,6 @@ module PerfectShape
|
|
69
75
|
Rectangle.new(x: min_x, y: min_y, width: width, height: height)
|
70
76
|
end
|
71
77
|
|
72
|
-
# Normalizes point args whether two-number Array or x, y args returning
|
73
|
-
# normalized point array of two BigDecimal's
|
74
|
-
#
|
75
|
-
# @param x_or_point The point or X coordinate of the point to test.
|
76
|
-
# @param y The Y coordinate of the point to test.
|
77
|
-
#
|
78
|
-
# @return Array of x and y BigDecimal's representing point
|
79
|
-
def normalize_point(x_or_point, y = nil)
|
80
|
-
x = x_or_point
|
81
|
-
x, y = x if y.nil? && x_or_point.is_a?(Array) && x_or_point.size == 2
|
82
|
-
x = BigDecimal(x.to_s)
|
83
|
-
y = BigDecimal(y.to_s)
|
84
|
-
[x, y]
|
85
|
-
end
|
86
|
-
|
87
78
|
# Subclasses must implement
|
88
79
|
def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
|
89
80
|
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.
|
5
|
+
# stub: perfect-shape 0.5.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "perfect-shape".freeze
|
9
|
-
s.version = "0.
|
9
|
+
s.version = "0.5.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["Andy Maleh".freeze]
|
14
|
-
s.date = "2022-01-
|
15
|
-
s.description = "Perfect Shape is a collection of pure Ruby geometric algorithms that are mostly useful for GUI manipulation like checking containment of a mouse click point in popular geometry shapes such as rectangle, square, arc (open, chord, and pie), ellipse, circle, polygon, and paths containing lines, quadratic b\u00E9zier curves, and cubic bezier curves (including both Ray Casting Algorithm, aka Even-odd Rule, and Winding Number Algorithm, aka Nonzero Rule). Additionally, it contains some purely mathematical algorithms like IEEEremainder (also known as IEEE-754 remainder).".freeze
|
14
|
+
s.date = "2022-01-18"
|
15
|
+
s.description = "Perfect Shape is a collection of pure Ruby geometric algorithms that are mostly useful for GUI manipulation like checking viewport rectangle intersection or containment of a mouse click point in popular geometry shapes such as rectangle, square, arc (open, chord, and pie), ellipse, circle, polygon, and paths containing lines, quadratic b\u00E9zier curves, and cubic bezier curves, potentially with affine transforms applied like translation, scale, rotation, shear/skew, and inversion (including both Ray Casting Algorithm, aka Even-odd Rule, and Winding Number Algorithm, aka Nonzero Rule). Additionally, it contains some purely mathematical algorithms like IEEEremainder (also known as IEEE-754 remainder).".freeze
|
16
16
|
s.email = "andy.am@gmail.com".freeze
|
17
17
|
s.extra_rdoc_files = [
|
18
18
|
"CHANGELOG.md",
|
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
|
|
25
25
|
"README.md",
|
26
26
|
"VERSION",
|
27
27
|
"lib/perfect-shape.rb",
|
28
|
+
"lib/perfect_shape/affine_transform.rb",
|
28
29
|
"lib/perfect_shape/arc.rb",
|
29
30
|
"lib/perfect_shape/circle.rb",
|
30
31
|
"lib/perfect_shape/composite_shape.rb",
|
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.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Maleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-01-
|
11
|
+
date: 2022-01-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: equalizer
|
@@ -95,12 +95,14 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
description: Perfect Shape is a collection of pure Ruby geometric algorithms that
|
98
|
-
are mostly useful for GUI manipulation like checking
|
99
|
-
point in popular geometry shapes such as rectangle,
|
100
|
-
pie), ellipse, circle, polygon, and paths containing
|
101
|
-
|
102
|
-
|
103
|
-
|
98
|
+
are mostly useful for GUI manipulation like checking viewport rectangle intersection
|
99
|
+
or containment of a mouse click point in popular geometry shapes such as rectangle,
|
100
|
+
square, arc (open, chord, and pie), ellipse, circle, polygon, and paths containing
|
101
|
+
lines, quadratic bézier curves, and cubic bezier curves, potentially with affine
|
102
|
+
transforms applied like translation, scale, rotation, shear/skew, and inversion
|
103
|
+
(including both Ray Casting Algorithm, aka Even-odd Rule, and Winding Number Algorithm,
|
104
|
+
aka Nonzero Rule). Additionally, it contains some purely mathematical algorithms
|
105
|
+
like IEEEremainder (also known as IEEE-754 remainder).
|
104
106
|
email: andy.am@gmail.com
|
105
107
|
executables: []
|
106
108
|
extensions: []
|
@@ -114,6 +116,7 @@ files:
|
|
114
116
|
- README.md
|
115
117
|
- VERSION
|
116
118
|
- lib/perfect-shape.rb
|
119
|
+
- lib/perfect_shape/affine_transform.rb
|
117
120
|
- lib/perfect_shape/arc.rb
|
118
121
|
- lib/perfect_shape/circle.rb
|
119
122
|
- lib/perfect_shape/composite_shape.rb
|