One end acts differently to the other?

I’m thinking this is due to the iteration direction but then again, why would it be if its all done in one frame anyway?


--# Main
function setup()
    r = Rope(vec2(50,600),vec2(700,350),40)
    touches = {}
end

function touched(t)
    if t.state == BEGAN then
        table.insert(touches,t)
    end
    for k,v in pairs(touches) do
        if v.id == t.id then
            if k == 1 then
                r.p1 = vec2(t.x,t.y)
            elseif k == 2 then
                r.p2 = vec2(t.x,t.y)
            end
        end
        if t.state == ENDED then
            table.remove(touches,k)
        end
    end
end

function draw()
    background()
    r:draw()
end
--# Rope
Rope = class()

function Rope:init(pos1,pos2,segamnt)
    -- you can accept and set parameters here
    self.p1 = pos1
    self.p2 = pos2
    self.m = mesh()
    self.r = {}
    self.dst = pos1:dist(pos2)
    self.segamnt = segamnt
    self.seglen = self.dst/segamnt
    self.segtbl = {}
    for i=1,segamnt do
        self.segtbl[i] = {}
        self.segtbl[i].pos = pos1+(pos2-pos1):normalize()*self.seglen*i
        self.segtbl[i].vel = vec2()
        self.segtbl[i].ang = 0
        self.r[i] = self.m:addRect(self.segtbl[i].pos.x,self.segtbl[i].pos.y,5,self.seglen)
    end
    self.i = 0
end

function Rope:draw()
    for i=1,self.segamnt do
        local maxl = self.seglen
        local prevpos = vec2()
        local pos = self.segtbl[i].pos
        local nextpos = vec2()
        if i==1 then
            prevpos = self.p1
            nextpos = self.segtbl[i+1].pos
        elseif i==self.segamnt then
            prevpos = self.segtbl[i-1].pos
            nextpos = self.p2
        else
            prevpos = self.segtbl[i-1].pos
            nextpos = self.segtbl[i+1].pos
        end
        pos = self.segtbl[i].pos
        local mlen = ((nextpos+prevpos)/2):dist(pos)
        if mlen > 0 then
             self.segtbl[i].pos = pos + (((nextpos+prevpos)/2)-pos):normalize()*mlen*1.8
        end  
        self.segtbl[i].pos = self.segtbl[i].pos -vec2(0,1)
        self.m:setRect(self.r[i],pos.x,pos.y,5,10,angleOfPoint(pos-prevpos)/mp)
    end
    self.m:draw()
end

function Rope:touched(touch)
    -- Codea does not automatically call this method
end

--# SpecFuncs
mp = 180/math.pi
function angleOfPoint( pt )
   local ang = math.atan2(pt.y,pt.x)*(180/math.pi)
    if ang < 0 then ang = 360+ang elseif ang > 360 then ang = 0+(ang-360) end
    return ang 
end

Physical rope with 2 attached ends, first finger moves left point, second moves right point. The rope seems very static when moved with the first finger but if you use a second finger to move the other point you get the more desired elastic effect, but this is not correct due to its correcting itself backwards instead of forwards. I tried doing two seperate iterations over the table in both directions which gave a static effect on both sides but the second finger point dragged behind. Has anyone dealt with rope yet with good frame rates? If so please give me some advice as I have never tried simulating rope between two points only one. It’s easy to get the desired effects from one end. Thanks!

Why not use the physics ROPE joint?

I will, this is to draw over it. The segments are physical it’s not a physical constraint.

Very nice effect! Is the problem that the rope is less wavy when only using one finger?

If you use two touches and move one end, then keeping both touches, keep that end static and move the other, it acts the same as one finger movement. I don’t know why this is, but given that you also said ‘first finger moves left point, second moves right point’, I suspect the issue lay somewhere in that logic. Not sure if this helps!

I know its in the logic of the rope, and I mean the left side touch point, when moved will move every single other point on that rope in the frame because it follows the direction (left to right, i=1,40) so you get the nicer effect when it’s setting the position backwards because its opposite to the iteration direction (just a theory!) but I’d like this effect on both sides. It needs to be controllable from both ends with the same effect.

Edit: This should give you a good idea, if you hold with the left touch then tap somewhere on the screen with right touch away from the second point it will draw segment by segment each frame actually iterating backwards to set the positions, this is why it is frame by frame and not all at once in one frame, which is what the left touch does. So you can tap with the left touch and all segments are automatically reproduced in the one frame in the right position or tap with the right touch whilst holding left touch down to get this backwards frame by frame position setting which appears to give an elastic effect when dragging.

I’m not thinking it is the left or right touch moving that matters, but if it is the first or second. If you swap the rope’s ends over, then the right touch acts as the left does initially. So, it’s always r.p2 moving that gives the right effect?

I don’t understand enough about the code to change it, just observing the behaviour and thinking out loud.

@time_trial I understand that I should have been more clear, r.p1 always has static movement and r.p2 doesn’t.

Here the ropes act as wanted both ends.

Rope = class()

function Rope:init(pos1,pos2,segamnt)
    -- you can accept and set parameters here
    self.p1 = pos1
    self.p2 = pos2
    self.m = mesh()
    self.r = {}
    self.dst = pos1:dist(pos2)
    self.segamnt = segamnt
    self.seglen = self.dst/segamnt
    self.segtbl = {}
    for i=1,segamnt do
        self.segtbl[i] = {}
        self.segtbl[i].pos = pos1+(pos2-pos1):normalize()*self.seglen*i
        self.segtbl[i].vel = vec2()
        self.segtbl[i].ang = 0
        self.r[i] = self.m:addRect(self.segtbl[i].pos.x,self.segtbl[i].pos.y,5,self.seglen)
    end
    self.i = 0
end

function Rope:draw()
    for i=1,self.segamnt do
        local n = self.segamnt+1-i
        local maxl = self.seglen
        local prevpos = vec2()
        local pos = self.segtbl[i].pos
        local nextpos = vec2()
        local prevpos2 = vec2()
        local nextpos2 = vec2()
        if i==1 then
            prevpos = self.p1
            nextpos = self.segtbl[i+1].pos
        elseif i==self.segamnt then
            prevpos = self.segtbl[i-1].pos
            nextpos = self.p2
        else
            prevpos = self.segtbl[i-1].pos
            nextpos = self.segtbl[i+1].pos
        end
        if n==1 then
            prevpos2 = self.segtbl[n+1].pos
            nextpos2 = self.p1
        elseif n==self.segamnt then
            prevpos2 = self.p2
            nextpos2 = self.segtbl[n-1].pos
        else
            prevpos2 = self.segtbl[n+1].pos
            nextpos2 = self.segtbl[n-1].pos
        end
        pos = self.segtbl[i].pos
        local mlen = ((nextpos+prevpos)/2):dist(pos)
        local mlen2 = ((nextpos2+prevpos2)/2):dist(self.segtbl[n].pos)
        if mlen2 > 0 then
            self.segtbl[n].pos = self.segtbl[n].pos
             + (((nextpos2+prevpos2)/2)-self.segtbl[n].pos):normalize()*mlen2
        end
        if mlen > 0 then
            self.segtbl[i].pos = pos + (((nextpos+prevpos)/2)-pos):normalize()*mlen
        end  
        self.segtbl[i].pos = self.segtbl[i].pos -vec2(0,1)
        self.m:setRect(self.r[i],pos.x,pos.y,5,10,angleOfPoint(pos-prevpos)/mp)
    end
    self.m:draw()
end

function Rope:touched(touch)
    -- Codea does not automatically call this method
end

I would like it to be more efficient though and not as messy, any clues?

If you can see what is major different between this one and the efficient one, perhaps you can adapt the efficient one to work this way?

@JakAttak believe me even the first one is a lot more efficient than previous tests, I had 4 lines of code controlling the rope but now I only have one. I think I might just have to go with verlet.