77 votes

Calculer les coordonnées de la boîte englobante à partir d'un rectangle pivoté

Je dispose des coordonnées du point supérieur gauche d'un rectangle ainsi que de sa largeur, de sa hauteur et de sa rotation de 0 à 180 et de -0 à -180.

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

Quel est le moyen le plus simple de calculer les coordonnées de la boîte englobante ?

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

Le point A n'est pas toujours sur la borne y minimale, il peut être n'importe où.

Je peux utiliser la matrice de la boîte à outils de transformation dans as3 si nécessaire.

1 votes

L'image (Image) n'est pas visible (L'image dit : Cliquez et découvrez Imageshack) ! !!

0 votes

C'est vrai, je me demande si je peux le récupérer à partir des archives de Google ou autre.

1 votes

Quel est le point A ?

85voto

MarkusQ Points 15612
  • Transformez les coordonnées des quatre coins
  • Trouvez la plus petite des quatre valeurs de x en tant que min_x
  • Trouvez la plus grande des quatre valeurs de x et dites-la. max_x
  • Idem pour les Y
  • Votre boîte englobante est (min_x,min_y), (min_x,max_y), (max_x,max_y), (max_x,min_y)

A priori, il n'y a pas de voie royale qui vous y conduise 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 tournez. Il se peut que vous deviez bricoler cela en fonction de vos fonctions trigonométriques (attendent-elles des degrés ou des radians), du sens / signe de votre système de coordonnées et de la façon dont vous spécifiez les angles, etc.

0 votes

En effet, l'utilisation de matrices pour tous les coins et leur comparaison a fait l'affaire, merci !

3 votes

En fait, en raison de la symétrie, vous n'avez besoin de transformer que 2 coins, et si vous y réfléchissez un peu plus, c'est juste 1 coin à faire pivoter.

1 votes

@ysap Cela n'est valable que dans le cas où vous effectuez une rotation autour du centre du rectangle.

28voto

Olie Points 10528

Je me rends compte que vous demandez ActionScript mais, au cas où quelqu'un viendrait chercher la réponse pour iOS ou OS-X, voici la réponse :

+ (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 à votre place, laissez-le faire ! :)

Swift :

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

2 votes

Je voulais ajouter que l'angle doit être en radians et non en degrés. Cela peut vous faire gagner du temps ;)

13voto

Troubadour Points 9485

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 autre méthode, plus efficace, consiste à tester dans quel quadrant se trouve votre angle de rotation, puis à calculer directement la réponse. Cette méthode est plus efficace car, dans le pire des cas, vous n'avez que deux instructions if (vérification de l'angle) alors que l'autre approche en a douze (6 pour chaque composant lors de la vérification des trois autres coins pour voir s'ils sont supérieurs au maximum actuel ou inférieurs au minimum 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 dénoté l'angle de rotation par thêta et j'y ai exprimé la vérification en degrés, car c'est du 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 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é tourné de 30 degrés dans votre diagramme semble indiquer que vous utilisez, je n'étais pas sûr de ce que la partie à droite essayait de dénoter. Si c'est le mauvais sens, il suffit de permuter les clauses symétriques et le signe des termes st.

0 votes

Je sais que c'est très vieux, mais c'est un premier résultat pour Google, alors je note : Appliquez simplement l'échelle aux h et w avant cela et cela devrait fonctionner correctement. Cette réponse est actuellement ma préférée pour ce problème car elle décompose le calcul en petits morceaux si bien. 2 branches et avec un peu de magie NEON dans ios, 4-6 opérations ou moins selon la façon dont vous êtes astucieux.

0 votes

Les gens continuent d'éditer incorrectement les "degrés" dans la deuxième déclaration if. Il s'agit d'un pseudo-code qui ne sera jamais compilé. Le but d'indiquer explicitement les "degrés" est que theta n'est clairement pas dans les degrés. Il s'agit d'un rappel lors de l'implémentation d'un code de travail correct pour que les unités angulaires soient correctes.

0 votes

Ok, alors pour éviter cette confusion, il serait utile que vous changiez toutes les instructions if de manière à ce qu'elles soient if (theta > 0 degrees) y if (theta > -90 degrees) pour qu'il soit cohérent. L'incohérence incite les gens à l'éditer.

7voto

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

2voto

George Profenza Points 24345

Bien que Code Guru ait indiqué la méthode GetBounds(), j'ai remarqué que la question est étiquetée as3, flex, donc voici 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().

0 votes

GetRect effectue la même opération que getBounds, mais sans le trait sur un objet : help.adobe.com/fr_US/FlashPlatform/reference/actionscript/3/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X