perfect-shape 0.1.0 → 0.3.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -3
- data/README.md +294 -14
- data/VERSION +1 -1
- data/lib/perfect_shape/composite_shape.rb +72 -0
- data/lib/perfect_shape/cubic_bezier_curve.rb +120 -0
- data/lib/perfect_shape/line.rb +4 -4
- data/lib/perfect_shape/multi_point.rb +2 -2
- data/lib/perfect_shape/path.rb +84 -63
- data/lib/perfect_shape/point.rb +4 -4
- data/lib/perfect_shape/polygon.rb +68 -62
- data/lib/perfect_shape/quadratic_bezier_curve.rb +190 -0
- data/lib/perfect_shape/rectangle.rb +10 -2
- data/perfect-shape.gemspec +8 -5
- metadata +10 -8
@@ -0,0 +1,120 @@
|
|
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/shape'
|
23
|
+
require 'perfect_shape/multi_point'
|
24
|
+
|
25
|
+
module PerfectShape
|
26
|
+
# Mostly ported from java.awt.geom: https://docs.oracle.com/javase/8/docs/api/java/awt/geom/QuadCurve2D.html
|
27
|
+
class CubicBezierCurve < Shape
|
28
|
+
class << self
|
29
|
+
# Calculates the number of times the cubic 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
|
37
|
+
def point_crossings(x1, y1, xc1, yc1, xc2, yc2, x2, y2, px, py, level = 0)
|
38
|
+
return 0 if (py < y1 && py < yc1 && py < yc2 && py < y2)
|
39
|
+
return 0 if (py >= y1 && py >= yc1 && py >= yc2 && py >= y2)
|
40
|
+
# Note y1 could equal yc1...
|
41
|
+
return 0 if (px >= x1 && px >= xc1 && px >= xc2 && px >= x2)
|
42
|
+
if (px < x1 && px < xc1 && px < xc2 && px < x2)
|
43
|
+
if (py >= y1)
|
44
|
+
return 1 if (py < y2)
|
45
|
+
else
|
46
|
+
# py < y1
|
47
|
+
return -1 if (py >= y2)
|
48
|
+
end
|
49
|
+
# py outside of y12 range, and/or y1==yc1
|
50
|
+
return 0
|
51
|
+
end
|
52
|
+
# double precision only has 52 bits of mantissa
|
53
|
+
return PerfectShape::Line.point_crossings(x1, y1, x2, y2, px, py) if (level > 52)
|
54
|
+
xmid = BigDecimal((xc1 + xc2).to_s) / 2;
|
55
|
+
ymid = BigDecimal((yc1 + yc2).to_s) / 2;
|
56
|
+
xc1 = BigDecimal((x1 + xc1).to_s) / 2;
|
57
|
+
yc1 = BigDecimal((y1 + yc1).to_s) / 2;
|
58
|
+
xc2 = BigDecimal((xc2 + x2).to_s) / 2;
|
59
|
+
yc2 = BigDecimal((yc2 + y2).to_s) / 2;
|
60
|
+
xc1m = BigDecimal((xc1 + xmid).to_s) / 2;
|
61
|
+
yc1m = BigDecimal((yc1 + ymid).to_s) / 2;
|
62
|
+
xmc1 = BigDecimal((xmid + xc2).to_s) / 2;
|
63
|
+
ymc1 = BigDecimal((ymid + yc2).to_s) / 2;
|
64
|
+
xmid = BigDecimal((xc1m + xmc1).to_s) / 2;
|
65
|
+
ymid = BigDecimal((yc1m + ymc1).to_s) / 2;
|
66
|
+
# [xy]mid are NaN if any of [xy]c0m or [xy]mc1 are NaN
|
67
|
+
# [xy]c0m or [xy]mc1 are NaN if any of [xy][c][01] are NaN
|
68
|
+
# These values are also NaN if opposing infinities are added
|
69
|
+
return 0 if (xmid.nan? || ymid.nan?)
|
70
|
+
point_crossings(x1, y1, xc1, yc1, xc1m, yc1m, xmid, ymid, px, py, level+1) +
|
71
|
+
point_crossings(xmid, ymid, xmc1, ymc1, xc2, yc2, x2, y2, px, py, level+1)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
include MultiPoint
|
76
|
+
include Equalizer.new(:points)
|
77
|
+
|
78
|
+
# Checks if cubic bézier curve contains point (two-number Array or x, y args)
|
79
|
+
#
|
80
|
+
# @param x The X coordinate of the point to test.
|
81
|
+
# @param y The Y coordinate of the point to test.
|
82
|
+
#
|
83
|
+
# @return {@code true} if the point lies within the bound of
|
84
|
+
# the cubic bézier curve, {@code false} if the point lies outside of the
|
85
|
+
# cubic bézier curve's bounds.
|
86
|
+
def contain?(x_or_point, y = nil)
|
87
|
+
x, y = normalize_point(x_or_point, y)
|
88
|
+
return unless x && y
|
89
|
+
|
90
|
+
# Either x or y was infinite or NaN.
|
91
|
+
# A NaN always produces a negative response to any test
|
92
|
+
# and Infinity values cannot be "inside" any path so
|
93
|
+
# they should return false as well.
|
94
|
+
return false if (!(x * 0.0 + y * 0.0 == 0.0))
|
95
|
+
# We count the "Y" crossings to determine if the point is
|
96
|
+
# inside the curve bounded by its closing line.
|
97
|
+
x1 = points[0][0]
|
98
|
+
y1 = points[0][1]
|
99
|
+
x2 = points[3][0]
|
100
|
+
y2 = points[3][1]
|
101
|
+
line = PerfectShape::Line.new(points: [[x1, y1], [x2, y2]])
|
102
|
+
crossings = line.point_crossings(x, y) + point_crossings(x, y);
|
103
|
+
(crossings & 1) == 1
|
104
|
+
end
|
105
|
+
|
106
|
+
# Calculates the number of times the cubic bézier curve
|
107
|
+
# crosses the ray extending to the right from (x,y).
|
108
|
+
# If the point lies on a part of the curve,
|
109
|
+
# then no crossings are counted for that intersection.
|
110
|
+
# the level parameter should be 0 at the top-level call and will count
|
111
|
+
# up for each recursion level to prevent infinite recursion
|
112
|
+
# +1 is added for each crossing where the Y coordinate is increasing
|
113
|
+
# -1 is added for each crossing where the Y coordinate is decreasing
|
114
|
+
def point_crossings(x_or_point, y = nil, level = 0)
|
115
|
+
x, y = normalize_point(x_or_point, y)
|
116
|
+
return unless x && y
|
117
|
+
CubicBezierCurve.point_crossings(points[0][0], points[0][1], points[1][0], points[1][1], points[2][0], points[2][1], points[3][0], points[3][1], x, y, level)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/perfect_shape/line.rb
CHANGED
@@ -207,16 +207,16 @@ module PerfectShape
|
|
207
207
|
#
|
208
208
|
# @param x The X coordinate of the point to test.
|
209
209
|
# @param y The Y coordinate of the point to test.
|
210
|
-
# @param
|
210
|
+
# @param distance_tolerance The distance from line to tolerate (0 by default)
|
211
211
|
#
|
212
212
|
# @return {@code true} if the point lies within the bound of
|
213
213
|
# the line, {@code false} if the point lies outside of the
|
214
214
|
# line's bounds.
|
215
|
-
def contain?(x_or_point, y = nil,
|
215
|
+
def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
|
216
216
|
x, y = normalize_point(x_or_point, y)
|
217
217
|
return unless x && y
|
218
|
-
|
219
|
-
point_segment_distance(x, y) <=
|
218
|
+
distance_tolerance = BigDecimal(distance_tolerance.to_s)
|
219
|
+
point_segment_distance(x, y) <= distance_tolerance
|
220
220
|
end
|
221
221
|
|
222
222
|
def point_segment_distance(x_or_point, y = nil)
|
@@ -26,8 +26,8 @@ module PerfectShape
|
|
26
26
|
module MultiPoint
|
27
27
|
attr_reader :points
|
28
28
|
|
29
|
-
def initialize(points:
|
30
|
-
self.points = points
|
29
|
+
def initialize(points: [])
|
30
|
+
self.points = points
|
31
31
|
end
|
32
32
|
|
33
33
|
# Sets points, normalizing to an Array of Arrays of (x,y) pairs as BigDecimal
|
data/lib/perfect_shape/path.rb
CHANGED
@@ -22,6 +22,8 @@
|
|
22
22
|
require 'perfect_shape/shape'
|
23
23
|
require 'perfect_shape/point'
|
24
24
|
require 'perfect_shape/line'
|
25
|
+
require 'perfect_shape/quadratic_bezier_curve'
|
26
|
+
require 'perfect_shape/cubic_bezier_curve'
|
25
27
|
require 'perfect_shape/multi_point'
|
26
28
|
|
27
29
|
module PerfectShape
|
@@ -30,7 +32,10 @@ module PerfectShape
|
|
30
32
|
include MultiPoint
|
31
33
|
include Equalizer.new(:shapes, :closed, :winding_rule)
|
32
34
|
|
33
|
-
|
35
|
+
# Available class types for path shapes
|
36
|
+
SHAPE_TYPES = [Array, PerfectShape::Point, PerfectShape::Line, PerfectShape::QuadraticBezierCurve, PerfectShape::CubicBezierCurve]
|
37
|
+
|
38
|
+
# Available winding rules
|
34
39
|
WINDING_RULES = [:wind_non_zero, :wind_even_odd]
|
35
40
|
|
36
41
|
attr_reader :winding_rule
|
@@ -38,8 +43,9 @@ module PerfectShape
|
|
38
43
|
alias closed? closed
|
39
44
|
|
40
45
|
# Constructs Path with winding rule, closed status, and shapes (must always start with PerfectShape::Point or Array of [x,y] coordinates)
|
41
|
-
# Shape class types can be any of SHAPE_TYPES: Array (x,y coordinates), PerfectShape::Point, or PerfectShape::
|
46
|
+
# Shape class types can be any of SHAPE_TYPES: Array (x,y coordinates), PerfectShape::Point, PerfectShape::Line, PerfectShape::QuadraticBezierCurve, or PerfectShape::CubicBezierCurve
|
42
47
|
# winding_rule can be any of WINDING_RULES: :wind_non_zero (default) or :wind_even_odd
|
48
|
+
# closed can be true or false
|
43
49
|
def initialize(shapes: [], closed: false, winding_rule: :wind_non_zero)
|
44
50
|
self.closed = closed
|
45
51
|
self.winding_rule = winding_rule
|
@@ -47,20 +53,27 @@ module PerfectShape
|
|
47
53
|
end
|
48
54
|
|
49
55
|
def points
|
50
|
-
|
56
|
+
the_points = []
|
57
|
+
@shapes.each do |shape|
|
51
58
|
case shape
|
52
59
|
when Point
|
53
|
-
shape.to_a
|
60
|
+
the_points << shape.to_a
|
54
61
|
when Array
|
55
|
-
shape
|
62
|
+
the_points << shape.map {|n| BigDecimal(n.to_s)}
|
56
63
|
when Line
|
57
|
-
shape.points.last.to_a
|
58
|
-
|
59
|
-
|
64
|
+
the_points << shape.points.last.to_a
|
65
|
+
when QuadraticBezierCurve
|
66
|
+
shape.points.each do |point|
|
67
|
+
the_points << point.to_a
|
68
|
+
end
|
69
|
+
when CubicBezierCurve
|
70
|
+
shape.points.each do |point|
|
71
|
+
the_points << point.to_a
|
72
|
+
end
|
60
73
|
end
|
61
|
-
end.tap do |the_points|
|
62
|
-
the_points << @shapes.first.to_a if closed?
|
63
74
|
end
|
75
|
+
the_points << @shapes.first.to_a if closed?
|
76
|
+
the_points
|
64
77
|
end
|
65
78
|
|
66
79
|
def points=(some_points)
|
@@ -76,8 +89,10 @@ module PerfectShape
|
|
76
89
|
:move_to
|
77
90
|
when Line
|
78
91
|
:line_to
|
79
|
-
|
80
|
-
|
92
|
+
when QuadraticBezierCurve
|
93
|
+
:quad_to
|
94
|
+
when CubicBezierCurve
|
95
|
+
:cubic_to
|
81
96
|
end
|
82
97
|
end
|
83
98
|
the_drawing_shapes << :close if closed?
|
@@ -96,8 +111,8 @@ module PerfectShape
|
|
96
111
|
# @param x The X coordinate of the point to test.
|
97
112
|
# @param y The Y coordinate of the point to test.
|
98
113
|
#
|
99
|
-
# @return
|
100
|
-
# the path
|
114
|
+
# @return true if the point lies within the bound of
|
115
|
+
# the path or false if the point lies outside of the
|
101
116
|
# path's bounds.
|
102
117
|
def contain?(x_or_point, y = nil)
|
103
118
|
x, y = normalize_point(x_or_point, y)
|
@@ -139,57 +154,63 @@ module PerfectShape
|
|
139
154
|
crossings = 0
|
140
155
|
ci = 2
|
141
156
|
1.upto(shapes.count - 1).each do |i|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
crossings += line.point_crossings(x, y)
|
147
|
-
end
|
148
|
-
movx = curx = coords[ci]
|
149
|
-
ci += 1
|
150
|
-
movy = cury = coords[ci]
|
151
|
-
ci += 1
|
152
|
-
when :line_to
|
153
|
-
endx = coords[ci]
|
154
|
-
ci += 1
|
155
|
-
endy = coords[ci]
|
156
|
-
ci += 1
|
157
|
-
line = PerfectShape::Line.new(points: [[curx, cury], [endx, endy]])
|
157
|
+
case drawing_types[i]
|
158
|
+
when :move_to
|
159
|
+
if cury != movy
|
160
|
+
line = PerfectShape::Line.new(points: [[curx, cury], [movx, movy]])
|
158
161
|
crossings += line.point_crossings(x, y)
|
159
|
-
curx = endx;
|
160
|
-
cury = endy;
|
161
|
-
# when :quad_to # TODO
|
162
|
-
# crossings +=
|
163
|
-
# Curve.point_crossings_for_quad(x, y,
|
164
|
-
# curx, cury,
|
165
|
-
# coords[ci++],
|
166
|
-
# coords[ci++],
|
167
|
-
# endx = coords[ci++],
|
168
|
-
# endy = coords[ci++],
|
169
|
-
# 0);
|
170
|
-
# curx = endx;
|
171
|
-
# cury = endy;
|
172
|
-
# when :cubic_to # TODO
|
173
|
-
# crossings +=
|
174
|
-
# Curve.point_crossings_for_cubic(x, y,
|
175
|
-
# curx, cury,
|
176
|
-
# coords[ci++],
|
177
|
-
# coords[ci++],
|
178
|
-
# coords[ci++],
|
179
|
-
# coords[ci++],
|
180
|
-
# endx = coords[ci++],
|
181
|
-
# endy = coords[ci++],
|
182
|
-
# 0);
|
183
|
-
# curx = endx;
|
184
|
-
# cury = endy;
|
185
|
-
when :close
|
186
|
-
if cury != movy
|
187
|
-
line = PerfectShape::Line.new(points: [[curx, cury], [movx, movy]])
|
188
|
-
crossings += line.point_crossings(x, y)
|
189
|
-
end
|
190
|
-
curx = movx
|
191
|
-
cury = movy
|
192
162
|
end
|
163
|
+
movx = curx = coords[ci]
|
164
|
+
ci += 1
|
165
|
+
movy = cury = coords[ci]
|
166
|
+
ci += 1
|
167
|
+
when :line_to
|
168
|
+
endx = coords[ci]
|
169
|
+
ci += 1
|
170
|
+
endy = coords[ci]
|
171
|
+
ci += 1
|
172
|
+
line = PerfectShape::Line.new(points: [[curx, cury], [endx, endy]])
|
173
|
+
crossings += line.point_crossings(x, y)
|
174
|
+
curx = endx;
|
175
|
+
cury = endy;
|
176
|
+
when :quad_to
|
177
|
+
quad_ctrlx = coords[ci]
|
178
|
+
ci += 1
|
179
|
+
quad_ctrly = coords[ci]
|
180
|
+
ci += 1
|
181
|
+
endx = coords[ci]
|
182
|
+
ci += 1
|
183
|
+
endy = coords[ci]
|
184
|
+
ci += 1
|
185
|
+
quad = PerfectShape::QuadraticBezierCurve.new(points: [[curx, cury], [quad_ctrlx, quad_ctrly], [endx, endy]])
|
186
|
+
crossings += quad.point_crossings(x, y)
|
187
|
+
curx = endx;
|
188
|
+
cury = endy;
|
189
|
+
when :cubic_to
|
190
|
+
cubic_ctrl1x = coords[ci]
|
191
|
+
ci += 1
|
192
|
+
cubic_ctrl1y = coords[ci]
|
193
|
+
ci += 1
|
194
|
+
cubic_ctrl2x = coords[ci]
|
195
|
+
ci += 1
|
196
|
+
cubic_ctrl2y = coords[ci]
|
197
|
+
ci += 1
|
198
|
+
endx = coords[ci]
|
199
|
+
ci += 1
|
200
|
+
endy = coords[ci]
|
201
|
+
ci += 1
|
202
|
+
cubic = PerfectShape::CubicBezierCurve.new(points: [[curx, cury], [cubic_ctrl1x, cubic_ctrl1y], [cubic_ctrl2x, cubic_ctrl2y], [endx, endy]])
|
203
|
+
crossings += cubic.point_crossings(x, y)
|
204
|
+
curx = endx;
|
205
|
+
cury = endy;
|
206
|
+
when :close
|
207
|
+
if cury != movy
|
208
|
+
line = PerfectShape::Line.new(points: [[curx, cury], [movx, movy]])
|
209
|
+
crossings += line.point_crossings(x, y)
|
210
|
+
end
|
211
|
+
curx = movx
|
212
|
+
cury = movy
|
213
|
+
end
|
193
214
|
end
|
194
215
|
if cury != movy
|
195
216
|
line = PerfectShape::Line.new(points: [[curx, cury], [movx, movy]])
|
data/lib/perfect_shape/point.rb
CHANGED
@@ -63,15 +63,15 @@ module PerfectShape
|
|
63
63
|
#
|
64
64
|
# @param x The X coordinate of the point to test.
|
65
65
|
# @param y The Y coordinate of the point to test.
|
66
|
-
# @param
|
66
|
+
# @param distance_tolerance The distance from point to tolerate (0 by default)
|
67
67
|
#
|
68
68
|
# @return {@code true} if the point is close enough within distance tolerance,
|
69
69
|
# {@code false} if the point is too far.
|
70
|
-
def contain?(x_or_point, y = nil,
|
70
|
+
def contain?(x_or_point, y = nil, outline: false, distance_tolerance: 0)
|
71
71
|
x, y = normalize_point(x_or_point, y)
|
72
72
|
return unless x && y
|
73
|
-
|
74
|
-
point_distance(x, y) <=
|
73
|
+
distance_tolerance = BigDecimal(distance_tolerance.to_s)
|
74
|
+
point_distance(x, y) <= distance_tolerance
|
75
75
|
end
|
76
76
|
|
77
77
|
def point_distance(x_or_point, y = nil)
|
@@ -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
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
80
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
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
|
-
|
100
|
-
|
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
|
116
|
+
|
117
|
+
(hits & 1) != 0
|
110
118
|
end
|
111
|
-
|
112
|
-
(hits & 1) != 0
|
113
119
|
end
|
114
120
|
end
|
115
121
|
end
|