diff options
Diffstat (limited to 'src/libjin-lua/scripts/physics/physics.lua')
-rw-r--r-- | src/libjin-lua/scripts/physics/physics.lua | 260 |
1 files changed, 130 insertions, 130 deletions
diff --git a/src/libjin-lua/scripts/physics/physics.lua b/src/libjin-lua/scripts/physics/physics.lua index 9b24a55..897af8d 100644 --- a/src/libjin-lua/scripts/physics/physics.lua +++ b/src/libjin-lua/scripts/physics/physics.lua @@ -69,22 +69,22 @@ local function rect_getSegmentIntersectionIndices(x,y,w,h, x1,y1,x2,y2, ti1,ti2) if side == 1 then nx,ny,p,q = -1, 0, -dx, x1 - x -- left elseif side == 2 then nx,ny,p,q = 1, 0, dx, x + w - x1 -- right elseif side == 3 then nx,ny,p,q = 0, -1, -dy, y1 - y -- top - else nx,ny,p,q = 0, 1, dy, y + h - y1 -- bottom + else nx,ny,p,q = 0, 1, dy, y + h - y1 -- bottom end if p == 0 then - if q <= 0 then return nil end + if q <= 0 then return nil end else - r = q / p - if p < 0 then - if r > ti2 then return nil - elseif r > ti1 then ti1,nx1,ny1 = r,nx,ny - end - else -- p > 0 - if r < ti1 then return nil - elseif r < ti2 then ti2,nx2,ny2 = r,nx,ny - end - end + r = q / p + if p < 0 then + if r > ti2 then return nil + elseif r > ti1 then ti1,nx1,ny1 = r,nx,ny + end + else -- p > 0 + if r < ti1 then return nil + elseif r < ti2 then ti2,nx2,ny2 = r,nx,ny + end + end end end @@ -94,19 +94,19 @@ end -- Calculates the minkowsky difference between 2 rects, which is another rect local function rect_getDiff(x1,y1,w1,h1, x2,y2,w2,h2) return x2 - x1 - w1, - y2 - y1 - h1, - w1 + w2, - h1 + h2 + y2 - y1 - h1, + w1 + w2, + h1 + h2 end local function rect_containsPoint(x,y,w,h, px,py) - return px - x > DELTA and py - y > DELTA and - x + w - px > DELTA and y + h - py > DELTA + return px - x > DELTA and py - y > DELTA and + x + w - px > DELTA and y + h - py > DELTA end local function rect_isIntersecting(x1,y1,w1,h1, x2,y2,w2,h2) return x1 < x2+w2 and x2 < x1+w1 and - y1 < y2+h2 and y2 < y1+h1 + y1 < y2+h2 and y2 < y1+h1 end local function rect_getSquareDistance(x1,y1,w1,h1, x2,y2,w2,h2) @@ -119,7 +119,7 @@ local function rect_detectCollision(x1,y1,w1,h1, x2,y2,w2,h2, goalX, goalY) goalX = goalX or x1 goalY = goalY or y1 - local dx, dy = goalX - x1, goalY - y1 + local dx, dy = goalX - x1, goalY - y1 local x,y,w,h = rect_getDiff(x1,y1,w1,h1, x2,y2,w2,h2) local overlaps, ti, nx, ny @@ -127,7 +127,7 @@ local function rect_detectCollision(x1,y1,w1,h1, x2,y2,w2,h2, goalX, goalY) if rect_containsPoint(x,y,w,h, 0,0) then -- item was intersecting other local px, py = rect_getNearestCorner(x,y,w,h, 0, 0) local wi, hi = min(w1, abs(px)), min(h1, abs(py)) -- area of intersection - ti = -wi * hi -- ti is the negative area of intersection + ti = -wi * hi -- ti is the negative area of intersection overlaps = true else local ti1,ti2,nx1,ny1 = rect_getSegmentIntersectionIndices(x,y,w,h, 0,0,dx,dy, -math.huge, math.huge) @@ -137,10 +137,10 @@ local function rect_detectCollision(x1,y1,w1,h1, x2,y2,w2,h2, goalX, goalY) and ti1 < 1 and (abs(ti1 - ti2) >= DELTA) -- special case for rect going through another rect's corner and (0 < ti1 + DELTA - or 0 == ti1 and ti2 > 0) + or 0 == ti1 and ti2 > 0) then - ti, nx, ny = ti1, nx1, ny1 - overlaps = false + ti, nx, ny = ti1, nx1, ny1 + overlaps = false end end @@ -150,17 +150,17 @@ local function rect_detectCollision(x1,y1,w1,h1, x2,y2,w2,h2, goalX, goalY) if overlaps then if dx == 0 and dy == 0 then - -- intersecting and not moving - use minimum displacement vector - local px, py = rect_getNearestCorner(x,y,w,h, 0,0) - if abs(px) < abs(py) then py = 0 else px = 0 end - nx, ny = sign(px), sign(py) - tx, ty = x1 + px, y1 + py + -- intersecting and not moving - use minimum displacement vector + local px, py = rect_getNearestCorner(x,y,w,h, 0,0) + if abs(px) < abs(py) then py = 0 else px = 0 end + nx, ny = sign(px), sign(py) + tx, ty = x1 + px, y1 + py else - -- intersecting and moving - move in the opposite direction - local ti1, _ - ti1,_,nx,ny = rect_getSegmentIntersectionIndices(x,y,w,h, 0,0,dx,dy, -math.huge, 1) - if not ti1 then return end - tx, ty = x1 + dx * ti1, y1 + dy * ti1 + -- intersecting and moving - move in the opposite direction + local ti1, _ + ti1,_,nx,ny = rect_getSegmentIntersectionIndices(x,y,w,h, 0,0,dx,dy, -math.huge, 1) + if not ti1 then return end + tx, ty = x1 + dx * ti1, y1 + dy * ti1 end else -- tunnel tx, ty = x1 + dx * ti, y1 + dy * ti @@ -168,8 +168,8 @@ local function rect_detectCollision(x1,y1,w1,h1, x2,y2,w2,h2, goalX, goalY) return { overlaps = overlaps, - ti = ti, - move = {x = dx, y = dy}, + ti = ti, + move = {x = dx, y = dy}, normal = {x = nx, y = ny}, touch = {x = tx, y = ty}, itemRect = {x = x1, y = y1, w = w1, h = h1}, @@ -206,11 +206,11 @@ local function grid_traverse_initStep(cellSize, ct, t1, t2) end local function grid_traverse(cellSize, x1,y1,x2,y2, f) - local cx1,cy1 = grid_toCell(cellSize, x1,y1) - local cx2,cy2 = grid_toCell(cellSize, x2,y2) + local cx1,cy1 = grid_toCell(cellSize, x1,y1) + local cx2,cy2 = grid_toCell(cellSize, x2,y2) local stepX, dx, tx = grid_traverse_initStep(cellSize, cx1, x1, x2) local stepY, dy, ty = grid_traverse_initStep(cellSize, cy1, y1, y2) - local cx,cy = cx1,cy1 + local cx,cy = cx1,cy1 f(cx, cy) @@ -219,13 +219,13 @@ local function grid_traverse(cellSize, x1,y1,x2,y2, f) -- when we are *next* to the last cell while abs(cx - cx2) + abs(cy - cy2) > 1 do if tx < ty then - tx, cx = tx + dx, cx + stepX - f(cx, cy) + tx, cx = tx + dx, cx + stepX + f(cx, cy) else - -- Addition: include both cells when going through corners - if tx == ty then f(cx + stepX, cy) end - ty, cy = ty + dy, cy + stepY - f(cx, cy) + -- Addition: include both cells when going through corners + if tx == ty then f(cx + stepX, cy) end + ty, cy = ty + dy, cy + stepY + f(cx, cy) end end @@ -260,9 +260,9 @@ local slide = function(world, col, x,y,w,h, goalX, goalY, filter) local tch, move = col.touch, col.move if move.x ~= 0 or move.y ~= 0 then if col.normal.x ~= 0 then - goalX = tch.x + goalX = tch.x else - goalY = tch.y + goalY = tch.y end end @@ -289,7 +289,7 @@ local bounce = function(world, col, x,y,w,h, goalX, goalY, filter) end col.bounce = {x = bx, y = by} - x,y = tch.x, tch.y + x,y = tch.x, tch.y goalX, goalY = bx, by local cols, len = world:project(col.item, x,y,w,h, goalX, goalY, filter) @@ -347,14 +347,14 @@ local function getDictItemsInCellRect(self, cl,ct,cw,ch) for cy=ct,ct+ch-1 do local row = self.rows[cy] if row then - for cx=cl,cl+cw-1 do - local cell = row[cx] - if cell and cell.itemCount > 0 then -- no cell.itemCount > 1 because tunneling - for item,_ in pairs(cell.items) do - items_dict[item] = true - end - end - end + for cx=cl,cl+cw-1 do + local cell = row[cx] + if cell and cell.itemCount > 0 then -- no cell.itemCount > 1 because tunneling + for item,_ in pairs(cell.items) do + items_dict[item] = true + end + end + end end end @@ -386,21 +386,21 @@ local function getInfoAboutItemsTouchedBySegment(self, x1,y1, x2,y2, filter) for i=1,len do cell = cells[i] for item in pairs(cell.items) do - if not visited[item] then - visited[item] = true - if (not filter or filter(item)) then - rect = self.rects[item] - l,t,w,h = rect.x,rect.y,rect.w,rect.h - - ti1,ti2 = rect_getSegmentIntersectionIndices(l,t,w,h, x1,y1, x2,y2, 0, 1) - if ti1 and ((0 < ti1 and ti1 < 1) or (0 < ti2 and ti2 < 1)) then - -- the sorting is according to the t of an infinite line, not the segment - tii0,tii1 = rect_getSegmentIntersectionIndices(l,t,w,h, x1,y1, x2,y2, -math.huge, math.huge) - itemInfoLen = itemInfoLen + 1 - itemInfo[itemInfoLen] = {item = item, ti1 = ti1, ti2 = ti2, weight = min(tii0,tii1)} - end - end - end + if not visited[item] then + visited[item] = true + if (not filter or filter(item)) then + rect = self.rects[item] + l,t,w,h = rect.x,rect.y,rect.w,rect.h + + ti1,ti2 = rect_getSegmentIntersectionIndices(l,t,w,h, x1,y1, x2,y2, 0, 1) + if ti1 and ((0 < ti1 and ti1 < 1) or (0 < ti2 and ti2 < 1)) then + -- the sorting is according to the t of an infinite line, not the segment + tii0,tii1 = rect_getSegmentIntersectionIndices(l,t,w,h, x1,y1, x2,y2, -math.huge, math.huge) + itemInfoLen = itemInfoLen + 1 + itemInfo[itemInfoLen] = {item = item, ti1 = ti1, ti2 = ti2, weight = min(tii0,tii1)} + end + end + end end end table.sort(itemInfo, sortByWeight) @@ -436,7 +436,7 @@ function World:project(item, x,y,w,h, goalX, goalY, filter) -- This could probably be done with less cells using a polygon raster over the cells instead of a -- bounding rect of the whole movement. Conditional to building a queryPolygon method - local tl, tt = min(goalX, x), min(goalY, y) + local tl, tt = min(goalX, x), min(goalY, y) local tr, tb = max(goalX + w, x+w), max(goalY + h, y+h) local tw, th = tr-tl, tb-tt @@ -446,22 +446,22 @@ function World:project(item, x,y,w,h, goalX, goalY, filter) for other,_ in pairs(dictItemsInCellRect) do if not visited[other] then - visited[other] = true + visited[other] = true - local responseName = filter(item, other) - if responseName then - local ox,oy,ow,oh = self:getRect(other) - local col = rect_detectCollision(x,y,w,h, ox,oy,ow,oh, goalX, goalY) + local responseName = filter(item, other) + if responseName then + local ox,oy,ow,oh = self:getRect(other) + local col = rect_detectCollision(x,y,w,h, ox,oy,ow,oh, goalX, goalY) - if col then - col.other = other - col.item = item - col.type = responseName + if col then + col.other = other + col.item = item + col.type = responseName - len = len + 1 - collisions[len] = col - end - end + len = len + 1 + collisions[len] = col + end + end end end @@ -474,7 +474,7 @@ function World:countCells() local count = 0 for _,row in pairs(self.rows) do for _,_ in pairs(row) do - count = count + 1 + count = count + 1 end end return count @@ -533,8 +533,8 @@ function World:queryRect(x,y,w,h, filter) if (not filter or filter(item)) and rect_isIntersecting(x,y,w,h, rect.x, rect.y, rect.w, rect.h) then - len = len + 1 - items[len] = item + len = len + 1 + items[len] = item end end @@ -553,8 +553,8 @@ function World:queryPoint(x,y, filter) if (not filter or filter(item)) and rect_containsPoint(rect.x, rect.y, rect.w, rect.h, x, y) then - len = len + 1 - items[len] = item + len = len + 1 + items[len] = item end end @@ -572,7 +572,7 @@ end function World:querySegmentWithCoords(x1, y1, x2, y2, filter) local itemInfo, len = getInfoAboutItemsTouchedBySegment(self, x1, y1, x2, y2, filter) - local dx, dy = x2-x1, y2-y1 + local dx, dy = x2-x1, y2-y1 local info, ti1, ti2 for i=1, len do info = itemInfo[i] @@ -580,10 +580,10 @@ function World:querySegmentWithCoords(x1, y1, x2, y2, filter) ti2 = info.ti2 info.weight = nil - info.x1 = x1 + dx * ti1 - info.y1 = y1 + dy * ti1 - info.x2 = x1 + dx * ti2 - info.y2 = y1 + dy * ti2 + info.x1 = x1 + dx * ti1 + info.y1 = y1 + dy * ti1 + info.x2 = x1 + dx * ti2 + info.y2 = y1 + dy * ti2 end return itemInfo, len end @@ -603,7 +603,7 @@ function World:add(item, x,y,w,h) local cl,ct,cw,ch = grid_toCellRect(self.cellSize, x,y,w,h) for cy = ct, ct+ch-1 do for cx = cl, cl+cw-1 do - addItemToCell(self, item, cx, cy) + addItemToCell(self, item, cx, cy) end end @@ -617,7 +617,7 @@ function World:remove(item) local cl,ct,cw,ch = grid_toCellRect(self.cellSize, x,y,w,h) for cy = ct, ct+ch-1 do for cx = cl, cl+cw-1 do - removeItemFromCell(self, item, cx, cy) + removeItemFromCell(self, item, cx, cy) end end end @@ -635,27 +635,27 @@ function World:update(item, x2,y2,w2,h2) if cl1 ~= cl2 or ct1 ~= ct2 or cw1 ~= cw2 or ch1 ~= ch2 then - local cr1, cb1 = cl1+cw1-1, ct1+ch1-1 - local cr2, cb2 = cl2+cw2-1, ct2+ch2-1 - local cyOut + local cr1, cb1 = cl1+cw1-1, ct1+ch1-1 + local cr2, cb2 = cl2+cw2-1, ct2+ch2-1 + local cyOut - for cy = ct1, cb1 do - cyOut = cy < ct2 or cy > cb2 - for cx = cl1, cr1 do - if cyOut or cx < cl2 or cx > cr2 then - removeItemFromCell(self, item, cx, cy) - end - end - end + for cy = ct1, cb1 do + cyOut = cy < ct2 or cy > cb2 + for cx = cl1, cr1 do + if cyOut or cx < cl2 or cx > cr2 then + removeItemFromCell(self, item, cx, cy) + end + end + end - for cy = ct2, cb2 do - cyOut = cy < ct1 or cy > cb1 - for cx = cl2, cr2 do - if cyOut or cx < cl1 or cx > cr1 then - addItemToCell(self, item, cx, cy) - end - end - end + for cy = ct2, cb2 do + cyOut = cy < ct1 or cy > cb1 + for cx = cl2, cr2 do + if cyOut or cx < cl1 or cx > cr1 then + addItemToCell(self, item, cx, cy) + end + end + end end @@ -690,7 +690,7 @@ function World:check(item, goalX, goalY, filter) while projected_len > 0 do local col = projected_cols[1] - len = len + 1 + len = len + 1 cols[len] = col visited[col.other] = true @@ -698,11 +698,11 @@ function World:check(item, goalX, goalY, filter) local response = getResponseByName(self, col.type) goalX, goalY, projected_cols, projected_len = response( - self, - col, - x, y, w, h, - goalX, goalY, - visitedFilter + self, + col, + x, y, w, h, + goalX, goalY, + visitedFilter ) end @@ -716,9 +716,9 @@ bump.newWorld = function(cellSize) cellSize = cellSize or 64 assertIsPositiveNumber(cellSize, 'cellSize') local world = setmetatable({ - cellSize = cellSize, - rects = {}, - rows = {}, + cellSize = cellSize, + rects = {}, + rows = {}, nonEmptyCells = {}, responses = {} }, World_mt) @@ -732,13 +732,13 @@ bump.newWorld = function(cellSize) end bump.rect = { - getNearestCorner = rect_getNearestCorner, + getNearestCorner = rect_getNearestCorner, getSegmentIntersectionIndices = rect_getSegmentIntersectionIndices, - getDiff = rect_getDiff, - containsPoint = rect_containsPoint, - isIntersecting = rect_isIntersecting, - getSquareDistance = rect_getSquareDistance, - detectCollision = rect_detectCollision + getDiff = rect_getDiff, + containsPoint = rect_containsPoint, + isIntersecting = rect_isIntersecting, + getSquareDistance = rect_getSquareDistance, + detectCollision = rect_detectCollision } bump.responses = { |