package { // PRESS "R" + CLICK TO ROTATE. // DRAG THE GREEN HANDLERS import net.hires.debug.Stats; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.geom.ColorTransform; import flash.geom.Matrix3D; import flash.geom.Rectangle; import flash.geom.Vector3D; public class DeformableBezier extends Sprite { //Variables que definen la malla... private var initLink : Particula = new Particula(); private var nodos : Array = new Array(); private var segmentsU : uint; private var segmentsV : uint; private var cantidad : Number; //Puntos de borde... private var Xt0 : Vector3D = new Vector3D(); private var Xt1 : Vector3D = new Vector3D(); private var Xb0 : Vector3D = new Vector3D(); private var Xb1 : Vector3D = new Vector3D(); //Puntos de control internos de las bezier... private var Cpt : Vector3D = new Vector3D(); private var Cpb : Vector3D = new Vector3D(); private var Cpl : Vector3D = new Vector3D(); private var Cpr : Vector3D = new Vector3D(); //Variable que contiene una referencia de los vectores de creación... private var pointsReference : Vector.<Vector3D> = new Vector.<Vector3D>; //Puntos que controlan el movimiento de los nodos de las curvas... public var angle : Number = 0; //BitmapData de representación de las partículas... private var bmd : BitmapData; private var rect : Rectangle; //Variable que controla la rotación del objeto espacialmente.... private var rotationMatrix : Matrix3D = new Matrix3D(); private var rotationParentMatrix : Matrix3D = new Matrix3D(); private const X_AXIS : Vector3D = new Vector3D(1, 0, 0); private const Y_AXIS : Vector3D = new Vector3D(0, 1, 0); private var PIVOT_POINT : Vector3D = new Vector3D(0, 0, 0); //Variable de colorTransform para limpiar el bitmapData... private var colorTransform : ColorTransform = new ColorTransform(0, 0, 0, 1, 0, 0, 0); //Variable que se encarga de la rotación... private var rotate : Boolean; private var mouse : Boolean; //Varible que contiene los distintas asas de control... private var asasContainer : Sprite = new Sprite(); private var lineContainer : Sprite = new Sprite(); private var persp : Number; //Variable que guarda referencia de las distintas asas de control... private var asas : Array = new Array(); private var pointer : Vector3D; private var asasMatrix : Matrix3D; private var asasParentMatrix : Matrix3D; //Variable que guarda una referencia a la perspectiva utilizada en las transformaciones... private var focus : Number = 600; private var zoom : Number = 3; // Variable de inicialización de la rotacion private var init : Boolean = false; //Constructor... public function DeformableBezier() { var particle : Particula = initLink; Particula.focus = focus; Particula.zoom = zoom; ControlPoint.focus = focus; ControlPoint.zoom = zoom; var i : uint; var lado : Number = 50; //Defino los puntos de borde... Xt0 = new Vector3D(-lado, 0, -lado); Xt1 = new Vector3D(lado, 0, -lado); Xb0 = new Vector3D(-lado, 0, lado); Xb1 = new Vector3D(lado, 0, lado); //Defino los puntos internos de control... Cpt = new Vector3D(0, 0, -lado); Cpb = new Vector3D(0, 0, lado); Cpl = new Vector3D(-lado, 0, 0); Cpr = new Vector3D(lado, 0, 0); //Guardo los vectores en un vector de referencia... pointsReference[0] = Xt0; pointsReference[1] = Cpt; pointsReference[2] = Xt1; pointsReference[3] = Cpr; pointsReference[4] = Xb1; pointsReference[5] = Cpb; pointsReference[6] = Xb0; pointsReference[7] = Cpl; //Inserto las distintas asas en el contenedor... for (i = 0;i < 8; i++) { asas.push(new ControlPoint(i)); asas[i].addEventListener(ControlPointEvent.CHANGE, updateControlPoints); asasContainer.addChild(asas[i]); } asasContainer.x = stage.stageWidth / 2; asasContainer.y = stage.stageHeight / 2; lineContainer.x = stage.stageWidth / 2; lineContainer.y = stage.stageHeight / 2; //Cantidad de puntos a mostrar... segmentsV = 60; segmentsU = 60; //Genero los puntos de las partículas... var length : uint = segmentsV * segmentsU; for(i = 0;i < length; i++) { nodos.push(new Vector3D(0, 0, 0)); } cantidad = nodos.length - 1; i = 0; while(cantidad >= 0) { particle.position = nodos[i]; particle.next = new Particula(); particle = particle.next; cantidad--; i++; } bmd = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0); rect = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight); addChild(new Bitmap(bmd, "auto", false)); addChild(lineContainer); addChild(asasContainer); addChild(new Stats()); //Inicio la animación... stage.addEventListener(KeyboardEvent.KEY_DOWN, initRotation); stage.addEventListener(KeyboardEvent.KEY_UP, stopRotation); stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownListener); stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpListener); stage.addEventListener(MouseEvent.MOUSE_WHEEL, updateZoom); stage.addEventListener(Event.ENTER_FRAME, updatePoints); } //Función que se encarga de actualizar los puntos de control... private function updateControlPoints(e : ControlPointEvent) : void { switch(e.cp) { case 0: pointsReference[0] = Xt0 = e.vector; break; case 1: pointsReference[1] = Cpt = e.vector; break; case 2: pointsReference[2] = Xt1 = e.vector; break; case 3: pointsReference[3] = Cpr = e.vector; break; case 4: pointsReference[4] = Xb1 = e.vector; break; case 5: pointsReference[5] = Cpb = e.vector; break; case 6: pointsReference[6] = Xb0 = e.vector; break; case 7: pointsReference[7] = Cpl = e.vector; break; } lineContainer.graphics.clear(); lineContainer.graphics.lineStyle(0, 0XFFFF00, 1); for(var i : uint = 0;i < pointsReference.length; i++) { if(i == 0) { lineContainer.graphics.moveTo(asas[i].x, asas[i].y); } else { lineContainer.graphics.lineTo(asas[i].x, asas[i].y); } } lineContainer.graphics.lineTo(asas[0].x, asas[0].y); } //Función que ejecuta la animación de manera secuencial... private function updatePoints(e : Event) : void { var particle : Particula = initLink; var i : uint = 0; //Defino las curvas bezier... //Top... var BT : Array = new Array(Xt0, Cpt, Xt1); //Bottom... var BB : Array = new Array(Xb0, Cpb, Xb1); //Izquierda... var BL : Array = new Array(Xt0, Cpl, Xb0); //Derecha... var BR : Array = new Array(Xt1, Cpr, Xb1); //Calculo los puntos... nodos = Bezier.getPatch(Xt0, Xt1, Xb0, Xb1, BT, BB, BL, BR, segmentsU, segmentsV); if(!init){ init = true; rotationMatrix.identity(); rotationParentMatrix.identity(); rotationMatrix.appendRotation(30, Y_AXIS, PIVOT_POINT); rotationParentMatrix.appendRotation(30, X_AXIS, PIVOT_POINT); asasMatrix = rotationMatrix.clone(); asasParentMatrix = rotationParentMatrix.clone(); lineContainer.graphics.clear(); lineContainer.graphics.lineStyle(0, 0XFFFF00, 1); asasMatrix.invert(); asasParentMatrix.invert(); for(i = 0;i < pointsReference.length; i++) { pointer = pointsReference[i]; pointer = rotationMatrix.deltaTransformVector(pointer); pointer = rotationParentMatrix.deltaTransformVector(pointer); persp = zoom / (1 + pointer.z / focus); asas[i].persp = persp; asas[i].matrix = asasMatrix; asas[i].parentMatrix = asasParentMatrix; if(persp > 0) { asas[i].x = pointer.x * persp; asas[i].y = pointer.y * persp; if(i == 0) { lineContainer.graphics.moveTo(asas[i].x, asas[i].y); } else { lineContainer.graphics.lineTo(asas[i].x, asas[i].y); } } else { asas[i].x = asas[i].y = -10000000; } } lineContainer.graphics.lineTo(asas[0].x, asas[0].y); } //Actualizo los asas de control... if(rotate && mouse) { rotationMatrix.identity(); rotationParentMatrix.identity(); rotationMatrix.appendRotation(360 * stage.mouseX / stage.stageWidth, Y_AXIS, PIVOT_POINT); rotationParentMatrix.appendRotation(90 * stage.mouseY / stage.stageHeight, X_AXIS, PIVOT_POINT); asasMatrix = rotationMatrix.clone(); asasParentMatrix = rotationParentMatrix.clone(); lineContainer.graphics.clear(); lineContainer.graphics.lineStyle(0, 0XFFFF00, 1); asasMatrix.invert(); asasParentMatrix.invert(); for(i = 0;i < pointsReference.length; i++) { pointer = pointsReference[i]; pointer = rotationMatrix.deltaTransformVector(pointer); pointer = rotationParentMatrix.deltaTransformVector(pointer); persp = zoom / (1 + pointer.z / focus); asas[i].persp = persp; asas[i].matrix = asasMatrix; asas[i].parentMatrix = asasParentMatrix; if(persp > 0) { asas[i].x = pointer.x * persp; asas[i].y = pointer.y * persp; if(i == 0) { lineContainer.graphics.moveTo(asas[i].x, asas[i].y); } else { lineContainer.graphics.lineTo(asas[i].x, asas[i].y); } } else { asas[i].x = asas[i].y = -10000000; } } lineContainer.graphics.lineTo(asas[0].x, asas[0].y); } i = 0; bmd.lock(); bmd.colorTransform(bmd.rect, colorTransform); //Actualizo las posiciones de las partículas... while(particle.next != null) { pointer = nodos[i]; pointer = rotationMatrix.deltaTransformVector(pointer); pointer = rotationParentMatrix.deltaTransformVector(pointer); particle.position = pointer; bmd.setPixel(Math.round(stage.stageWidth / 2 + particle.x), Math.round(stage.stageHeight / 2 + particle.y), 0XFFFFFF); particle = particle.next; i++; } bmd.unlock(); } //Función que se encarga de iniciar la rotación... private function initRotation(e : KeyboardEvent) : void { if(e.charCode == 114) rotate = true; } //Función que para la rotación... private function stopRotation(e : KeyboardEvent) : void { if(e.charCode == 114) rotate = false; } //Función que señala que el ratón esta presionado... private function mouseDownListener(E : MouseEvent) : void { mouse = true; } //Función que señala que el ratón esta presionado... private function mouseUpListener(E : MouseEvent) : void { mouse = false; } //Función que se encarga de modificar el zoom... private function updateZoom(e : MouseEvent) : void { trace(e.delta); zoom += e.delta / 10; Particula.zoom = zoom; ControlPoint.zoom = zoom; var persp : Number; lineContainer.graphics.clear(); lineContainer.graphics.lineStyle(0, 0XFFFF00, 1); for(var i : uint = 0;i < pointsReference.length; i++) { var pointer : Vector3D = pointsReference[i]; pointer = rotationMatrix.deltaTransformVector(pointer); pointer = rotationParentMatrix.deltaTransformVector(pointer); persp = zoom / (1 + pointer.z / focus); asas[i].persp = persp; var asasMatrix : Matrix3D = rotationMatrix.clone(); asasMatrix.invert(); var asasParentMatrix : Matrix3D = rotationParentMatrix.clone(); asasParentMatrix.invert(); asas[i].matrix = asasMatrix; asas[i].parentMatrix = asasParentMatrix; if(persp > 0) { asas[i].x = pointer.x * persp; asas[i].y = pointer.y * persp; if(i == 0) { lineContainer.graphics.moveTo(asas[i].x, asas[i].y); } else { lineContainer.graphics.lineTo(asas[i].x, asas[i].y); } } else { asas[i].x = asas[i].y = -10000000; } } lineContainer.graphics.lineTo(asas[0].x, asas[0].y); } } } class MathFunctions { //Función que se encarga de generar un signo aleatorio... public static function signo() : Number { return Math.random() >= 0.5 ? 1 : -1; } //Función que cambia de degrees a radianes... public static function radians(angle : Number) : Number { return angle * Math.PI / 180; } //Función que cambia de radianes adegrees... public static function degrees(angle : Number) : Number { return angle * 180 / Math.PI; } //Función que se encarga de la combinatoria entre dos números... public static function combinatoria(n : Number, i : Number) : Number { return factorial(n) / (factorial(i) * factorial(n - i)); } //Función que calcula el factorial de un número entero public static function factorial(n : Number) : Number { var i : Number; var salida : Number = 1; if(n != 0) { for(i = 1;i <= n; i++) { salida *= i; } } else { salida = 1; } return salida; } //Fin del programa.... } import flash.geom.Vector3D; /** * @author hector arellano * Clase que se encarga de generar una parametrización * Bezier de trayectoria o superficie. * */ class Bezier { //Variable que permite tener los datos de la combinatoria para cada grado... private static var combinatoriaData : Array = new Array(); combinatoriaData[0] = [0]; combinatoriaData[1] = [1]; //A partír de 2 se tienen la cantidad mínima de puntos para interpolar combinatoriaData[2] = new Array(1, 1); combinatoriaData[3] = new Array(1, 2, 1); combinatoriaData[4] = new Array(1, 3, 3, 1); combinatoriaData[5] = new Array(1, 4, 6, 4, 1); combinatoriaData[6] = new Array(1, 5, 10, 10, 5, 1); combinatoriaData[7] = new Array(1, 6, 15, 20, 15, 6, 1); combinatoriaData[8] = new Array(1, 7, 21, 35, 35, 21, 7, 1); combinatoriaData[9] = new Array(1, 8, 28, 56, 70, 56, 28, 8, 1); combinatoriaData[10] = new Array(1, 9, 36, 84, 126, 126, 84, 36, 9, 1); combinatoriaData[11] = new Array(1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1); combinatoriaData[12] = new Array(1, 11, 56, 165, 330, 462, 462, 330, 165, 56, 11, 1); //Variables estáticas que permiten guardar los valores de segmentación de la malla (evita recalcular la cantidad de puntos...) private static var Ne : uint = 0; private static var Nn : uint = 0; private static var paramsE : Array = new Array(); private static var paramsN : Array = new Array(); //Variable que contiene las constantes de la curva bezier para un grado y una parametrización (cantidad de puntos) fija. private static var coeficientesCurva : Array = new Array(); //Función que calcula los coeficientes de la curva... public static function bezier(t : Number, controlPoints : Array) : Vector3D { var salida : Vector3D = new Vector3D; var coeficientes : Array = new Array(); var i : uint; var n : uint = controlPoints.length - 1; //Si los valores de la combinatoria para la cantidad de puntos no estan definidos, los defino... if(combinatoriaData[controlPoints.length] == undefined) { combinatoriaData[controlPoints.length] = setCoeficients(n); } //Determino los coeficientes... for (i = 0;i <= n; i++) { coeficientes[i] = combinatoriaData[controlPoints.length][i] * Math.pow(t, i) * Math.pow((1 - t), (n - i)); } //Obtengo los valores de salida del vector3D... for(i = 0;i <= n; i++) { salida.x += coeficientes[i] * controlPoints[i].x; salida.y += coeficientes[i] * controlPoints[i].y; salida.z += coeficientes[i] * controlPoints[i].z; } salida.w = 1; return salida; } //Función que se encarga de obtener un conjunto de puntos xyz agrupados en un array para una curva bezier... public static function bezierPoints(m : uint, controlPoints : Array) : Vector.<Vector3D> { var salida : Vector.<Vector3D> = new Vector.<Vector3D>(); var n : uint = controlPoints.length - 1; var i : uint; var j : uint; //Si los valores de la combinatoria para la cantidad de puntos no estan definidos, los defino... if(combinatoriaData[controlPoints.length] == undefined) { combinatoriaData[controlPoints.length] = setCoeficients(m); } //Si no hay un arreglo que guarde la referencia para un grado definido, se define... if(coeficientesCurva[n] == undefined) { coeficientesCurva[n] = new Array(); } //Si no hay un arreglo que guarde los coeficientes para "m" puntos se define... //Se guarda un arreglo de cuatro dimensiones según la siguiente definición... // //coeficientes[n][m][j][i] donde: //n : grado, //m : cantidad de puntos a parametrizar, //j : vector de coeficientes para un valor de parametrización perteneciente al rango [0, 1] //i : coeficientes a multiplicar por cada punto para la parametrización anterior... // if(coeficientesCurva[n][m] == undefined) { coeficientesCurva[n][m] = new Array(); for (j = 0;j < m; j++) { //Defino la parametrización... var delta : Number = j / (m - 1); coeficientesCurva[n][m][j] = new Array(); for(i = 0;i <= n; i++) { coeficientesCurva[n][m][j].push(combinatoriaData[controlPoints.length][i] * Math.pow(delta, i) * Math.pow((1 - delta), (n - i))); } } } //Obtengo los distintos puntos que componen el vector de salida... for(j = 0;j < m; j++) { var pointer : Vector3D = new Vector3D(); for(i = 0;i <= n; i++) { pointer.x += coeficientesCurva[n][m][j][i] * controlPoints[i].x; pointer.y += coeficientesCurva[n][m][j][i] * controlPoints[i].y; pointer.z += coeficientesCurva[n][m][j][i] * controlPoints[i].z; } pointer.w = 1; salida.push(pointer); } return salida; } //Función que se encarga de conseguir todos los puntos de una malla generada por bordes... //Entrega los puntos ordenados de la siguiente manera suponiendo un arreglo de 3X3... // // 0 1 2 // 3 4 5 // 6 7 8 // public static function getPatch(Xt0 : Vector3D, Xt1 : Vector3D, Xb0 : Vector3D, Xb1 : Vector3D, BT : Array, BB : Array, BL : Array, BR : Array, Ne : uint = 10, Nn : uint = 10) : Array { var i : uint; var points : Array = new Array(); //Obtengo los puntos de las curvas para interpolar... var b_top : Vector.<Vector3D> = Bezier.bezierPoints(Ne, BT); var b_bottom : Vector.<Vector3D> = Bezier.bezierPoints(Ne, BB); var b_left : Vector.<Vector3D> = Bezier.bezierPoints(Nn, BL); var b_right : Vector.<Vector3D> = Bezier.bezierPoints(Nn, BR); //Genero los parámetros si no estan definidos... if(Bezier.Ne != Ne && Bezier.Nn != Ne) { Bezier.Ne = Ne; Bezier.Nn = Nn; paramsN = []; paramsE = []; for (i = 1;i <= Nn; i++) { paramsN.push((i - 1) / (Nn - 1)); } for (i = 1;i <= Ne; i++) { paramsE.push((i - 1) / (Ne - 1)); } } //Genero el arreglo de puntos... var j : uint; var jMax : uint = paramsN.length; var iMax : uint = paramsE.length; for (j = 0;j < jMax; j++) { for (i = 0;i < iMax; i++) { points.push(TFI(paramsE[i], paramsN[j], b_top[i], b_bottom[i], b_left[j], b_right[j], Xt0, Xt1, Xb0, Xb1)); } } return points; } //Función que permite liberar la memoria de la clase Bezier... public static function clearMemory() : void{ coeficientesCurva = []; paramsN = []; paramsE = []; combinatoriaData = []; coeficientesCurva = paramsN = paramsE = combinatoriaData = null; } //Función que genera una interpolación tranfinita TFI para un grupo de cuatro curvas de borde bezier. //Se pasan los puntos de borde Xt0, Xt1, Xb0, Xb1 y los valores e, n definidos de 0 a 1 para pasar de //estado plano al definido por las cuatro curvas... se busca generar los puntos P intermedios... // // // Xt0 Xt Xt Xt Xt Xt Xt Xt1 // Xl Xr // Xl Xr // Xl P Xr // Xl Xr // Xl Xr // Xl Xr // Xl Xr // Xb0 Xb Xb Xb Xb Xb Xb Xb1 // // private static function TFI(e : Number, n : Number, Xt : Vector3D, Xb : Vector3D, Xl : Vector3D, Xr : Vector3D , Xt0 : Vector3D, Xt1 : Vector3D, Xb0 : Vector3D, Xb1 : Vector3D) : Vector3D { var TFIPoint : Vector3D = new Vector3D(); //Evalúo la interpolación para cada coordenada del punto, x, y, z... TFIPoint.x = (1 - n) * Xt.x + n * Xb.x + (1 - e) * Xl.x + e * Xr.x - (e * n * Xb1.x + e * (1 - n) * Xt1.x + n * (1 - e) * Xb0.x + (1 - n) * (1 - e) * Xt0.x); TFIPoint.y = (1 - n) * Xt.y + n * Xb.y + (1 - e) * Xl.y + e * Xr.y - (e * n * Xb1.y + e * (1 - n) * Xt1.y + n * (1 - e) * Xb0.y + (1 - n) * (1 - e) * Xt0.y); TFIPoint.z = (1 - n) * Xt.z + n * Xb.z + (1 - e) * Xl.z + e * Xr.z - (e * n * Xb1.z + e * (1 - n) * Xt1.z + n * (1 - e) * Xb0.z + (1 - n) * (1 - e) * Xt0.z); TFIPoint.w = 1; return TFIPoint; } //Función que realiza un set de los coeficientes en caso que cantidad de puntos sean distintos a los valores almacenados... private static function setCoeficients(n : Number) : Array { var datos : Array = new Array(); var i : Number; for (i = 0;i <= n; i++) { datos.push(MathFunctions.combinatoria(n, i)); } return datos; } //fín del programa.... } import flash.geom.Point; import flash.geom.Vector3D; class Particula { public var next : Particula = null; public var x : Number; public var y : Number; public static var focus : Number = 300; public static var zoom : Number = 0; private var persp : Number; public function Particula() { //En la posición w del vector se define la modificación por la música.... } public function set position(vector : Vector3D) : void { persp = zoom * focus / (focus + vector.z); if(persp > 0) { this.x = vector.x * persp; this.y = vector.y * persp; } else { this.x = this.y = -10000000; } } public function get XY() : Point { return new Point(this.x, this.y); } //Fín del programa... } import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Matrix3D; import flash.geom.Vector3D; class ControlPoint extends Sprite { public var persp : Number; public var matrix : Matrix3D; public var parentMatrix : Matrix3D; //Variable que guarda la referencia al punto ... private var id : uint; private var lado : uint = 7; //Variable que guarda una referencia a la perspectiva utilizada en las transformaciones... public static var focus : Number = 0; public static var zoom : Number = 0; public function ControlPoint(id : uint) { this.id = id; graphics.lineStyle(0, 0X00FF00, 1); graphics.beginFill(0); graphics.drawRect(-lado / 2, -lado / 2, lado, lado); this.buttonMode = true; this.addEventListener(MouseEvent.MOUSE_DOWN, update3DPosition); } //Función que se encarga de actualizar la posición 3D del asa de tangencia en base a su posición 2D... private function update3DPosition(e : MouseEvent) : void { this.startDrag(); trace(id); this.addEventListener(Event.ENTER_FRAME, updatePosition); stage.addEventListener(MouseEvent.MOUSE_UP, stopDragEvent); } //Función que se encarga de actualizar la posición... private function updatePosition(e : Event) : void { var salida : Vector3D = new Vector3D(); salida.x = this.x / persp; salida.y = this.y / persp; salida.z = focus * (zoom - persp) / persp; salida = parentMatrix.deltaTransformVector(salida); salida = matrix.deltaTransformVector(salida); dispatchEvent(new ControlPointEvent(ControlPointEvent.CHANGE, salida, id)); } //Función que se encarga de parar el arrastre... private function stopDragEvent(e : MouseEvent) : void { this.stopDrag(); this.removeEventListener(Event.ENTER_FRAME, updatePosition); stage.removeEventListener(MouseEvent.MOUSE_UP, stopDragEvent); } //Fín del programa... } import flash.events.Event; import flash.geom.Vector3D; class ControlPointEvent extends Event { //Variable estática del tipo de evento.. public static const CHANGE : String = "CHANGE"; //Puntos del arreglo... public var vector : Vector3D; public var cp : uint; //Constructor... public function ControlPointEvent(type : String, vector : Vector3D, cp : uint, bubbles : Boolean = false, cancelable : Boolean = false) : void { super(type, bubbles, cancelable); this.cp = cp; this.vector = vector; } //Función que devuelve un clon del evento... public override function clone() : Event { return new ControlPointEvent(type, vector, cp, bubbles, cancelable); } } Particles 3D