Mouse inside a triangle checker

I’m trying to make a program that will detect if the mouse is inside a triangle or not. It has to be any type of triangle. My idea is taking a triangle ABC and the mouse point K and calculating the areas Aabc Aabk Aakc Akbc and figuring out that
if Aabc- (Aabk+Aakc+Akbc) = 0 the point is inside. The function I’m using to find the area is making a matrix 3x3 where column one has x1,x2,x3 and column two has y1,y2,y3 and last column is just filled with ones. This function is mathematically correct but in the code it doesnt work well for the last 3 areas, giving me weird values. I’ll paste down the code. (The notes are in italian, sorry about it but I’m in quite of an hurry and it doesn’t seem hard to follow)

int mK = 255;
int K;
int bgr = 220;
float x1 = 200;
float x2 = 600;
float x3 = 400;
float y1 = 500;
float y2 = 500;
float y3 = 100;
float A;
float A1;
float A2;
float A3;
float At;

void setup(){
  size (800,600);
  }


void draw(){
  
  //Aree calcolo
  A = abs(x1*y2*1+y1*1*x3+1*x2*y3-y1*x2*1-x1*1*y3-1*y2*x3)/2;
  A1 = abs(x1*y2*1+y1*1*mouseX+1*x2*mouseY-y1*x2*1-x1*1*mouseY-1*y2*mouseY)/2;
  A2 = abs(x1*mouseY*1+y1*1*x3+1*mouseX*y3-y1*mouseX*1-x1*1*y3-1*mouseY*x3)/2;
  A3 = abs(mouseX*y2*1+mouseY*1*x3+1*x2*y3-mouseY*x2*1-mouseX*1*y3-1*y2*x3)/2;
  At = A1+A2+A3;
  
  //Resetta lo sfondo e i colori
  background(bgr);
  K=255;
  
  //Disegna i triangoli
  fill(K);
  triangle(x1,y1,x2,y2,x3,y3);
  triangle(x1,y1,x2,y2,mouseX,mouseY);
  triangle(x1,y1,mouseX,mouseY,x3,y3);
  triangle(mouseX,mouseY,x2,y2,x3,y3);
  
  //Mostra i valori
  fill(bgr);
  rect(0,0,200,200);
  fill(0);
  text("A = " + A,10,20);
  text("A1 = " + A1,10,30);
  text("A2 = " + A2,10,40);
  text("A3 = " + A3,10,50);
  text("At = " + At,10,60);
  text("mX = " + mouseX,10,70);
  text("mY = " + mouseY,10,80);
}
1 Like

and then calcolating the sauruss determinant of it and taking the absolute value of it, and dividing by two. Sorry I forgot to finish the formula xD

You can also draw the triangle in its own unique color c1 and take the color c2 with get(mouseX, mouseY); and compare the two colors. When they are the same, you are inside the triangle.

This is also possible when you draw the triangle on an invisible PGraphics of the same size and do the checks there

Welcome to the forum - have fun!

1 Like

thank you, any idea for doing this on a triangle which color is the same as the background?

Alternatively you can isolate all the edges, and the midpoints of said edges, then use line intersection tests to see if the mouse is inside or outside of the triangle.

image
if the mouse is outside of the triangle that it shouldnt intersect with any of the bounding edges.

Heres what the code might look like, please bear in mind this makes use of a boundary class, which I wont post because its too big and a line intersection function

trit = 0;
        for(int i=0;i<a.Boundaries.size();i++){
          
          Boundary b = a.Boundaries.get(i);
          
          Boundary A = new Boundary((b.x1 + b.x2)/2,(b.y1 + b.y2)/2,X,Y);
          
          for(int j=0;j<a.Boundaries.size();j++){
            
            Boundary c = a.Boundaries.get(j);
            
            Boundary C = new Boundary(c.x1,c.y1,c.x2,c.y2);
            PVector i1 = check_intersect(A,C);
            if(i1!=null&&i!=j){
              trit++;
            }}}
            return trit<1;
PVector check_intersect(Boundary a, Boundary b){

    float a1 = a.y2 - a.y1;
    float b1 = a.x1 - a.x2;
    float c1 = a1 * a.x1 + b1 * a.y1;
    float a2 = b.y2 - b.y1;
    float b2 = b.x1 - b.x2;
    float c2 = a2 * b.x1 + b2 * b.y1;
    float denom = a1 * b2 - a2 * b1;
    
    if((a.x1==b.x1||a.x2==b.x2)&&(a.y1==b.y1||a.y2==b.y2)){
      
      return null;
    }
     else{
        
    Float X = (b2 *c1 - b1 * c2) / denom;
    Float Y = (a1 *c2 - a2 * c1) / denom;
    
    PVector p = new PVector(X,Y);   
      boolean Linea = ((p.x<a.x1&&p.x>a.x2)||(p.x>a.x1&&p.x<a.x2))||((p.y<a.y1&&p.y>a.y2)||(p.y>a.y1&&p.y<a.y2));
      boolean Lineb = ((p.x<b.x1&&p.x>b.x2)||(p.x>b.x1&&p.x<b.x2))||((p.y<b.y1&&p.y>b.y2)||(p.y>a.y1&&p.y<b.y2));
      
        if(Linea&&Lineb){
        //stroke(255);
        //strokeWeight(20);
        //point(p.x,p.y);
        //strokeWeight(1);
        return p;
      }
      else{
      return null;
      }}
  };

(then your triangle is invisible?)

But Sure.

draw the triangle on an invisible PGraphics (other background color) of the same size as your size() command and draw the triangle there as well and do the checks there.

use mid points and not edge points, as some caases will yield false positives.
image

I don’t know how to do the invisible PGraphics. I’d rather fix my function

I don’t really know if in that case the determinant would give me the area

thanks for the idea, I’d rather not stray too far from the starting sketch though

Here you go feel free to use it.

ArrayList<Boundary> Boundaries = new ArrayList<Boundary>();
Triangle test = new Triangle(new PVector(100,200),new PVector(300,200),new PVector(200,400));


void setup(){
  size(500,500);
  
  
};

void draw(){
  background(51);
  test.draw();
};

class Boundary{
  
  PVector a,b;
  float x1,x2,y1,y2;
  
  Boundary(PVector A,PVector B){
    
    a = A;
    b = B;
    x1 = a.x;
    y1 = a.y;
    x2 = b.x;
    y2 = b.y;
  }
  Boundary(Float X1,Float Y1,Float X2, Float Y2){
    
    x1 = X1;
    y1 = Y1;
    x2 = X2;
    y2 = Y2;
  }
  
};

class Triangle{
  
  int trit;
  PVector a,b,c;
  float x1,y1,x2,y2,x3,y3;
  
  ArrayList<Boundary> Boundaries = new ArrayList<Boundary>();
  
  Triangle(PVector A,PVector B, PVector C){
    a = A;
    b = B;
    c = C;
    x1 = a.x;
    y1 = a.y;
    x2 = b.x;
    y2 = b.y;
    x3 = c.x;
    y3 = c.y;
    Boundaries.add(new Boundary(a,b));
    Boundaries.add(new Boundary(b,c));
    Boundaries.add(new Boundary(c,a));
  }
  
  void draw(){
    
    beginShape();
    for(int i=0;i<Boundaries.size();i++){
      Boundary b = Boundaries.get(i);
      
      stroke(0);
      fill(0);
      if(pos()){
        fill(255);
      }
      vertex(b.a.x,b.a.y);
      vertex(b.b.x,b.b.y);
      //line(b.a.x,b.a.y,b.b.x,b.b.y);
    }
    endShape(CLOSE);
  };
  
  boolean pos(){
    boolean k = false;
    float X = mouseX;
    float Y = mouseY;
    
    trit = 0;
        for(int i=0;i<Boundaries.size();i++){
          
          Boundary b = Boundaries.get(i);
          /* this calculates mid point for the boundary*/
          Boundary A = new Boundary((b.x1 + b.x2)/2,(b.y1 + b.y2)/2,X,Y);
          /* loop through all boundaries and test for intersection, remember if there is an 
           intersection it means the mouse is outside*/
          for(int j=0;j<Boundaries.size();j++){
            
            Boundary c = Boundaries.get(j);
            
            Boundary C = new Boundary(c.x1,c.y1,c.x2,c.y2);
            PVector i1 = check_intersect(A,C);
            if(i1!=null&&i!=j){
              // if there is then increment the counter;
              trit++;
            }}
          
        }
        /*if the counter is 0 then there is no midpoint to boundary intersections and the mouse is 
          inside*/
        if(trit<1){
          return true;
        }
    return k;
  }
  
};

PVector check_intersect(Boundary a, Boundary b){

    float a1 = a.y2 - a.y1;
    float b1 = a.x1 - a.x2;
    float c1 = a1 * a.x1 + b1 * a.y1;
    float a2 = b.y2 - b.y1;
    float b2 = b.x1 - b.x2;
    float c2 = a2 * b.x1 + b2 * b.y1;
    float denom = a1 * b2 - a2 * b1;
    
    if((a.x1==b.x1||a.x2==b.x2)&&(a.y1==b.y1||a.y2==b.y2)){
      
      return null;
    }
     else{
        
    Float X = (b2 *c1 - b1 * c2) / denom;
    Float Y = (a1 *c2 - a2 * c1) / denom;
    
    PVector p = new PVector(X,Y);   
      boolean Linea = ((p.x<a.x1&&p.x>a.x2)||(p.x>a.x1&&p.x<a.x2))||((p.y<a.y1&&p.y>a.y2)||(p.y>a.y1&&p.y<a.y2));
      boolean Lineb = ((p.x<b.x1&&p.x>b.x2)||(p.x>b.x1&&p.x<b.x2))||((p.y<b.y1&&p.y>b.y2)||(p.y>a.y1&&p.y<b.y2));
      
        if(Linea&&Lineb){
        //stroke(255);
        //strokeWeight(20);
        //point(p.x,p.y);
        //strokeWeight(1);
        return p;
      }
      else{
      return null;
      }}
  };

thanks, but I only am able to use simpler language for now and the triangle is just a part of a bigger project, I need to do it the way I was doing it. Thanks for the example though

1 Like

No worries, good luck with your project.

this might help

nah my issue is matematic ill check it out tho

alright, I’m down, lets give it a go, can you help me with your variables though, they’re throwing errors;

A = abs(x1y21+y11x3+1x2y3-y1x21-x11y3-1y2x3)/2;
A1 = abs(x1y21+y11mouseX+1x2mouseY-y1x21-x11mouseY-1y2mouseY)/2;
A2 = abs(x1mouseY1+y11x3+1mouseXy3-y1mouseX1-x11y3-1mouseYx3)/2;
A3 = abs(mouseXy21+mouseY1x3+1x2y3-mouseYx21-mouseX1y3-1y2x3)/2;

you didn’t add the moltiplication sign in those. It’s a determinant of a matrix. Like when solving systems of more variables. https://en.wikipedia.org/wiki/Rule_of_Sarrus

| x1 y1 1 |
| x2 y2 1 |
| x3 y3 1 |

here is my PGraphics



ArrayList<TriangleClass> triangles=new ArrayList();
PGraphics pg;

// ---------------------------------------------------------------

void setup() {
  // init (runs once)
  size(800, 600);

  addOne();
  addOne(); 
  addOne(); 
  addOne(); 
  addOne(); 
  addOne(); 
  addOne();
  addOne();
  addOne(); 
  addOne(); 
  addOne(); 
  addOne(); 
  addOne(); 
  addOne();
  addOne();
  addOne(); 
  addOne(); 
  addOne(); 
  addOne(); 
  addOne(); 
  addOne();

  pg = createGraphics(width, height);
  pg.beginDraw();
  pg.background(255, 0, 0); 
  pg.endDraw();
} // func 

void draw() { 
  // runs again and again 
  background(255);

  // draw them all 
  for (int i=0; i < triangles.size(); i++) {
    // get object from ArrayList
    TriangleClass myCurrentTriangle;
    myCurrentTriangle = triangles.get(i);
    // show it 
    myCurrentTriangle.display();
    // when mouse is over - make outline thicker
    if (myCurrentTriangle.mouseOver()) {
      myCurrentTriangle.triangleStrokeWeight += 0.1;
    }//if
  } // for

  if (keyPressed)
    showPG();
} // func 

// ---------------------------------------------------------------

void addOne() {
  // add one new triangle

  int widthX= 50;
  int widthY= 50;
  int widthXhalf= widthX/2;
  int widthYhalf= widthY/2;

  color myColor1 = color (0) ; // color( random (255), random (255), random (255));   
  // A new TriangleClass object is added to the ArrayList (by default to the end)
  //             
  float x=random(width);
  float y=random(height);

  triangles.add(
    new TriangleClass(
    x-widthXhalf, y+widthY/2, 
    x, y-widthY/2, 
    x+widthXhalf, y+widthY/2, 
    myColor1, 
    triangles.size()
    ));
}

void showPG() {
  image(pg, 
    0, 0) ;
}

// =====================================================================
// Simple class

class TriangleClass {
  // 
  float x1, y1, x2, y2, x3, y3;   // points 
  color myColor;                  // fill color

  color myColorIntern= 0; //  color(random(255));      // fill color for pg - could be improved by using ID 

  float mouseSensorX, mouseSensorY;// one check point for dist to mouse

  float triangleStrokeWeight=0.6; // the thickness of lines  

  boolean  selected=false; 
  // constructor
  TriangleClass(
    float tempX1, float tempY1, 
    float tempX2, float tempY2, 
    float tempX3, float tempY3, 
    color tempmyColor1, 
    int ID_) {

    // for pg
    myColorIntern=color(ID_);
    println(ID_);

    x1 = tempX1;
    y1 = tempY1;
    x2 = tempX2;
    y2 = tempY2;
    x3 = tempX3;
    y3 = tempY3;    

    myColor=tempmyColor1;

    mouseSensorX = (x1+x2+x3 )/ 3;
    mouseSensorY = (y1+y2+y3 )/ 3;
  }  // constructor

  boolean mouseOver() {
    // tells if mouse is over
    if (pg.get ( mouseX, mouseY ) == myColorIntern) {
      selected=true;
      return true;
    } // if

    triangleStrokeWeight=0.6; // reset 
    selected=false;
    return false;
  } // func

  boolean mouseOverOld() {
    // tells if mouse is over
    if (dist ( mouseX, mouseY, mouseSensorX, mouseSensorY ) < 20) {
      selected=true;
      return true;
    } // if

    selected=false;
    return false;
  } // func

  void display() {
    // show rectangle
    fill(myColor); 
    strokeWeight(triangleStrokeWeight);
    noStroke(); 
    if (selected)
      stroke(255, 0, 0);
    triangle(x1, y1, x2, y2, x3, y3);

    // and pg
    pg.beginDraw();
    pg.fill(myColorIntern); 
    pg.triangle(x1, y1, x2, y2, x3, y3);
    pg.endDraw();
  } // func
} // class
// =====================================================================

You problem is that your formula for the area of a triangle is broken.

First, use floating-point division, not integer division: 2.0.

Next, it is impossible to read your calculations and try to find errors. Instead, create a function with your equation:

float triarea (float ax, float ay, float bx, float by, float cx, float cy) {
  return abs( (ax * ( by - cy ) + bx * ( cy - ay ) + cx * ( ay - by )) / 2.0 );
}

Then, call that function so that you can see what you are doing:

  A  = triarea(x1, y1, x2, y2, x3, y3);
  A1 = triarea(x1, y1, x2, y2, mouseX, mouseY);
  A2 = triarea(x1, y1,  mouseX, mouseY, x3, y3);
  A3 = triarea( mouseX, mouseY, x2, y2, x3, y3);
  At = A1+A2+A3;

This fixes your design, so that At == A while the mouse is inside the triangle.

Finally, if you want this to work on triangles with fractional point locations (like y2=100.3) then you can’t always count on if(At==A) – because you are using floating point math, which is imprecise. When you compare, you may need a small fudge factor, such as if(abs(At-A) < .0005). You can test this on the largest triangles you expect to create to see what the largest difference is in practice.

3 Likes