From a8b921e05bde775677b38691ac66b3584ef5da7a Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Sat, 17 Dec 2022 13:19:43 -0800 Subject: [PATCH 1/6] Simplify rope recalculator (still slow) --- chameleonic.p8 | 95 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 33 deletions(-) diff --git a/chameleonic.p8 b/chameleonic.p8 index c03b108..6aae30f 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -676,38 +676,76 @@ end function rope:_tidy_up_gen() if (self:busy()) return - for i=0,#self.ancs+1 do - local a=self:_anc(i) - a.dirty=true - end + if (self.under_destruction) return - 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 + local settled=true + 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 then + anc.dirty=anc.changed + end + end + + if (settled) break + end end function rope:_drag1( i,x,y ) - local a_old=self:_anc(i) - local a_new={x=x,y=y} - if (_point_eq(a_old, a_new)) return + local a=self:_anc(i) + if (_point_eq(a, {x=x,y=y})) return - a_old.x=x - a_old.y=y + a.x=x + a.y=y + a.dirty=true end function rope:_find_needed_anchors(i) @@ -741,11 +779,9 @@ function rope:_find_needed_anchors(i) self:_can_stretch(a1,a2) then 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:_anc(i-1).dirty=true - self:_anc(i+1).dirty=true return true end end @@ -765,12 +801,9 @@ function rope:_find_touched_anchors(i) -- and self:_can_stretch(p,a2) then 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:_anc(i-1).dirty=true - self:_anc(i+1).dirty=true - return true end end @@ -808,8 +841,6 @@ function rope:_elide_point(i) end deli(self.ancs,i) - self:_anc(i-1).dirty=true - self:_anc(i).dirty=true return true end @@ -1034,7 +1065,6 @@ function rope:_tug() if force or not level:pcoll(x,y) then s.x=x s.y=y - s.dirty=true end return true end} @@ -1048,7 +1078,6 @@ function rope:_tug() end for node=ancs[i-1].ix-1,ancs[i].ix+1 do local anc=self:_anc(node) - if (anc!=nil) anc.dirty=true end return end -- 2.34.1 From a20ad44f75fa24198a9a00f18c483028941efa7a Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Sat, 17 Dec 2022 13:24:54 -0800 Subject: [PATCH 2/6] Faster anchor search in some common cases --- chameleonic.p8 | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/chameleonic.p8 b/chameleonic.p8 index 6aae30f..81e043f 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -214,6 +214,17 @@ function level:anchor_points() return pairs(self._anch) 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) local ax,ay=self:p2a(px,py) local anc=self._anch[_amix(ax,ay)] @@ -762,14 +773,8 @@ function rope:_find_needed_anchors(i) local anchors_bydist={} local x0,x2=_mnmx(a0.x,a2.x) local y0,y2=_mnmx(a0.y,a2.y) - for _,a1 in level:anchor_points() 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)}) - end + for a1 in all(level:anchors_in(x0-1,y0-1,x2+1,y2+1)) do + add(anchors_bydist,{el=a1,key=_linedist(a0,a1,a2)}) end shellsort(anchors_bydist) -- 2.34.1 From 13e6c382be348312e345224856655f836c89a886 Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Sat, 17 Dec 2022 13:30:48 -0800 Subject: [PATCH 3/6] My fat bresenham implementation is really slow --- chameleonic.p8 | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/chameleonic.p8 b/chameleonic.p8 index 81e043f..a2219d2 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -967,34 +967,27 @@ function rope:_rast( if (y0dy then err=dx/2.0 return function() - if (queue==nil) return - if (x==x1) queue=nil return x1,y1 - if #queue==0 then - add(queue,{x,y}) - err-=dy - if (err<0) y+=sy add(queue,{x,y}) err+=dx - x+=sx - end - return unpack(deli(queue,1)) + if (done) return + if (x==x1) done=true return x1,y1 + local oldx,oldy=x,y + err-=dy + if (err<0) y+=sy err+=dx + x+=sx + return oldx,oldy end else err=dy/2.0 return function() - if (queue==nil) return - if (y==y1) queue=nil return x1,y1 - if #queue==0 then - add(queue,{x,y}) - - local oldx,oldy=x,y - err-=dx - if (err<0) x+=sx add(queue,{x,y}) err+=dy - y+=sy - end - return unpack(deli(queue,1)) + if (done) return + if (y==y1) done=true return x1,y1 + local oldx,oldy=x,y + err-=dx + if (err<0) x+=sx err+=dy + y+=sy + return oldx,oldy end end end -- 2.34.1 From 29d86556cabfca8f7397e7246ee710011ea988a9 Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Sat, 17 Dec 2022 13:45:46 -0800 Subject: [PATCH 4/6] Again, reduce the level of useless work --- chameleonic.p8 | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/chameleonic.p8 b/chameleonic.p8 index a2219d2..d4f9d99 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -679,8 +679,13 @@ function rope:drag( for x,y in self:_rast( anc.x,anc.y,x,y ) do - self:_drag1(i(),x,y) - self:_tidy_up_gen() + local a=self:_anc(i()) + if not (_point_eq(a, {x=x,y=y})) then + a.x=x + a.y=y + a.dirty=true + self:_tidy_up_gen() + end end end @@ -748,17 +753,6 @@ function rope:_tidy_up_gen() end end -function rope:_drag1( - i,x,y -) - local a=self:_anc(i) - if (_point_eq(a, {x=x,y=y})) return - - a.x=x - a.y=y - a.dirty=true -end - function rope:_find_needed_anchors(i) if (i<=0) return false if (#self.ancs+1 Date: Sat, 17 Dec 2022 13:52:37 -0800 Subject: [PATCH 5/6] Move in bigger hops while dragging --- chameleonic.p8 | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/chameleonic.p8 b/chameleonic.p8 index d4f9d99..2863aca 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -586,7 +586,6 @@ function rope:_make_consistent() ) if #self.latch.rec.todo==0 then - self:_tidy_up_gen() for i=0,#self.ancs do local a0=self:_anc(i) local a1=self:_anc(i+1) @@ -676,23 +675,25 @@ function rope:drag( i,x,y ) local anc=self:_anc(i()) - for x,y in self:_rast( - anc.x,anc.y,x,y + local busy=self:busy() + + for x,y in self:_rastn( + anc.x,anc.y,x,y,2,2 ) do local a=self:_anc(i()) - if not (_point_eq(a, {x=x,y=y})) then + if not (_point_eq(a,{x=x,y=y})) then a.x=x a.y=y a.dirty=true - self:_tidy_up_gen() + self.dirty=true + if (not busy) self:_tidy_up_gen() end end end function rope:_tidy_up_gen() - if (self:busy()) return - if (self.under_destruction) return + if (not self.dirty) return local settled=true local touched={} @@ -744,13 +745,13 @@ function rope:_tidy_up_gen() for a=0,#self.ancs+1,1 do local anc=self:_anc(a) - if anc.seen then - anc.dirty=anc.changed - end + if (anc.seen) anc.dirty=anc.changed end if (settled) break end + + self.dirty=false end function rope:_find_needed_anchors(i) @@ -861,7 +862,7 @@ function rope:_can_move_midpoint(a0,a1_0,a1_1,a2) if not self:_can_stretch(a1_1,a2) then return false 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} if not self:_can_stretch(a0,tm) then return false @@ -936,7 +937,7 @@ function rope:_can_stretch( if (level:pcoll(p2.x,p2.y)) return false 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 res=false break @@ -946,8 +947,8 @@ function rope:_can_stretch( return res end -function rope:_rastm( - x0,y0,x1,y1 +function rope:_rastn( + x0,y0,x1,y1,dx,dy ) -- todo: more optimized implementation? local iter=self:_rast(x0,y0,x1,y1) @@ -960,8 +961,8 @@ function rope:_rastm( if (x==nil) done=true return x1, y1 - local x8 = x\8 - local y8 = y\8 + local x8 = x\dx + local y8 = y\dy if not (x8==prevx and y8==prevy) then prevx,prevy=x8,y8 return x,y @@ -1080,6 +1081,7 @@ function rope:_tug() if force or not level:pcoll(x,y) then s.x=x s.y=y + self.dirty=true end return true end} @@ -1132,6 +1134,8 @@ function rope:_tug() mx0,my0, dmx,dmy ) + -- be busy for 4 ticks while the crate moves + self:_anc(0).todo={{},{},{},{}} end end end -- 2.34.1 From f7170428c96a944e349db1be91d131fa5eabbe97 Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Sat, 17 Dec 2022 14:09:54 -0800 Subject: [PATCH 6/6] Attempts to make this fast keep breaking things --- chameleonic.p8 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/chameleonic.p8 b/chameleonic.p8 index 2863aca..d99a15d 100644 --- a/chameleonic.p8 +++ b/chameleonic.p8 @@ -677,8 +677,8 @@ function rope:drag( local anc=self:_anc(i()) local busy=self:busy() - for x,y in self:_rastn( - anc.x,anc.y,x,y,2,2 + for x,y in self:_rast( + anc.x,anc.y,x,y ) do local a=self:_anc(i()) if not (_point_eq(a,{x=x,y=y})) then @@ -799,8 +799,9 @@ function rope:_find_touched_anchors(i) for bx,by in self:_rast(a0.x,a0.y,a2.x,a2.y) do local a1=level:point_anchor(bx,by) - if a1!=nil and not _point_eq(a0,a1) and not _point_eq(a1,a2) - and _linedist(a0,a1,a2) < 0.01 + if + 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) then local id=self.id -- 2.34.1