Basic Touch zone detection, how should it be re-written to optimise the code

I am just getting my feet wet with Codea and don’t fully grasp the concept of creating separate classes and passing information where the code refers to nnnnn.self etc.

I have written my simple method of zone detection on a 5 x 5 matrix of squares. It provide the correct output, however looking at other examples it looks like I need to get my head round other concepts before I try to take my idea further.

Anyone who has any suggestion as to how the code should be written, feel free to comment. All the code below exists in the main routine.

---- Touch Detection

– Use this function to perform your initial setup
function setup()
print(“Hello World!”)
end

– This function gets called once every frame
function draw()
background(40, 40, 50)

strokeWidth(5)
noFill()
x = -5
y = -5
z = 0
regiona = {}
regionb = {}
regionc = {}
regiond = {}


for j = 1,5 do
    y = y + 105
    for i = 1,5 do
        x = x + 105
        rect(x,y,100,100)
       z = z + 1
        regiona[z] = x + 20
        regionb[z] = y + 20
        regionc[z] = x + 80
        regiond[z] = y + 80
      end
       x = -5
        end  
end


function touched(touch)
if touch.state == ENDED then
    print("x = "..touch.x.." y = "..touch.y)
    text(touch.x,touch.x,touch.y)

--check touch zones
z = 0
for z=1,25 do
    if touch.x > regiona[z] and touch.x < regionc[z] and touch.y > regionb[z] and touch.y < regiond[z] then
    print("zone "..z)
    break
    end
end
end
    
end

@time2innov8 Here’s another version. Tap a square. The size of the squares can be changed by changing size.


displayMode(FULLSCREEN)

function setup()
    size=100    -- size of the squares
    tab={}    -- table for square x,y coordinates
    for x=1,5 do     -- create table
        for y=1,5 do
            table.insert(tab,vec2(x*size,y*size))
        end
    end
    zone=0    -- variable for touched zone
end

function draw()
    background(40,40,50)
    strokeWidth(2)
    stroke(255)
    for a,b in pairs(tab) do  -- loop thru table      
        noFill()
        if a==zone then    -- square touched, fill with color
            fill(255, 0, 0)
        end
        rect(b.x,b.y,size,size)    -- draw squares
    end
end

function touched(touch)
    if touch.state == ENDED then    
        for a,b in pairs(tab) do   --check touch zones
            if touch.x > b.x and touch.x < b.x+size and 
                    touch.y > b.y and touch.y < b.y+size then
                zone=a  -- save zone
                break
            end
        end
    end
end

As an alternative for checking the touch zones you could use:


if math.abs(touch.x-b.x)<size and math.abs(touch.y-b.y)<size then

This reduces the number of times you type each variable and each comparator reducing the chance of getting a typo in either.

@time2innov8 - see the posts on classes in the index on this page, they try to explain the whole self thing. I found it very confusing at first, too!

http://coolcodea.wordpress.com/2013/06/19/index-of-posts/#more-8884

Thanks @West, I always like to see easier way of doing something.

@West I tried your simplified code along with code from @dave1707 which I modified to keep a track of the touched areas by keeping them green. A strange issue is happening with the simplified, I am getting multiple iterations of the touch loop for one contact. I added a loop counter to check what was happening, can’t explain why its occuring. The code is listed below.

-- testcode

function setup()
size=100    -- size of the squares
tab={}    -- table for square x,y coordinates
tempzone={}
for y=1,5 do     -- create table
    for x=1,5 do
        table.insert(tab,vec2(x*size,y*size))
    end
end
for c=1,25 do
    tempzone[c]=0
    end
zone=0    -- variable for touched zone
gamestate="menu"
loopcount = 0

end

function draw()
background(40,40,50)

strokeWidth(2)
stroke(255)
for a,b in pairs(tab) do  -- loop thru table      
    noFill()
    if tempzone[a] == 1 then
        fill(0,255,0)
        end
    if a == zone then    -- square touched, fill with color
        fill(255, 0, 0)
    end
    
    rect(b.x,b.y,size,size)    -- d
end
end



function touched(touch)

if touch.state == ENDED then    
    for a,b in pairs(tab) do   --check touch zones

     --  if math.abs(touch.x-b.x)<size and math.abs(touch.y-b.y)<size then
    -- causes multiple loop iterations on touch
    
 if touch.x > b.x and touch.x < b.x+size and touch.y > b.y and touch.y < b.y+size then  
-- works correctly 

 zone = a
if tempzone[a] == 0 then
tempzone[a] = 1
else
    tempzone[a] = 0
break
end
loopcount = loopcount + 1
print("start"..loopcount)
print(a)
end
end
end
end

@time2innov8 You were missing a break in the for loop and weren’t exiting correctly. See the code below where I added the break and comment.

EDIT: Or you could remove the 2 break statements and put 1 break after loopcount=loop count+1 .


-- testcode

function setup()
    size=100 -- size of the squares
    tab={} -- table for square x,y coordinates
    tempzone={}
    for y=1,5 do -- create table
        for x=1,5 do
            table.insert(tab,vec2(x*size,y*size))
        end
    end
    for c=1,25 do
        tempzone[c]=0
        end
    zone=0 -- variable for touched zone
    gamestate="menu"
    loopcount = 0
end

function draw()
    background(40,40,50)
    
    strokeWidth(2)
    stroke(255)
    for a,b in pairs(tab) do -- loop thru table      
        noFill()
        if tempzone[a] == 1 then
            fill(0,255,0)
            end
        if a == zone then -- square touched, fill with color
            fill(255, 0, 0)
        end   
        rect(b.x,b.y,size,size) -- d
    end
end



function touched(touch)
    if touch.state == ENDED then  
        for a,b in pairs(tab) do --check touch zones   
            if math.abs(touch.x-b.x)<size and math.abs(touch.y-b.y)<size then
            --if touch.x > b.x and touch.x < b.x+size and 
                --touch.y > b.y and touch.y < b.y+size then  
                zone = a
                if tempzone[a] == 0 then
                    tempzone[a] = 1
                    break                        -- break added here
                else
                    tempzone[a] = 0
                    break
                end              
                loopcount = loopcount + 1
                --print("start"..loopcount)
                --print(a)
            end
        end
    end
end

@dave1707 I was getting confused regarding the usage of the break command, I was thinking it would exit the if statement as opposed to the for loop.

All part of the learning experience :slight_smile:

However if I take all the breaks out of the touched function, I still get the multiple positive iterations, given the the if statement is a shortened form I cannot understand whats going on. The reason it works with the additional breaks you identified is the first contact logged is correct. There should be no reason why checking every zone with the shortened if sequence causes the incorrect response.

-- testcode

function setup()
size=100 -- size of the squares
tab={} -- table for square x,y coordinates
tempzone={}
for y=1,5 do -- create table
    for x=1,5 do
        table.insert(tab,vec2(x*size,y*size))
    end
end
for c=1,25 do
    tempzone[c]=0
    end
zone=0 -- variable for touched zone
gamestate="menu"
loopcount = 0
end

function draw()
background(40,40,50)

strokeWidth(2)
stroke(255)
for a,b in pairs(tab) do -- loop thru table      
    noFill()
    if tempzone[a] == 1 then
        fill(0,255,0)
        end
    if a == zone then -- square touched, fill with color
        fill(255, 0, 0)
    end   
    rect(b.x,b.y,size,size) -- d
end
end



function touched(touch)
if touch.state == ENDED then  
    for a,b in pairs(tab) do --check touch zones   
  
         if math.abs(touch.x-b.x)<size and math.abs(touch.y-b.y)<size then
    -- multiple loop interations for single contact

--    if touch.x > b.x and touch.x < b.x+size and touch.y > b.y and touch.y < b.y+size then  
 -- works correctly  
            zone = a
            if tempzone[a] == 0 then
                tempzone[a] = 1
            else
                tempzone[a] = 0
            end          
            loopcount = loopcount + 1
            print("loop"..a)
            end
      end
end
end

@time2innov8 The shortened version isn’t as restrictive as the long version I used in my original example. The short version actually matches 4 conditions for each touch. The break forces an exit after the 1st match. The short version would probably work for touch areas that aren’t close to each other, but in your example where the touch areas are next to each other, you’ll get 4 conditions that match. In the long version, the break is used just to exit the loop because there won’t be another match, so there’s no reason to continue to keep checking. The break in the short version is used to not give you the wrong touch match after the 1st match. I haven’t tried this yet with the short version, but I think that if I set up a for loop going in the reverse order, doing a break on the first match will be the wrong match. Hope that makes sense.

EDIT: I changed the for loop to go in the reverse order and the short version with the break always matched the wrong square.

@time2innov8 @West I wanted to figure out what was wrong with the short version of the touch area code. I put together this demo to illustrate what is actually happening. After I saw what was going on, I made a modification to the short version of code to make it work. Run this program and keep moving your finger around and over the green squares. A white dot will show where each version of the touch code thinks is a valid x,y position. You’ll see on the short version that the valid touch area is beyond the edge of the square. For the modified short version, I just divided size by 2.


supportedOrientations(LANDSCAPE_ANY)

function setup()
    tab1={}
    tab2={}
    tab3={}
    x1=200
    y1=600
    x2=400
    y2=400
    x3=200
    y3=200
    size=100
end

function draw()
    background(40,40,50)
        
    -- draw square and dots for short version
    fill(0, 255, 0, 255)
    rectMode(CENTER)
    rect(x1,y1,size,size)
    fill(255)
    for a,b in pairs(tab1) do
        ellipse(b.x,b.y,8)
    end 
    text("short version",400,600)
    
    -- draw square and dots for long version
    fill(0, 255, 0, 255)
    rectMode(CORNER)
    rect(x2,y2,size,size)
    fill(255)
    for a,b in pairs(tab2) do
        ellipse(b.x,b.y,8)
    end
    text("long version",600,450)
    
    -- draw square and dots for modified short version
    fill(0, 255, 0, 255)
    rectMode(CENTER)
    rect(x3,y3,size,size)
    fill(255)
    for a,b in pairs(tab3) do
        ellipse(b.x,b.y,8)
    end 
    text("modified short version",400,200)
end

function touched(t)
    if t.state == MOVING then  
        -- touch area short version       
        if math.abs(t.x-x1)<size and math.abs(t.y-y1)<size then
            table.insert(tab1,vec2(t.x,t.y))
        end

        -- touch area long version
        if t.x>x2 and t.x<x2+size and t.y>y2 and t.y<y2+size then  
            table.insert(tab2,vec2(t.x,t.y))
        end
        
        -- touch area modified short version       
        if math.abs(t.x-x3)<size/2 and math.abs(t.y-y3)<size/2 then
            table.insert(tab3,vec2(t.x,t.y))
        end
    end
end

@dave1707 I’m glad I wasn’t going mad thinking something was wrong with the original short code. Your example code proves exactly what was going on.

Oops sorry - my mistake. Trying to do it from memory rather than testing on ipad.
@dave1707, not sure if it is an easier way… :slight_smile: