web-dev-qa-db-ja.com

回転した長方形からバウンディングボックスの座標を計算する

長方形の左上の点の座標と、幅、高さ、0〜180および-0〜-180の回転があります。

私は長方形の周りの実際のボックスの境界座標を取得しようとしています。

境界ボックスの座標を計算する簡単な方法は何ですか

  • 最小y、最大y、最小x、最大x?

Aポイントは常に最小y境界にあるわけではなく、どこにでもあります。

必要に応じて、as3で変換ツールキットのマトリックスを使用できます。

67
coulix
  • 4つの角すべての座標を変換する
  • min_xとして4つすべてのxの最小値を見つけます
  • 4つのxすべての中で最大のものを見つけて、max_xと呼びます
  • Yの同上
  • バウンディングボックスは(min_x,min_y), (min_x,max_y), (max_x,max_y), (max_x,min_y)です

私の知る限り、あなたをより早くそこに導く王道はありません。

座標を変換する方法を知りたい場合は、次を試してください。

x2 = x0+(x-x0)*cos(theta)+(y-y0)*sin(theta)
y2 = y0-(x-x0)*sin(theta)+(y-y0)*cos(theta)

ここで、(x0、y0)は回転する中心です。三角関数(角度またはラジアンを想定)、座標系のセンス/符号、角度の指定方法などに応じて、これを調整する必要がある場合があります。

77
MarkusQ

あなたがActionScriptを求めていることを理解していますが、iOSまたはOS-Xの答えを探している人がここに来た場合、これは次のとおりです:

+ (CGRect) boundingRectAfterRotatingRect: (CGRect) rect toAngle: (float) radians
{
    CGAffineTransform xfrm = CGAffineTransformMakeRotation(radians);
    CGRect result = CGRectApplyAffineTransform (rect, xfrm);

    return result;
}

OSがすべてのハードワークを提供することを提案している場合は、それをしましょう! :)

迅速:

func boundingRectAfterRotatingRect(rect: CGRect, toAngle radians: CGFloat) -> CGRect {
    let xfrm = CGAffineTransformMakeRotation(radians)
    return CGRectApplyAffineTransform (rect, xfrm)
}
24
Olie

MarkusQによって概説された方法は完全に機能しますが、すでにポイントAがある場合、他の3つのコーナーを変換する必要はないことに留意してください。

より効率的な別の方法は、回転角度がどの象限に入っているかをテストし、単純に直接答えを計算することです。これは、2つのifステートメント(角度のチェック)のワーストケースしか持っていないので、効率的です現在の最小値以下).

以下に、ピタゴラスの定理の一連のアプリケーションを使用するだけの基本的なアルゴリズムを示します。回転角度をthetaで示し、擬似コードであるため、そこでのチェックを度で表しました。

ct = cos( theta );
st = sin( theta );

hct = h * ct;
wct = w * ct;
hst = h * st;
wst = w * st;

if ( theta > 0 )
{
    if ( theta < 90 )
    {
        // 0 < theta < 90
        y_min = A_y;
        y_max = A_y + hct + wst;
        x_min = A_x - hst;
        x_max = A_x + wct;
    }
    else
    {
        // 90 <= theta <= 180
        y_min = A_y + hct;
        y_max = A_y + wst;
        x_min = A_x - hst + wct;
        x_max = A_x;
    }
}
else
{
    if ( theta > -90 )
    {
        // -90 < theta <= 0
        y_min = A_y + wst;
        y_max = A_y + hct;
        x_min = A_x;
        x_max = A_x + wct - hst;
    }
    else
    {
        // -180 <= theta <= -90
        y_min = A_y + wst + hct;
        y_max = A_y;
        x_min = A_x + wct;
        x_max = A_x - hst;
    }
}

このアプローチでは、ポイントAと[-180、180]の範囲にあるthetaの値を持っていると言います。また、ダイアグラムで30度回転した長方形が使用していることを示すように見えるため、シータは時計回りに増加すると仮定しました。右側の部分が何を示しているのかわかりませんでした。これが間違った方法である場合は、対称句とst項の符号を入れ替えるだけです。

10
Troubadour
    fitRect: function( rw,rh,radians ){
            var x1 = -rw/2,
                x2 = rw/2,
                x3 = rw/2,
                x4 = -rw/2,
                y1 = rh/2,
                y2 = rh/2,
                y3 = -rh/2,
                y4 = -rh/2;

            var x11 = x1 * Math.cos(radians) + y1 * Math.sin(radians),
                y11 = -x1 * Math.sin(radians) + y1 * Math.cos(radians),
                x21 = x2 * Math.cos(radians) + y2 * Math.sin(radians),
                y21 = -x2 * Math.sin(radians) + y2 * Math.cos(radians), 
                x31 = x3 * Math.cos(radians) + y3 * Math.sin(radians),
                y31 = -x3 * Math.sin(radians) + y3 * Math.cos(radians),
                x41 = x4 * Math.cos(radians) + y4 * Math.sin(radians),
                y41 = -x4 * Math.sin(radians) + y4 * Math.cos(radians);

            var x_min = Math.min(x11,x21,x31,x41),
                x_max = Math.max(x11,x21,x31,x41);

            var y_min = Math.min(y11,y21,y31,y41);
                y_max = Math.max(y11,y21,y31,y41);

            return [x_max-x_min,y_max-y_min];
        }
6
bajie

gDI +を使用している場合、新しいGrpaphicsPathを作成できます->任意のポイントまたはシェイプを追加します->回転変換を適用します-> GraphicsPath.GetBounds()を使用すると、回転したシェイプを囲む長方形が返されます。

(編集)VB.Netサンプル

Public Shared Sub RotateImage(ByRef img As Bitmap, degrees As Integer)
' http://stackoverflow.com/questions/622140/calculate-bounding-box-coordinates-from-a-rotated-rectangle-picture-inside#680877
'
Using gp As New GraphicsPath
  gp.AddRectangle(New Rectangle(0, 0, img.Width, img.Height))

  Dim translateMatrix As New Matrix
  translateMatrix.RotateAt(degrees, New PointF(img.Width \ 2, img.Height \ 2))
  gp.Transform(translateMatrix)

  Dim gpb = gp.GetBounds

  Dim newwidth = CInt(gpb.Width)
  Dim newheight = CInt(gpb.Height)

  ' http://www.codeproject.com/Articles/58815/C-Image-PictureBox-Rotations
  '
  Dim rotatedBmp As New Bitmap(newwidth, newheight)

  rotatedBmp.SetResolution(img.HorizontalResolution, img.VerticalResolution)

  Using g As Graphics = Graphics.FromImage(rotatedBmp)
    g.Clear(Color.White)
    translateMatrix = New Matrix
    translateMatrix.Translate(newwidth \ 2, newheight \ 2)
    translateMatrix.Rotate(degrees)
    translateMatrix.Translate(-img.Width \ 2, -img.Height \ 2)
    g.Transform = translateMatrix
    g.DrawImage(img, New PointF(0, 0))
  End Using
  img.Dispose()
  img = rotatedBmp
End Using

サブ終了

3
Bishoy

Code GuruはGetBounds()メソッドを述べましたが、質問にas3、flexというタグが付けられていることに気付きました。そのため、ここにアイデアを示すas3スニペットがあります。

var box:Shape = new Shape();
box.graphics.beginFill(0,.5);
box.graphics.drawRect(0,0,100,50);
box.graphics.endFill();
box.rotation = 20;
box.x = box.y = 100;
addChild(box);

var bounds:Rectangle = box.getBounds(this);

var boundingBox:Shape = new Shape();
boundingBox.graphics.lineStyle(1);
boundingBox.graphics.drawRect(bounds.x,bounds.y,bounds.width,bounds.height);
addChild(boundingBox);

同じことを行うように見える2つのメソッドがあることに気付きました:getBounds()とgetRect()

2
George Profenza
/**
     * Applies the given transformation matrix to the rectangle and returns
     * a new bounding box to the transformed rectangle.
     */
    public static function getBoundsAfterTransformation(bounds:Rectangle, m:Matrix):Rectangle {
        if (m == null) return bounds;

        var topLeft:Point = m.transformPoint(bounds.topLeft);
        var topRight:Point = m.transformPoint(new Point(bounds.right, bounds.top));
        var bottomRight:Point = m.transformPoint(bounds.bottomRight);
        var bottomLeft:Point = m.transformPoint(new Point(bounds.left, bounds.bottom));

        var left:Number = Math.min(topLeft.x, topRight.x, bottomRight.x, bottomLeft.x);
        var top:Number = Math.min(topLeft.y, topRight.y, bottomRight.y, bottomLeft.y);
        var right:Number = Math.max(topLeft.x, topRight.x, bottomRight.x, bottomLeft.x);
        var bottom:Number = Math.max(topLeft.y, topRight.y, bottomRight.y, bottomLeft.y);
        return new Rectangle(left, top, right - left, bottom - top);
    }
2
Nick Bilyk

私のオープンソースライブラリの3つの関数を次に示します。関数はJavaで完全にテストされていますが、式は任意の言語に簡単に翻訳できます。

署名は次のとおりです。

public static float getAngleFromPoint(final Point centerPoint、final Point touchPoint)

public static float getTwoFingerDistance(float firstTouchX、float firstTouchY、float secondTouchX、float secondTouchY)

ポイントgetPointFromAngle(最終二重角、最終二重半径)

このソリューションでは、ピクセル密度が等間隔であると想定しています。オブジェクトを回転する前に、次を実行します。

  1. GetAngleFromPointを使用して、中心から右上隅までの角度を計算します(これは20度を返します)。つまり、左上隅が-20度または340度であることを意味します。

  2. GetTwoFingerDistanceを使用して、中心点と右上隅との間の対角距離を返します(この距離はすべてのコーナーで明らかに同じになるはずです。この距離は次の計算で使用されます)。

  3. 次に、オブジェクトを時計回りに30度回転させます。これで、右上隅が50度、左上隅が10度でなければならないことがわかりました。

  4. これで、左上隅と右上隅でgetPointFromAngle関数を使用できるようになります。手順2から返された半径を使用します。右上隅から2倍したX位置に新しい幅が与えられ、左上隅からY位置に2を掛けた値が新しい高さになります。

上記の4つのステップは、オブジェクトを他の方向にどれだけ回転させたかに基づいて、高さを幅として、幅を高さとして返す条件に入れる必要があります。

角度関数は0-360ではなく0-1の係数で表されます(必要に応じて360で乗算または除算します):

// 0 -1の係数として表される2点から角度を取得します(0は0/360、0.25は90度など)

public float getAngleFromPoint(final Point centerPoint, final Point touchPoint) {

    float returnVal = 0;

    //+0 - 0.5
    if(touchPoint.x > centerPoint.x) {

        returnVal = (float) (Math.atan2((touchPoint.x - centerPoint.x), (centerPoint.y - touchPoint.y)) * 0.5 / Math.PI);

    }
    //+0.5
    else if(touchPoint.x < centerPoint.x) {

        returnVal = (float) (1 - (Math.atan2((centerPoint.x - touchPoint.x), (centerPoint.y - touchPoint.y)) * 0.5 / Math.PI));

    }//End if(touchPoint.x > centerPoint.x)

    return returnVal;

}

// 2点間の対角距離を測定します

public float getTwoFingerDistance(final float firstTouchX, final float firstTouchY, final float secondTouchX, final float secondTouchY) {

    float pinchDistanceX = 0;
    float pinchDistanceY = 0;

    if(firstTouchX > secondTouchX) {

        pinchDistanceX = Math.abs(secondTouchX - firstTouchX);

    }
    else if(firstTouchX < secondTouchX) {

        pinchDistanceX = Math.abs(firstTouchX - secondTouchX);

    }//End if(firstTouchX > secondTouchX)

    if(firstTouchY > secondTouchY) {

        pinchDistanceY = Math.abs(secondTouchY - firstTouchY);

    }
    else if(firstTouchY < secondTouchY) {

        pinchDistanceY = Math.abs(firstTouchY - secondTouchY);

    }//End if(firstTouchY > secondTouchY)

    if(pinchDistanceX == 0 && pinchDistanceY == 0) {

        return 0;

    }
    else {

        pinchDistanceX = (pinchDistanceX * pinchDistanceX);
        pinchDistanceY = (pinchDistanceY * pinchDistanceY);
        return (float) Math.abs(Math.sqrt(pinchDistanceX + pinchDistanceY));

    }//End if(pinchDistanceX == 0 && pinchDistanceY == 0)

}

//与えられた半径から角度からXY座標を取得します(角度は0/360度である0〜1 0、270である0.75などの係数で表されます)

public Point getPointFromAngle(final double angle, final double radius) {

    final Point coords = new Point();
    coords.x = (int) (radius * Math.sin((angle) * 2 * Math.PI));
    coords.y = (int) -(radius * Math.cos((angle) * 2 * Math.PI));

    return coords;

}

これらのコードスニペットは、私のオープンソースライブラリのものです。 https://bitbucket.org/warwick/hgdialrepo および https://bitbucket.org/warwick/hacergestov2 。 1つはAndroidのジェスチャライブラリで、もう1つはAndroidのダイヤルコントロールです。また、ダイヤルコントロールのOpenGLES 2.0実装は次の場所にあります。 https://bitbucket.org/warwick/hggldial

1
user2288580

回転マトリックスをコーナーポイントに適用します。次に、取得したx、y座標のそれぞれ最小値/最大値を使用して、新しい境界ボックスを定義します。

1
ypnos

理解できるかどうかはわかりませんが、複合変換マトリックスは、関係するすべてのポイントの新しい座標を提供します。変換後、長方形が画像化可能領域上にあふれる可能性があると思われる場合は、クリッピングパスを適用します。

マトリックスの正確な定義に慣れていない場合は、 here を見てください。

0
dirkgently

最初に長方形を回転させるために領域を使用し、次にその回転した領域を使用してその長方形を検出します

        r = new Rectangle(new Point(100, 200), new Size(200, 200));         
        Color BorderColor = Color.WhiteSmoke;
        Color FillColor = Color.FromArgb(66, 85, 67);
        int angle = 13;
        Point pt = new Point(r.X, r.Y);
        PointF rectPt = new PointF(r.Left + (r.Width / 2),
                               r.Top + (r.Height / 2));
       //declare myRegion globally 
        myRegion = new Region(r);

        // Create a transform matrix and set it to have a 13 degree

        // rotation.
        Matrix transformMatrix = new Matrix();
        transformMatrix.RotateAt(angle, pt);

        // Apply the transform to the region.
        myRegion.Transform(transformMatrix);
        g.FillRegion(Brushes.Green, myRegion);
        g.ResetTransform();

今、その長方形を検出する

        private void panel_MouseMove(object sender, MouseEventArgs e)
    {


        Point point = e.Location;
        if (myRegion.IsVisible(point, _graphics))
        {
            // The point is in the region. Use an opaque brush.
            this.Cursor = Cursors.Hand;
        }
        else {
            this.Cursor = Cursors.Cross;
        }

    }
0
Abdulrehman