forked from pyrex/chameleonic
Update to current project state #1
217
chameleonic.p8
217
chameleonic.p8
@ -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
|
||||||
end
|
a.x=x
|
||||||
end
|
a.y=y
|
||||||
|
a.dirty=true
|
||||||
function rope:_tidy_up_gen()
|
self.dirty=true
|
||||||
if (self:busy()) return
|
if (not busy) self:_tidy_up_gen()
|
||||||
|
|
||||||
for i=0,#self.ancs+1 do
|
|
||||||
local a=self:_anc(i)
|
|
||||||
a.dirty=true
|
|
||||||
end
|
|
||||||
|
|
||||||
local a=0
|
|
||||||
while a<=#self.ancs+1 do
|
|
||||||
local anc=self:_anc(a)
|
|
||||||
if anc.dirty and #anc.todo==0 then
|
|
||||||
while not self.under_destruction and (
|
|
||||||
self:_find_needed_anchors(a) or
|
|
||||||
self:_find_touched_anchors(a) or
|
|
||||||
self:_elide_point(a)
|
|
||||||
) do end
|
|
||||||
|
|
||||||
anc.dirty=false
|
|
||||||
a=0
|
|
||||||
else
|
|
||||||
a+=1
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function rope:_drag1(
|
function rope:_tidy_up_gen()
|
||||||
i,x,y
|
if (self.under_destruction) return
|
||||||
)
|
if (not self.dirty) return
|
||||||
local a_old=self:_anc(i)
|
|
||||||
local a_new={x=x,y=y}
|
|
||||||
if (_point_eq(a_old, a_new)) return
|
|
||||||
|
|
||||||
a_old.x=x
|
local settled=true
|
||||||
a_old.y=y
|
local touched={}
|
||||||
|
local loop=function(f)
|
||||||
|
local a=0
|
||||||
|
while a<=#self.ancs+1 do
|
||||||
|
local anc=self:_anc(a)
|
||||||
|
if anc.dirty then
|
||||||
|
anc.seen=true
|
||||||
|
if self[f](self,a) then
|
||||||
|
settled=false anc.changed=true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
a+=1
|
||||||
|
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
|
||||||
|
|
||||||
|
local propagate_dirty=function(f)
|
||||||
|
for a=0,#self.ancs+1,1 do
|
||||||
|
local a1=self:_anc(a)
|
||||||
|
if a1.dirty then
|
||||||
|
local a0=self:_anc(a-1)
|
||||||
|
if (a0!=nil) a0.dirty=true
|
||||||
|
|
||||||
|
local a2=self:_anc(a+1)
|
||||||
|
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,14 +768,8 @@ 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
|
add(anchors_bydist,{el=a1,key=_linedist(a0,a1,a2)})
|
||||||
-- 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)})
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
shellsort(anchors_bydist)
|
shellsort(anchors_bydist)
|
||||||
|
|
||||||
@ -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 err+=dx
|
||||||
if (err<0) y+=sy add(queue,{x,y}) err+=dx
|
x+=sx
|
||||||
x+=sx
|
return oldx,oldy
|
||||||
end
|
|
||||||
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
|
local oldx,oldy=x,y
|
||||||
add(queue,{x,y})
|
err-=dx
|
||||||
|
if (err<0) x+=sx err+=dy
|
||||||
local oldx,oldy=x,y
|
y+=sy
|
||||||
err-=dx
|
return oldx,oldy
|
||||||
if (err<0) x+=sx add(queue,{x,y}) err+=dy
|
|
||||||
y+=sy
|
|
||||||
end
|
|
||||||
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
|
||||||
|
Loading…
Reference in New Issue
Block a user