p5.js offers transformation functions like rotate(), translate(), and scale() which, when combined, allow for very complicated drawing. However, trying to detect whether or not someone is currently hovering their mouse over a transformed drawing is a nightmare. One usually has to track the translated amount in a variable and use that for mouse hovers, however, tracking rotation & scale is incredibly difficult.

Using transformation matrices, I have fixed that issue once and for all.


// You can put the below in a library if u want
window._boundGraphicsMethods = {};
window._bindGraphicsMethod = function(name,funct) {
  window[name] = funct.bind(window);
  window._boundGraphicsMethods[name] = funct;
}
window._createGraphics = window.createGraphics;
window.createGraphics = function(w,h,gl) {
  var buf = window._createGraphics(w,h,gl);
  buf.width *= buf._pixelDensity;
  buf.height *= buf._pixelDensity;
  for (var i in window._boundGraphicsMethods) {
    buf[i] = window._boundGraphicsMethods[i].bind(buf);
  }
  return buf;
};

// Transforms
_bindGraphicsMethod("transformWorldToCanvas",function(x,y) {
  // from world to canvas space
  if (typeof x == "object") {
    y = x.y;
    x = x.x;
  }
  var s = this._pixelDensity; // Get pixel density
  var matrix = this._renderer.drawingContext.getTransform(); // This is a DOMMatrix
  matrix = matrix.invertSelf(); // Invert the matrix
  var p = matrix.transformPoint({x:x*s,y:y*s,z:0,w:1}); // Transform the point
  return {x:p.x,y:p.y};
});
_bindGraphicsMethod("transformCanvasToWorld",function(x,y) {
  // from canvas to world space
  if (typeof x == "object") {
    y = x.y;
    x = x.x;
  }
  var s = this._pixelDensity; // Get pixel density
  var matrix = this._renderer.drawingContext.getTransform(); // This is a DOMMatrix
  var p = matrix.transformPoint({x:x,y:y,z:0,w:1}); // Transform the point
  return {x:p.x/s,y:p.y/s};
});
// Mouse Hovering
_bindGraphicsMethod("mouseRect",function(x,y,width,height) {
  var p = this.transformWorldToCanvas(mouseX,mouseY);
  return (
    p.x > x &&
    p.x < x+width &&
    p.y > y && 
    p.y < y+height
  );
});
_bindGraphicsMethod("mouseEllipse",function(cx,cy,width,height) {
  var p = this.transformWorldToCanvas(mouseX,mouseY);
  var dx = (p.x - cx) / width;
  var dy = (p.y - cy) / (height||width);
  var dist = dx*dx + dy*dy;
  return dist < 0.25;
});

// END

function draw() {
  background(255);
  translate(200,200);
  var t = frameCount/100;
  push();
  translate(noise(t,0)*50,noise(t,1)*50);
  scale(cos(t+45)*2,cos(t)*2);
  rotate(t*90);
  translate(50,0);
  var h = mouseRect(20,5,20,10);
  fill(h?100:200);
  rect(20,5,20,10);
  var p = transformCanvasToWorld(20,5);
  pop();
  push();
  translate(noise(t,3)*50,noise(t,4)*50);
  scale(cos(t-45)*2,cos(t)*2);
  rotate(t*-45);
  translate(50,0);
  var h2 = mouseEllipse(-30,-5,20,15);
  fill(h2?100:200);
  ellipse(-30,-5,20,15);
  pop();
  translate(-200,-200);
  rect(p.x,p.y,10,10);
}

By transforming the mouseX, and mouseY to "canvas space" from "world space" you can detect mouse hover regardless of transformation. This also works in p5!
(Graphics binding fixes pixelDensity problems)

Please note that the graphics will not be accurate unless drawn like image(buf,0,0)

Added buffer support cuz why not


// You can put the below in a library if u want
window._boundGraphicsMethods = {};
window._bindGraphicsMethod = function(name,funct) {
  window[name] = funct.bind(window);
  window._boundGraphicsMethods[name] = funct;
}
window._createGraphics = window.createGraphics;
window.createGraphics = function(w,h,gl) {
  var buf = window._createGraphics(w,h,gl);
  buf.twidth = w;
  buf.theight = h;
  buf.width *= buf._pixelDensity;
  buf.height *= buf._pixelDensity;
  for (var i in window._boundGraphicsMethods) {
    buf[i] = window._boundGraphicsMethods[i].bind(buf);
  }
  return buf;
};

// Transforms
_bindGraphicsMethod("transformWorldToCanvas",function(x,y) {
  // from world to canvas space
  if (typeof x == "object") {
    y = x.y;
    x = x.x;
  }
  var s = this._pixelDensity; // Get pixel density
  var matrix = this._renderer.drawingContext.getTransform(); // This is a DOMMatrix
  matrix = matrix.invertSelf(); // Invert the matrix
  var p = matrix.transformPoint({x:x*s,y:y*s,z:0,w:1}); // Transform the point
  return {x:p.x,y:p.y};
});
_bindGraphicsMethod("transformCanvasToWorld",function(x,y) {
  // from canvas to world space
  if (typeof x == "object") {
    y = x.y;
    x = x.x;
  }
  var s = this._pixelDensity; // Get pixel density
  var matrix = this._renderer.drawingContext.getTransform(); // This is a DOMMatrix
  var p = matrix.transformPoint({x:x,y:y,z:0,w:1}); // Transform the point
  return {x:p.x/s,y:p.y/s};
});
_bindGraphicsMethod("transformBufferToCanvas",function(buf,x,y,width,height,px,py) {
  // from canvas to world space
  if (typeof px == "object") {
    py = px.y;
    px = px.x;
  }
  px = px/buf.twidth*width+x;
  py = py/buf.theight*height+y;
  return {x:px,y:py};
});
_bindGraphicsMethod("transformCanvasToBuffer",function(buf,x,y,width,height,px,py) {
  // from canvas to world space
  if (typeof px == "object") {
    py = px.y;
    px = px.x;
  }
  px = (px-x)*buf.twidth/width;
  py = (py-y)*buf.theight/height;
  return {x:px,y:py};
});
// Mouse Hovering
_bindGraphicsMethod("mouseFrame",function(buf,x,y,width,height) {
  var p = this.transformWorldToCanvas(this.mouseX,this.mouseY);
  buf.hover = (
    p.x > x &&
    p.x < x+width &&
    p.y > y && 
    p.y < y+height
  );
  p = this.transformCanvasToBuffer(buf,x,y,width,height,p);
  buf.mouseX = p.x;
  buf.mouseY = p.y;
});
_bindGraphicsMethod("mouseRect",function(x,y,width,height) {
  if (this.hover === false) return false;
  var p = this.transformWorldToCanvas(this.mouseX,this.mouseY);
  return (
    p.x > x &&
    p.x < x+width &&
    p.y > y && 
    p.y < y+height
  );
});
_bindGraphicsMethod("mouseEllipse",function(cx,cy,width,height) {
  if (this.hover === false) return false;
  var p = this.transformWorldToCanvas(this.mouseX,this.mouseY);
  var dx = (p.x - cx) / width;
  var dy = (p.y - cy) / (height||width);
  var dist = dx*dx + dy*dy;
  return dist < 0.25;
});

// END

var g = createGraphics(50,100);
var g2 = createGraphics(50,100);
function draw() {
  background(255);
  translate(200,200);
  var t = frameCount/100;
  push();
  translate(noise(t,0)*50,noise(t,1)*50);
  scale(cos(t+45)*2,cos(t)*2);
  rotate(t*90);
  translate(50,0);
  var h = mouseRect(20,5,20,10);
  fill(h?100:200);
  rect(20,5,20,10);
  var p = transformCanvasToWorld(20,5);
  pop();
  push();
  translate(noise(t,3)*50,noise(t,4)*50);
  scale(cos(t-45)*2,cos(t)*2);
  rotate(t*-45);
  translate(50,0);
  var h2 = mouseEllipse(-30,-5,20,15);
  fill(h2?100:200);
  ellipse(-30,-5,20,15);
  pop();
  translate(-200,-200);
  rect(p.x,p.y,10,10);
  //
  push();
  translate(50,150);
  scale(cos(t-45)*2,cos(t)*2);
  rotate(t*-45);
  mouseFrame(g,10,10,100,100);
  g.clear();
  g.push();
  g.translate(abs(cos(t*45))*40,abs(sin(t*90))*90);
  var h3 = g.mouseRect(0,0,20,20);
  g.fill(h3?100:200);
  g.rect(0,0,20,20);
  g.mouseFrame(g2,0,0,20,20);
  g2.clear();
  g2.push();
  g2.translate(sin(t*45)*20+20,sin(t*60)*20+20);
  g2.rotate(t*20);
  var h4 = g2.mouseRect(-10,-10,20,20);
  g2.fill(h4?100:200);
  g2.rect(-10,-10,20,20);
  var p2 = g2.transformCanvasToWorld(10,10);
  p2 = g2.transformBufferToCanvas(g2,0,0,20,20,p2);
  g.fill(100);
  g.rect(p2.x,p2.y,5,5);
  var p3 = g.transformCanvasToWorld(p2);
  p3 = g.transformBufferToCanvas(g,10,10,100,100,p3);
  g.image(g2,0,0,20,20);
  g2.pop();
  g.pop();
  image(g,10,10,100,100);
  noFill();
  rect(10,10,100,100);
  fill(100);
  rect(p3.x,p3.y,5,5);
  p4 = transformCanvasToWorld(p3);
  pop();
  fill(100);
  rect(p4.x,p4.y,10,10);
}

Also threw it in FGUI

Chat