Programing

회전 된 직사각형에서 경계 상자 좌표 계산

crosscheck 2020. 11. 8. 09:18
반응형

회전 된 직사각형에서 경계 상자 좌표 계산


직사각형의 왼쪽 상단 지점의 좌표와 너비, 높이 및 회전이 0에서 180까지, -0에서 -180까지입니다.

사각형 주위의 실제 상자의 경계 좌표를 얻으려고합니다.

경계 상자의 좌표를 계산하는 간단한 방법은 무엇입니까

  • 최소 y, 최대 y, 최소 x, 최대 x?

A 지점은 항상 최소 y 경계에있는 것은 아니며 어디든있을 수 있습니다.

필요한 경우 as3에서 변환 툴킷 매트릭스를 사용할 수 있습니다.


  • 네 모서리의 좌표를 모두 변환
  • 4 개의 x 중 가장 작은 것을 다음과 같이 찾으십시오. min_x
  • 4 개의 x 중 가장 큰 것을 찾아서 max_x
  • Y와 마찬가지로
  • 경계 상자는 (min_x,min_y), (min_x,max_y), (max_x,max_y), (max_x,min_y)

AFAIK, 당신을 훨씬 더 빨리 데려다 줄 왕도는 없습니다.

좌표를 변환하는 방법이 궁금하다면 다음을 시도하십시오.

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

여기서 (x0, y0)은 회전하는 중심입니다. 삼각 함수 (도 또는 라디안을 예상 하는가)에 따라 좌표계의 감각 / 기호 대 각도를 지정하는 방법 등에 따라이를 수정해야 할 수도 있습니다.


나는 당신이 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)
}

MarkusQ에서 설명한 방법은 완벽하게 작동하지만 점 A가 이미있는 경우 다른 세 모서리를 변형 할 필요가 없다는 점을 명심하십시오.

더 효율적인 대안은 회전 각도가 어느 사분면에 있는지 테스트 한 다음 간단히 답을 직접 계산하는 것입니다. 두 개의 if 문 (각도 확인)의 최악의 경우 만있는 반면 다른 접근 방식에서는 최악의 경우 12 개 (다른 세 모서리를 확인하여 현재보다 큰지 확인하는 경우 각 구성 요소에 대해 6 개)가 있기 때문에 더 효율적입니다. 최대 또는 현재 최소 이하) 생각합니다.

피타고라스 정리의 일련의 응용 프로그램 만 사용하는 기본 알고리즘은 다음과 같습니다. 나는 세타로 회전 각도를 표시하고 의사 코드이므로 거기에 수표를 표시했습니다.

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

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

if ( theta > 0 )
{
    if ( theta < 90 degrees )
    {
        // 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]에있는 세타에 대한 값이 있습니다. 또한 세타가 시계 방향으로 증가한다고 가정했습니다. 다이어그램에서 30도 회전 한 사각형이 사용 중임을 나타내는 것처럼 보이므로 오른쪽 부분이 무엇을 나타내려고하는지 잘 모르겠습니다. 이것이 잘못된 방법이라면 대칭 절과 st 항의 부호를 바꾸십시오.


    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];
        }

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

End Sub


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);

동일한 작업을 수행하는 것으로 보이는 두 가지 메서드 인 getBounds () 및 getRect ()를 발견했습니다.


/**
     * 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);
    }

코너 포인트에 회전 매트릭스를 적용합니다. 그런 다음 얻은 x, y 좌표의 최소 / 최대를 각각 사용하여 새 경계 상자를 정의합니다.


다음은 내 오픈 소스 라이브러리의 세 가지 기능입니다. 함수는 Java에서 완전히 테스트되었지만 공식은 모든 언어로 쉽게 번역 될 수 있습니다.

서명은 다음과 같습니다.

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

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

Point getPointFromAngle (최종 이중 각도, 최종 이중 반경)

이 솔루션은 픽셀 밀도의 간격이 균등하다고 가정합니다. 개체를 회전하기 전에 다음을 수행하십시오.

  1. getAngleFromPoint를 사용하여 중앙에서 오른쪽 상단 모서리까지의 각도를 계산합니다 (20도를 반환한다고 가정). 즉, upp 왼쪽 모서리가 -20도 또는 340 도임을 의미합니다.

  2. getTwoFingerDistance를 사용하여 중심점과 오른쪽 상단 모서리 사이의 대각선 거리를 반환합니다 (이 거리는 모든 모서리에서 분명히 동일해야합니다.이 거리는 다음 계산에 사용됩니다).

  3. 이제 개체를 시계 방향으로 30도 회전한다고 가정 해 보겠습니다. 이제 오른쪽 위 모서리가 50도에 있어야하고 왼쪽 위 모서리가 10도에 있어야한다는 것을 알고 있습니다.

  4. 이제 왼쪽 상단과 오른쪽 상단에서 getPointFromAngle 함수를 사용할 수 있습니다. 2 단계에서 반환 된 반경을 사용합니다. X 위치에 오른쪽 상단 모서리에서 2를 곱하면 새 너비가 제공되고 Y 위치에 왼쪽 상단 모서리에서 2를 곱하면 새 높이가됩니다.

위의 4 단계는 개체를 얼마나 멀리 회전했는지에 따라 조건을 설정해야합니다. 그렇지 않으면 높이를 너비로, 너비를 높이로 반환 할 수 있습니다.

각도 함수는 0-360 대신 0-1의 요소로 표현된다는 점을 염두에 두십시오 (적절한 경우 360으로 곱하거나 나누기 만하면됩니다).

// 0-1의 계수로 표현 된 두 점에서 각도를 가져옵니다 (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;

}

// 두 점 사이의 대각선 거리를 측정합니다.

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)

}

//Get XY coordinates from an angle given a radius (The angle is expressed in a factor of 0-1 0 being 0/360 degrees and 0.75 being 270 etc)

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;

}

These code snippets are from my open source libraries: https://bitbucket.org/warwick/hgdialrepo and https://bitbucket.org/warwick/hacergestov2. One is a gesture library for Android and the other is a dial control for Android. There is also an OpenGLES 2.0 implementation of the dial control at: https://bitbucket.org/warwick/hggldial


I am not sure I understand, but a compound transformation matrix will give you the new co-ordinates for all points concerned. If you think the rectangle may spill over the imagable area post transformation apply a clipping path.

In case you are unfamiliar with the exact definition of the matrices take a look here.


I used Region for First rotating the rectangle and then use that rotated region to detect that rectangle

        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();

now to detecting that rectangle

        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;
        }

    }

참고URL : https://stackoverflow.com/questions/622140/calculate-bounding-box-coordinates-from-a-rotated-rectangle

반응형