1-- GunFu Deadlands 2-- Copyright 2009-2011 Christiaan Janssen, September 2009-October 2011 3-- 4-- This file is part of GunFu Deadlands. 5-- 6-- GunFu Deadlands is free software: you can redistribute it and/or modify 7-- it under the terms of the GNU General Public License as published by 8-- the Free Software Foundation, either version 3 of the License, or 9-- (at your option) any later version. 10-- 11-- GunFu Deadlands is distributed in the hope that it will be useful, 12-- but WITHOUT ANY WARRANTY; without even the implied warranty of 13-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14-- GNU General Public License for more details. 15-- 16-- You should have received a copy of the GNU General Public License 17-- along with GunFu Deadlands. If not, see <http://www.gnu.org/licenses/>. 18 19 20mymath = {} 21 22-- 2d rotation of a vector, angle given in radians 23function mymath.get_rotation_matrix( angle ) 24 local c,s = math.cos(angle), math.sin(angle) 25 return { c, -s, s, c } 26end 27 28function mymath.rotate(vector, angle) 29 local m = mymath.get_rotation_matrix(angle) 30 return {vector[1]*m[1]+vector[2]*m[2],vector[1]*m[3]+vector[2]*m[4]} 31end 32 33function mymath.disturb_vector(vector, deviation) 34 local normal_dist = mymath.randn2() 35 local direction = {vector[1] + normal_dist[1] * deviation, vector[2] + normal_dist[2] * deviation } 36 -- force normalization again 37 return mymath.get_dir_vector( 0, 0, direction[1], direction[2] ) 38end 39 40function mymath.sign( a ) 41 if a>=0 then return 1 end 42 return -1 43end 44 45function mymath.round( r ) 46 return math.floor(r+0.5) 47end 48 49function mymath.normalize_vector( vec ) 50 local mag = math.sqrt( vec[1]*vec[1] + vec[2]*vec[2] ) 51 return { vec[1] / mag, vec[2] / mag } 52end 53 54function mymath.get_angle( v1, v2 ) 55 nv1 = mymath.normalize_vector( v1 ) 56 nv2 = mymath.normalize_vector( v2 ) 57 local cosangle = math.acos( nv1[1]*nv2[1] + nv1[2]*nv2[2] ) 58 local sinangle = math.asin( nv1[1]*nv2[2] - nv1[2]*nv2[1] ) 59 60 return cosangle*mymath.sign(sinangle) 61 62end 63 64 65 66-- returns gaussian random number 67-- from http://www.taygeta.com/random/gaussian.html 68-- Algorithm by Dr. Everett (Skip) Carter, Jr. 69 70function mymath.randn() 71 local x1,x2 72 local w = 1 73 while w >= 1 and w>0 do 74 x1 = 2 * math.random() - 1 75 x2 = 2 * math.random() - 1 76 w = x1*x1 + x2*x2 77 end 78 79 w = math.sqrt( (-2 * math.log( w ) / w ) ) 80 return x1 * w 81end 82 83function mymath.randn2() 84 local x1,x2 85 local w = 1 86 while w >= 1 and w>0 do 87 x1 = 2 * math.random() - 1 88 x2 = 2 * math.random() - 1 89 w = x1*x1 + x2*x2 90 end 91 92 w = math.sqrt( (-2 * math.log( w ) / w ) ) 93 return {x1 * w, x2 * w} 94end 95 96-- returns a list with the ordinals of a random permutation of length len 97function mymath.permutation( len ) 98 used = {} 99 seq = {} 100 101 for i=1,len do 102 used[i]=0 103 end 104 105 for i=1,len do 106 newnum = math.random(len-i+1) 107 j=0 108 while newnum>0 do 109 j=j+1 110 if used[j]==0 then 111 newnum = newnum-1 112 end 113 end 114 115 used[j]=1 116 seq[i]=j 117 end 118 119 return seq 120 121end 122 123function mymath.get_distance(point1, point2) 124 local dx = point2[1] - point1[1] 125 local dy = point2[2] - point1[2] 126 return math.sqrt(dx*dx + dy*dy) 127end 128 129function mymath.get_distanceSq(point1, point2) 130 local dx = point2[1] - point1[1] 131 local dy = point2[2] - point1[2] 132 return (dx*dx + dy*dy) 133end 134 135-- the "public" function 136function mymath.check_intersection(segment, box) 137 138 -- the first point is inside the box? 139 if segment[1]>= box[1] and segment[1]<=box[3] and segment[2]>=box[2] and segment[2]<=box[4] then 140 return true 141 end 142 143 -- the second point is inside the box? 144 if segment[3]>= box[1] and segment[3]<=box[3] and segment[4]>=box[2] and segment[4]<=box[4] then 145 return true 146 end 147 148 -- we sort here the segment coordinates because we treat it as a box 149 if not mymath.check_boxes( 150 { math.min(segment[1],segment[3]), math.min(segment[2],segment[4]), 151 math.max(segment[1],segment[3]), math.max(segment[2],segment[4]) }, 152 box 153 ) then 154 return false 155 end 156 157 158 -- for the real intersection check, the points must not be sorted 159 return mymath.check_intersection_internal( segment, box ) 160 161end 162 163 164-- for internal use 165function mymath.check_intersection_internal( segment, box ) 166 local dX = segment[3] - segment[1] 167 local dY = segment[4] - segment[2] 168 169 if math.abs(dX) > 0.001 then 170 -- find intersection with left side of the box 171 local tL = (box[1]-segment[1])/dX 172 if tL >= 0 and tL <= 1 -- in the valid range 173 then 174 local yL = segment[2] + tL*dY 175 if yL <= box[4] and yL >= box[2] then 176 return true -- yes they are! 177 end 178 end 179 180 181 -- find intersection with right side of the box 182 local tR = (box[3]-segment[1])/dX 183 if tR >= 0 and tR <= 1 -- in the valid range 184 then 185 local yR = segment[2] + tR*dY 186 if yR <= box[4] and yR >= box[2] then 187 return true -- yes they are! 188 end 189 end 190 191 end 192 193 if math.abs(dY) > 0.001 then 194 -- find intersection with upper side of the box 195 local tU = (box[2]-segment[2])/dY 196 if tU >= 0 and tU <= 1 -- in the valid range 197 then 198 local xU = segment[1] + tU*dX 199 if xU >= box[1] and xU <= box[3] then 200 return true -- yes they are! 201 end 202 end 203 204 -- find intersection with lower side of the box 205 local tD = (box[4]-segment[2])/dY 206 if tD >= 0 and tD <= 1 -- in the valid range 207 then 208 local xD = segment[1] + tD*dX 209 if xD >= box[1] and xD <= box[3] then 210 return true -- yes they are! 211 end 212 end 213 end 214 215 return false 216end 217 218-- for internal use (returns true if boxes intersect 219function mymath.check_boxes(box1, box2) 220 if box1[3]<box2[1] or 221 box1[1]>box2[3] or 222 box1[2]>box2[4] or 223 box1[4]<box2[2] then 224 return false 225 end 226 return true 227end 228 229function mymath.check_boxinbox(small_box, big_box) 230 if small_box[1] < big_box[1] or 231 small_box[2] < big_box[2] or 232 small_box[3] > big_box[3] or 233 small_box[4] > big_box[4] then 234 return false 235 end 236 return true 237end 238 239function mymath.check_pointinbox(point, box) 240 if point[1] >= box[1] and point[1] <= box[3] and 241 point[2] >= box[2] and point[2] <= box[4] then 242 return true 243 else 244 return false 245 end 246end 247 248function mymath.get_dir_vector(origx, origy, destx, desty) 249 -- local destv = { love.mouse.getX() - origx, love.mouse.getY() - origy } 250 local destv = { destx - origx, desty - origy } 251 local modul = math.sqrt( destv[1]*destv[1] + destv[2]*destv[2] ) 252 if modul < 0.001 then 253 modul = 0.001 254 end 255 return { destv[1]/modul, destv[2]/modul } 256end 257 258 259-- more complex collision detection 260function mymath.check_pointinbuilding( point, building ) 261 -- for each box... 262 -- first check if the point is inside the box 263 -- if it is, check if it's also inside all the halfspaces 264 for i,colli in ipairs(building.collision) do 265 if mymath.check_pointinbox(point, colli) then 266 return true 267 268 end 269 end 270 return false 271end 272 273function mymath.check_segmentinbuilding( segment, building ) 274 -- for each box... 275 -- first check if the segment intersects the bounding box 276 -- if it does, check if at least one of the points is actually inside 277 for i,colli in ipairs(building.collision) do 278 if mymath.check_intersection(segment, colli) then 279 return true 280 281 end 282 end 283 return false 284 285end 286 287-- assuming the point is outside of the box 288-- returns { distance squared, pointx, pointy } 289-- the returned point is the closest point in the box's edge to the input point 290function mymath.distanceSq_to_box( point, box ) 291 -- directly above or below? 292 if point[1] >= box[1] and point[1]<= box[3] then 293 if point[2] < box[2] then 294 -- above 295 local diff = box[2] - point[2] 296 return { diff*diff, point[1], box[2] } 297 else 298 -- below 299 local diff = box[4] - point[2] 300 return { diff*diff, point[1], box[4] } 301 end 302 end 303 304 -- directly on the side? 305 if point[2] >= box[2] and point[2]<= box[4] then 306 if point[1] < box[1] then 307 -- left 308 local diff = box[1] - point[1] 309 return { diff*diff, box[1], point[2] } 310 else 311 -- right 312 local diff = box[3] - point[1] 313 return { diff*diff, box[3], point[2] } 314 end 315 end 316 317 -- diagonal... 318 local p1 319 if point[1] < box[1] then 320 p1 = box[1] 321 else 322 p1 = box[3] 323 end 324 325 local p2 326 if point[2] < box[2] then 327 p2 = box[2] 328 else 329 p2 = box[4] 330 end 331 332 local d1,d2 = p1 - point[1], p2 - point[2] 333 return {d1*d1+d2*d2, p1, p2} 334end 335 336function mymath.check_boxinbuilding( box1, building ) 337 -- first check mutual box collision 338 -- if it happens, check each individual point and see if at least 339 -- one of them is inside the building 340 for i,colli in ipairs(building.collision) do 341 if mymath.check_boxes(box1, colli) then 342 return true 343 end 344 end 345 return false 346end 347 348 349