Comunidad de diseño web y desarrollo en internet online

Deformar bitmaps con el metodo drawTriangles de Actionscript 3

En este tutorial mostraré un par de sencillos ejemplos en cuanto al uso de una de las novedades de Adobe Flash CS4 que mas me ha llamado la atención (en cuanto a ActionScript se refiere), estoy hablando de la adición del método DrawTriangles() a la clase Graphics.

Este método es muy útil al momento de crear figuras tridimensionales a partir de triángulos, así como también para modificar la apariencia de un bitmap (que explicare mas adelante), y en el mas sencillo de los casos dibujar una figura vectorizada (con forma de triangulo) similar a lo que harían el resto de los métodos de Graphics.

A continuación, el resultado final:

Content on this page requires a newer version of Adobe Flash Player.

Get Adobe Flash player


Arrastra los recuadros negros, para modificar la imagen.

Como podrás ver, el bitmap esta dividido por 2 triángulos rectángulos, unidos a través de sus hipotenusas formando un rectángulo. Explico a detalle como crearlo:

Aprendiendo la Teoria

Antes de hecharle un vistazo al código completo, pienso que es mas importante comprender la manera básica de como trabaja este.

Primero hay que imaginar que ya tenemos en nuestro escenario, un objeto bitmap sin mayor chiste, cuyo contorno esta formado por algún tipo de cuadrángulo al cual aplicaremos la deformación mas adelante:

Para ello vamos a dividirlo en un par de triángulos iguales, considerando las cuatro vértices propias del cuadrángulo y que son 3 vértices por cada triangulo nos quedarían faltando 2 puntos para completar la figura:

Por fortuna podemos optimizar ese paso, y evitarnos el tener que crear otros dos objetos adicionales definiendo como puntos en común P2 y P3 así ahorrándonos los 2 vértices que nos faltaban:

Para poder graficar y manejar texturas con drawTriangles() es necesario emplear las coordenadas U para el eje horizontal cuyo valor sera igual al ancho total de la imagen (x, width), y V para el eje vertical cuyo valor sera el de la altura total de la imagen (y, heigth).

Como ejemplo usando la siguiente imagen, el triangulo 1 (T1) estará formado por la unión de P1 (0, 0), a P2 (1, 0) para terminar en P3 (0, 1).


Ejemplo utilizando como puntos de coordenadas U, y V

Así mismo podemos definir cualquier triangulo sin batallar por el tamaño de la imagen ya que este sera definido por el tamaño total, en la siguiente imagen podemos ver un triangulo cuyos puntos estarán unidos por P1 (.5, 0), P2 (1, .25) y P3 (0, 1).

Comprendiendo la Practica

Si estabas esperando a ver el código fuente del primer ejemplo, te diré que jamas has estado tan cerca pero aun te falta llegar mas abajo.

Primero crearemos un triangulo rellenado con la bitmapData de un elemento en la biblioteca, instanciado como "Image" y utilizaremos la nueva Clase Vector para ello, no entraré mucho en eso.

graphics.clear(); // Borramos cualquier dibujo creado por graphics
graphics.beginBitmapFill(new Image(0,0)); // Indicamos que se use "Image" como color de relleno graphics.drawTriangles( // Comenzamos con el método a explicar  
Vector.<Number>([10,10, 100,100, 10,300, 100,200]), // Linea A
Vector.<int>([0,1,2, 1,3,2]), // Linea B  
Vector.<Number>([0,0, 1,0, 0,1, 1,1])); // Linea C  

Primero que nada en la linea A fijaremos las coordenadas dentro del escenario de cada punto, estos puntos estarán determinados a través del sistema de coordenadas (U, V) en la linea C tal y como ya explique arriba y finalmente en la linea B indicamos como trazar los triángulos a partir de cada punto que conforman la figura, cosa que también ya fue mencionada. Así se formara una figura similar a la siguiente:

Ahora, comprendido todo lo anterior me parece apropiado mostrar la clase fuente del primer ejemplo con todas sus explicaciones comentadas entre lineas, con algunas referencias hacia los ejemplos anteriores:

package 
{ 
 // Importamos todos los paquetes necesarios 
 import flash.geom.*; 
 import flash.events.Event; 
 import flash.events.TimerEvent; 
 import flash.events.MouseEvent; 
 import flash.display.Sprite; 
 import flash.display.Bitmap; 
 import flash.display.BitmapData; 
 import flash.display.TriangleCulling; 
 import flash.display.MovieClip; 
 import flash.display.Graphics; 
 // 
 public class Main extends MovieClip { 
   // Este sera el tamaño de la imagen una vez cargada dentro del escenario 
   private var size:int = 260; 
   // Aqui se guardara la bitmapData, como su nombre indica sera el contenedor... 
   // ...cargado al centro del escenario 
   private var container:Sprite; 
   // La bitmapData sobre la cual trabajaremos 
   private var bitmapData:BitmapData; 
   // Los dos vectores que representaran los catetos y vertices al dibujar los Triangulos 
   private var vertices:Vector.<Number>; 
   private var catetos:Vector.<Number>; 
   // Estos seran los Sprites encargados de modificar la BitmapData... 
   // ...al igual que en los ejemplos anteriores se trataran de P1,P2,P3 y P4 
   private var P1:Sprite; 
   private var P2:Sprite; 
   private var P3:Sprite; 
   private var P4:Sprite; 
   //   
   public function Main():void 
   { 
    // Lo primero a hacer sera cargar el objeto bitmap de nuestra biblioteca 
    bitmapData = new bitmap(0, 0); 
    // Luego, creamos el contenedor de la BitmapData ya cargada... 
    container = new Sprite(); 
    // ...y lo colocamos centrado en el escenario 
    container.x = (stage.stageWidth - bitmapData.width) /2; 
    container.y = (stage.stageHeight - bitmapData.height) /2; 
    addChild(container); 
    // Finalmente llamamos a estas dos funciones, que explicare mas adelante... 
    setPoints(); 
    drawTriangle(); 
   } 
   // A las siguientes dos funciones llegaran todos los listeners de cada punto... 
   // ...por lo que cumplen una importante funcion al querer arrastrar y modificar la bitmap 
   private function EventHandler(event:Event) 
   { 
    // Diferenciamos los eventos recibidos 
    switch(event.type) 
    { 
      // Detectamos el evento MouseUp 
      case MouseEvent.MOUSE_UP: 
       removeEventListener(MouseEvent.MOUSE_MOVE, EventHandler); 
       stopDrag(); 
      // 
      default: drawTriangle(); 
    } 
   } 
   private function startPointDrag(event:MouseEvent):void 
   { 
    addEventListener(MouseEvent.MOUSE_MOVE, EventHandler); 
    event.currentTarget.startDrag(); 
    drawTriangle(); 
   } 
   // Esta es la funcion que dividira la imagen cargada desde la bilioteca... 
   // ...en dos triangulos, de esto ya hablamos mas arriba 
   private function drawTriangle():void 
   { 
    vertices = new Vector.<Number>(); 
    catetos = new Vector.<Number>(); 
    // Señalamos los puntos que forman el Triangulo 1 
    vertices.push(P1.x, P1.y); 
    vertices.push(P2.x, P2.y); 
    vertices.push(P3.x, P3.y); 
    // ...y los del Triangulo 2 
    vertices.push(P2.x, P2.y); 
    vertices.push(P4.x, P4.y); 
    vertices.push(P3.x, P3.y);     
    // Una vez hecho eso, dividimos el cuadrangulo en dos triangulos 
    // Utilizando las coordenadas (U, V) tanto para el Triangulo 1 
    catetos.push(0, 0); 
    catetos.push(1, 0); 
    catetos.push(0, 1); 
    // ...como para el Triangulo 2 
    catetos.push(1, 0); 
    catetos.push(1, 1); 
    catetos.push(0, 1); 
    // 
    container.graphics.clear(); 
    container.graphics.beginBitmapFill(bitmapData); 
    container.graphics.drawTriangles(vertices, null, catetos, TriangleCulling.NONE); 
   } 
   // Esta funcion esta encargada de acomodar los puntos P1~P4 y... 
   // ...agregarles sus respectivos listeners 
   private function setPoints():void { 
    P1 = newPoint(); 
    P2 = newPoint(); 
    P3 = newPoint(); 
    P4 = newPoint(); 
    // Fijamos cada uno de los puntos en los margenes de la imagen cargada... 
    // ...logrando que cuando utilizemos las coordenadas (U, V) estas queden... 
    // ...acomodadas en los limites debidos 
    P4.x = P2.x = bitmapData.width; 
    P4.y = P3.y = bitmapData.height; 
    // Añadimos todos los listeners necesarios para cada punto 
    P1.addEventListener(MouseEvent.MOUSE_DOWN, startPointDrag); 
    P2.addEventListener(MouseEvent.MOUSE_DOWN, startPointDrag); 
    P3.addEventListener(MouseEvent.MOUSE_DOWN, startPointDrag); 
    P4.addEventListener(MouseEvent.MOUSE_DOWN, startPointDrag); 
    stage.addEventListener(MouseEvent.MOUSE_UP, EventHandler); 
   } 
   // Esta funcion, llamada por "setPoints" dibuja los puntos P1~P4 a arrastrar 
   private function newPoint():Sprite { 
    var point:Sprite = new Sprite(); 
    point.graphics.beginFill(0x000000); 
    point.graphics.drawRect(-5, -5, 10, 10); 
    point.graphics.endFill(); 
    point.buttonMode = true; 
    // 
    container.addChild(point); 
    return point; 
   } 
 } 
}

NOTA: Hay un pequeño detalle a tener en cuenta al utilizar este método sobre imágenes vectoriales, y es que al vaciar la imagen como relleno en un bitmap la rasteriza en el proceso, por lo que al dar un acercamiento muy grande los pixeles se harán evidentes. 

Extendiendo el código

Con un poco de imaginación resulta fácil ajustar el código anterior para que haga lo mismo sobre un MC con un vídeo y/o animación cualquiera dentro. Tan solo refrescando la bitmapData copiada cada lapso de tiempo gracias a un Timer.

Content on this page requires a newer version of Adobe Flash Player.

Get Adobe Flash player



Arrastra los recuadros negros, para modificar el video.

¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.

Publica tu comentario

El autor de este artículo ha cerrado los comentarios. Si tienes preguntas o comentarios, puedes hacerlos en el foro

Entra al foro y participa en la discusión

o puedes...

¿Estás registrado en Cristalab y quieres
publicar tu URL y avatar?

¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?

Registrate