̬̬̬̈̬̬̬̬̈̬̬ ̪̬̬̬̪쬪̬̬̬̪쬪DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ʬ >w* 0303 i -- title: distance to line (library) -- author: atesin (atesin#gmail.com) -- desc: get closest point from line to other point -- site: https://tic80.com/dev?id=8725 -- license: WTFPL, but to mention me would be nice -- version: 0.2 -- script: lua -- better viewed with narrow font -- main changes from previous version: -- joystick/keypad operation replaced with mouse drags --[[---------------------------------------------------- motivation: i am learning and trying to make some games, so i was exploring collision detection techniques, and realized that the fastest and simplest is collision between 2 circles, followed by hitboxes collision (AABB). i would like to add diagonal shapes (right triangle) and collision between different shapes, so i realized to achieve that, a fast algorithm like this would be essential. check plNearest() function. --]]---------------------------------------------------- function BOOT() origin = {x=120, y=68} -- location of {0,0} on screen points = { {x= 20, y=-20}, -- point outside given line {x=-20, y=-20}, -- edge of line {x= 20, y= 20}, -- the other edge of line } end function TIC() updateScene() proj, clam = plNearest(points[1], points[2], points[3]) drawScene() end function copyObj(table) local obj = {} for k,v in pairs(table) do obj[k]=v end return obj end function plNearest(p, la, lb) --[[ this function is the CORE of this cartridge. it finds the nearest point(s) from point p to line a-b. light and fast, no square roots, just 1 division. arguments: three {x=?,y=?} points. returns: nearest point projected outside infinite line, nearest point constrained inside segment. dependency: copyObj() function. idea: https://stackoverflow.com/a/6853926/15576834 --]] local lx = lb.x - la.x -- a-b x component local ly = lb.y - la.y -- a-b y component local sqlen = lx*lx + ly*ly -- a-b squared lenght if sqlen==0 then -- 0 length line: all points coincide return copyObj(la), copyObj(la) end -- dot/sqlen, no idea what is xD local howfar = ((p.x - la.x)*lx + (p.y - la.y)*ly)/sqlen -- projected point local pp = {x=lx*howfar + la.x, y=ly*howfar + la.y} if howfar<0 then -- constrain 2nd point to line edge return pp, copyObj(la) elseif howfar>1 then return pp, copyObj(lb) end return pp, copyObj(pp) -- both points fits inside line end function updateScene() local mx, my, mb = mouse() mx, my = mx-origin.x, my-origin.y if math.abs(mx) > origin.x or math.abs(my) > origin.y then return -- mouse cursor outside screen elseif not mb then -- lmb not pressed/released clickp, selPoint = false, nil return elseif not clickp then -- lmb pressed but not in last frame clickp, selPoint = true, nil for i, p in ipairs(points) do -- q: which point to grab? if math.abs(p.x-mx) < 3 and math.abs(p.y-my) < 3 then selPoint = p break end end return -- a: none end if selPoint then selPoint.x, selPoint.y = mx, my -- set coords same as mouse end end function drawScene() cls() print('nearest point in line, from other point.',0 ,0) print('drag white points/edges with mouse.', 0, 8) print('orange: nearest point in segment.', 0, 16) print('blue: nearest point in line (projected).', 0, 24) print('read code comments for more info.', 0, 32) oLine(points[2].x, points[2].y, points[3].x, points[3].y, 12) oCircb(points[1].x, points[1].y, 2, 12) oCircb(points[2].x, points[2].y, 2, 12) oCircb(points[3].x, points[3].y, 2, 12) oCircb(proj.x, proj.y, 2, 10) -- projected line point oCircb(clam.x, clam.y, 2, 3) -- segment contained point end function oLine(ax, ay, bx, by, c) -- centered in origin line(ax + origin.x, ay + origin.y, bx + origin.x, by + origin.y, c) end function oCircb(x, y, r, c) -- centered in origin circb(x + origin.x, y + origin.y, r, c) end