2010-02-02 10 views
2

La situationComment détecter le type de transformation d'un élément (échelle, déplacement, rotation) pour les éléments pivotés en fonction de la position du curseur?

J'ai écrit la méthode suivante (en actionscript 3, il est une application flash) qui retourne ce type de transformation est nécessaire pour la position de la souris par rapport à une position d'élément donné.

Un élément peut être déplacé, mis à l'échelle et pivoté. Cette méthode retourne si l'une de ces transformations est applicable avec les coordonnées données:

(ne vous inquiétez pas, vous n'avez pas besoin de déboguer cette méthode, ça marche - je l'ai juste ajouté ici pour que vous compreniez ce que je fais)

//returns whether the mouse is at a transforming position 
     private static function mouseAtTransformPosition(cX:Number, cY:Number, eX:Number, eY:Number, eW:Number, eH:Number, margin:Number):String 
     { 
      //initialize the transformation type 
      var transformType:String = null; 

      //set the transformation type depending on the given coordinates 
      if ((cX > eX) && (cX < eX + eW) && (cY > eY) && (cY < eY + eH)) 
      { 
       transformType = "mover"; 
      } 
      else if ((cX > eX) && (cX < eX + eW) && (cY <= eY) && (cY >= eY - margin)) 
      { 
       transformType = "scalerUp"; 
      } 
      else if ((cX >= eX + eW) && (cX <= eX + eW + margin) && (cY > eY) && (cY < eY + eH)) 
      { 
       transformType = "scalerRight"; 
      } 
      else if ((cX > eX) && (cX < eX + eW) && (cY >= eY + eH) && (cY <= eY + eH + margin)) 
      { 
       transformType = "scalerDown"; 
      } 
      else if ((cX <= eX) && (cX >= eX - margin) && (cY > eY) && (cY < eY + eH)) 
      { 
       transformType = "scalerLeft"; 
      } 
      else if ((cX >= eX - margin) && (cX <= eX) && (cY >= eY - margin) && (cY <= eY)) 
      { 
       transformType = "scalerUpLeft"; 
      } 
      else if ((cX >= eX + eW) && (eX <= eX + eW + margin) && (cY >= eY - margin) && (cY <= eY)) 
      { 
       transformType = "scalerUpRight"; 
      } 
      else if ((cX >= eX + eW) && (cX <= eX + eW + margin) && (cY >= eY + eH) && (cY <= eY + eH + margin)) 
      { 
       transformType = "scalerDownRight"; 
      } 
      else if ((cX >= eX - margin) && (cX <= eX) && (cY >= eY + eH) && (cY <= eY + eH + margin)) 
      { 
       transformType = "scalerDownLeft"; 
      } 
      else if ((cX >= eX - margin * 2) && (cX <= eX) && (cY >= eY - margin * 2) && (cY <= eY)) 
      { 
       transformType = "rotatorTopLeft"; 
      } 
      else if ((cX >= eX + eW) && (cX <= eX + eW + margin * 2) && (cY >= eY - margin * 2) && (cY <= eY)) 
      { 
       transformType = "rotatorTopRight"; 
      } 
      else if ((cX >= eX + eW) && (cX <= eX + eW + margin * 2) && (cY >= eY + eH) && (cY <= eY + eH + margin * 2)) 
      { 
       transformType = "rotatorBottomRight"; 
      } 
      else if ((cX >= eX - margin * 2) && (cX <= eX) && (cY >= eY + eH) && (cY <= eY + eH + margin * 2)) 
      { 
       transformType = "rotatorBottomLeft"; 
      } 

      //return the found transformation type 
      return transformType; 
     } 

Une élaboration de tous les paramètres requis:

cX: cursor X position 
cY: cursor Y position 
eX: element X position 
eY: element Y position 
eW: element width 
eH: element height 
margin: how far the cursor can be removed from an exact transformation location to still be applicable 

Si aucune transformation est applicable, elle renvoie null.

Le problème

Maintenant, ma fonction semble fonctionner à merveille. Le problème se pose maintenant que j'introduis la rotation aux éléments qui doivent être transformés.

Lorsque vous faites pivoter un élément, les coins se déplacent vers une position différente. Le curseur doit maintenant être à différentes positions pour obtenir les mêmes types de transformation en fonction de la situation, tout comme ma méthode ci-dessus (s'il n'y a pas de rotation):

cursor is... 
    inside the element -> move 
    at top of element -> scale up 
    at right of element -> scale right 
    at bottom of element -> scale down 
    at left of element -> scale left 
    at top left of element -> scale up-left 
    at top right of element -> scale up-right 
    at bottom left of element -> scale down-left 
    at bottom right of element -> scale down-right 
    further top left of element -> rotate right/left 
    further top right of element -> rotate right/left 
    further bottom right of element -> rotate right/left 
    further bottom left of element -> rotate right/left 

La question

Mon la méthode le fait parfaitement, s'il n'y a pas de rotation. Cependant, comment modifier cette fonction pour prendre en compte la rotation, par exemple le changement de coordonnées des coins?

Mise à jour pour Justin

//checks whether mouse transforming is applicable 
     private static function checkMouseTransforming(cX:Number, cY:Number, render:Sprite):void 
     {   
      //temp disable rotation to get accurate width/height, x and y 
      var realRotation:Number = render.getChildAt(0).rotation; 
      render.getChildAt(0).rotation = 0; 

      var eX:Number = render.x; 
      var eY:Number = render.y; 
      var eW:Number = render.width; 
      var eH:Number = render.height; 
      var margin:uint = 10; 

      //restore real rotation 
      render.getChildAt(0).rotation = realRotation; 

      cX -= eX + eW/2; 
      cY -= eY + eH/2; 

      var theta:Number = render.getChildAt(0).rotation * Math.PI/180; 
      var newcX:Number = Math.cos(theta) * cX - Math.sin(theta) * cY; 
      var newcY:Number = Math.sin(theta) * cX + Math.cos(theta) * cY; 

      newcX += eX + eW/2; 
      newcY += eY + eH/2; 

      var transformType:String = mouseAtTransformPosition(newcX, newcY, eX, eY, eW, eH, margin); 
         if (transformType) 
         { 
          //... 
         } 

Comme vous pouvez le voir, la rotation est réglée et a reçu de l'enfant de l'objet rendu. C'est parce que l'objet réel est un positionWrapper et à l'intérieur de ce wrapper il y a un rotateWrapper (auquel la rotation est définie) et à l'intérieur de ce wrapper, l'élément visible reste. Les valeurs x, y, width et height du wrapper de position sont toujours égales à celles de l'objet d'affichage visible, cela ne devrait donc pas poser de problème.

Maintenant, ce sont les résultats:

  • Sans rotation, tout fonctionne comme prévu
  • Avec environ 45 rotation, quand je souris sur au objet affichage visuel de:
    • centre en bas: il retourne "scalerLeft", alors qu'il devrait retourner "scalerDown"
    • right center: il renvoie scalerDown, alors qu'il devrait retourner scalerRight
    • centre supérieur: il retourne scalerRight, alors qu'il doit retourner scalerUp
    • centre gauche: il retourne scalerUp, alors qu'il devrait revenir scalerLeft (notez le modèle décalé ici)
    • le moteur semble revenir assez exactement
    • le RO- semble retourner au fond direct d'un coin, pas où il devrait
    • le mesureur bottomLeft semble retourner dans le coin inférieur droit, je pense que ce même décalage est le problème et est probablement le même pour tous les autres scalers aussi

Ceci est un bugger à déboguer, mais peut-être que cela aidera.

Mise à jour # 2 pour Justin alt text http://feedpostal.com/transformBug.gif Le décalage/bug semble se renforcer plus la rotation est. Avec 0 rotation, ce bug n'apparaît pas.

Est-ce que cela a du sens pour vous ou quelqu'un d'autre? Merci d'avance.

+0

Ajouté bounty vaut 150 réputation à la question, je sais que ma question est assez complexe! – Tom

+0

J'ai peur de m'être réjouie trop tôt. Il y a un bug notable que j'ai expliqué dans ma question mise à jour. Je sais que j'ai déjà accepté une réponse mais j'apprécierais vraiment que quelqu'un puisse donner un coup d'oeil à ce bug, car je n'ai aucune idée de ce qui pourrait le provoquer. – Tom

+0

Mise à jour: nevermind, c'était un bug dans mon code interne qui l'a causé. Fixé maintenant Merci. – Tom

Répondre

2

J'ai fait quelques suppositions ici. J'ai supposé que l'objet est rectangulaire et bidimensionnel (pas de rotation 3D). J'ai supposé que c'est le seul objet qui puisse être transformé pour simplifier les choses. Aussi, à partir des commentaires sur l'autre réponse, je comprends que les dimensions de l'objet (eX, eY, eW, eH) sont toujours listées comme étant les mêmes après la rotation.

Maintenant, voici ma solution. Vous aurez certainement besoin de connaître l'angle de rotation (que je supposerai positif pour la rotation dans le sens des aiguilles d'une montre) de l'objet que j'appellerai thêta. Au début, je pensais que vous devriez faire pivoter les boîtes de délimitation pour les différentes zones (la zone où la transformation est «déménageur» par exemple), mais c'est beaucoup plus compliqué. Ce que vous devez faire est de faire pivoter les coordonnées x et y de la souris autour du centre de l'objet et dans la direction opposée de thêta.Voici quelques étapes explicites:

  1. Soustraire le déplacement x et y de déplacement du centre de l'objet à partir de la x- respective et y les coordonnées (Cx et Cy). Les valeurs résultantes éloignent les déplacements x et y du centre de l'objet.

  2. Tourner dans le sens inverse des aiguilles d'une montre les coordonnées résultantes en utilisant l'angle thêta.

  3. Ajoute le déplacement x et le déplacement y du centre de l'objet aux coordonnées x et y résultantes de l'étape précédente.

  4. Utilisez les valeurs x et y résultantes comme valeurs cX et cY dans votre fonction mouseAtTransformPosition.

Voici maintenant du code pour illustrer cela. NewcX et newcY seront les coordonnées réellement utilisées dans mouseAtTransformPosition. Faites-moi savoir si cela n'a aucun sens ou si vous avez des questions.

*** Mise à jour:

Je pense que vous faites pivoter les coordonnées de la mauvaise façon. Pour changer le sens de rotation, utiliser:

newcX = cos(theta)*cX + sin(theta)*cY; 
newcY = -sin(theta)*cX + cos(theta)*cY; 

au lieu de ce que j'ai ci-dessus. Essayez ceci et dites-moi comment ça se passe.

+0

Cela semble très prometteur. Malheureusement, il ne semble pas acquérir les bonnes positions. J'ai mis à jour ma question pour vous montrer comment je l'utilise et quels sont les résultats. – Tom

+0

Votre thêta est-il défini comme je l'ai décrit ou est-il inversé (dans le sens des aiguilles d'une montre ou dans le sens contraire des aiguilles d'une montre)? Si elle est inversée, vous devez changer les signes sur les deux fonctions sin (- à +, + à -). En outre, quel genre de comportement obtenez-vous? –

+0

Lisez ma question mise à jour, tout est là. :) – Tom

1

Si vous utilisez les propriétés mouseX/mouseY de l'objet que vous transformez au lieu de celles de son parent (ou de la scène d'ailleurs), vous les obtiendrez dans l'espace des coordonnées des objets.
Ainsi, vous n'aurez jamais à vous soucier de la rotation ou de la mise à l'échelle, et vous pourrez garder votre code à peu près tel quel.

Je recommande également d'utiliser scaleX/scaleY pour modifier la taille, depuis width et height changera lorsque vous faites pivoter l'objet.

+0

Je ne sais pas comment vous feriez cela. Que se passe-t-il si la souris n'est pas directement au-dessus de l'objet mais toujours proche, comment détecteriez-vous où elle se trouve et quelle transformation est nécessaire? – Tom

+0

vous obtiendrez toujours les coordonnées de la souris en ignorant si le pointeur de la souris est terminée ou non. Faites un test rapide avec un clip rotatif et tracez les coordonnées de la souris et vous verrez. – grapefrukt

+0

Je m'en rends compte. Cependant, comment sauriez-vous si vous êtes actuellement dans un coin, par exemple? J'ai une idée et je vais tester cet après-midi, mais je ne suis pas sûr. – Tom