Search code examples
geometryangle

Calculate interior bisectors in closed polygon


I have a polygon which is oriented counter clock wise. I am trying to figure out what the bisectors are for each adjacent edge. I have come up with a solution, but I am wondering if this is the most efficient way ...

I need to check the interior angle. Is it bigger or smaller then pi. I need to do this, because I need to flip either the incoming vector, or the outgoing vector.

The question basically is: is there a more efficient way to determine if the interior angle > pi (or 180deg)?

The procedure in javascript I have now is this:

export const getBisectors = (polygon) => {
    //get bisectors, including length based on the unit normal vectors of the edges (inward)
    let bisectors = [];
    let prevPoint = polygon[polygon.length - 1];

    polygon.forEach((p, i) => {
        let nextPoint = i === polygon.length - 1 ? polygon[0] : polygon[i + 1];

        //vector going to p
        let v1 = normalizeVector({ x: p.x - prevPoint.x, y : p.y - prevPoint.y });
        let radIn = Math.acos(v1.x);
        if (v1.y < 0) radIn = TwoPI - radIn;

        // vector to next point
        let v2 = normalizeVector({ x: nextPoint.x - p.x, y : nextPoint.y - p.y });
        let radOut = Math.acos(v2.x);
        if (v2.y < 0) radOut = TwoPI - radOut;

        let rotation = radIn - radOut;
        if (rotation < 0) rotation += TwoPI; 

        if (rotation > Math.PI) {
            //invert outgoing
            v2 = multiply(v2, -1);
        } else {
            //invert incoming
            v1 = multiply(v1, -1);
        }
        let bisector = addVectors(v1, v2);

        bisectors.push({bisector: bisector, p : p  });
        prevPoint = p;
    });
    return bisectors;
}

After the partial answer I ended up with the following method:

export const getIntersection = (p1, v1, p2, v2) => {
    //find s
    //p1 + s * v1 == p2 + t * v2
    var denominator = cross(v1, v2);

    if (Math.abs(denominator) < epsilon) {
        return p1;
    }

    var s = (p2.x * v2.y + p1.y * v2.x - p2.y * v2.x - p1.x * v2.y) / denominator;
    return {x : p1.x + s * v1.x, y : p1.y + s * v1.y};
}

function getBisector(prevPoint, point, nextPoint) {
    let v1 = { x: point.x - prevPoint.x, y : point.y - prevPoint.y };
    let n1 = normalizeVector( { x: v1.y, y : -( v1.x ) } )
    let n2 = normalizeVector( { x: (nextPoint.y - point.y), y : -(nextPoint.x - point.x) } )

    let bisector = addVectors(n1, n2);    
    let i = getIntersection(point, bisector, addVectors(prevPoint, n1), v1);

    return {x : i.x - point.x, y : i.y - point.y};
}

and some examples: bisectors 5-shape

enter image description here


Solution

  • let v1 = normalizeVector({ x: p.x - prevPoint.x, y : p.y - prevPoint.y });
    let v2 = normalizeVector({ x: nextPoint.x - p.x, y : nextPoint.y - p.y });
    
    k = v1.x * v2.y - v1.y * v2.x;
    
    if (k < 0){
       //the angle is greater than pi, invert outgoing, 
       //ready to get interior bisector 
       v2 = multiply(v2, -1);  
    }
    else{
       //the angle is less than pi, invert incoming, 
       v1 = multiply(v1, -1);
    }
    
    bisector = normalizeVector({ x: v1.x + v2.x, y : v1.y + v2.y });
    

    Etit: Here is even faster code for generating interior bisector, without the use of any normals: Matlab code, which I tested. It generates the unit bisectors pointing in the interior of the polygon.

    function  B = bisectors(P)
    
       %P is 2 x n matrix, column P(:,j) is a vertex of a polygon in the plane,
       %P is the ordered set of vertices of the polygon
    
       [~,n] = size(P); 
       B = zeros(2,n);
    
       for j=1:n
    
           if j == 1
              v_in = P(:,1) - P(:,n);
              v_out = P(:,2) - P(:,1);
           elseif j == n
              v_in = P(:,j) - P(:,j-1);
              v_out = P(:,1) - P(:,j);
           else
              v_in = P(:,j) - P(:,j-1);
              v_out = P(:,j+1) - P(:,j);
           end
    
           v_in = v_in/sqrt(v_in'*v_in); %normalize edge-vector
           v_out = v_out/sqrt(v_out'*v_out); %normalize edge-vector
    
           % bisector of the complementary angle at the vertex j, 
           % pointing counter clockwise and displacing the vertex so that
           % the resulting polygon is 1 unit inwards in normal direction:
           bisector = v_in + v_out; 
           bisector = bisector/abs(bisector'*v_in);
    
           % 90 degree counter clockwise rotation of complementary bisector:
           B(1,j) = - bisector(2);
           B(2,j) = bisector(1);
    
       end
    
    end
    

    And in your notation:

    function getBisector(prevPoint, point, nextPoint) {
    
        let v1 = normalizeVector({ x : point.x - prevPoint.x, y : point.y - prevPoint.y });
        let v2 = normalizeVector({ x : nextPoint.x - point.x, y : nextPoint.y - point.y });
    
    
        let bisectorPerp = addVectors(v1, v2); 
        bisectorPerp = bisectorPerp / absoluteValue(dot(v1, bisectorPerp));   
    
        return {x : - (bisectorPerp.y), y : bisectorPerp.x};
    }
    

    This function returns bisectors of the same length as in your last function, without the need of the extra getIntersection function.