Question

J'ai les coordonnées du point 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.

Qu'est-ce qu'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 de la boîte à outils de transformation dans as3 si nécessaire.

Était-ce utile?

La solution

  • Transformez les coordonnées des quatre coins
  • Trouvez le plus petit des quatre x sous min_x
  • Trouvez le plus grand des quatre x et appelez-le max_x
  • Idem avec le y
  • Votre cadre de sélection est (min_x, min_y), (min_x, max_y), (max_x, max_y), (max_x, min_y)
D'après ce que j'en sais, il n'y a 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. Vous devrez peut-être bricoler cela 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.

Autres conseils

Je me rends compte que vous demandez ActionScript, mais au cas où quelqu'un chercherait ici la réponse à iOS ou OS-X, voici ce qui se passe:

+ (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! :)

Swift:

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

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

Une autre méthode, plus efficace, consiste à tester le quadrant dans lequel se trouve votre angle de rotation, puis à 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 plus grands que le courant. max ou inférieur au min actuel) je pense.

L’algorithme de base, qui n’utilise rien d’autre 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 en tant que 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 )
    {
        // 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 le rectangle qui a été pivoté de 30 degrés dans votre diagramme semble indiquer que vous utilisez, je ne savais pas ce que la partie de droite essayait de représenter. Si ce n'est pas le bon sens, échangez simplement les clauses symétriques et le signe des termes 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];
        }

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

(modifier) ??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

Bien que Code Guru ait déclaré la méthode GetBounds (), j'ai remarqué que la question était étiquetée as3, 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 existe deux méthodes qui semblent faire la même chose: getBounds () et 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);
    }

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 nouveau cadre de sélection.

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 statique float getAngleFromPoint (pointPoint final, point de contact final)

public float float 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 du centre au coin supérieur droit (disons que ceci renvoie 20 degrés), ce qui signifie que le coin supérieur gauche correspond à -20 degrés ou à 340 degrés.

  2. Utilisez la méthode getTwoFingerDistance pour renvoyer la distance en 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 dans le 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. en utilisant le rayon renvoyé à 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 donner la nouvelle hauteur.

Ces 4 étapes ci-dessus doivent être placées 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 (multiplier ou diviser par 360 si nécessaire):

// 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 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’une est une bibliothèque de gestes pour Android et l’autre, une commande à cadran pour Android. Il existe également une implémentation OpenGLES 2.0 du contrôle de numérotation à l’adresse: https://bitbucket.org/warwick/hggldial

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 imageable après la transformation, appliquez un tracé de détourage.

Si vous ne connaissez pas la définition exacte des matrices, consultez ici .

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

    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top