Compare commits

..

3 Commits

Author SHA1 Message Date
f052186c97 Optimize raycast in sweep 2022-12-31 19:03:08 -08:00
0d0a2c41a7 Make all APIs point-oriented 2022-12-31 17:27:00 -08:00
71b150cb33 Golf the sweep code 2022-12-31 16:18:24 -08:00

View File

@ -138,6 +138,41 @@ function _rast(
_add() _add()
end end
-->8
-- anchor operations (for the rope code)
function _anch_eq(a0,a1)
if (a0 and a1) return a0[1]==a1[1] and a0[2]==a1[2]
return a0==a1
end
--[[
function _anch_unpack(anch)
assert(anch[1])
assert(anch[2])
return anch[1],anch[2]
end
]]--
_anch_unpack=unpack
function _anch_new(prev,next,xy)
local out={prev=prev,next=next,_anch_unpack(xy)}
if (prev) prev.next=out
if (next) next.prev=out
return out
end
function _anch_del(n1,xy)
local n0,n2=n1.prev,n1.next
if (n0) n0.next=n2
if (n2) n2.prev=n0
end
function _anch_update(a0,a1)
a0[1]=a1[1]
a0[2]=a1[2]
end
-->8 -->8
-- input -- input
@ -373,7 +408,7 @@ function level:recollide_reanchor()
) then ) then
local key="GEOM"..mx0..","..my0..","..dx..","..dy local key="GEOM"..mx0..","..my0..","..dx..","..dy
anch_new[key]= { anch_new[key]= {
ax=max(mx0,mx1),ay=max(my0,my1),adx=-dx,ady=-dy max(mx0,mx1),max(my0,my1),adx=-dx,ady=-dy
} }
end end
end end
@ -384,7 +419,7 @@ function level:recollide_reanchor()
local mx0,my0=cr.mx,cr.my local mx0,my0=cr.mx,cr.my
local mx1,my1=mx0+dx,my0+dy local mx1,my1=mx0+dx,my0+dy
anch_new[key]={ anch_new[key]={
ax=max(mx0,mx1),ay=max(my0,my1),adx=-dx,ady=-dy max(mx0,mx1),max(my0,my1),adx=-dx,ady=-dy
} }
end end
end end
@ -394,17 +429,19 @@ function level:recollide_reanchor()
local old=(self._anch or {})[k] local old=(self._anch or {})[k]
if old then if old then
anch_new[k]=new anch_new[k]=new
if (old.ax!=new.ax or old.ay!=new.ay) add(moves,{old.ax,old.ay,new.ax,new.ay,old,key=k}) if (not _anch_eq(old,new)) add(moves,{old,new,key=k})
end end
end end
self._anch=anch_new self._anch=anch_new
self._anch_keys={} self._anch_keys={}
for k,_ in pairs(self._anch) do self._anch_by_position={}
for k,v in pairs(self._anch) do
add(self._anch_keys,{key=k}) add(self._anch_keys,{key=k})
local pkey=_mix(_anch_unpack(v))
self._anch_by_position[pkey]=self._anch_by_position[pkey] or v
end end
shellsort(self._anch_keys) shellsort(self._anch_keys)
shellsort(moves) shellsort(moves)
--printh("!!STARTING!!")
if player.rope then if player.rope then
player.rope:experience_anchor_moves(moves) player.rope:experience_anchor_moves(moves)
@ -428,10 +465,8 @@ function level:anchor_points()
end end
end end
function level:anchor_at(ax,ay) function level:anchor_at(point)
for i in self:anchor_points() do return self._anch_by_position[_mix(_anch_unpack(point))]
if (i.ax==ax and i.ay==ay) return i
end
end end
function level:get_open_pit(mx,my) function level:get_open_pit(mx,my)
@ -485,10 +520,6 @@ function level:_mget(mx,my)
) )
end end
function _amix(ax,ay)
return ax..","..ay
end
function _mix(mx,my) function _mix(mx,my)
return mx..","..my return mx..","..my
end end
@ -672,8 +703,8 @@ function player:update()
while not level:mcoll(x,y) do x+=dx y+=dy end while not level:mcoll(x,y) do x+=dx y+=dy end
self.rope=rope:new( self.rope=rope:new(
x+0.5-dx*0.5,y+0.5-dy*0.5, {x+0.5-dx*0.5,y+0.5-dy*0.5},
self.x+0.5,self.y+0.5, {self.x+0.5,self.y+0.5},
level:get_latch(dx,dy,x*8,y*8) level:get_latch(dx,dy,x*8,y*8)
) )
@ -700,10 +731,10 @@ function player:update()
self.y=latch.rec.my+latch.dy self.y=latch.rec.my+latch.dy
end end
self.rope:drag_dst( self.rope:drag_dst{
self.x+self.px/8+0.5, self.x+self.px/8+0.5,
self.y+self.py/8+0.5 self.y+self.py/8+0.5
) }
local tdx,tdy=self.rope:tug_orientxy() local tdx,tdy=self.rope:tug_orientxy()
if (tdx) self.orientx=tdx if (tdx) self.orientx=tdx
@ -815,19 +846,15 @@ rope={}
rope.__index=rope rope.__index=rope
function rope:new( function rope:new(
src_ax,src_ay,dst_ax,dst_ay,latch src,dst,latch
) )
local r={ local r={
id=0, id=0,
anchors={
{ax=src_ax,ay=src_ay},
{ax=dst_ax,ay=dst_ay}
},
state={name="cast",frame=0}, state={name="cast",frame=0},
latch=latch, latch=latch,
} }
r.src=r.anchors[1] r.src=src
r.dst=r.anchors[2] r.dst=dst
r.src.next=r.dst r.src.next=r.dst
r.dst.prev=r.src r.dst.prev=r.src
setmetatable(r,rope) setmetatable(r,rope)
@ -855,10 +882,10 @@ function rope:update()
if (not self.latch) wrongbleep:bleep(5) self:destroy() return if (not self.latch) wrongbleep:bleep(5) self:destroy() return
if self.latch.rec then if self.latch.rec then
self:drag_src( self:drag_src{
self.latch.rec.mx+0.5+self.latch.ax_offset, self.latch.rec.mx+0.5+self.latch.ax_offset,
self.latch.rec.my+0.5+self.latch.ay_offset self.latch.rec.my+0.5+self.latch.ay_offset
) }
if self.latch.rec.dead==true then if self.latch.rec.dead==true then
self:destroy() self:destroy()
@ -991,7 +1018,7 @@ function rope:draw(artificial_px,artificial_py)
local sy=0 local sy=0
while true do while true do
if (n1==nil) break if (n1==nil) break
local anch=level:anchor_at(n1.ax,n1.ay) local anch=level:anchor_at(n1)
local x=n1.ax*8 local x=n1.ax*8
local y=n1.ay*8 local y=n1.ay*8
if anch then if anch then
@ -1032,18 +1059,18 @@ function rope:draw(artificial_px,artificial_py)
]] ]]
end end
function rope:drag_dst(x,y) function rope:drag_dst(xy)
self:drag(self.dst,x,y) self:drag(self.dst,xy)
end end
function rope:drag_src(x,y) function rope:drag_src(xy)
self:drag(self.src,x,y) self:drag(self.src,xy)
end end
function rope:drag(n1,ax_new,ay_new) function rope:drag(n1,axy)
self:relax() self:relax()
self:_drag(n1,ax_new,n1.ay) self:_drag(n1,{axy[1],n1[2]})
self:_drag(n1,ax_new,ay_new) self:_drag(n1,axy)
self:relax() self:relax()
end end
@ -1053,11 +1080,8 @@ function rope:relax()
local n1=n0.next local n1=n0.next
if (not n1) break if (not n1) break
local n2=n1.next local n2=n1.next
if _anch_eq(n0,n1) then
_anch_del(n1)
if n0.ax==n1.ax and n0.ay==n1.ay then
n0.next=n2
if (n2) n2.prev=n0
else else
n0=n0.next n0=n0.next
end end
@ -1070,20 +1094,11 @@ function rope:relax()
local n2=n1.next local n2=n1.next
if (not n2) return if (not n2) return
local x0,y0=n0.ax,n0.ay local anch=level:anchor_at(n1)
local x1,y1=n1.ax,n1.ay local wouldstick,position_new=would_stick(anch,n0,n1,n2)
local x2,y2=n2.ax,n2.ay if not (wouldstick or _anch_eq(n1,position_new)) then
self:_drag(n1,position_new,{_anch_unpack(n1)})
local anch=level:anchor_at(n1.ax,n1.ay) _anch_del(n1)
local would,x1_new,y1_new=would_stick(anch,x0,y0,x1,y1,x2,y2)
if not would and not (n1.ax==x1_new and n1.ay==y1_new) then
self:_drag(n1,x1_new,y1_new,n1.ax,n1.ay)
n0=n1.prev
n2=n1.next
n0.next=n2
n2.prev=n0
n1.next=nil
n1.prev=nil
else n0=n0.next end else n0=n0.next end
end end
end end
@ -1099,7 +1114,9 @@ function rope:_check_sane()
local n1=n0.next local n1=n0.next
if (not n1) break if (not n1) break
_rast(qxs,qys,flr(n0.ax*2),flr(n0.ay*2),flr(n1.ax*2),flr(n1.ay*2)) local n0ax,n0ay=_anch_unpack(n0)
local n1ax,n1ay=_anch_unpack(n1)
_rast(qxs,qys,flr(n0ax*2),flr(n0ay*2),flr(n1ax*2),flr(n1ay*2))
n0=n1 n0=n1
end end
@ -1143,7 +1160,6 @@ function rope:_check_sane()
if dmx==1 and dmy==1 and level:mcoll(mx0,my2) and level:mcoll(mx2,my0) then if dmx==1 and dmy==1 and level:mcoll(mx0,my2) and level:mcoll(mx2,my0) then
else else
--printh("ok! "..tostring({qxs[i-2],qys[i-2]})..tostring({qxs[i],qys[i]})..tostring(m0)..tostring(m2))
ok=true ok=true
end end
end end
@ -1156,12 +1172,12 @@ function rope:_check_sane()
return true return true
end end
function would_stick(anchor,x0,y0,x1,y1,x2,y2) function would_stick(anchor,xy0,xy1,xy2)
x1,y1=x1 or anchor.ax,y1 or anchor.ay local x0,y0=_anch_unpack(xy0)
local x1,y1=_anch_unpack(xy1 or anchor)
local x2,y2=_anch_unpack(xy2)
local dx,dy=x2-x0,y2-y0 local dx,dy=x2-x0,y2-y0
if (x1==x0 and y1==y0) return
if (x1==x2 and y1==y2) return
local function switch_ends() local function switch_ends()
dx,dy,x0,y0,x2,y2=-dx,-dy,x2,y2,x0,y0 dx,dy,x0,y0,x2,y2=-dx,-dy,x2,y2,x0,y0
@ -1185,7 +1201,7 @@ function would_stick(anchor,x0,y0,x1,y1,x2,y2)
return return
anchor and anchor.adx==adx and anchor.ady==ady, anchor and anchor.adx==adx and anchor.ady==ady,
x1_new,y1_new {x1_new,y1_new}
end end
function rope:experience_anchor_moves(moves) function rope:experience_anchor_moves(moves)
@ -1198,15 +1214,15 @@ function rope:_be_dragged_by(moves)
local n=self.src local n=self.src
while n do while n do
for t in all(moves) do for t in all(moves) do
local ax_old,ay_old,ax_new,ay_new=unpack(t) local xy_old,xy_new=unpack(t)
if (ax_old==n.ax and ay_old==n.ay) n.dest={ax_new,ay_new} break if (_anch_eq(xy_old,n)) n.dest=xy_new break
end end
n=n.next n=n.next
end end
n=self.src n=self.src
while n do while n do
if (n.dest) self:_drag(n,unpack(n.dest)) n.dest=nil if (n.dest) self:_drag(n,n.dest) n.dest=nil
n=n.next n=n.next
end end
end end
@ -1217,14 +1233,18 @@ function rope:_be_pushed_by(moves)
end end
end end
function rope:_be_pushed_by1(ax_old,ay_old,ax_new,ay_new,anch) function rope:_be_pushed_by1(anch_old,anch_new)
local n0=self.src local n0=self.src
while true do
local ax_old,ay_old=_anch_unpack(anch_old)
local ax_new,ay_new=_anch_unpack(anch_new)
while n0 do
n1=n0.next n1=n0.next
if (not n1) return if (not n1) return
local nx0,ny0=n0.ax,n0.ay local nx0,ny0=_anch_unpack(n0)
local nx1,ny1=n1.ax,n1.ay local nx1,ny1=_anch_unpack(n1)
local nxmn,nxmx = _mnmx(nx0,nx1) local nxmn,nxmx = _mnmx(nx0,nx1)
local nymn,nymx = _mnmx(ny0,ny1) local nymn,nymx = _mnmx(ny0,ny1)
@ -1233,9 +1253,9 @@ function rope:_be_pushed_by1(ax_old,ay_old,ax_new,ay_new,anch)
(ax_new!=ax_old or (nxmn<ax_new and ax_new<nxmx)) and (ax_new!=ax_old or (nxmn<ax_new and ax_new<nxmx)) and
(ay_new!=ay_old or (nymn<ay_new and ay_new<nymx)) and (ay_new!=ay_old or (nymn<ay_new and ay_new<nymx)) and
(_which_side(ax_old,ay_old,nx0,ny0,nx1,ny1)!= (_which_side(anch_old,n0,n1)!=
_which_side(ax_new,ay_new,nx0,ny0,nx1,ny1) _which_side(anch_new,n0,n1)
) and would_stick(anch,nx0,ny0,nil,nil,nx1,ny1) ) and would_stick(anch_new,n0,nil,n1)
then then
local nx05,ny05 local nx05,ny05
if ax_new==ax_old then if ax_new==ax_old then
@ -1246,101 +1266,84 @@ function rope:_be_pushed_by1(ax_old,ay_old,ax_new,ay_new,anch)
nx05=nx0+(ny05-ny0)/(ny1-ny0) * (nx1-nx0) nx05=nx0+(ny05-ny0)/(ny1-ny0) * (nx1-nx0)
end end
local n05={ax=nx05,ay=ny05,prev=n0,next=n1} local n05=_anch_new(n0,n1,{nx05,ny05})
n0.next=n05 self:_drag(n05,{ax_new,ay_new})
n1.prev=n05
-- printh("creating: "..tostring{anch,{n0.ax,n0.ay},{n05.ax,n05.ay},{n1.ax,n1.ay}})
-- printh("dragging: "..tostring{anch,{n05.ax,n05.ay},{ax_new,ay_new}})
self:_drag(n05,ax_new,ay_new)
else else
n0=n0.next n0=n0.next
end end
end end
end end
function rope:_drag(n1,ax1_new,ay1_new,ax_removing,ay_removing) function rope:_drag(n1,new,removing)
local function _sweep_radar(ax_lhs,ay_lhs,ax_rhs,ay_rhs,ax_pivot,ay_pivot,ax_far0,ay_far0,ax_far1,ay_far1) local function _sweep_radar(lhs,rhs,pivot,src,dst)
if (_anch_eq(src,dst)) return
local function _uncreatable(anchor) local function _uncreatable(anchor)
return (anchor.ax==ax_lhs and anchor.ay==ay_lhs) or return _anch_eq(anchor,lhs) or _anch_eq(anchor,lhs) or _anch_eq(anchor,removing)
(anchor.ax==ax_rhs and anchor.ay==ay_rhs) or
(anchor.ax==ax_removing and anchor.ay==ay_removing)
end end
if (ax_far0==ax_far1 and ay_far0==ay_far1) return local function _sweep(extent_orig,extent_final,cb_in_bounds,cb_make_point)
local eligible={}
local point_orig=cb_make_point(extent_orig)
local point_final=cb_make_point(extent_final)
if ax_far0==ax_far1 then
local ax_far=ax_far0
local ax0,ax1=_mnmx(ax_pivot,ax_far)
ay_far_old=ay_far0
for ay_far_new in _stepfrom(ay_far0,ay_far1) do
for anchor in level:anchor_points() do for anchor in level:anchor_points() do
if -- figure out which anchors we even can stick to
not _uncreatable(anchor) and if cb_in_bounds(anchor) and not _uncreatable(anchor) then
(ax0<=anchor.ax and anchor.ax<=ax1) and local side_orig=_which_side(anchor,pivot,point_orig)
would_stick(anchor,ax_pivot,ay_pivot,nil,nil,ax_far,ay_far_new) and local side_final=_which_side(anchor,pivot,point_final)
(
_which_side(anchor.ax,anchor.ay,ax_pivot,ay_pivot,ax_far,ay_far_old) !=
_which_side(anchor.ax,anchor.ay,ax_pivot,ay_pivot,ax_far,ay_far_new)
)
then
return anchor
end
end
ay_far_old=ay_far_new
end
elseif ay_far0==ay_far1 then
local ay_far=ay_far0
local ay0,ay1=_mnmx(ay_pivot,ay_far)
ax_far_old=ax_far0 if (side_orig!=side_final and would_stick(anchor,pivot,nil,point_final)) add(eligible,{anchor,side_final})
for ax_far_new in _stepfrom(ax_far0,ax_far1) do
for anchor in level:anchor_points() do
if
not _uncreatable(anchor) and
(ay0<=anchor.ay and anchor.ay<=ay1) and
would_stick(anchor,ax_pivot,ay_pivot,nil,nil,ax_far_new,ay_far) and
(
_which_side(anchor.ax,anchor.ay,ax_pivot,ay_pivot,ax_far_old,ay_far) !=
_which_side(anchor.ax,anchor.ay,ax_pivot,ay_pivot,ax_far_new,ay_far)
)
then
return anchor
end end
end end
ax_far_old=ax_far_new
-- if no one's in the race, don't raycast
if (#eligible==0) return
if (#eligible==1) return eligible[1][1]
-- see who wins the race
for extent in _stepfrom(extent_orig,extent_final) do
local point = cb_make_point(extent)
for anchor_final in all(eligible) do
if (_which_side(anchor_final[1],pivot,point)==anchor_final[2]) return anchor_final[1]
end end
end end
end end
local ax1_old,ay1_old=n1.ax,n1.ay local ax_pivot,ay_pivot=_anch_unpack(pivot)
n1.ax=ax1_new local ax_src,ay_src=_anch_unpack(src)
n1.ay=ay1_new local ax_dst,ay_dst=_anch_unpack(dst)
if ax_src==ax_dst then
return _sweep(
ay_src,ay_dst,
function(anchor) return mid(ax_pivot,anchor[1],ax_dst)==anchor[1] end,
function(ay) return {ax_dst,ay} end
)
else
return _sweep(
ax_src,ax_dst,
function(anchor) return mid(ay_pivot,anchor[2],ay_dst)==anchor[2] end,
function(ax) return {ax,ay_dst} end
)
end
end
local old={_anch_unpack(n1)}
_anch_update(n1,new)
local n0=n1.prev local n0=n1.prev
while n0 do while n0 do
local anch=_sweep_radar( local anch=_sweep_radar(n0,n1,n0,old,new)
n0.ax,n0.ay,n1.ax,n1.ay,
n0.ax,n0.ay,ax1_old,ay1_old,ax1_new,ay1_new
)
if (not anch) break if (not anch) break
local n05={ax=anch.ax,ay=anch.ay,prev=n0,next=n1} n0=_anch_new(n0,n1,anch)
n0.next=n05
n1.prev=n05
n0=n05
end end
local n2=n1.next local n2=n1.next
while n2 do while n2 do
local anch=_sweep_radar( local anch=_sweep_radar(n1,n2,n2,old,new)
n1.ax,n1.ay,n2.ax,n2.ay,
n2.ax,n2.ay,ax1_old,ay1_old,ax1_new,ay1_new
)
if (not anch) break if (not anch) break
local n15={ax=anch.ax,ay=anch.ay,prev=n1,next=n2} n2=_anch_new(n1,n2,anch)
n1.next=n15
n2.prev=n15
n2=n15
end end
end end
@ -1353,9 +1356,7 @@ function _stepfrom(x0,x1)
end end
end end
local mul=0.5 local mul=1
x0*=2
x1*=2
if (x0>x1) x0,x1,mul=-x0,-x1,-mul if (x0>x1) x0,x1,mul=-x0,-x1,-mul
local i=flr(x0) local i=flr(x0)
local top=flr(x1) local top=flr(x1)
@ -1371,7 +1372,11 @@ function _stepfrom(x0,x1)
end end
end end
function _which_side(x,y,x0,y0,x1,y1) function _which_side(xy,x0y0,x1y1)
local x,y=_anch_unpack(xy)
local x0,y0=_anch_unpack(x0y0)
local x1,y1=_anch_unpack(x1y1)
return sgn0((x1-x0)*(y-y0) - (y1-y0)*(x-x0)) return sgn0((x1-x0)*(y-y0) - (y1-y0)*(x-x0))
end end
@ -1399,7 +1404,8 @@ function rope:collide_mrect(mx0,my0,mw,mh,exclude_src,exclude_dst)
if (not nd) return if (not nd) return
end end
local x1,y1,x2,y2=n0.ax,n0.ay,n1.ax,n1.ay local x1,y1=_anch_unpack(n0)
local x2,y2=_anch_unpack(n1)
local function _line_line(x3,y3,x4,y4) local function _line_line(x3,y3,x4,y4)
local denom=(y4-y3)*(x2-x1)-(x4-x3)*(y2-y1) local denom=(y4-y3)*(x2-x1)-(x4-x3)*(y2-y1)
@ -1429,12 +1435,15 @@ end
function rope:tug_orientxy() function rope:tug_orientxy()
local a1=self.dst local a1=self.dst
local a0=self.dst.prev local a0=self.dst.prev
local dx=a0.ax-a1.ax local ax0,ay0=_anch_unpack(a0)
local ax1,ay1=_anch_unpack(a1)
local dx=ax0-ax1
local tdx local tdx
if (dx>3/8) tdx=1 if (dx>3/8) tdx=1
if (dx<-3/8) tdx=-1 if (dx<-3/8) tdx=-1
local dy=a0.ay-a1.ay local dy=ay0-ay1
local tdy local tdy
if abs(dy)>abs(dx)/2 then if abs(dy)>abs(dx)/2 then
if (dy>3/8) tdy=1 if (dy>3/8) tdy=1
@ -1619,11 +1628,12 @@ function rope:_anchors_simplified()
end end
a=self.src a=self.src
while a do while a do
local ax,ay=_anch_unpack(a)
local point={ local point={
x=flr(a.ax*8+0.5),y=flr(a.ay*8+0.5), ax,ay,
ax=a.ax,ay=a.ay x=flr(ax*8+0.5),y=flr(ay*8+0.5),
} }
local aw=level:anchor_at(a.ax,a.ay) local aw=level:anchor_at(a)
local l=self.latch local l=self.latch
if aw then if aw then
if (aw.adx==1) point.x-=1 if (aw.adx==1) point.x-=1