Triangle Button

Some code i found and adapted from somewhere. It works by basically finding the areas of the 3 triangles formed by the touch position and the corners of the triangle and comparing this with the area of the triangle

function setup()
    pt1x=100
    pt1y=100
    pt2x=500
    pt2y=100
    pt3x=500
    pt3y=500
    triStroke=color(255)
end

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

    stroke(triStroke)
    strokeWidth(5)
    line(pt1x,pt1y,pt2x,pt2y)
    line(pt2x,pt2y,pt3x,pt3y)
    line(pt3x,pt3y,pt1x,pt1y)
    
    if tpos then
        stroke(255,0,0)
        line(tpos.x,tpos.y,pt1x,pt1y)
        line(tpos.x,tpos.y,pt2x,pt2y)
        line(tpos.x,tpos.y,pt3x,pt3y)
    end
end

function touched(touch)
    
    if isPointInTriangle(touch.x,touch.y,pt1x,pt1y,pt2x,pt2y,pt3x,pt3y) then
        triStroke=color(0,255,0)  else triStroke=color(255)  end
    if touch.state==ENDED then tpos=nil triStroke=color(255) else
    tpos=vec2(touch.x,touch.y) end
end

function triArea(p1x,p1y,p2x,p2y,p3x,p3y)
    local dA = p1x - p3x
    local dB = p1y - p3y
    local dC = p2x - p3x
    local dD = p2y - p3y
    return 0.5 * math.abs((dA*dD)-(dB*dC))
end

function isPointInTriangle(px,py,p1x,p1y,p2x,p2y,p3x,p3y)
    local areaT=triArea(p1x,p1y,p2x,p2y,p3x,p3y)
    local areaA=triArea(px,py,p1x,p1y,p2x,p2y)
    local areaB=triArea(px,py,p3x,p3y,p2x,p2y)
    local areaC=triArea(px,py,p1x,p1y,p3x,p3y)
    return (areaT==(areaA+areaB+areaC))
end


```

Very cool

Had to tweak it a little bit to make it work though

I don’t know if it’s any more or less efficient but here’s my version

Triangle = class()

function Triangle:init(p1,p2,p3,loc,strokew)
    self.p = {}
    self.p[1],self.p[2],self.p[3]=p1 or vec2(0,0),p2 or vec2(100,0),p3 or vec2(50,87)
    self.cent = vec2((1/3)*(self.p[1].x+self.p[2].x+self.p[3].x),(1/3)*(self.p[1].y+self.p[2].y+self.p[3].y))
    for i=1,3 do
        self.p[i] = self.p[i]-self.cent
    end
    self.cent = vec2(0,0)
    self.loc = loc or vec2(WIDTH/2,HEIGHT/2)
    self.ang = 0
    self.col = color(255)
    self.q={}
    self:rebuildq()
    self.sw = strokew or 1
end

function Triangle:draw()
    pushStyle()
    stroke(self.col)
    strokeWidth(self.sw)
    line(self.q[1].x,self.q[1].y,self.q[2].x,self.q[2].y)
    line(self.q[2].x,self.q[2].y,self.q[3].x,self.q[3].y)
    line(self.q[1].x,self.q[1].y,self.q[3].x,self.q[3].y)
    popStyle()
end

function Triangle:rotate(ang,additive)
    if ang then
        ang = math.rad(ang)
        if additive then
            self.ang = self.ang + ang
            else
            self.ang = ang
        end
        else
        self.ang = 0
    end
    self:qrot()
end

function Triangle:qrot()
    for i=1,3 do
        local dist = self.p[i]-self.cent
        self.q[i] = self.q[4]+dist:rotate(self.ang)
    end
end

function Triangle:setcol(newcol)
    self.col = newcol
end

function Triangle:setloc(loc,additive)
    if loc then
        if additive then
            self.loc = self.loc + loc
            else
            self.loc = loc
        end
    end
    self:rebuildq()
end

function Triangle:move(x,y)
    if x and y then
        self:setloc(vec2(x,y),true)
    end
end

function Triangle:rebuildq()
    if not self.q[5] then
        self.q[5] = vec2(0,0)
        for i=1,3 do
            if not self.q[i] then
                self.q[i] = self.p[i]
            end
        end
    end
    local adjust = self.loc-self.q[5]
    for i=1,3 do
        self.q[i] = self.q[i]+adjust
    end
    self.q[4] = self.cent+adjust
    self.q[5] = self.loc
end

function Triangle:touched(touch)
    local alpha = ((self.q[2].y-self.q[3].y)*(touch.x-self.q[3].x)+(self.q[3].x-self.q[2].x)*(touch.y-self.q[3].y))/((self.q[2].y-self.q[3].y)*(self.q[1].x-self.q[3].x)+(self.q[3].x-self.q[2].x)*(self.q[1].y-self.q[3].y))
    local beta = ((self.q[3].y - self.q[1].y)*(touch.x - self.q[3].x) + (self.q[1].x - self.q[3].x)*(touch.y - self.q[3].y)) /((self.q[2].y - self.q[3].y)*(self.q[1].x - self.q[3].x) + (self.q[3].x - self.q[2].x)*(self.q[1].y - self.q[3].y))
    local gamma = 1.0 - alpha - beta
    return (alpha > 0 and beta > 0 and gamma > 0)
end

EDIT: further optimized, added rotation
EDITEDIT: I replied early in the morning and seem to have forgotten to be polite. Great job, App_maker! :slight_smile: triangles are very difficult to work with :stuck_out_tongue:

Not sure how it work but it looks good

Nice. I can share this version if anyone needs touch code for 3d triangle. Returns distance to triangle or false.

-- http://en.wikipedia.org/wiki/Möller–Trumbore_intersection_algorithm
function trintersect(origin, dir, v1, v2, v3)
    local epsilon = .001
    local e1, e2 = v2-v1, v3-v1
    local pv = dir:cross(e2)
    local det = e1:dot(pv)
    if det > -epsilon and det < epsilon then return false end
    local invDet = 1/det
    local tv = origin - v1
    local u = tv:dot(pv) * invDet
    if (u < 0) or (u > 1) then return false end
    local qv = tv:cross(e1)
    local v = dir:dot(qv) * invDet
    if v < 0 or (u+v) > 1 then return false end
    local hitDistance = e2:dot(qv) * invDet
    return (hitDistance >= 0) and hitDistance or false
end

Here’s a version of triangle buttons that I have.


displayMode(FULLSCREEN)

function setup()
    tri={}  -- table for triangles
    -- setup triangle points and color
    table.insert(tri,triangle(vec2(200,300),vec2(400,300),
            vec2(300,500),color(0,255,0)))
    table.insert(tri,triangle(vec2(600,200),vec2(700,300),
            vec2(600,400),color(0,0,255)))
    table.insert(tri,triangle(vec2(400,500),vec2(500,500),
            vec2(600,700),color(255,0,255)))
    table.insert(tri,triangle(vec2(100,500),vec2(200,500),
            vec2(200,700),color(200,40,100)))
    table.insert(tri,triangle(vec2(500,50),vec2(700,50),
            vec2(300,200),color(100,200,200)))
    table.insert(tri,triangle(vec2(600,600),vec2(750,600),
            vec2(650,500),color(200,100,100)))
end

function draw()
    background(40, 40, 50)
    fill(255)
    text("Touch a triangle",WIDTH/2,HEIGHT-50)
    for a,b in pairs(tri) do    -- draw triangles
        b:draw()  
    end
end

function touched(t)
    for a,b in pairs(tri) do    -- check if triangle was touched
        b:touched(t)  
    end
end

triangle = class()

function triangle:init(p1,p2,p3,col)    -- 3 points and color
    self.col=col
    self.tx=0
    self.ty=0
    self.pressed=false
    self.m=mesh()
    self.m.vertices={p1,p2,p3}
    self.m:setColors(self.col)
end

function triangle:draw()
    self.m:draw()
    self:check()   
    if self.pressed then
        self.m:setColors(255,0,0)   -- set touched color to red
        fill(255,0,0)
        text("PRESSED",WIDTH/2,HEIGHT-80)
    else
        self.m:setColors(self.col)  -- set color to original color
    end
end

function triangle:touched(t)    -- get touch point
    if t.state==BEGAN or t.state==MOVING then
        self.tx=t.x
        self.ty=t.y
    else
        self.tx=0
        self.ty=0
        self.pressed=false
    end
end

function triangle:check()   -- check if touched inside of triangle
    local s,a1,a2,b1,b2,b3
    s=#self.m.vertices
    self.pressed=false
    for z=1,s do
        a1,a2=false,false
        if self.m.vertices[z].y>self.ty then
            a1=true
        end
        if self.m.vertices[s].y>self.ty then
            a2=true
        end
        b1=self.m.vertices[s].x-self.m.vertices[z].x
        b2=self.ty-self.m.vertices[z].y
        b3=self.m.vertices[s].y-self.m.vertices[z].y
        if a1~=a2 and self.tx<(b1*b2/b3+self.m.vertices[z].x) then
            self.pressed=not self.pressed
        end
        s=z
    end 
end