0

I have drawn a text on the canvas in coordinates X, Y and saved them. I have a simple method that checks if a mouse click has happened inside the text boundaries. The problem is when I rotate the text 45 Degree I cannot check whether a mouse click has happened within the rotated text or not.

In short, how can I check whether a mouse click is inside a rotated text or shape?

ilgaar
  • 804
  • 1
  • 12
  • 31
  • Check out the answer to [this question][1]. :) [1]: http://stackoverflow.com/questions/622140/calculate-bounding-box-coordinates-from-a-rotated-rectangle-picture-inside – bvaughn Feb 24 '15 at 21:58

2 Answers2

4

Create a rect object which is rotated at the same angle as the text, but not drawn.

Then use:

// set transforms here.
// add rect representing text region:
ctx.beginPath();
ctx.rect(x, y, w, h);  // region for text
if (ctx.isPointInPath(mx, my)) {
    // we clicked inside the region
}
ctx.setTransform(1,0,0,1,0,0);  // reset transforms after test

Demo:

var canvas = document.querySelector("canvas"),
    ctx = canvas.getContext("2d"),
    txt = "ROTATED TEXT", tw, region;

// transform and draw some rotated text:
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "32px sans-serif";
ctx.translate(150, 75);
ctx.rotate(0.33);
ctx.translate(-150, -75);
ctx.fillText(txt, 150, 75);
tw = ctx.measureText(txt).width;

// define a region for text:
region = {x: 150 - tw*0.5, y: 75 - 16, w: tw, h:32}; // approx. text region

// function to check if mouse x/y is inside (transformed) region
function isInText(region, x, y) {
  ctx.beginPath();
  ctx.rect(region.x, region.y, region.w, region.h);
  return ctx.isPointInPath(x, y);
}

// test for demo
canvas.onmousemove = function(e) {
  var rect = canvas.getBoundingClientRect(),
      x = e.clientX - rect.left,
      y = e.clientY - rect.top;
  
  // just to visualize:
  ctx.clearRect(0,0,300,150);
  ctx.fillStyle = isInText(region, x, y) ? "red" : "black";
  ctx.fillText(txt, 150, 75);
};
<canvas></canvas>
  • 1
    The problem with this solution is that everything else on the canvas is also getting rotated as no context.restore() is called. – Nausherwan Aslam Feb 24 '15 at 23:21
  • 1
    @NausherwanAslam, This answer is the way to hit-test your text. If your canvas is being rotated, then just rotate the hit-rect at the same angle. The `isPointInPath` works fine even for rotated paths. – markE Feb 25 '15 at 00:04
  • Thanks markE, I am able to implement it based on the answer above and your suggestion. – Nausherwan Aslam Feb 25 '15 at 00:56
0

DO NOT USE BELOW CODE DIRECTLY. HERE I'M TRYING TO EXPLAIN ONLY THE LOGIC THAT HOW YOU CAN PROCEED WITH THIS KIND OF PROBLEMS. YOU MAY HAVE TO WRITE CODE ACCORDING TO YOUR PROBLEM, OR EXTEND THIS SOLUTION TO FIT INTO YOUR PROBLEM

This is more of the mathematical problem where you have to find whether a point is inside rotated Rectangle or not(same as what @user1693593 said) You can find a rectangle that is covering text(rect(x1: text.x1, y1: text.y1, x2: text.x2, y2: text.y2) assuming (text.x1, text.y1) denotes top-left corner of text. Now rotate the rect about it's centre by theta angle and find out x1,y1,x2,y2:

Assume all angle calculation will happen in +ve angles.

if theta < 0 then theta = (360 - abs(theta))

Now calculate angle between centre point and point(x2, y2)(Angle from centre to any corner of rect will be same, we are calculating for (x2, y2) because it will give us positive angle)

var width = (x2-x1);
var height = (y2-y1)
var hypotenuse = Math.pow(width*width + height*height, 0.5);
var innerAngle = Math.acos(x2 - x2 / hypotenuse);

Now calculate final angle of corners from centre

(x1, y1), angle((Math.PI + baseAngle) + angle) %(Math.PI*2)
(x2, y1), angle((Math.PI*2) - baseAngle + angle) % (Math.PI*2)}
(x1, y2), angle((Math.PI - baseAngle) + angle) % (Math.PI*2)}
(x2, y2), angle(baseAngle + angle) % (Math.PI*2)}

Now find out rotated Points of rectangle:

point.x = centre.x + (hypotenuse * Math.cos(point.angle));
point.y = centre.y + (hypotenuse * Math.sin(point.angle));

After rotating points It may happen that their order get changed, to make sure the correct order sort them based on x-axis, if two points have same x-axis then sort them based on y-axis (use any sorting) Now we got sorted points. Name them based on their position p0, p1, p2, p3 Find new boundary of rotated rect from these points, find boundary in x direction and y direction:

var boundary_x1 = ( (p1.x - p0.x) / (p1.y - p0.y) ) * (mousePos.y - p0.y) + p0.x;
var boundary_x2 = ( (p3.x - p2.x) / (p3.y - p2.y) ) * (mousePos.y - p2.y) + p2.x;
var boundary_y1 = ( (p2.y - p0.y) / (p2.x - p0.x)) * (mousePos.x - p0.x) + p0.y;
var boundary_y2 = ( (p3.y - p1.y) / (p3.x - p1.x)) * (mousePos.x - p1.x) + p1.y;

after getting boundary points in x and y direction we can easily put mid point condition and can find whether a point(mousepoint) is clicked in rect or not:

var clickedInside = (mousePos.x > boundary_x1 && mousePos.x < boundary_x2) && (mousePos.y > boundary_y1 && mousePos.y < boundary_y2);

if clickedInside is true then mouseclick happened inside rect

Mahipal
  • 589
  • 4
  • 7