Calculer les coordonnées de la zone de délimitation à partir d'un rectangle rotatif

j'ai les coordonnées du point supérieur 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 la boîte autour du rectangle.

Qu'est-ce qu'une façon simple de calculer les coordonnées de la zone de délimitation

  • y Min, max y min x max x?

le point A n'est pas toujours sur le y min lié, il peut être n'importe où.

je peux utiliser la matrice transform toolkit en as3 si nécessaire.

59
demandé sur Peter O. 2009-03-07 20:03:41

11 réponses

  • transformer les coordonnées des quatre coins
  • trouver le plus petit des quatre x comme min_x
  • trouvez le plus grand des quatre x et appelez-le max_x
  • idem avec les y
  • votre boîte limite est (min_x,min_y), (min_x,max_y), (max_x,max_y), (max_x,min_y)

AFAIK, il n'y a pas de route royale qui vous y mènera beaucoup plus vite.

si vous êtes 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 tournez. Vous pouvez avoir besoin de bricoler avec cela en fonction de vos fonctions de trigonométrie (est-ce qu'ils attendent des degrés ou des radians) le sens / signe de votre système de coordonnées en fonction de la façon dont vous spécifiez des angles, etc.

70
répondu MarkusQ 2009-03-07 17:25:42

je me rends compte que vous demandez ActionScript mais, juste au cas où quelqu'un arrive ici à la recherche de la réponse iOS ou OS-X, c'est ceci:

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

    return result;
}

si votre OS offre de faire tout le travail pour vous, laissez-le! :)

Swift:

func boundingRectAfterRotatingRect(rect: CGRect, toAngle radians: CGFloat) -> CGRect {
    let xfrm = CGAffineTransformMakeRotation(radians)
    return CGRectApplyAffineTransform (rect, xfrm)
}
23
répondu Olie 2015-05-05 17:19:56

la méthode décrite par MarkusQ fonctionne parfaitement mais gardez à l'esprit que vous n'avez pas besoin de transformer les trois autres coins si vous avez déjà le point A.

une méthode alternative, qui est plus efficace, est de tester dans quel quadrant votre angle de rotation est et puis simplement calculer la réponse directement. Ceci est plus efficace car vous avez seulement un pire cas de deux déclarations if (vérifier l'angle) alors que l'autre approche a un pire cas de douze (6 pour chaque composant lors de la vérification des trois autres coins pour voir s'ils sont plus grands que le courant max ou moins que le courant min) 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 montré ci-dessous. J'ai indiqué l'angle de rotation par theta et j'y ai exprimé le contrôle en degrés comme si c'était 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 qui se situe dans la gamme [-180, 180]. J'ai également supposé que theta augmente dans le sens des aiguilles d'une montre que ce est ce que le rectangle qui a été tourné de 30 degrés dans votre diagramme semble indiquer que vous utilisez, Je n'étais pas sûr de ce que la partie sur la droite essayait de dénoter. Si ce n'est pas le cas, alors il suffit d'échanger les clauses symétriques et aussi le signe des termes st.

8
répondu Troubadour 2015-03-17 20:29:42
    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
répondu bajie 2010-10-06 02:38:49

si vous utilisez GDI+ , vous pouvez créer un nouveau GrpaphicsPath -> ajouter des points ou des formes -> appliquer la transformation de rotation -> utiliser GraphicsPath.GetBounds () et il retournera un rectangle qui limite votre forme de rotation.

(edit) VB.Net échantillon

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
répondu Bishoy 2016-11-02 20:12:42

bien que Code Guru ait déclaré la méthode GetBounds (), j'ai remarqué que la question est marquée as3, flex, donc voici un extrait d'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
répondu George Profenza 2009-04-18 00:30:43
/**
     * 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
répondu Nick Bilyk 2011-02-20 21:02:01

appliquez la matrice de rotation à vos points d'angle. Ensuite, utilisez le minimum/maximum respectivement des coordonnées x,y obtenues pour définir votre nouvelle zone de délimitation.

1
répondu ypnos 2009-03-07 17:19:45

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:

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

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

Point getPointFromAngle (final double angle, final double rayon)

cette solution suppose que la densité des pixels est uniformément espacée. Avant de tourner l'objet faire ce qui suit:

  1. utilisez getAngleFromPoint pour calculer l'angle du centre au coin supérieur droit (disons que cela retourne 20 degrés) ce qui signifie que le coin gauche upp est de -20 degrés ou 340 degrés.

  2. utiliser la distance getTwoFingerDistance pour retourner la diagonale entre le point central et le coin supérieur droit (cette distance devrait être obvoiusly la même à tous les coins, cette distance sera utilisée dans le prochain calcul).

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

  4. vous devriez maintenant être en mesure d'utiliser la fonction getPointFromAngle en haut à gauche et en haut à droite. en utilisant le rayon retourné de l'étape 2. La position X multipliée par 2 à partir du coin supérieur droit devrait vous donner la nouvelle largeur et la position Y multipliée par 2 à partir du coin supérieur gauche devrait vous donner la nouvelle hauteur.

ces 4 étapes ci-dessus doivent être mises dans des conditions basées sur la mesure dans laquelle vous avez tourné votre objet autrement sage vous peut retourner la hauteur que la largeur et la largeur que la hauteur.

garder à L'esprit que les fonctions d'angle sont exprimées en facteurs de 0-1 au lieu de 0-360 (multiplier ou diviser par 360 le cas échéant):

/ / obtient un angle à partir de deux points exprimés 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 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)

}

/ / Get Coordonnées XY à partir d'un angle donné d'un rayon (l'angle est exprimé par un facteur de 0 à 10 é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 composition pour Android. Il y a aussi un OpenGLES 2.0 mise en œuvre de la commande de composition à: https://bitbucket.org/warwick/hggldial

1
répondu user2288580 2017-09-10 00:28:12

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 sur la zone imagable post transformation appliquer un chemin de découpage.

dans le cas où vous n'êtes pas familier avec la définition exacte des matrices jeter un oeil ici .

0
répondu dirkgently 2009-03-07 17:12:55

j'ai utilisé la région pour faire tourner D'abord le rectangle, puis j'ai utilisé 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 à la détection de 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
répondu Abdulrehman 2018-04-27 10:04:46