Berechnen Sie die Koordinaten der Begrenzungsrahmen aus einem gedrehten Rechteck


72

Ich habe die Koordinaten des oberen linken Punktes eines Rechtecks ​​sowie dessen 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.

Was ist eine einfache Methode zur Berechnung der Koordinaten des Begrenzungsrahmens?

  • Min y, max y, min x, max x?

Der A-Punkt ist nicht immer auf der Min-Grenze, er kann überall sein.

Bei Bedarf kann ich das Transformations-Toolkit in as3 als Matrix verwenden.


1
Bild (Bild) ist nicht sichtbar .. (Das Bild sagt: Klicken und entdecken Sie Imageshack) !!!
Pratham

Richtig, ich frage mich, ob ich es aus dem Google-Archiv oder so zurückbekommen kann.
Coulix

1
Was ist der A-Punkt?
Xhark

Antworten:


81
  • Transformieren Sie die Koordinaten aller vier Ecken
  • Finden Sie das kleinste aller vier x als min_x
  • Finde das größte aller vier x und nenne es max_x
  • Das Gleiche gilt für die ys
  • Ihr Begrenzungsrahmen ist (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 viel schneller dorthin bringt.

Wenn Sie sich fragen, wie Sie die Koordinaten transformieren sollen, 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) das Zentrum, um das Sie sich drehen. Abhängig von Ihren Triggerfunktionen (erwarten sie Grad oder Bogenmaß) müssen Sie möglicherweise den Sinn / das Vorzeichen Ihres Koordinatensystems im Vergleich zur Angabe von Winkeln usw. basteln.


In der Tat hat die Verwendung von Matrizen für alle Ecken und deren Vergleich den Job gemacht, danke!
Coulix

3
Eigentlich aufgrund der Symmetrie, müssen Sie nur zwei Ecken zu transformieren, und wenn Sie ein wenig zusätzlichen Gedanken geben, ist es nur 1 Ecke zu drehen.
Ysap

1
@ysap Dies gilt nur für den Fall, dass Sie sich um die Mitte des Rechtecks ​​drehen.
Sidon

@ Sidon - das ist wahr. So machen es jedoch die meisten Zeichenprogramme.
Ysap

5
@MarkusQ Die Gleichung, die Sie für x2 erwähnen, sollte x0 + (x-x0) * cos (Theta) - (y-y0) * sin (Theta) anstelle von x0 + (x-x0) * cos (Theta) + (y-y0) sein ) * sin (theta), habe ich recht?
Mihir

26

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

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

    return result;
}

Wenn Ihr Betriebssystem anbietet, die ganze harte Arbeit für Sie zu erledigen, lassen Sie es! :) :)

Schnell:

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

2
Wollte hinzufügen, dass der Winkel im Bogenmaß sein muss, nicht in Grad. Kann Ihnen Zeit sparen. ;)
Thomas Johannesmeyer

12

Die von MarkusQ beschriebene Methode funktioniert einwandfrei. Beachten Sie jedoch, dass Sie die anderen drei Ecken nicht transformieren müssen, wenn Sie bereits Punkt A haben.

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

Der grundlegende Algorithmus, der nur eine Reihe von Anwendungen des Satzes von Pythagoras verwendet, ist unten dargestellt. Ich habe den Drehwinkel mit Theta bezeichnet und die Prüfung dort in Grad als Pseudocode ausgedrückt.

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 das haben, was Sie sagen, dh Punkt A und einen Wert für Theta, der im Bereich [-180, 180] liegt. Ich habe auch angenommen, dass Theta im Uhrzeigersinn zunimmt, da das Rechteck, das in Ihrem Diagramm um 30 Grad gedreht wurde, darauf hinweist, dass Sie es verwenden. Ich war mir nicht sicher, was der Teil rechts zu bezeichnen versuchte. Wenn dies falsch herum ist, tauschen Sie einfach die symmetrischen Klauseln und auch das Vorzeichen der st-Begriffe aus.


Ich weiß, dass dies sehr alt ist, aber es ist ein erster Treffer für Google, also werde ich hier bemerken: Wenden Sie einfach die Skala auf das h und w an, und es sollte gut funktionieren. Diese Antwort ist derzeit mein Favorit für dieses Problem, da sie die Berechnung so gut in kleine Teile aufteilt. 2 Zweige und mit etwas NEON-Magie in iOS, 4-6 Operationen oder weniger, je nachdem, wie knifflig Sie werden.
Diniden

Die Leute bearbeiten die "Grade" in der zweiten if-Anweisung immer wieder falsch. Dies ist Pseudocode, daher wird er niemals kompiliert. Der Zweck der expliziten Angabe von "Abschlüssen" besteht darin, dass thetaes sich eindeutig nicht um Abschlüsse handelt. Es dient als Erinnerung bei der Implementierung des richtigen Arbeitscodes, um die Winkeleinheiten korrekt zu machen.
Troubadour

Ok, gut , dann diese Verwirrung zu vermeiden , wäre es hilfreich, wenn Sie alle der if - Anweisungen so ändern, dass sie waren if (theta > 0 degrees)und if (theta > -90 degrees)so , dass sie im Einklang steht. Die Inkonsistenz fordert die Benutzer auf, sie zu bearbeiten.
wcmatthysen

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

3

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

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


2

Obwohl Code Guru die GetBounds () -Methode angegeben hat, ist mir aufgefallen, dass die Frage als 3, flex markiert ist. Hier ist ein as3-Snippet, das 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);

Mir ist aufgefallen, dass es zwei Methoden gibt, die dasselbe zu tun scheinen: getBounds () und getRect ()


getRect führt die gleiche Operation wie getBounds aus, jedoch abzüglich des Strichs für ein Objekt: help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/…
Danny Parker

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

1
In dem Problem sagt der Benutzer, dass er eine Drehung in Grad hat. Ihre Lösung erfordert eine Transformationsmatrix. Wie geht man von einer Rotation in Grad zu einer Rotationstransformationsmatrix?
pgreen2

1

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


1

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

Die Unterschriften sind:

public static float getAngleFromPoint (Endpunkt centerPoint, Endpunkt touchPoint)

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

Punkt getPointFromAngle (endgültiger Doppelwinkel, endgültiger Doppelradius)

Diese Lösung setzt voraus, dass die Pixeldichte gleichmäßig verteilt ist. Gehen Sie vor dem Drehen des Objekts wie folgt vor:

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

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

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

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

Diese obigen 4 Schritte sollten in Bedingungen versetzt werden, die davon abhängen, wie weit Sie Ihr Objekt gedreht haben. Andernfalls können Sie die Höhe als Breite und die Breite als Höhe zurückgeben.

Beachten Sie jedoch, dass die Winkelfunktionen in Faktoren von 0-1 anstelle von 0-360 ausgedrückt werden (multiplizieren oder dividieren Sie sie gegebenenfalls durch 360):

// 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 abrufen (Der Winkel wird in einem Faktor von 0-1 0 ausgedrückt, der 0/360 Grad und 0,75 ist, 270 usw.)

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 Codefragmente 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ählsteuerung für Android. Es gibt auch eine OpenGLES 2.0-Implementierung der Wählsteuerung unter: https://bitbucket.org/warwick/hggldial


0

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

Falls Sie mit der genauen Definition der Matrizen nicht vertraut sind, schauen Sie hier .


0

Ich habe Region verwendet, um zuerst das Rechteck zu drehen, und dann diesen gedrehten Bereich, um dieses Rechteck zu erkennen

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

Nun zum Erkennen dieses Rechtecks

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

    }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.