Compare commits

..

6 Commits

View File

@ -214,6 +214,17 @@ function level:anchor_points()
return pairs(self._anch) return pairs(self._anch)
end end
function level:anchors_in(px0,py0,px1,py1)
ancs={}
for ax=px0\4,(px1+3)\4 do
for ay=py0\4,(py1+3)\4 do
local anc=self._anch[_amix(ax,ay)]
if (anc!=nil) add(ancs, anc)
end
end
return ancs
end
function level:point_anchor(px,py) function level:point_anchor(px,py)
local ax,ay=self:p2a(px,py) local ax,ay=self:p2a(px,py)
local anc=self._anch[_amix(ax,ay)] local anc=self._anch[_amix(ax,ay)]
@ -575,7 +586,6 @@ function rope:_make_consistent()
) )
if #self.latch.rec.todo==0 then if #self.latch.rec.todo==0 then
self:_tidy_up_gen()
for i=0,#self.ancs do for i=0,#self.ancs do
local a0=self:_anc(i) local a0=self:_anc(i)
local a1=self:_anc(i+1) local a1=self:_anc(i+1)
@ -665,49 +675,83 @@ function rope:drag(
i,x,y i,x,y
) )
local anc=self:_anc(i()) local anc=self:_anc(i())
local busy=self:busy()
for x,y in self:_rast( for x,y in self:_rast(
anc.x,anc.y,x,y anc.x,anc.y,x,y
) do ) do
self:_drag1(i(),x,y) local a=self:_anc(i())
self:_tidy_up_gen() if not (_point_eq(a,{x=x,y=y})) then
a.x=x
a.y=y
a.dirty=true
self.dirty=true
if (not busy) self:_tidy_up_gen()
end
end end
end end
function rope:_tidy_up_gen() function rope:_tidy_up_gen()
if (self:busy()) return if (self.under_destruction) return
if (not self.dirty) return
for i=0,#self.ancs+1 do
local a=self:_anc(i)
a.dirty=true
end
local settled=true
local touched={}
local loop=function(f)
local a=0 local a=0
while a<=#self.ancs+1 do while a<=#self.ancs+1 do
local anc=self:_anc(a) local anc=self:_anc(a)
if anc.dirty and #anc.todo==0 then if anc.dirty then
while not self.under_destruction and ( anc.seen=true
self:_find_needed_anchors(a) or if self[f](self,a) then
self:_find_touched_anchors(a) or settled=false anc.changed=true
self:_elide_point(a) end
) do end end
anc.dirty=false
a=0
else
a+=1 a+=1
end end
end end
local mark_unseen=function()
touched={}
for a=0,#self.ancs+1,1 do
local anc=self:_anc(a)
anc.seen=false
anc.changed=false
end
end end
function rope:_drag1( local propagate_dirty=function(f)
i,x,y for a=0,#self.ancs+1,1 do
) local a1=self:_anc(a)
local a_old=self:_anc(i) if a1.dirty then
local a_new={x=x,y=y} local a0=self:_anc(a-1)
if (_point_eq(a_old, a_new)) return if (a0!=nil) a0.dirty=true
a_old.x=x local a2=self:_anc(a+1)
a_old.y=y if (a2!=nil) a2.dirty=true
end
end
end
while true do
settled=true
mark_unseen()
propagate_dirty()
loop("_find_needed_anchors")
loop("_find_touched_anchors")
loop("_elide_point")
for a=0,#self.ancs+1,1 do
local anc=self:_anc(a)
if (anc.seen) anc.dirty=anc.changed
end
if (settled) break
end
self.dirty=false
end end
function rope:_find_needed_anchors(i) function rope:_find_needed_anchors(i)
@ -724,15 +768,9 @@ function rope:_find_needed_anchors(i)
local anchors_bydist={} local anchors_bydist={}
local x0,x2=_mnmx(a0.x,a2.x) local x0,x2=_mnmx(a0.x,a2.x)
local y0,y2=_mnmx(a0.y,a2.y) local y0,y2=_mnmx(a0.y,a2.y)
for _,a1 in level:anchor_points() do for a1 in all(level:anchors_in(x0-1,y0-1,x2+1,y2+1)) do
-- the new anchor point must be in the bounding box
-- of the old line
-- note: this fudge never turned out necessary
-- i just would be ok with it
if x0-1<=a1.x and a1.x<=x2+1 and y0-1<=a1.y and a1.y<=y2+1 then
add(anchors_bydist,{el=a1,key=_linedist(a0,a1,a2)}) add(anchors_bydist,{el=a1,key=_linedist(a0,a1,a2)})
end end
end
shellsort(anchors_bydist) shellsort(anchors_bydist)
for a1 in all(anchors_bydist) do for a1 in all(anchors_bydist) do
@ -741,11 +779,9 @@ function rope:_find_needed_anchors(i)
self:_can_stretch(a1,a2) self:_can_stretch(a1,a2)
then then
local id=self.id local id=self.id
add(self.ancs,{id=id,x=a1.x,y=a1.y,todo={}},i) add(self.ancs,{id=id,x=a1.x,y=a1.y,dirty=true,todo={}},i)
self.id+=1 self.id+=1
self:_anc(i-1).dirty=true
self:_anc(i+1).dirty=true
return true return true
end end
end end
@ -758,19 +794,20 @@ function rope:_find_touched_anchors(i)
local a0=self:_anc(i-1) local a0=self:_anc(i-1)
local a2=self:_anc(i) local a2=self:_anc(i)
if (level:pcoll(a0.x,a0.y)) return false
if (level:pcoll(a2.x,a2.y)) return false
for bx,by in self:_rast(a0.x,a0.y,a2.x,a2.y) do for bx,by in self:_rast(a0.x,a0.y,a2.x,a2.y) do
local a1=level:point_anchor(bx,by) local a1=level:point_anchor(bx,by)
if a1!=nil and not _point_eq(a0,a1) and not _point_eq(a1,a2) if
and _linedist(a0,a1,a2) < 0.01 a1!=nil and not _point_eq(a0,a1) and not _point_eq(a1,a2)
and _linedist(a0,a1,a2) == 0.0
-- and self:_can_stretch(p,a2) -- and self:_can_stretch(p,a2)
then then
local id=self.id local id=self.id
add(self.ancs,{id=id,x=a1.x,y=a1.y,todo={}},i) add(self.ancs,{id=id,x=a1.x,y=a1.y,dirty=true,todo={}},i)
self.id+=1 self.id+=1
self:_anc(i-1).dirty=true
self:_anc(i+1).dirty=true
return true return true
end end
end end
@ -808,12 +845,15 @@ function rope:_elide_point(i)
end end
deli(self.ancs,i) deli(self.ancs,i)
self:_anc(i-1).dirty=true
self:_anc(i).dirty=true
return true return true
end end
function rope:_can_move_midpoint(a0,a1_0,a1_1,a2) function rope:_can_move_midpoint(a0,a1_0,a1_1,a2)
if (level:pcoll(a0.x,a0.y)) return false
if (level:pcoll(a2.x,a2.y)) return false
if (level:pcoll(a1_0.x,a1_0.y)) return false
if (level:pcoll(a1_1.x,a1_1.y)) return false
if not self:_can_stretch(a1_0, a1_1) then if not self:_can_stretch(a1_0, a1_1) then
return false return false
end end
@ -823,7 +863,7 @@ function rope:_can_move_midpoint(a0,a1_0,a1_1,a2)
if not self:_can_stretch(a1_1,a2) then if not self:_can_stretch(a1_1,a2) then
return false return false
end end
for x,y in self:_rastm(a1_0.x,a1_0.y,a1_1.x,a1_1.y) do for x,y in self:_rastn(a1_0.x,a1_0.y,a1_1.x,a1_1.y,8,8) do
local tm={x=x,y=y} local tm={x=x,y=y}
if not self:_can_stretch(a0,tm) then if not self:_can_stretch(a0,tm) then
return false return false
@ -879,11 +919,26 @@ end
function rope:_can_stretch( function rope:_can_stretch(
p1,p2 p1,p2
) )
-- faster implementation for straight lines
if p1.y\8==p2.y\8 then
local my=p2.y\8
for mx=p1.x\8,p2.x\8 do
if (level:mcoll(mx,my)) return false
end
end
if p1.x\8==p2.x\8 then
local mx=p2.x\8
for my=p1.y\8,p2.y\8 do
if (level:mcoll(mx,my)) return false
end
end
if (level:pcoll(p1.x,p1.y)) return false if (level:pcoll(p1.x,p1.y)) return false
if (level:pcoll(p2.x,p2.y)) return false if (level:pcoll(p2.x,p2.y)) return false
local res=true local res=true
for x,y in self:_rastm(p1.x,p1.y,p2.x,p2.y) do for x,y in self:_rastn(p1.x,p1.y,p2.x,p2.y,8,8) do
if level:pcoll(x,y) then if level:pcoll(x,y) then
res=false res=false
break break
@ -893,8 +948,8 @@ function rope:_can_stretch(
return res return res
end end
function rope:_rastm( function rope:_rastn(
x0,y0,x1,y1 x0,y0,x1,y1,dx,dy
) )
-- todo: more optimized implementation? -- todo: more optimized implementation?
local iter=self:_rast(x0,y0,x1,y1) local iter=self:_rast(x0,y0,x1,y1)
@ -907,8 +962,8 @@ function rope:_rastm(
if (x==nil) done=true return x1, y1 if (x==nil) done=true return x1, y1
local x8 = x\8 local x8 = x\dx
local y8 = y\8 local y8 = y\dy
if not (x8==prevx and y8==prevy) then if not (x8==prevx and y8==prevy) then
prevx,prevy=x8,y8 prevx,prevy=x8,y8
return x,y return x,y
@ -931,34 +986,27 @@ function rope:_rast(
if (y0<y1) sy=1 if (y0<y1) sy=1
local done=false,err local done=false,err
local queue={}
if dx>dy then if dx>dy then
err=dx/2.0 err=dx/2.0
return function() return function()
if (queue==nil) return if (done) return
if (x==x1) queue=nil return x1,y1 if (x==x1) done=true return x1,y1
if #queue==0 then local oldx,oldy=x,y
add(queue,{x,y})
err-=dy err-=dy
if (err<0) y+=sy add(queue,{x,y}) err+=dx if (err<0) y+=sy err+=dx
x+=sx x+=sx
end return oldx,oldy
return unpack(deli(queue,1))
end end
else else
err=dy/2.0 err=dy/2.0
return function() return function()
if (queue==nil) return if (done) return
if (y==y1) queue=nil return x1,y1 if (y==y1) done=true return x1,y1
if #queue==0 then
add(queue,{x,y})
local oldx,oldy=x,y local oldx,oldy=x,y
err-=dx err-=dx
if (err<0) x+=sx add(queue,{x,y}) err+=dy if (err<0) x+=sx err+=dy
y+=sy y+=sy
end return oldx,oldy
return unpack(deli(queue,1))
end end
end end
end end
@ -1034,7 +1082,7 @@ function rope:_tug()
if force or not level:pcoll(x,y) then if force or not level:pcoll(x,y) then
s.x=x s.x=x
s.y=y s.y=y
s.dirty=true self.dirty=true
end end
return true return true
end} end}
@ -1048,7 +1096,6 @@ function rope:_tug()
end end
for node=ancs[i-1].ix-1,ancs[i].ix+1 do for node=ancs[i-1].ix-1,ancs[i].ix+1 do
local anc=self:_anc(node) local anc=self:_anc(node)
if (anc!=nil) anc.dirty=true
end end
return return
end end
@ -1088,6 +1135,8 @@ function rope:_tug()
mx0,my0, mx0,my0,
dmx,dmy dmx,dmy
) )
-- be busy for 4 ticks while the crate moves
self:_anc(0).todo={{},{},{},{}}
end end
end end
end end