web-dev-qa-db-fra.com

Calculer les coordonnées du cadre de sélection à partir d'un rectangle en rotation

J'ai les coordonnées du point en haut à gauche d'un rectangle ainsi que sa largeur, sa hauteur et sa rotation de 0 à 180 et de -0 à -180.

J'essaie d'obtenir les coordonnées de délimitation de la boîte réelle autour du rectangle.

Quel est un moyen simple de calculer les coordonnées du cadre de sélection 

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

Le point A n'est pas toujours au minimum, il peut être n'importe où.

Je peux utiliser la matrice du toolkit de transformation dans as3 si nécessaire.

60
coulix
  • Transformer les coordonnées des quatre coins
  • Trouvez le plus petit des quatre x en tant que min_x 
  • Trouvez le plus grand des quatre x et appelez-le max_x
  • Idem avec les y
  • Votre cadre de sélection est (min_x,min_y), (min_x,max_y), (max_x,max_y), (max_x,min_y)

Autant que je sache, il n’ya pas de route royale qui vous mènera beaucoup plus vite.

Si vous vous demandez comment transformer les coordonnées, essayez:

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

où (x0, y0) est le centre autour duquel vous faites pivoter. En fonction de vos fonctions trigonométriques (attendent-elles des degrés ou des radians), le sens/signe de votre système de coordonnées par rapport à la manière dont vous spécifiez les angles, etc.

71
MarkusQ

Je me rends compte que vous demandez ActionScript, mais au cas où quelqu'un arriverait ici à la recherche de la réponse iOS ou OS-X, voici ceci:

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

    return result;
}

Si votre système d'exploitation vous propose de faire tout le travail difficile pour vous, laissez-le! :)

Rapide:

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

La méthode décrite par MarkusQ fonctionne parfaitement, mais gardez à l'esprit qu'il n'est pas nécessaire de transformer les trois autres coins si vous avez déjà le point A.

Une méthode alternative, plus efficace, consiste à tester le quadrant dans lequel se trouve votre angle de rotation et à calculer la réponse directement. C’est plus efficace car vous n’avez que le cas le plus défavorable de deux déclarations if (vérification de l’angle) alors que l’autre approche a un cas pire de douze (6 pour chaque composant lorsqu’il vérifie les trois autres coins pour voir s’ils sont supérieurs au courant max ou inférieur au min actuel) je pense.

L'algorithme de base, qui n'utilise rien de plus qu'une série d'applications du théorème de Pythagore, est présenté ci-dessous. J'ai noté l'angle de rotation par thêta et exprimé le contrôle en degrés, car il s'agit d'un pseudo-code.

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

Cette approche suppose que vous avez ce que vous dites avoir, c'est-à-dire le point A et une valeur pour thêta comprise dans l'intervalle [-180, 180]. J'ai également supposé que thêta augmente dans le sens des aiguilles d'une montre, car c'est ce que semble indiquer l'utilisation du rectangle qui a été pivoté de 30 degrés dans votre diagramme. Je ne savais pas exactement ce que la partie de droite essayait de représenter. Si ce n'est pas le cas, il suffit d'échanger les clauses symétriques et le signe des premiers termes.

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

si vous utilisez GDI +, vous pouvez créer un nouveau GrpaphicsPath -> Ajouter des points ou des formes à celui-ci -> Appliquer une transformation de rotation -> utiliser GraphicsPath.GetBounds () et il retournera un rectangle qui limite votre forme pivotée.

(edit) Exemple 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

3
Bishoy

Bien que Code Guru ait déclaré la méthode GetBounds (), j'ai remarqué que la question était marquée comme 3, flex, voici donc un extrait as3 qui illustre l'idée.

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

J'ai remarqué qu'il y a deux méthodes qui semblent faire la même chose: getBounds () et 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

Voici trois fonctions de mes bibliothèques open source. Les fonctions sont entièrement testées en Java mais les formules peuvent être facilement traduites dans n'importe quelle langue.

Les signatures sont:

getAngleFromPoint public (float statique final (PointPoint final, Point touchPoint final))

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

Point getPointFromAngle (double angle final, double rayon final)

Cette solution suppose que la densité de pixels est régulièrement espacée . Avant de faire pivoter l'objet, procédez comme suit:

  1. Utilisez getAngleFromPoint pour calculer l'angle entre le centre et le coin supérieur droit (disons que cela retourne 20 degrés), ce qui signifie que le coin supérieur gauche est égal à -20 degrés ou 340 degrés.

  2. Utilisez la méthode getTwoFingerDistance pour indiquer la distance diagonale entre le point central et le coin supérieur droit (cette distance doit évidemment être identique à tous les coins. Cette distance sera utilisée lors du prochain calcul).

  3. Maintenant, disons que nous faisons pivoter l'objet de 30 degrés dans le sens des aiguilles d'une montre. Nous savons maintenant que le coin supérieur droit doit être à 50 degrés et le coin supérieur gauche à 10 degrés.

  4. Vous devriez maintenant pouvoir utiliser la fonction getPointFromAngle dans le coin supérieur gauche et supérieur droit. Utilisation du rayon renvoyé à l'étape 2 . La position X multipliée par 2 à partir du coin supérieur droit doit vous donner la nouvelle largeur et la position Y multipliée par 2 à partir du coin supérieur gauche doit indiquer la nouvelle hauteur.

Ces 4 étapes ci-dessus doivent être mises dans des conditions basées sur la distance de rotation de votre objet, sinon vous pouvez retourner la hauteur en largeur et la largeur en hauteur.

Bare in mind les fonctions d’angle sont exprimées en facteurs de 0-1 au lieu de 0-360 (il suffit de multiplier ou de diviser par 360 le cas échéant):

// Obtient un angle à partir de deux points exprimé par un facteur de 0 -1 (0 étant 0/360, 0.25 étant 90 degrés, etc.)

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;

}

// Mesure la distance diagonale entre deux points

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)

}

// Obtenir les coordonnées XY à partir d'un angle donné sous un angle donné (l'angle est exprimé par un facteur de 0-1 0 étant 0/360 degrés et 0,75 étant 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;

}

Ces extraits de code proviennent de mes bibliothèques open source: https://bitbucket.org/warwick/hgdialrepo et https://bitbucket.org/warwick/hacergestov2 . L'un est une bibliothèque de gestes pour Android et l'autre est un contrôle de numérotation pour Android. Il existe également une implémentation OpenGLES 2.0 du contrôle de numérotation à l’adresse: https://bitbucket.org/warwick/hggldial

1
user2288580

Appliquez la matrice de rotation à vos points d’angle. Utilisez ensuite le minimum/maximum des coordonnées x, y obtenues pour définir votre nouveau cadre de sélection.

1
ypnos

Je ne suis pas sûr de comprendre, mais une matrice de transformation composée vous donnera les nouvelles coordonnées pour tous les points concernés. Si vous pensez que le rectangle peut déborder de la post-transformation, appliquez un tracé de détourage.

Si vous ne connaissez pas la définition exacte des matrices, jetez un oeil ici .

0
dirkgently

J'ai utilisé Region for First pour faire pivoter le rectangle, puis utiliser cette région pour détecter ce 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();

maintenant à détecter ce 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;
        }

    }
0
Abdulrehman