Advanced touch detection [SOLVED]

I have a sample code like this

function draw()
    translate(300,300)
    rotate(30)
    scale(2,1.5)

    fill(255, 255, 255, 255)
    rect(0,0,100,100)
end

Which draws a tilted rectangle somewhere on the screen. I wonder how can I detect if a given point (e.g. touch) is inside the rectangle? Any thoughts are appreciated.

SOLUTION

Thanks to @Ignatz and @LoopSpace I ended up with pretty simple and elegant solution for 2D rects. Just few lines of code allow to convert a point from world coordinates to current matrix and vice verse. The full code is below.

mm = matrix()

function draw()    
    translate(300,300)
    rotate(30)
    scale(2,1.5)
    
    fill(255, 255, 255, 255)
    rect(0,0,100,100)
    
    mm = modelMatrix()
end
    
function touched(touch)
    if touch.state ~= BEGAN then return end

    local localPoint = worldToLocal(touch)
    local worldPoint = localToWorld(localPoint)
    
    print(touch.x, touch.y) -- the original touch point
    print(localPoint)       -- the point in rect coordinates
    print(worldPoint)       -- this should be the same as the original touch
    
    if localPoint.x >= 0 and localPoint.x <= 100 and
    localPoint.y >= 0 and localPoint.y <= 100 then
        print("HIT!")
    end
end

function multiplyVec2ByMatrix(vec, mat)
    vec = vec4(vec.x, vec.y, 0, 1)
    -- Normaly the code below should look like "local resultVec = mat*vec"
    -- but as soon as something is broken in Codea right now multiply them manualy
    local resultVec = vec4(
        mat[1]*vec[1] + mat[5]*vec[2] + mat[09]*vec[3] + mat[13]*vec[4],
        mat[2]*vec[1] + mat[6]*vec[2] + mat[10]*vec[3] + mat[14]*vec[4],
        mat[3]*vec[1] + mat[7]*vec[2] + mat[11]*vec[3] + mat[15]*vec[4],
        mat[4]*vec[1] + mat[8]*vec[2] + mat[12]*vec[3] + mat[16]*vec[4]
    )
    return vec2(resultVec.x, resultVec.y)
end

function localToWorld(localPoint)
    return multiplyVec2ByMatrix(localPoint, mm)
end

function worldToLocal(worldPoint)
    if mm:determinant() == 0 then
        return nil
    else
        local resultVec = multiplyVec2ByMatrix(worldPoint, mm:inverse())
        return vec2(resultVec.x, resultVec.y)
    end
end

https://coolcodea.wordpress.com/2015/01/13/192-locating-the-3d-position-of-a-2d-point-with-math/

@Iavmax Are all of your rectangles going to be drawn with translate and rotate.

Many of them. Mostly sprites. Rectangle is just an example.
Trying to figure out how this locating from coolcodea works.
Meanwhile what is the difference between modelMatrix(), viewMatrix() and projectionMatrix()?

@Iavmax If your going to be drawing sprites, then maybe you could use circles or overlapping circles or ellipses instead of rectangles. It’s very easy to determine if a touch is within a circle or ellipse.

@Ignatz This code works like a charm. Thank you! And I actually have no idea how exactly it works, so it’s a real magic :slight_smile:

That code is thanks to @LoopSpace.

I explain modelMatrix here.

https://coolcodea.wordpress.com/2014/11/26/184-what-is-modelmatrix/

Someone else had a go at explaining the other matrices

http://codea.io/talk/discussion/2909/clarity-on-identitymatrix-modelmatrix-viewmatrix-projectionmatrix

But wait until you try 3D before trying too hard to understand them.

This is where I usually link to my explanation of the matrices, but I’m told that it’s a bit too mathematical for general use!

@LoopSpace Well, if you have it already explained somewhere, please link, I would try to read for self education even if it’s too math.

BTW. I get how to convert a world point (touch) to my rect or sprite coordinates. But is it possible somehow to use this code for the opposite conversion? I mean if I have a point inside a rect how do I get a wold point?

@lavmax - in 2D, if you have a vec2 point p, then

p2 = modelMatrix() * p 

gives you the world point

@Ignatz except that mat4 \* vec4 is broken in Codea right now:

    print(matrix()*vec4(1,0,0,0))
    print(matrix()*vec4(0,1,0,0))
    print(matrix()*vec4(0,0,1,0))
    print(matrix()*vec4(0,0,0,1))

@lavmax http://loopspace.mathforge.org/HowDidIDoThat/Codea/Matrices/ but don’t say I didn’t warn you.

@Ignatz thank you, this really works. And mat4 * vec4 is really broken in Codea. But thanks to @LoopSpace I have function applymatrix4(v,m) which works :slight_smile:

So if

worldPoint = localPoint * modelMatrix()

then I can assume that getting local point should be as simple as

localPoint = worldPoint / modelMatrix()

But how to divide vec4 / mat4? Or it’s too naive and I can’t really do like this?j

PS. It’s all about 2D

Nope, it doesn’t work like that, you would need to invert modelMatrix

But you should be able to use the code I gave you above for a 3D plane, just make z=0.

@Ignatz and @LoopSpace - thank you guys for your help. I finally figured it out. The solution is in the original question above to make it easier to find if somebody will need it.

I’m sorry to make this old post up, but I had to say thank you because it helped me a lot

Thank you @lavmax , @Ignatz and @LoopSpace !