webentwicklung-frage-antwort-db.com.de

Berechnen Sie die Begrenzungsrahmenkoordinaten aus einem gedrehten Rechteck

Ich habe die Koordinaten des oberen linken Punkts eines Rechtecks ​​sowie seine Breite, Höhe und Drehung von 0 bis 180 und -0 bis -180.

Ich versuche, die Begrenzungskoordinaten der tatsächlichen Box um das Rechteck zu erhalten.

Wie lassen sich die Koordinaten des Begrenzungsrahmens auf einfache Weise berechnen? 

  • Miny, maxy, minx, maxx?

Der A-Punkt befindet sich nicht immer an der Mindestgrenze, er kann überall sein.

Ich kann das Transformations-Toolkit in as3 verwenden, wenn nötig.

60
coulix
  • Transformieren Sie die Koordinaten aller vier Ecken
  • Finde das kleinste aller vier x als min_x 
  • Finde die größte aller vier x und nenne sie max_x
  • Dito mit den y's
  • Ihr Begrenzungsrahmen lautet (min_x,min_y), (min_x,max_y), (max_x,max_y), (max_x,min_y)

AFAIK, es gibt keine königliche Straße, die Sie schneller dorthin bringt.

Wenn Sie sich fragen, wie Sie die Koordinaten transformieren können, versuchen Sie Folgendes:

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

dabei ist (x0, y0) der Mittelpunkt, um den Sie sich drehen. Möglicherweise müssen Sie dies abhängig von Ihren Triggerfunktionen (erwarten Sie Grad oder Radiant), dem Sinn/Vorzeichen Ihres Koordinatensystems oder der Winkeleinstellung usw.

71
MarkusQ

Mir ist klar, dass Sie nach ActionScript fragen, aber für den Fall, dass jemand hier nach der Antwort von iOS oder OS-X sucht, ist dies Folgendes:

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

    return result;
}

Wenn Ihr Betriebssystem all die harte Arbeit für Sie erledigt, lassen Sie es! :)

Schnell:

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

Die von MarkusQ beschriebene Methode funktioniert perfekt. Denken Sie jedoch daran, dass Sie die anderen drei Ecken nicht umwandeln müssen, wenn Sie bereits über Punkt A verfügen.

Eine alternative Methode, die effizienter ist, besteht darin, zu testen, in welchem ​​Quadranten sich Ihr Drehwinkel befindet, und die Antwort dann direkt zu berechnen. Dies ist effizienter, da Sie nur einen Worst-Case von zwei if-Anweisungen (Winkelprüfung) haben, während der andere Ansatz einen Worst-Case von zwölf hat (6 für jede Komponente, wenn Sie die anderen drei Ecken prüfen, ob sie größer als der aktuelle Wert sind.) max oder weniger als die aktuelle min) denke ich.

Der grundlegende Algorithmus, der nichts anderes als eine Reihe von Anwendungen des Satzes von Pythagoras verwendet, ist unten dargestellt. Ich habe den Drehwinkel mit Theta bezeichnet und die Überprüfung dort in Grad ausgedrückt, da es sich um einen Pseudocode handelt.

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

Bei diesem Ansatz wird davon ausgegangen, dass Sie über das verfügen, was Sie sagen, d. H. Punkt A und einen Wert für Theta, der im Bereich [-180, 180] liegt. Ich bin auch davon ausgegangen, dass Theta im Uhrzeigersinn zunimmt, da das in Ihrem Diagramm um 30 Grad gedrehte Rechteck darauf hinweist, dass Sie es verwenden. Wenn dies der falsche Weg ist, tauschen Sie einfach die symmetrischen Klauseln und auch das Vorzeichen der st-Ausdrücke aus.

8
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

wenn Sie GDI + verwenden, können Sie einen neuen GrpaphicsPath erstellen -> Punkte oder Formen hinzufügen -> Rotationstransformation anwenden -> GraphicsPath.GetBounds () verwenden. Es wird ein Rechteck zurückgegeben, das Ihre gedrehte Form begrenzt.

(edit) VB.Net-Beispiel

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

3
Bishoy

Obwohl Code Guru die GetBounds () -Methode angegeben hat, ist mir aufgefallen, dass die Frage mit "as3, flex" markiert wurde. Hier ist ein as3-Snippet, der die Idee veranschaulicht.

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

Ich habe festgestellt, dass es zwei Methoden gibt, die dasselbe zu tun scheinen: getBounds () und 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

Hier sind drei Funktionen aus meinen Open Source-Bibliotheken. Die Funktionen sind vollständig in Java getestet, die Formeln können jedoch problemlos in jede Sprache übersetzt werden.

Die Unterschriften sind:

öffentlicher statischer Float getAngleFromPoint (final Point centerPoint, final Point touchPoint)

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

Point getPointFromAngle (endgültiger Doppelwinkel, letzter Doppelradius)

Bei dieser Lösung wird davon ausgegangen, dass die Pixeldichte gleichmäßig verteilt ist. Bevor Sie das Objekt drehen, gehen Sie wie folgt vor:

  1. Verwenden Sie getAngleFromPoint, um den Winkel von der Mitte zur oberen rechten Ecke zu berechnen (etwa 20 Grad). Dies bedeutet, dass die linke obere Ecke -20 Grad oder 340 Grad beträgt.

  2. Verwenden Sie getTwoFingerDistance, um den diagonalen Abstand zwischen dem Mittelpunkt und der rechten oberen Ecke zurückzugeben (dieser Abstand sollte zwingend zu allen Ecken gleich sein. Dieser Abstand wird in der nächsten Berechnung verwendet).

  3. Nehmen wir an, wir drehen das Objekt um 30 Grad im Uhrzeigersinn. Wir wissen jetzt, dass die obere rechte Ecke 50 Grad und die obere linke Ecke 10 Grad betragen muss.

  4. Sie sollten jetzt in der Lage sein, die Funktion getPointFromAngle in der oberen linken und rechten oberen Ecke zu verwenden. Verwenden Sie den von Schritt 2 zurückgegebenen Radius ..__ Die X-Position multipliziert mit 2 aus der oberen rechten Ecke sollte die neue Breite und die Y-Position mal 2 aus der oberen linken Ecke die neue Höhe ergeben.

Diese 4 Schritte sollten in Abhängigkeit von der Position des Objekts gestellt werden, in dem Sie das Objekt gedreht haben. In anderen Fällen können Sie die Höhe als Breite und die Breite als Höhe angeben.

Unwägend, die Winkelfunktionen werden in Faktoren von 0-1 anstelle von 0-360 ausgedrückt (einfach multiplizieren oder durch 360 teilen, wo dies angebracht ist):

// Ruft einen Winkel von zwei Punkten ab, ausgedrückt als Faktor 0 -1 (0 ist 0/360, 0,25 ist 90 Grad usw.)

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;

}

// Misst den diagonalen Abstand zwischen zwei Punkten

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-Koordinaten aus einem Winkel mit einem bestimmten Radius erhalten (Der Winkel wird in einem Faktor von 0-1 0 ausgedrückt, der 0/360 Grad und 0,75 ein Wert von 270 usw. ist)

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;

}

Diese Codeausschnitte stammen aus meinen Open-Source-Bibliotheken: https://bitbucket.org/warwick/hgdialrepo und https://bitbucket.org/warwick/hacergestov2 . Eine ist eine Gestenbibliothek für Android und die andere ist eine Wählscheibe für Android. Es gibt auch eine OpenGLES 2.0-Implementierung der Wählsteuerung unter: https://bitbucket.org/warwick/hggldial

1
user2288580

Wenden Sie die Rotationsmatrix auf Ihre Eckpunkte an. Verwenden Sie dann das Minimum bzw. Maximum der erhaltenen XY-Koordinaten, um Ihren neuen Begrenzungsrahmen zu definieren.

1
ypnos

Ich bin nicht sicher, ob ich es verstehe, aber eine zusammengesetzte Transformationsmatrix gibt Ihnen die neuen Koordinaten für alle betroffenen Punkte. Wenn Sie der Meinung sind, dass sich das Rechteck nach der Transformation über den Bildbereich ausbreitet, wenden Sie einen Beschneidungspfad an.

Falls Sie mit der genauen Definition der Matrizen nicht vertraut sind, werfen Sie einen Blick auf hier .

0
dirkgently

Ich habe die Region zum ersten Drehen des Rechtecks ​​und dann die gedrehte Region zum Erkennen des Rechtecks ​​verwendet

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

jetzt das Rechteck zu erkennen

        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