Search code examples
javascripthtmlmathgeometry

Check if a given point is within the boundary of the rotated element


I have a rectangle that rotated 45° degree as shown below, I have the points of all four corners, I need to find out if given point is within the boundary of the rotated rectangle. Note that rotation will change to any number, so somehow rotation need to be part of the formula.

So we have:

Width: 10

Height: 10

Rotation degree: 45°

Coordinates of rotated rectangle: A B C D

Given coordinates: E

enter image description here

Appreciate any help.


Solution

  • Assuming you don't have the transform matrix for the rectangle.

    If you do have the matrix then multiply the point by the inverse transform matrix. Then just test the point against the bounds of the rectangle top,left, right, and bottom

    Point inside convex polygon

    Consider a convex polygon with points going around in a clockwise direction.

    A point is inside that polygon if it is to the left of every edge (line) of the polygon.

    If the point is right of one or more edges it is outside the polygon.

    Left is defined as your left as if standing at the start of the line looking along its length.

    Cross product

    To find out which side a point is of a line you get the cross product of the vector from the line start to end, and the vector from the line start to the point. If the cross product is positive the point is to the left, if zero it is on the line, else it is to the right.

    Is left

    const Point = (x, y) => ({x, y});
    const Line = (p1, p2) => ({p1, p2});
    
    function isPointLeft(l, p) { // l is line, p is point
        return 0 < (l.p2.x - l.p1.x) * (p.y - l.p1.y) - (l.p2.y - l.p1.y) * (p.x - l.p1.x);
    }
    

    Clockwise check

    Thus give a set of points in clockwise order that represent a rectangle check each side against the point. If all left you are inside.

    Function returns true if point is inside polygon. Assumes points for polygon are in clockwise order

    function isPointInsidePoly(point, poly) {
        var i = 0;
        const line = Line(poly[poly.length - 1]);
        while (i < poly.length) {
             line.p2 = poly[i++];
             if (!isPointLeft(line, point)) { return false }
             line.p1 = line.p2;
        }
        return true;
    }
    

    Demo

    Simple demo creates a set of points and rotates a rectangle.

    Renders each point. If point is inside rectangle then point is drawn a little larger.

    const Point = (x = 0, y = 0) => ({x, y});
    const Line = (p1, p2) => ({p1, p2});
    function isPointLeft(l, p) { // l is line, p is point
        return 0 < (l.p2.x - l.p1.x) * (p.y - l.p1.y) - (l.p2.y - l.p1.y) * (p.x - l.p1.x);
    }
    function isPointInsidePoly(point, poly) {
        var i = 0;
        const line = Line(poly[poly.length - 1]);
        while (i < poly.length) {
             line.p2 = poly[i++];
             if (!isPointLeft(line, point)) { return false }
             line.p1 = line.p2;
        }
        return true;
    }
    
    
    requestAnimationFrame(renderLoop);
    const ctx = canvas.getContext("2d");
    const [W, H] = [canvas.width, canvas.height];
    const rand = (m, M) => Math.random() * (M - m) + m;
    const setOf = (count, cb, i = 0, a = []) => {while (i < count) { a.push(cb(i++)) } return a}
    function drawPoint(p, size) {
        ctx.strokeStyle = "#000";
        ctx.beginPath();
        ctx.arc(p.x, p.y, size, 0, Math.PI * 2);
        ctx.stroke();
    }
    const rect = {
        x: W / 2, y: H / 2,// x,y center of rectangle
        w: 80, h: 20,      // w h from center
        points: [Point(), Point(), Point(), Point()],
        update(angle) {
            const transform = (x, y, res) => {
                res.x = x * ax - y * ay + this.x;
                res.y = x * ay + y * ax + this.y;
            }
            const [ax, ay] = [Math.cos(angle), Math.sin(angle)];
            const p = this.points;
            transform( this.w,  this.h, p[0]);
            transform(-this.w,  this.h, p[1]);
            transform(-this.w, -this.h, p[2]);
            transform( this.w, -this.h, p[3]);  
        },
        draw(ctx) {
            ctx.lineWidth = 1;
            ctx.strokeStyle = "red";
            ctx.beginPath();
            for (const p of this.points) { ctx.lineTo(p.x, p.y) }
            ctx.closePath();
            ctx.stroke();
         }
    }; 
    const testPoints = setOf(20, () => Point(rand(20, W - 20), rand(20, H - 20)));
    function renderLoop(time) {
        ctx.clearRect(0, 0, W, H);
        rect.update(time / 2300);
        rect.draw(ctx);
        for (const p of testPoints) {
            if (isPointInsidePoly(p, rect.points)) { drawPoint(p, 3) }
            drawPoint(p, 1);
        }
        requestAnimationFrame(renderLoop);
    }
    <canvas id="canvas" width="200" height="200"></canvas>