web-dev-qa-db-ja.com

2つの回転した長方形の間の交差をチェックする方法は?

1つの回転した長方形が他の長方形と交差しているかどうかを確認する方法を誰かが説明できますか

25
Buron
  1. 両方のポリゴンの各エッジについて、それを分離線として使用できるかどうかを確認します。もしそうなら、あなたは終わった:交差点なし。
  2. 分割線が見つからなかった場合は、交差点があります。
/// Checks if the two polygons are intersecting.
bool IsPolygonsIntersecting(Polygon a, Polygon b)
{
    foreach (var polygon in new[] { a, b })
    {
        for (int i1 = 0; i1 < polygon.Points.Count; i1++)
        {
            int i2 = (i1 + 1) % polygon.Points.Count;
            var p1 = polygon.Points[i1];
            var p2 = polygon.Points[i2];

            var normal = new Point(p2.Y - p1.Y, p1.X - p2.X);

            double? minA = null, maxA = null;
            foreach (var p in a.Points)
            {
                var projected = normal.X * p.X + normal.Y * p.Y;
                if (minA == null || projected < minA)
                    minA = projected;
                if (maxA == null || projected > maxA)
                    maxA = projected;
            }

            double? minB = null, maxB = null;
            foreach (var p in b.Points)
            {
                var projected = normal.X * p.X + normal.Y * p.Y;
                if (minB == null || projected < minB)
                    minB = projected;
                if (maxB == null || projected > maxB)
                    maxB = projected;
            }

            if (maxA < minB || maxB < minA)
                return false;
        }
    }
    return true;
}

詳細については、次の記事を参照してください。 2Dポリゴン衝突検出-コードプロジェクト

NB:アルゴリズムは、時計回りまたは反時計回りのいずれかの順序で指定された凸多角形に対してのみ機能します。

32
Markus Jarderot

JavaScriptでは、まったく同じアルゴリズムが(便宜上)あります。

/**
 * Helper function to determine whether there is an intersection between the two polygons described
 * by the lists of vertices. Uses the Separating Axis Theorem
 *
 * @param a an array of connected points [{x:, y:}, {x:, y:},...] that form a closed polygon
 * @param b an array of connected points [{x:, y:}, {x:, y:},...] that form a closed polygon
 * @return true if there is any intersection between the 2 polygons, false otherwise
 */
function doPolygonsIntersect (a, b) {
    var polygons = [a, b];
    var minA, maxA, projected, i, i1, j, minB, maxB;

    for (i = 0; i < polygons.length; i++) {

        // for each polygon, look at each Edge of the polygon, and determine if it separates
        // the two shapes
        var polygon = polygons[i];
        for (i1 = 0; i1 < polygon.length; i1++) {

            // grab 2 vertices to create an Edge
            var i2 = (i1 + 1) % polygon.length;
            var p1 = polygon[i1];
            var p2 = polygon[i2];

            // find the line perpendicular to this Edge
            var normal = { x: p2.y - p1.y, y: p1.x - p2.x };

            minA = maxA = undefined;
            // for each vertex in the first shape, project it onto the line perpendicular to the Edge
            // and keep track of the min and max of these values
            for (j = 0; j < a.length; j++) {
                projected = normal.x * a[j].x + normal.y * a[j].y;
                if (isUndefined(minA) || projected < minA) {
                    minA = projected;
                }
                if (isUndefined(maxA) || projected > maxA) {
                    maxA = projected;
                }
            }

            // for each vertex in the second shape, project it onto the line perpendicular to the Edge
            // and keep track of the min and max of these values
            minB = maxB = undefined;
            for (j = 0; j < b.length; j++) {
                projected = normal.x * b[j].x + normal.y * b[j].y;
                if (isUndefined(minB) || projected < minB) {
                    minB = projected;
                }
                if (isUndefined(maxB) || projected > maxB) {
                    maxB = projected;
                }
            }

            // if there is no overlap between the projects, the Edge we are looking at separates the two
            // polygons, and we know there is no overlap
            if (maxA < minB || maxB < minA) {
                CONSOLE("polygons don't intersect!");
                return false;
            }
        }
    }
    return true;
};

これが誰かを助けることを願っています。

33
mstenroos

これは、Java=の同じアルゴリズムで、もし誰かが興味があれば。

boolean isPolygonsIntersecting(Polygon a, Polygon b)
{
    for (int x=0; x<2; x++)
    {
        Polygon polygon = (x==0) ? a : b;

        for (int i1=0; i1<polygon.getPoints().length; i1++)
        {
            int   i2 = (i1 + 1) % polygon.getPoints().length;
            Point p1 = polygon.getPoints()[i1];
            Point p2 = polygon.getPoints()[i2];

            Point normal = new Point(p2.y - p1.y, p1.x - p2.x);

            double minA = Double.POSITIVE_INFINITY;
            double maxA = Double.NEGATIVE_INFINITY;

            for (Point p : a.getPoints())
            {
                double projected = normal.x * p.x + normal.y * p.y;

                if (projected < minA)
                    minA = projected;
                if (projected > maxA)
                    maxA = projected;
            }

            double minB = Double.POSITIVE_INFINITY;
            double maxB = Double.NEGATIVE_INFINITY;

            for (Point p : b.getPoints())
            {
                double projected = normal.x * p.x + normal.y * p.y;

                if (projected < minB)
                    minB = projected;
                if (projected > maxB)
                    maxB = projected;
            }

            if (maxA < minB || maxB < minA)
                return false;
        }
    }

    return true;
}

Oren Beckerが設計した、回転した長方形とフォームの交差を検出する方法を確認してください。

struct _Vector2D 
{
    float x, y;
};

// C:center; S: size (w,h); ang: in radians, 
// rotate the plane by [-ang] to make the second rectangle axis in C aligned (vertical)
struct _RotRect 
{
    _Vector2D C;
    _Vector2D S;
    float ang;
};

また、次の関数を呼び出すと、2つの回転した長方形が交差するかどうかが返されます。

// Rotated Rectangles Collision Detection, Oren Becker, 2001
bool check_two_rotated_rects_intersect(_RotRect * rr1, _RotRect * rr2)
{
    _Vector2D A, B,   // vertices of the rotated rr2
       C,      // center of rr2
       BL, TR; // vertices of rr2 (bottom-left, top-right)

 float ang = rr1->ang - rr2->ang, // orientation of rotated rr1
       cosa = cos(ang),           // precalculated trigonometic -
       sina = sin(ang);           // - values for repeated use

 float t, x, a;      // temporary variables for various uses
 float dx;           // deltaX for linear equations
 float ext1, ext2;   // min/max vertical values

 // move rr2 to make rr1 cannonic
 C = rr2->C;
 SubVectors2D(&C, &rr1->C);

 // rotate rr2 clockwise by rr2->ang to make rr2 axis-aligned
 RotateVector2DClockwise(&C, rr2->ang);

 // calculate vertices of (moved and axis-aligned := 'ma') rr2
 BL = TR = C;
 /*SubVectors2D(&BL, &rr2->S);
 AddVectors2D(&TR, &rr2->S);*/

 //-----------------------------------
 BL.x -= rr2->S.x/2;    BL.y -= rr2->S.y/2;
 TR.x += rr2->S.x/2;    TR.y += rr2->S.y/2;

 // calculate vertices of (rotated := 'r') rr1
 A.x = -(rr1->S.y/2)*sina; B.x = A.x; t = (rr1->S.x/2)*cosa; A.x += t; B.x -= t;
 A.y =  (rr1->S.y/2)*cosa; B.y = A.y; t = (rr1->S.x/2)*sina; A.y += t; B.y -= t;
 //---------------------------------------

 //// calculate vertices of (rotated := 'r') rr1
 //A.x = -rr1->S.y*sina; B.x = A.x; t = rr1->S.x*cosa; A.x += t; B.x -= t;
 //A.y =  rr1->S.y*cosa; B.y = A.y; t = rr1->S.x*sina; A.y += t; B.y -= t;

 t = sina*cosa;

 // verify that A is vertical min/max, B is horizontal min/max
 if (t < 0)
 {
  t = A.x; A.x = B.x; B.x = t;
  t = A.y; A.y = B.y; B.y = t;
 }

 // verify that B is horizontal minimum (leftest-vertex)
 if (sina < 0) { B.x = -B.x; B.y = -B.y; }

 // if rr2(ma) isn't in the horizontal range of
 // colliding with rr1(r), collision is impossible
 if (B.x > TR.x || B.x > -BL.x) return 0;

 // if rr1(r) is axis-aligned, vertical min/max are easy to get
 if (t == 0) {ext1 = A.y; ext2 = -ext1; }
 // else, find vertical min/max in the range [BL.x, TR.x]
 else
 {
  x = BL.x-A.x; a = TR.x-A.x;
  ext1 = A.y;
  // if the first vertical min/max isn't in (BL.x, TR.x), then
  // find the vertical min/max on BL.x or on TR.x
  if (a*x > 0)
  {
   dx = A.x;
   if (x < 0) { dx -= B.x; ext1 -= B.y; x = a; }
   else       { dx += B.x; ext1 += B.y; }
   ext1 *= x; ext1 /= dx; ext1 += A.y;
  }

  x = BL.x+A.x; a = TR.x+A.x;
  ext2 = -A.y;
  // if the second vertical min/max isn't in (BL.x, TR.x), then
  // find the local vertical min/max on BL.x or on TR.x
  if (a*x > 0)
  {
   dx = -A.x;
   if (x < 0) { dx -= B.x; ext2 -= B.y; x = a; }
   else       { dx += B.x; ext2 += B.y; }
   ext2 *= x; ext2 /= dx; ext2 -= A.y;
  }
 }

 // check whether rr2(ma) is in the vertical range of colliding with rr1(r)
 // (for the horizontal range of rr2)
 return !((ext1 < BL.y && ext2 < BL.y) ||
      (ext1 > TR.y && ext2 > TR.y));
}

inline void AddVectors2D(_Vector2D * v1, _Vector2D * v2)
{ 
    v1->x += v2->x; v1->y += v2->y; 
}

inline void SubVectors2D(_Vector2D * v1, _Vector2D * v2)
{ 
    v1->x -= v2->x; v1->y -= v2->y; 
}

inline void RotateVector2DClockwise(_Vector2D * v, float ang)
{
    float t, cosa = cos(ang), sina = sin(ang);
    t = v->x; 
    v->x = t*cosa + v->y*sina; 
    v->y = -t*sina + v->y*cosa;
}
2
herohuyongtao

多分それは誰かを助けるでしょう。 PHPの同じアルゴリズム:

function isPolygonsIntersecting($a, $b) {
    $polygons = array($a, $b);

        for ($i = 0; $i < count($polygons); $i++) {
            $polygon = $polygons[$i];

            for ($i1 = 0; $i1 < count($polygon); $i1++) {
                $i2 = ($i1 + 1) % count($polygon);
                $p1 = $polygon[$i1];
                $p2 = $polygon[$i2];

                $normal = array(
                    "x" => $p2["y"] - $p1["y"], 
                    "y" => $p1["x"] - $p2["x"]
                );

                $minA = NULL; $maxA = NULL;
                for ($j = 0; $j < count($a); $j++) {
                    $projected = $normal["x"] * $a[$j]["x"] + $normal["y"] * $a[$j]["y"];
                    if (!isset($minA) || $projected < $minA) {
                        $minA = $projected;
                    }
                    if (!isset($maxA) || $projected > $maxA) {
                        $maxA = $projected;
                    }
                }

                $minB = NULL; $maxB = NULL;
                for ($j = 0; $j < count($b); $j++) {
                    $projected = $normal["x"] * $b[$j]["x"] + $normal["y"] * $b[$j]["y"];
                    if (!isset($minB) || $projected < $minB) {
                        $minB = $projected;
                    }
                    if (!isset($maxB) || $projected > $maxB) {
                        $maxB = $projected;
                    }
                }

                if ($maxA < $minB || $maxB < $minA) {
                    return false;
                }
            }
        }
        return true;
    }
2
sashko

Rect.IntersectsWith() を使用することもできます。

たとえば、WPFでは、RenderTransformを使用してCanvasに配置された2つのUIElementがあり、それらが交差するかどうかを確認したい場合は、次のようなものを使用できます。

bool IsIntersecting(UIElement element1, UIElement element2)
{
    Rect area1 = new Rect(
        (double)element1.GetValue(Canvas.TopProperty),
        (double)element1.GetValue(Canvas.LeftProperty),
        (double)element1.GetValue(Canvas.WidthProperty),
        (double)element1.GetValue(Canvas.HeightProperty));

    Rect area2 = new Rect(
        (double)element2.GetValue(Canvas.TopProperty),
        (double)element2.GetValue(Canvas.LeftProperty),
        (double)element2.GetValue(Canvas.WidthProperty),
        (double)element2.GetValue(Canvas.HeightProperty));

    Transform transform1 = element1.RenderTransform as Transform;
    Transform transform2 = element2.RenderTransform as Transform;

    if (transform1 != null)
    {
        area1.Transform(transform1.Value);
    }

    if (transform2 != null)
    {
        area2.Transform(transform2.Value);
    }

    return area1.IntersectsWith(area2);
}
1
Miro Bucko

Pythonの場合:

def do_polygons_intersect(a, b):
    """
 * Helper function to determine whether there is an intersection between the two polygons described
 * by the lists of vertices. Uses the Separating Axis Theorem
 *
 * @param a an ndarray of connected points [[x_1, y_1], [x_2, y_2],...] that form a closed polygon
 * @param b an ndarray of connected points [[x_1, y_1], [x_2, y_2],...] that form a closed polygon
 * @return true if there is any intersection between the 2 polygons, false otherwise
    """

    polygons = [a, b];
    minA, maxA, projected, i, i1, j, minB, maxB = None, None, None, None, None, None, None, None

    for i in range(len(polygons)):

        # for each polygon, look at each Edge of the polygon, and determine if it separates
        # the two shapes
        polygon = polygons[i];
        for i1 in range(len(polygon)):

            # grab 2 vertices to create an Edge
            i2 = (i1 + 1) % len(polygon);
            p1 = polygon[i1];
            p2 = polygon[i2];

            # find the line perpendicular to this Edge
            normal = { 'x': p2[1] - p1[1], 'y': p1[0] - p2[0] };

            minA, maxA = None, None
            # for each vertex in the first shape, project it onto the line perpendicular to the Edge
            # and keep track of the min and max of these values
            for j in range(len(a)):
                projected = normal['x'] * a[j][0] + normal['y'] * a[j][1];
                if (minA is None) or (projected < minA): 
                    minA = projected

                if (maxA is None) or (projected > maxA):
                    maxA = projected

            # for each vertex in the second shape, project it onto the line perpendicular to the Edge
            # and keep track of the min and max of these values
            minB, maxB = None, None
            for j in range(len(b)): 
                projected = normal['x'] * b[j][0] + normal['y'] * b[j][1]
                if (minB is None) or (projected < minB):
                    minB = projected

                if (maxB is None) or (projected > maxB):
                    maxB = projected

            # if there is no overlap between the projects, the Edge we are looking at separates the two
            # polygons, and we know there is no overlap
            if (maxA < minB) or (maxB < minA):
                print("polygons don't intersect!")
                return False;

    return True
0
dabs

Type(Java)Script実装で、(ex)includeに「タッチ」シチュエーションへの切り替えが含まれている場合:

class Position {
    private _x: number;
    private _y: number;

    public constructor(x: number = null, y: number = null) {
        this._x = x;
        this._y = y;
    }

    public get x() { return this._x; }
    public set x(value: number) { this._x = value; }

    public get y() { return this._y; }
    public set y(value: number) { this._y = value; }
}

class Polygon {
    private _positions: Array<Position>;

    public constructor(positions: Array<Position> = null) {
        this._positions = positions;
    }

    public addPosition(position: Position) {
        if (!position) {
            return;
        }

        if (!this._positions) {
            this._positions = new Array<Position>();
        }

        this._positions.Push(position);
    }

    public get positions(): ReadonlyArray<Position> { return this._positions; }

/**
 * https://stackoverflow.com/a/12414951/468910
 * 
 * Helper function to determine whether there is an intersection between the two polygons described
 * by the lists of vertices. Uses the Separating Axis Theorem
 *
 * @param polygonToCompare a polygon to compare with
 * @param allowTouch consider it an intersection when polygons only "touch"
 * @return true if there is any intersection between the 2 polygons, false otherwise
 */
  public isIntersecting(polygonToCompare: Polygon, allowTouch: boolean = true): boolean {
    const polygons: Array<ReadonlyArray<Position>> = [this.positions, polygonToCompare.positions]

    const firstPolygonPositions: ReadonlyArray<Position> = polygons[0];
    const secondPolygonPositions: ReadonlyArray<Position> = polygons[1];

    let minA, maxA, projected, i, i1, j, minB, maxB;

    for (i = 0; i < polygons.length; i++) {

        // for each polygon, look at each Edge of the polygon, and determine if it separates
        // the two shapes
        const polygon = polygons[i];
        for (i1 = 0; i1 < polygon.length; i1++) {

            // grab 2 vertices to create an Edge
            const i2 = (i1 + 1) % polygon.length;
            const p1 = polygon[i1];
            const p2 = polygon[i2];

            // find the line perpendicular to this Edge
            const normal = {
                x: p2.y - p1.y,
                y: p1.x - p2.x
            };

            minA = maxA = undefined;
            // for each vertex in the first shape, project it onto the line perpendicular to the Edge
            // and keep track of the min and max of these values
            for (j = 0; j < firstPolygonPositions.length; j++) {
                projected = normal.x * firstPolygonPositions[j].x + normal.y * firstPolygonPositions[j].y;

                if (!minA || projected < minA || (!allowTouch && projected === minA)) {
                    minA = projected;
                }

                if (!maxA || projected > maxA || (!allowTouch && projected === maxA)) {
                    maxA = projected;
                }
            }

            // for each vertex in the second shape, project it onto the line perpendicular to the Edge
            // and keep track of the min and max of these values
            minB = maxB = undefined;
            for (j = 0; j < secondPolygonPositions.length; j++) {
                projected = normal.x * secondPolygonPositions[j].x + normal.y * secondPolygonPositions[j].y;

                if (!minB || projected < minB || (!allowTouch && projected === minB)) {
                    minB = projected;
                }

                if (!maxB || projected > maxB || (!allowTouch && projected === maxB)) {
                    maxB = projected;
                }
            }

            // if there is no overlap between the projects, the Edge we are looking at separates the two
            // polygons, and we know there is no overlap
            if (maxA < minB || (!allowTouch && maxA === minB) || maxB < minA || (!allowTouch && maxB === minA)) {
                return false;
            }
        }
    }

    return true;
}
0
Bernoulli IT

これはLUAにあります。誰かがそれを必要とするときに役立つことを願っています。

function doPolygonsIntersect(a, b)
    local polygons = { a, b };
    local minA, maxA, projected, i, i1, j, minB, maxB;

    for i = 1, #polygons do
        --// for each polygon, look at each Edge of the polygon, and determine if it separates
        --// the two shapes
        local polygon = polygons[i];
        for i1 = 0, (#polygon-1) do
            --// grab 2 vertices to create an Edge
            local i2 = (i1 + 1) % (#polygon);
            local p1 = polygon[i1+1];
            local p2 = polygon[i2+1];

            --// find the line perpendicular to this Edge
            local normal = { x = p2.y - p1.y, y = p1.x - p2.x };
            minA = nil;
            maxA = nil;

            --// for each vertex in the first shape, project it onto the line perpendicular to the Edge
            --// and keep track of the min and max of these values
            for j = 1, #a do
                projected = normal.x * a[j].x + normal.y * a[j].y;
                if (minA == nil or projected < minA) then
                    minA = projected;
                end
                if (maxA == nil or projected > maxA) then
                    maxA = projected;
                end
            end

            --// for each vertex in the second shape, project it onto the line perpendicular to the Edge
            --// and keep track of the min and max of these values
            minB = nil;
            maxB = nil;
            for j = 1, #b do
                projected = normal.x * b[j].x + normal.y * b[j].y;
                if (minB == nil or projected < minB) then
                    minB = projected;
                end
                if (maxB == nil or projected > maxB) then
                    maxB = projected;
                end
            end

            if (maxA < minB or maxB < minA) then
                return false;
            end
        end
    end

    return true;
end
0
Wouter Willems

SriのJavaScriptを取得し、Phaser 3ポリゴンで動作するようにしました。

    /// Checks if the two Phaser 3 polygons are intersecting.
    gameScene.doPolygonsIntersect=function(a, b) {
        // https://stackoverflow.com/questions/10962379/how-to-check-intersection-between-2-rotated-rectangles#10965077
        /**
         * Helper function to determine whether there is an intersection between the two polygons described
         * by the lists of vertices. Uses the Separating Axis Theorem
         *
         * @param a an array of connected points [{x:, y:}, {x:, y:},...] that form a closed polygon
         * @param b an array of connected points [{x:, y:}, {x:, y:},...] that form a closed polygon
         * @return true if there is any intersection between the 2 polygons, false otherwise
         */

        var polygons = [a, b];
        var minA, maxA, projected, i, i1, j, minB, maxB;

        for (i = 0; i < polygons.length; i++) {

            // for each polygon, look at each Edge of the polygon, and determine if it separates
            // the two shapes
            var polygon = polygons[i];
            for (i1 = 0; i1 < polygon.points.length; i1++) {

                // grab 2 vertices to create an Edge
                var i2 = (i1 + 1) % polygon.points.length;
                var p1 = polygon.points[i1];
                var p2 = polygon.points[i2];

                // find the line perpendicular to this Edge
                var normal = { x: p2.y - p1.y, y: p1.x - p2.x };

                minA = maxA = undefined;
                // for each vertex in the first shape, project it onto the line perpendicular to the Edge
                // and keep track of the min and max of these values
                for (j = 0; j < a.points.length; j++) {
                    projected = normal.x * a.points[j].x + normal.y * a.points[j].y;
                    if (!isDef(minA) || projected < minA) {
                        minA = projected;
                    }
                    if (!isDef(maxA) || projected > maxA) {
                        maxA = projected;
                    }
                }

                // for each vertex in the second shape, project it onto the line perpendicular to the Edge
                // and keep track of the min and max of these values
                minB = maxB = undefined;
                for (j = 0; j < b.points.length; j++) {
                    projected = normal.x * b.points[j].x + normal.y * b.points[j].y;
                    if (!isDef(minB) || projected < minB) {
                        minB = projected;
                    }
                    if (!isDef(maxB) || projected > maxB) {
                        maxB = projected;
                    }
                }

                // if there is no overlap between the projects, the Edge we are looking at separates the two
                // polygons, and we know there is no overlap
                if (maxA < minB || maxB < minA) {
                    console.log("polygons don't intersect!");
                    return false;
                }
            }
        }
        return true;
    };
0
Joseph Delgado

Love2dフレームワークに組み込まれたLua実装。衝突検出機能はとにかく純粋なルアで動作します

math.inf = 1e309
function love.load()
    pol = {{0, 0}, {30, 2}, {8, 30}}
    pol2 = {{60, 60}, {90, 61}, {98, 100}, {80, 100}}
end
function love.draw()
    for k,v in ipairs(pol) do
        love.graphics.line(pol[k][1], pol[k][2], pol[k % #pol + 1][1], pol[k % #pol + 1][2])
    end
    for k,v in ipairs(pol2) do
        love.graphics.line(pol2[k][1], pol2[k][2], pol2[k % #pol2 + 1][1], pol2[k % #pol2 + 1][2])
    end
end

function love.update(dt)
    pol[1][1] = love.mouse.getX()
    pol[1][2] = love.mouse.getY()
    pol[2][1] = pol[1][1] + 30
    pol[2][2] = pol[1][2] + 2
    pol[3][1] = pol[1][1] + 8
    pol[3][2] = pol[1][2] + 30

    --lazy way to see that's function works
    print(doPolygonsIntersect(pol, pol2))
end
-------------------------------------------------------------------------
function doPolygonsIntersect(a,b)
polygons = {a,b}
for i=1, #polygons do
    polygon = polygons[i]
    for i1=1, #polygon do
        i2 = i1 % #polygon + 1
        p1 = polygon[i1]
        p2 = polygon[i2]

        nx,ny = p2[2] - p1[2], p1[1] - p2[1]

        minA = math.inf
        maxA = -math.inf
        for j=1, #a do
            projected = nx * a[j][1] + ny * a[j][2]
            if projected < minA then minA = projected end
            if projected > maxA then maxA = projected end
        end

        minB = math.inf
        maxB = -math.inf
        for j=1, #b do
            projected = nx * b[j][1] + ny * b[j][2]
            if projected < minB then minB = projected end
            if projected > maxB then maxB = projected end
        end

        if maxA < minB or maxB < minA then return false end
    end
end
return true
end
0
Papaew