perfect-shape 0.1.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -37,79 +37,85 @@ module PerfectShape
37
37
  # @return {@code true} if the point lies within the bound of
38
38
  # the polygon, {@code false} if the point lies outside of the
39
39
  # polygon's bounds.
40
- def contain?(x_or_point, y = nil)
40
+ def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
41
41
  x, y = normalize_point(x_or_point, y)
42
42
  return unless x && y
43
- npoints = points.count
44
- xpoints = points.map(&:first)
45
- ypoints = points.map(&:last)
46
- return false if npoints <= 2 || !bounding_box.contain?(x, y)
47
- hits = 0
48
-
49
- lastx = xpoints[npoints - 1]
50
- lasty = ypoints[npoints - 1]
51
-
52
- # Walk the edges of the polygon
53
- npoints.times do |i|
54
- curx = xpoints[i]
55
- cury = ypoints[i]
56
-
57
- if cury == lasty
58
- lastx = curx
59
- lasty = cury
60
- next
61
- end
62
-
63
- if curx < lastx
64
- if x >= lastx
65
- lastx = curx
66
- lasty = cury
67
- next
68
- end
69
- leftx = curx
70
- else
71
- if x >= curx
72
- lastx = curx
73
- lasty = cury
74
- next
75
- end
76
- leftx = lastx
43
+ if outline
44
+ points.zip(points.rotate(1)).any? do |point1, point2|
45
+ Line.new(points: [[point1.first, point1.last], [point2.first, point2.last]]).contain?(x, y, distance_tolerance: distance_tolerance)
77
46
  end
78
-
79
- if cury < lasty
80
- if y < cury || y >= lasty
47
+ else
48
+ npoints = points.count
49
+ xpoints = points.map(&:first)
50
+ ypoints = points.map(&:last)
51
+ return false if npoints <= 2 || !bounding_box.contain?(x, y)
52
+ hits = 0
53
+
54
+ lastx = xpoints[npoints - 1]
55
+ lasty = ypoints[npoints - 1]
56
+
57
+ # Walk the edges of the polygon
58
+ npoints.times do |i|
59
+ curx = xpoints[i]
60
+ cury = ypoints[i]
61
+
62
+ if cury == lasty
81
63
  lastx = curx
82
64
  lasty = cury
83
65
  next
84
66
  end
85
- if x < leftx
86
- hits += 1
87
- lastx = curx
88
- lasty = cury
89
- next
67
+
68
+ if curx < lastx
69
+ if x >= lastx
70
+ lastx = curx
71
+ lasty = cury
72
+ next
73
+ end
74
+ leftx = curx
75
+ else
76
+ if x >= curx
77
+ lastx = curx
78
+ lasty = cury
79
+ next
80
+ end
81
+ leftx = lastx
90
82
  end
91
- test1 = x - curx
92
- test2 = y - cury
93
- else
94
- if y < lasty || y >= cury
95
- lastx = curx
96
- lasty = cury
97
- next
83
+
84
+ if cury < lasty
85
+ if y < cury || y >= lasty
86
+ lastx = curx
87
+ lasty = cury
88
+ next
89
+ end
90
+ if x < leftx
91
+ hits += 1
92
+ lastx = curx
93
+ lasty = cury
94
+ next
95
+ end
96
+ test1 = x - curx
97
+ test2 = y - cury
98
+ else
99
+ if y < lasty || y >= cury
100
+ lastx = curx
101
+ lasty = cury
102
+ next
103
+ end
104
+ if x < leftx
105
+ hits += 1
106
+ lastx = curx
107
+ lasty = cury
108
+ next
109
+ end
110
+ test1 = x - lastx
111
+ test2 = y - lasty
98
112
  end
99
- if x < leftx
100
- hits += 1
101
- lastx = curx
102
- lasty = cury
103
- next
104
- end
105
- test1 = x - lastx
106
- test2 = y - lasty
113
+
114
+ hits += 1 if (test1 < (test2 / (lasty - cury) * (lastx - curx)))
107
115
  end
108
-
109
- hits += 1 if (test1 < (test2 / (lasty - cury) * (lastx - curx)))
116
+
117
+ (hits & 1) != 0
110
118
  end
111
-
112
- (hits & 1) != 0
113
119
  end
114
120
  end
115
121
  end
@@ -26,33 +26,41 @@ module PerfectShape
26
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
+ # Calculates the number of times the quadratic bézier curve from (x1,y1) to (x2,y2)
30
+ # crosses the ray extending to the right from (x,y).
31
+ # If the point lies on a part of the curve,
32
+ # then no crossings are counted for that intersection.
33
+ # the level parameter should be 0 at the top-level call and will count
34
+ # up for each recursion level to prevent infinite recursion
35
+ # +1 is added for each crossing where the Y coordinate is increasing
36
+ # -1 is added for each crossing where the Y coordinate is decreasing
29
37
  def point_crossings(x1, y1, xc, yc, x2, y2, px, py, level = 0)
30
- return BigDecimal('0') if (py < y1 && py < yc && py < y2)
31
- return BigDecimal('0') if (py >= y1 && py >= yc && py >= y2)
38
+ return 0 if (py < y1 && py < yc && py < y2)
39
+ return 0 if (py >= y1 && py >= yc && py >= y2)
32
40
  # Note y1 could equal y2...
33
- return BigDecimal('0') if (px >= x1 && px >= xc && px >= x2)
41
+ return 0 if (px >= x1 && px >= xc && px >= x2)
34
42
  if (px < x1 && px < xc && px < x2)
35
43
  if (py >= y1)
36
- return BigDecimal('1') if (py < y2)
44
+ return 1 if (py < y2)
37
45
  else
38
46
  # py < y1
39
- return BigDecimal('-1') if (py >= y2)
47
+ return -1 if (py >= y2)
40
48
  end
41
49
  # py outside of y11 range, and/or y1==y2
42
- return BigDecimal('0')
50
+ return 0
43
51
  end
44
52
  # double precision only has 52 bits of mantissa
45
53
  return PerfectShape::Line.point_crossings(x1, y1, x2, y2, px, py) if (level > 52)
46
- x1c = (x1 + xc) / BigDecimal('2')
47
- y1c = (y1 + yc) / BigDecimal('2')
48
- xc1 = (xc + x2) / BigDecimal('2')
49
- yc1 = (yc + y2) / BigDecimal('2')
50
- xc = (x1c + xc1) / BigDecimal('2')
51
- yc = (y1c + yc1) / BigDecimal('2')
54
+ x1c = BigDecimal((x1 + xc).to_s) / 2
55
+ y1c = BigDecimal((y1 + yc).to_s) / 2
56
+ xc1 = BigDecimal((xc + x2).to_s) / 2
57
+ yc1 = BigDecimal((yc + y2).to_s) / 2
58
+ xc = BigDecimal((x1c + xc1).to_s) / 2
59
+ yc = BigDecimal((y1c + yc1).to_s) / 2
52
60
  # [xy]c are NaN if any of [xy]0c or [xy]c1 are NaN
53
61
  # [xy]0c or [xy]c1 are NaN if any of [xy][0c1] are NaN
54
62
  # These values are also NaN if opposing infinities are added
55
- return BigDecimal('0') if (xc.nan? || yc.nan?)
63
+ return 0 if (xc.nan? || yc.nan?)
56
64
  point_crossings(x1, y1, x1c, y1c, xc, yc, px, py, level+1) +
57
65
  point_crossings(xc, yc, xc1, yc1, x2, y2, px, py, level+1);
58
66
  end
@@ -69,7 +77,7 @@ module PerfectShape
69
77
  # @return {@code true} if the point lies within the bound of
70
78
  # the quadratic bézier curve, {@code false} if the point lies outside of the
71
79
  # quadratic bézier curve's bounds.
72
- def contain?(x_or_point, y = nil, distance: 0)
80
+ def contain?(x_or_point, y = nil)
73
81
  x, y = normalize_point(x_or_point, y)
74
82
  return unless x && y
75
83
 
@@ -21,6 +21,7 @@
21
21
 
22
22
  require 'perfect_shape/shape'
23
23
  require 'perfect_shape/rectangular_shape'
24
+ require 'perfect_shape/line'
24
25
 
25
26
  module PerfectShape
26
27
  # Mostly ported from java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/Rectangle2D.html
@@ -36,10 +37,17 @@ module PerfectShape
36
37
  # @return {@code true} if the point lies within the bound of
37
38
  # the rectangle, {@code false} if the point lies outside of the
38
39
  # rectangle's bounds.
39
- def contain?(x_or_point, y = nil)
40
+ def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
40
41
  x, y = normalize_point(x_or_point, y)
41
42
  return unless x && y
42
- x.between?(self.x, self.x + self.width) && y.between?(self.y, self.y + self.height)
43
+ if outline
44
+ Line.new(points: [[self.x, self.y], [self.x + width, self.y]]).contain?(x, y, distance_tolerance: distance_tolerance) or
45
+ Line.new(points: [[self.x + width, self.y], [self.x + width, self.y + height]]).contain?(x, y, distance_tolerance: distance_tolerance) or
46
+ Line.new(points: [[self.x + width, self.y + height], [self.x, self.y + height]]).contain?(x, y, distance_tolerance: distance_tolerance) or
47
+ Line.new(points: [[self.x, self.y + height], [self.x, self.y]]).contain?(x, y, distance_tolerance: distance_tolerance)
48
+ else
49
+ x.between?(self.x, self.x + width) && y.between?(self.y, self.y + height)
50
+ end
43
51
  end
44
52
  end
45
53
  end
@@ -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.1.1 ruby lib
5
+ # stub: perfect-shape 0.3.1 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "perfect-shape".freeze
9
- s.version = "0.1.1"
9
+ s.version = "0.3.1"
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-22"
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 b\u00E9zier 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-08"
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
16
16
  s.email = "andy.am@gmail.com".freeze
17
17
  s.extra_rdoc_files = [
18
18
  "CHANGELOG.md",
@@ -27,6 +27,8 @@ Gem::Specification.new do |s|
27
27
  "lib/perfect-shape.rb",
28
28
  "lib/perfect_shape/arc.rb",
29
29
  "lib/perfect_shape/circle.rb",
30
+ "lib/perfect_shape/composite_shape.rb",
31
+ "lib/perfect_shape/cubic_bezier_curve.rb",
30
32
  "lib/perfect_shape/ellipse.rb",
31
33
  "lib/perfect_shape/line.rb",
32
34
  "lib/perfect_shape/math.rb",
@@ -44,7 +46,7 @@ Gem::Specification.new do |s|
44
46
  ]
45
47
  s.homepage = "http://github.com/AndyObtiva/perfect-shape".freeze
46
48
  s.licenses = ["MIT".freeze]
47
- s.rubygems_version = "3.2.31".freeze
49
+ s.rubygems_version = "3.3.1".freeze
48
50
  s.summary = "Perfect Shape - Geometric Algorithms".freeze
49
51
 
50
52
  if s.respond_to? :specification_version then
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.1.1
4
+ version: 0.3.1
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-22 00:00:00.000000000 Z
11
+ date: 2022-01-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: equalizer
@@ -98,7 +98,7 @@ description: Perfect Shape is a collection of pure Ruby geometric algorithms tha
98
98
  are mostly useful for GUI manipulation like checking containment of a mouse click
99
99
  point in popular geometry shapes such as rectangle, square, arc (open, chord, and
100
100
  pie), ellipse, circle, polygon, and paths containing lines, quadratic bézier curves,
101
- and cubic bézier curves (including both Ray Casting Algorithm, aka Even-odd Rule,
101
+ and cubic bezier curves (including both Ray Casting Algorithm, aka Even-odd Rule,
102
102
  and Winding Number Algorithm, aka Nonzero Rule). Additionally, it contains some
103
103
  purely mathematical algorithms like IEEEremainder (also known as IEEE-754 remainder).
104
104
  email: andy.am@gmail.com
@@ -116,6 +116,8 @@ files:
116
116
  - lib/perfect-shape.rb
117
117
  - lib/perfect_shape/arc.rb
118
118
  - lib/perfect_shape/circle.rb
119
+ - lib/perfect_shape/composite_shape.rb
120
+ - lib/perfect_shape/cubic_bezier_curve.rb
119
121
  - lib/perfect_shape/ellipse.rb
120
122
  - lib/perfect_shape/line.rb
121
123
  - lib/perfect_shape/math.rb
@@ -149,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
151
  - !ruby/object:Gem::Version
150
152
  version: '0'
151
153
  requirements: []
152
- rubygems_version: 3.2.31
154
+ rubygems_version: 3.3.1
153
155
  signing_key:
154
156
  specification_version: 4
155
157
  summary: Perfect Shape - Geometric Algorithms