minor tweaks to manual tests while pushing to all forks
[view.love.git] / geom.lua
blobea007b72ec0945b69a54537880f20caa1a190dad
1 geom = {}
3 function geom.on_shape(x,y, drawing, shape)
4 if shape.mode == 'freehand' then
5 return geom.on_freehand(x,y, drawing, shape)
6 elseif shape.mode == 'line' then
7 return geom.on_line(x,y, drawing, shape)
8 elseif shape.mode == 'manhattan' then
9 local p1 = drawing.points[shape.p1]
10 local p2 = drawing.points[shape.p2]
11 if p1.x == p2.x then
12 if x ~= p1.x then return false end
13 local y1,y2 = p1.y, p2.y
14 if y1 > y2 then
15 y1,y2 = y2,y1
16 end
17 return y >= y1-2 and y <= y2+2
18 elseif p1.y == p2.y then
19 if y ~= p1.y then return false end
20 local x1,x2 = p1.x, p2.x
21 if x1 > x2 then
22 x1,x2 = x2,x1
23 end
24 return x >= x1-2 and x <= x2+2
25 end
26 elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
27 return geom.on_polygon(x,y, drawing, shape)
28 elseif shape.mode == 'circle' then
29 local center = drawing.points[shape.center]
30 local dist = geom.dist(center.x,center.y, x,y)
31 return dist > shape.radius*0.95 and dist < shape.radius*1.05
32 elseif shape.mode == 'arc' then
33 local center = drawing.points[shape.center]
34 local dist = geom.dist(center.x,center.y, x,y)
35 if dist < shape.radius*0.95 or dist > shape.radius*1.05 then
36 return false
37 end
38 return geom.angle_between(center.x,center.y, x,y, shape.start_angle,shape.end_angle)
39 elseif shape.mode == 'deleted' then
40 else
41 assert(false, ('unknown drawing mode %s'):format(shape.mode))
42 end
43 end
45 function geom.on_freehand(x,y, drawing, shape)
46 local prev
47 for _,p in ipairs(shape.points) do
48 if prev then
49 if geom.on_line(x,y, drawing, {p1=prev, p2=p}) then
50 return true
51 end
52 end
53 prev = p
54 end
55 return false
56 end
58 function geom.on_line(x,y, drawing, shape)
59 local p1,p2
60 if type(shape.p1) == 'number' then
61 p1 = drawing.points[shape.p1]
62 p2 = drawing.points[shape.p2]
63 else
64 p1 = shape.p1
65 p2 = shape.p2
66 end
67 if p1.x == p2.x then
68 if math.abs(p1.x-x) > 2 then
69 return false
70 end
71 local y1,y2 = p1.y,p2.y
72 if y1 > y2 then
73 y1,y2 = y2,y1
74 end
75 return y >= y1-2 and y <= y2+2
76 end
77 -- has the right slope and intercept
78 local m = (p2.y - p1.y) / (p2.x - p1.x)
79 local yp = p1.y + m*(x-p1.x)
80 if yp < y-2 or yp > y+2 then
81 return false
82 end
83 -- between endpoints
84 local k = (x-p1.x) / (p2.x-p1.x)
85 return k > -0.005 and k < 1.005
86 end
88 function geom.on_polygon(x,y, drawing, shape)
89 local prev
90 for _,p in ipairs(shape.vertices) do
91 if prev then
92 if geom.on_line(x,y, drawing, {p1=prev, p2=p}) then
93 return true
94 end
95 end
96 prev = p
97 end
98 return geom.on_line(x,y, drawing, {p1=shape.vertices[1], p2=shape.vertices[#shape.vertices]})
99 end
101 -- are (x3,y3) and (x4,y4) on the same side of the line between (x1,y1) and (x2,y2)
102 function geom.same_side(x1,y1, x2,y2, x3,y3, x4,y4)
103 if x1 == x2 then
104 return math.sign(x3-x1) == math.sign(x4-x1)
106 if y1 == y2 then
107 return math.sign(y3-y1) == math.sign(y4-y1)
109 local m = (y2-y1)/(x2-x1)
110 return math.sign(m*(x3-x1) + y1-y3) == math.sign(m*(x4-x1) + y1-y4)
113 function math.sign(x)
114 if x > 0 then
115 return 1
116 elseif x == 0 then
117 return 0
118 elseif x < 0 then
119 return -1
123 function geom.angle_with_hint(x1, y1, x2, y2, hint)
124 local result = geom.angle(x1,y1, x2,y2)
125 if hint then
126 -- Smooth the discontinuity where angle goes from positive to negative.
127 -- The hint is a memory of which way we drew it last time.
128 while result > hint+math.pi/10 do
129 result = result-math.pi*2
131 while result < hint-math.pi/10 do
132 result = result+math.pi*2
135 return result
138 -- result is from -π/2 to 3π/2, approximately adding math.atan2 from Lua 5.3
139 -- (LÖVE is Lua 5.1)
140 function geom.angle(x1,y1, x2,y2)
141 local result = math.atan((y2-y1)/(x2-x1))
142 if x2 < x1 then
143 result = result+math.pi
145 return result
148 -- is the line between x,y and cx,cy at an angle between s and e?
149 function geom.angle_between(ox,oy, x,y, s,e)
150 local angle = geom.angle(ox,oy, x,y)
151 if s > e then
152 s,e = e,s
154 -- I'm not sure this is right or ideal..
155 angle = angle-math.pi*2
156 if s <= angle and angle <= e then
157 return true
159 angle = angle+math.pi*2
160 if s <= angle and angle <= e then
161 return true
163 angle = angle+math.pi*2
164 return s <= angle and angle <= e
167 function geom.dist(x1,y1, x2,y2) return ((x2-x1)^2+(y2-y1)^2)^0.5 end