1

I am trying to use the HTML5 canvas features. I set up a rectangle on the canvas and it is rotated. It is not returning back a true when I click inside the rotated rectangle. Perhaps I am just tripping up on understanding the solution. What am I doing incorrectly? I have the code below.

I am using the solution referenced in:
Mouse position within rotated rectangle in HTML5 Canvas

        <?php

    $canvas_width=800;
    $canvas_height=1336;


    echo <<<_END



    <canvas id="myCanvas" width=$canvas_width height=$canvas_height style="border:1px solid #d3d3d3;">
    Your browser does not support the HTML5 canvas tag.
    </canvas>

    <script>

    //Get the canvas element by using the getElementById method.
               canvas = document.getElementById('myCanvas');

               HEIGHT = canvas.height;
               WIDTH = canvas.width;
               CANVAS_LEFT = 9;
               CANVAS_RIGHT = canvas.width+CANVAS_LEFT;

               CANVAS_TOP = 9;
               CANVAS_BOTTOM = canvas.height+CANVAS_TOP;

                    lastMouseX = 0;                 // The last seen mouse X coordinate
                        lastMouseY = 0;                 // the last seen mouse Y coordinate
                rectangleDrawAndRotated=false;
                    mouseX =0;
                mouseY=0;
                    offsetX = 0;
                        offsetY = 0;
                originX=0;
                originY=0;
                 width=0;
                height=0;

    //Get a 2D drawing context for the canvas.

                ctx = canvas.getContext('2d');


            myimage=new Image()
            myimage.src='/Farm_planner/images/The_Farm_at_Dover_Vineyards_Google_Maps.png'  

            myimage.onload=function()
            {
            ctx.drawImage(myimage,1,1,800,1336)
            }



    // When true, moving the mouse draws on the canvas


    isDrawing = false;
    x = 0;
    y = 0;


    // event.offsetX, event.offsetY gives the (x,y) offset from the edge of the canvas.

    // Add the event listeners for mousedown, mousemove, and mouseup

      canvas.addEventListener('mousedown', e => {
      x = e.offsetX;
      y = e.offsetY;
      isDrawing = true;

       console.log("mouse down",x,y,width,height);

       if (rectangleDrawAndRotated ===true){
         originX = x + width/2;
         originY = y + height/2;


       getMouse(e);

    //   get the locations of the mouse and assign to mouseX and mouseY


       clickState=false;
       console.log(clickState,mouseX, mouseY,x,y);

      if (clickedOnRectangle(x,y)) clickState=true;


       console.log(clickState,mouseX, mouseY,x,y);
            }

    });

    canvas.addEventListener('mousemove', e => {
      if (isDrawing === true) {
     //   drawLine(ctx, x, y, e.offsetX, e.offsetY);
     //   x = e.offsetX;
     //   y = e.offsetY;
      }
    });

    window.addEventListener('mouseup', e => {
      if (isDrawing === true) {
        drawLine(ctx, x, y, e.offsetX, e.offsetY);
        isDrawing = false;
        mouseX =0;
        mouseY=0;
        rectangleDrawAndRotated=true;

            console.log("mouse up",x,y,width,height);
      }
    });

    function drawLine(ctx, x1, y1, x2, y2) {
    // ctx.beginPath();
    //  ctx.strokeStyle = 'black';
        ctx.lineWidth = 3;
    //  ctx.moveTo(x1, y1);
    //  ctx.lineTo(x2, y2);
    //  ctx.stroke();
    //  ctx.closePath();
            width=x2-x1;
            height=y2-y1;
            xCoord=x1;
            yCoord=y1;

        drawRectangle(x1,y1,width,height)   ;

        rotateDrawing(x1,y1,width,height);
        lastMouseX = 0;                 // The last seen mouse X coordinate
        lastMouseY = 0;                 // the last seen mouse Y coordinate  

    }



       function drawRectangle(xCoord,yCoord,width,height) {

      //  ctx.strokeRect(xCoord,yCoord,width,height);
            ctx.rect(xCoord,yCoord,width,height);
             originX = xCoord + width/2;
             originY = yCoord + height/2;
             r=45*(Math.PI / 180);  

      }


       function clickedOnRectangle(x,y) {

     //   dtermine if the user clicked on the area of the rectangle that has been rotated

     // Our origin of rotation is the center of the rectangle
     // Our rectangle has its upper-left corner defined by x,y, its width
     // defined in w, height in h, and rotation(in radians) in r.  

     // translate mouse point values to origin

        dx = mouseX - originX;
        dy = mouseY - originY;

      // distance between the point and the center of the rectangle

        var h1 = Math.sqrt(dx*dx + dy*dy);
        var currA = Math.atan2(dy,dx);

      // Angle of point rotated around origin of rectangle in opposition

        var newA = currA - r;

      // New position of mouse point when rotated

        var x2 = Math.cos(newA) * h1;
        var y2 = Math.sin(newA) * h1;

        console.log(x,y,width,height,mouseX,mouseY,originX,originY,dx,dy,h1,currA,r,newA,x2,y2)

      // Check relative to center of rectangle

        if (x2 > -0.5 * width && x2 < 0.5 * width && y2 > -0.5 * height && y2 < 0.5 * height){
            console.log('true');
            return true;
        }

       }

    // Sets mouseX and mouseY variables taking into account padding and borders
                    function getMouse(e) {
                            var element = canvas;
                            var offsetX = 0;
                            var offsetY = 0;

    // Calculate offsets
                            if (element.offsetParent) {
                                    do {
                                            offsetX += element.offsetLeft;
                                            offsetY += element.offsetTop;
                                    } while ((element = element.offsetParent));
                            }   

    // Calculate the mouse location
                            mouseX = e.pageX - offsetX;
                            mouseY = e.pageY - offsetY;

    // Calculate the change in mouse position for the last
    // time getMouse was called
                            changeInX = mouseX - lastMouseX;
                            changeInY = mouseY - lastMouseY;

    // Store the current mouseX and mouseY positions
                            lastMouseX = mouseX;
                            lastMouseY = mouseY;



                    }      



          function rotateDrawing(xCoord,yCoord,width,height) 
    {
            ctx.setTransform(1,0,0,1,0,0);
            ctx.translate(xCoord+.5*width,yCoord+.5*height );
            r=45*(Math.PI / 180);  // 45 degrees  as radian
            ctx.rotate(r); // 
            ctx.fillStyle = "red";
            ctx.fillRect(-.5*width, -.5*height, width, height);
            console.log(xCoord,yCoord,width,height)
            }




    </script>    

    _END;
Jack Stein
  • 123
  • 1
  • 14

1 Answers1

1

Yes, you are tripping on the solution, treat your rectangle as a polygon and you don't have to worry about doing rotation, also you can have more complex shapes than rectangles.

I'm using the ray-casting algorithm: https://github.com/substack/point-in-polygon/blob/master/index.js
With that, all we need to do is check if the mouse is inside the polygon, that is all.

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext("2d");
const rect = canvas.getBoundingClientRect();

const poly = [[89, 9], [13, 19], [19, 56], [98, 36], [89, 9]]

function draw(p) {
  p.map(x => ctx.lineTo(x[0], x[1]));
  ctx.stroke();
}

function inside(p, vs) {
  var inside = false;
  for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) {
    var xi = vs[i][0], yi = vs[i][1];
    var xj = vs[j][0], yj = vs[j][1];
    var intersect = ((yi > p[1]) != (yj > p[1])) && (p[0] < (xj - xi) * (p[1] - yi) / (yj - yi) + xi);
    if (intersect) inside = !inside;
  }
  return inside;
};

draw(poly)
canvas.addEventListener('mousemove', function(evt) {
  ctx.clearRect(100, 0, canvas.width, canvas.height);
  let x = inside([ evt.clientX - rect.left, evt.clientY - rect.top ], poly)
  ctx.fillText(x, 110, 20);
}, false);
<canvas id="canvas"></canvas>

I did it on mousemove so you can see the change right away...
but same can be applied to any event you like

Helder Sepulveda
  • 15,500
  • 4
  • 29
  • 56
  • Thank you. Looks like a winning solution. I see you hard coded the vertices of the poly. In my use case, the rectangle is drawn dynamically and it depicts a farm field and is being rotated to fit on the farm (I don't have that part in place so it is hard coded to 45 degrees), then dragged to its position on the canvas. I need to know when the user clicks on the farm field and starts the dragging. How would you determine the vertices of a rotated rectangle? – Jack Stein Sep 09 '21 at 17:26
  • determine the vertices of a rotated rectangle ... that is a completely different question, my guess you need to give the user the option of creating their own polygon, start with something like: https://stackoverflow.com/questions/66937243/whats-wrong-with-the-addeventlisteners/66976078#66976078 – Helder Sepulveda Sep 09 '21 at 21:03
  • Helder-thank you again, I took your code and that of the link you pointed me to. I have 2 issues: a. When there is an image on the canvas, the poly is not visible. b. I am doing something incorrectly in referencing the array in the inside function. Any ideas what I am doing wrong? I placed the code in https://jsfiddle.net/jwv09odg/ – Jack Stein Sep 10 '21 at 17:46
  • On the function 'inside' I managed to fix the code as follows (still not sure why the poly is not showing up on the canvas when there is an image. // var xi = vs[i][0], yi = vs[i][1]; // var xj = vs[j][0], yj = vs[j][1]; var xi = vs[i].x, yi = vs[i].y; var xj = vs[j].x, yj = vs[j].y; – Jack Stein Sep 10 '21 at 18:32
  • @JackStein create a proper jsfiddle, that one does not work ... and best to create other questions with these other problems – Helder Sepulveda Sep 10 '21 at 23:02
  • Helder. Sorry about all the mix up. I am new to all this. I fixed the jfiddle at https://jsfiddle.net/jwv09odg/1/ and it is updated. As you can see when you have an image loaded the poly does not show up. When you removed the image the poly works. The image is loaded in lines 12-18. Let me know if you prefer that I create a new question on this. – Jack Stein Sep 11 '21 at 01:15
  • Yes please create new questions and please focus on one issue only, questions with multiple issues are quite often closed... this question was about checking mouse position within a rotated rectangle, I think I addressed that issue please accept the answer and up-vote – Helder Sepulveda Sep 11 '21 at 15:49
  • I made a few corrections to your fiddle: https://jsfiddle.net/heldersepu/egt92403/36/ – Helder Sepulveda Sep 11 '21 at 16:33
  • Thank you. Your solution works. I have other questions (like when you move the image down with the scroll bar the ability to do anything with the poly vanishes. I will post additional questions in a new post. – Jack Stein Sep 11 '21 at 18:00