Best Method To Track Length and Time of Touches

Hi All,
My latest project involves the development of a system to store and track the times that a user has touched the screen.
I ran in to an issue with the storing of the touch data - the time began and the time ended. I’ve found a solution to my problem (just insert the times into a table and if it’s odd its when the touch began, if it’s even, it’s when the touch ended), however it is very crude and will not work for an larger version of the project.
Here is my code:

function setup()
    input = {}
    numTouches = 0
    isTouched = false
end

-- This function gets called once every frame
function draw()
    background(255, 255, 255, 255)
    stroke(0, 0, 0, 255)
    lineCapMode(ROUND)
    strokeWidth(10)
    if isTouched then
        if numTouches == 0 then
            print("touch ended")
            table.insert(input, ElapsedTime)
            isTouched = false
        end
    else
        if numTouches > 0 then
            print("touch began")
            table.insert(input, ElapsedTime)
            isTouched = true
        end
    end
    local m = 100
    for i = 1, math.ceil(#input / 2) do
        local began = ElapsedTime - input[i * 2 - 1]
        local ended = ElapsedTime - (input[i * 2] or ElapsedTime)
        line(WIDTH * .75 - began * m, HEIGHT / 2, WIDTH * .75 - ended * m, HEIGHT / 2)
    end
    strokeWidth(2)
    line(WIDTH * .75, 0, WIDTH * .75, HEIGHT )
end

function touched(touch)
    if touch.state == BEGAN then
        numTouches = numTouches + 1
    elseif touch.state == ENDED then
        numTouches = numTouches - 1 
    end
end

The specifically problematic part is the loop for i = 1, math.ceil(#input / 2) do

Any suggestions on how to refine this? Preferably on the structure of the data?

@Jordan Maybe something like this. Just save the touch state and the time in a table. Then you can read the table and look for the began (0) and ended (2) values and subtract the times to get the length of the touch. This doesn’t save anything when the state is MOVING. I print the table when the touch state is ENDED, but you can do it whenever you want.

function setup()
    tab={}
end

function draw()
end

function touched(t)
    if t.state~=MOVING then
        table.insert(tab,vec2(t.state,os.time())) 
    end
    if t.state==ENDED then
        output.clear()
        for a,b in pairs(tab) do
            print(b)
        end
    end
end

Thanks @Dave1707, a more elegant solution, but I realized I was not being too clear in my original post: I want to be store the data in a more organized format in that table (preferably {time began, time ended}) but I can’t see a way to do that which still allows me to interact with the data in real time - I can’t process a touch that isn’t finished with an alternative system

@Jordan Here’s a version the saves the BEGAN time, the ENDED time, and the difference between the two.

function setup()
    tab={}
end

function draw()
end

function touched(t)
    if t.state==BEGAN then
        bTime=os.time()
    end
    if t.state==ENDED then
        table.insert(tab,vec3(bTime,os.time(),os.time()-bTime))
        output.clear()
        for a,b in pairs(tab) do
            print(b.x,b.y,b.z)
        end
    end
end

How are you planning on handling multitouch? Do you want to process multiple touches separately? Or, if only processing one touch at a time, do you exclude all subsequent touches once a touch has began, or let a subsequent touch interrupt the first finger that was on the screen?

@yojimbo2000, This is all for a prototype morse code keyboard that I’m working on, so I want it to measure time the screen is touched, regardless of how many fingers (as I used numTouches > 0in my original code)

@Jordan It’s easy to help when we know exactly what you’re trying to do. Here’s an example of a morse code key. You can adjust the time between key touches and the time between letters. A table of letters can be added to decode the dots and dashes to a letter. This doesn’t go to multiple lines. It’s more of an example for the key timing.

displayMode(FULLSCREEN)

function setup()
    textMode(CORNER)
    parameter.number("clicks",.1,1,.2)   -- time between clicks
    parameter.number("letters",.1,1,.5)  -- time between letters
    font("Courier-Bold")
    tab={}
    tm=0
    eTime=0
end

function draw()
    background(0)
    fill(255)
    str=string.format("Dot < %.2f seconds, Dash > %.2f seconds",clicks,clicks)
    text(str,100,HEIGHT-100)
    str=string.format("Seconds between letters %.2f",letters)
    text(str,100,HEIGHT-130)
    tm=tm+DeltaTime
    for a,b in pairs(tab) do
        if b=="." then
            text(b,a*10,500)
        else
            text(b,a*10,498)
        end
    end
end

function touched(t)
    if t.state==BEGAN then
        bTime=tm
        if bTime-eTime>letters then
            table.insert(tab," ")
        end
    end
    if t.state==ENDED then
        eTime=tm
        if tm-bTime>clicks then
            table.insert(tab,"-")
        else
            table.insert(tab,".")
        end
    end
end

@Dave1707 I’m one step ahead of you :slight_smile: I’ve already got the code to fully interpret morse code input, but I’m trying to do away with the need for a set length for the dot length, so it dynamically works out the speed at which one is typing in morse and able to then figure out the dits and dahs - hence the need to store the touches and their lengths so I can compare them in real time

I can post the current working version of my code if you’re interested, but it’s still very rusty

@Jordan It would be interesting to see your code, but maybe I should wait until you get it finalized. I’ve never played around with morse code other than seeing tables with the letters and the corresponding dots and dashes. I guess someone who was good at morse code could do it really fast, hence a shorter duration for the dots and dashes. Maybe instead of storing the information in a table and having to go thru it, you could just have 2 averages, one for dots and one for dashes. As someone increases their speed, the average times would decrease. To determine the dots and dashes, they would be within a certain plus or minus amount of their average times.

Here is my working demo (without automatic speed)

https://www.youtube.com/watch?v=FafTdWC2W6o

Code:

-- morse

-- Use this function to perform your initial setup
function setup()
    parameter.watch("morse.buffer")
    parameter.watch("morse.lastTouch")
    music("Project:morse", true)
    music.volume = 0
end

-- This function gets called once every frame
function draw()
    music.volume = (morse.numTouches > 0 or (morse.lastTouch and (ElapsedTime - morse.lastTouch) < morse.unit)) and 1 or 0
    background(0)
    morse:update()
    
    fill(255, 255, 255, 255)
    font("SourceSansPro-Regular")
    fontSize(30)
    text(morse.text .. (morse.code[morse.buffer] or ""), WIDTH / 2, HEIGHT / 2)
    text(morse.buffer, WIDTH / 2, HEIGHT / 4 * 3)
    
    if morse.numTouches ~= 0 then
        ellipse(CurrentTouch.x, CurrentTouch.y, 100)
    end
end

function touched(touch)
    if touch.state == BEGAN then
        morse:newTouch(touch)
    elseif touch.state == ENDED then
        morse:endTouch(touch)
    end
end

morse = {}

morse.code = {
    [".-"] = "a",
    ["-..."] = "b",
    ["-.-."] = "c",
    ["-.."] = "d",
    ["."] = "e",
    ["..-."] = "f",
    ["--."] = "g",
    ["...."] = "h",
    [".."] = "i",
    [".---"] = "j",
    ["-.-"] = "k",
    [".-.."] = "l",
    ["--"] = "m",
    ["-."] = "n",
    ["---"] = "o",
    [".--."] = "p",
    ["--.-"] = "q",
    [".-."] = "r",
    ["..."] = "s",
    ["-"] = "t",
    ["..-"] = "u",
    ["...-"] = "v",
    [".--"] = "w",
    ["-..-"] = "x",
    ["-.--"] = "y",
    ["--.."] = "z",
["-----"] = "0",
[".----"] = "1",
["..---"] = "2",
["...--"] = "3",
["....-"] = "4",
["....."] = "5",
["-...."] = "6",
["--..."] = "7",
["---.."] = "8",
["----."] = "9",
[".-.-"] = "\
",
[".-.-.-"] = ".",
["--..--"] = ",",
["..--.."] = "?",
["-.-.--"] = "!",

}

morse.touches = {}
morse.numTouches = 0
morse.lastTouch = nil
morse.unit = 0.12
morse.buffer = ""
morse.text = ""
function morse:newTouch(touch)

    self.touches[touch.id] = 0
    self.numTouches = morse.numTouches + 1
end

function morse:update()
    for k, v in pairs(self.touches) do
        self.touches[k] = self.touches[k] + DeltaTime
    end
    if self.numTouches == 0 then
        if self.lastTouch then
            self:processSpace(ElapsedTime - self.lastTouch)
        end
    end
end

function morse:endTouch(touch)
    self:processTouch(self.touches[touch.id])
    self.touches[touch.id] = nil
    self.numTouches = self.numTouches - 1
    if self.numTouches == 0 then
        self.lastTouch = ElapsedTime
    end
end

function morse:processTouch(t)
    if t < self.unit then
        self.buffer = self.buffer .. "." 
    else
        self.buffer = self.buffer .. "-" 
    end
end

function morse:processSpace(t)
    if t > self.unit * 7 then
        self:addSpace()
    elseif t > self.unit * 3 then
        self:processBuffer()
    end
end

function morse:processBuffer()
    if morse.code[self.buffer] then
        print(morse.code[self.buffer], self.buffer)
        morse.text = morse.text .. morse.code[self.buffer]
    elseif self.buffer == "........" then
        morse.text = morse.text:sub(1, -3)
    end
    self.buffer = ""
end

function morse:addSpace()
    print()
    morse.text = morse.text .. " " 
    morse.lastTouch = nil
end

@Dave1707, Thank you for the suggestion, I’m going to have to try it out with a demo of different typing speeds to see it, because I can’t visualise the results. I’m also thinking about a solution that involves checking the high and low ends of the spectrum of lengths of the input and working from there. I’ll try implement both today and we’ll see the results!

@Jordan Very interesting. I don’t have time to look thru the code and play with it right now. Another suggestion. You probably only need 1 average. If it’s below the average, it’s a dot, above its a dash.