lunes, 8 de diciembre de 2008

JavaFX: Circulo arrastrable que cambia de color


Este ejemplo dibuja un circulo de radio "rad=60", que cambia de color y que puede ser arrastrado con el mouse. Además, incluye una función que se "dispara" al cambiar el color del círculo (color=0 on replace oldColor {})




package javafxclasses;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.text.*;
import javafx.scene.input.*;
import javafx.scene.shape.Circle;
import javafx.scene.transform.Transform;

def rad=60.0;
var x1=rad;
var y1=rad;
var dx:Number;
var dy:Number;

var colors = [Color.RED, Color.BLUE, Color.GREEN, Color.YELLOW];

var color=0 on replace oldColor {
println("\nALERT! El color cambio!");
println("Antiguo color: {oldColor}");
println("Nuevo color: {color}");
}

Stage {
title: "Application title"
width: 400
height: 400

scene: Scene {
content: [

Circle {

centerX: bind x1;
centerY: bind y1;
radius: rad
fill: bind colors[color]

onMousePressed: function(e) {

// obtiene distancia entre el centro del círculo
// arrastrado y la posición del mouse al hacer click
dx = x1 - e.x;
dy = y1 - e.y;

if (++color==sizeof colors)
color=0;
}

onMouseDragged : function(e) {
// calcula el centro del círculo aplicando la
// diferencia respecto al centro del círculo al
// comenzar a arrastrarlo
x1 = e.x + dx;
y1 = e.y + dy;
}

}

]

}

}

sábado, 6 de diciembre de 2008

JavaFX: La rueda de la fortuna


Este ejemplo genera una "rueda" de colores de distinta intensidad y que gira... el ejemplo original viene incluido con Netbeans 6.5, yo sólo le agregué el giro. Pueden modificar la velocidad del giro cambiando el tiempo en el Timeline (0.5s, o medio segundo para una vuelta completa).

Básicamente, lo único que agregué fue un rotate: bind angle; asociado a la rueda ya generada incluida en la escena (Scene) y una "linea de tiempo" (Timeline) que incrementa el ángulo entre 0º y 360º, en el tiempo especificado. Una vez que llega a 360º, comienza por el ángulo 0º. Así queda la impresión que la ruega gira indefinidamente.



package color;

import javafx.scene.Group;
import javafx.scene.CustomNode;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcType;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.animation.Timeline;
import javafx.lang.FX;

class ColorWheelNode extends CustomNode {

var segments : Number = 12;
var steps : Number = 6;
var radius : Number = 95;
var stripes : Arc[];

override function create() : Node {
return Group {
content: bind stripes
};
}

init {
// Set radius
var r = radius;
var rStep = radius / steps;
var colors : Color[];
for( i in [1..
segments + 1] ) {
insert Color.rgb( 255, 255, 0 ) into colors;
}
for( i in [0..
steps - 1] ) {
colors[1] = Color.rgb( 255 - ( 255 / steps * i ), 255 - ( 255 / steps * i ), 0 );
colors[2] = Color.rgb( 255 - ( 255 / steps ) * i, ( 255 / 1.5 ) - (( 255 / 1.5 ) / steps ) * i, 0 );
colors[3] = Color.rgb( 255 - ( 255 / steps ) * i, ( 255 / 2 ) - ( ( 255 / 2 ) / steps ) * i, 0 );
colors[4] = Color.rgb( 255 - ( 255 / steps ) * i, ( 255 / 2.5 ) - (( 255 / 2.5 ) / steps ) * i, 0 );
colors[5] = Color.rgb( 255 - ( 255 / steps ) * i, 0, 0 );
colors[6] = Color.rgb( 255 - ( 255 / steps ) * i, 0, ( 255 / 2 ) - (( 255 / 2 ) / steps ) * i );
colors[7] = Color.rgb( 255 - ( 255 / steps ) * i, 0, 255 - ( 255 / steps ) * i );
colors[8] = Color.rgb(( 255 / 2 ) - (( 255 / 2 ) / steps ) * i, 0, 255 - ( 255 / steps ) * i );
colors[9] = Color.rgb( 0, 0, 255 - ( 255 / steps ) * i );
colors[10] = Color.rgb( 0, 255 - ( 255 / steps ) * i, ( 255 / 2.5 ) - (( 255 / 2.5 ) / steps ) * i );
colors[11] = Color.rgb( 0 , 255 - ( 255 / steps ) * i, 0 );
colors[12] = Color.rgb(( 255 / 2 ) - (( 255 / 2 ) / steps ) * i, 255 - ( 255 / steps ) * i, 0 );
for( j in [1..segments] ) {
var c = colors[
j.intValue()];
insert Arc {
centerX: 100,
centerY: 100
radiusX: r ,
radiusY: r
startAngle: 360 / segments * ( segments - j - 0.5)
length: 360 / segments
fill: c
type: ArcType.ROUND
} into stripes;
}
r -= rStep;
}
}
}


var angle:Integer;

Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse: false
keyFrames: [
at (0s) {angle => 0},
at (0.5s) {angle => 360},
]
}.play();



Stage {
scene: Scene {
fill: Color.GRAY
content: ColorWheelNode {
rotate: bind angle;
}
}

width: 216
height: 232
title: "Color Wheel"
}

JavaFX: Pelota 3D con sombra rebotando sobre video


Ok, solo para que vayan descubriendo el poder de JavaFX. Ayer yo no sabía NADA de JavaFX script. Luego de jugar unas pocas horas, ya tengo este ejemplo: Una "habitación" que tiene en el piso un VIDEO y sobre el cual rebota una bola de apariencia 3D, que incluso se aleja del frente de la pantalla y que proyecta una sombra que varia tanto en intensidad como tamaño dependiendo de la altura a la que se encuentra la bola.



Por ahora sólo les dejo el código. Más adelante postearé otro artículo con la explicación del mismo.

Atención: este ejemplo requiere el mediacomponent.jar entre los Libraries del proyecto. Si crean un proyecto demo basado en el SimpleMediaPlayer (incluido con Netbeans 6.5) y luego reemplanzan el código de Main.fx, la biblioteca mediacomponent.jar quedará incluida en el proyecto automáticamente. Si no, tienen que buscar donde se encuentra en su disco la biblioteca e incluirla entre los libraries del proyecto haciendo click con el botón derecho del mouse en Libraries (a la izquierda en Netbeans) y seleccionado Add Library... (ahí tienen que decirle donde está el archivo). Pero como les digo, lo más fácil es comenzar con un proyecto de ejemplo de mediaplayer y reemplazar el código de Main.fx por el código que incluyo a continuación:

package simplevideoplayer;

import javafx.stage.Stage;
import javafx.scene.Scene;
import com.sun.fxmediacomponent.*;
import javafx.scene.Group;
import javafx.scene.shape.Circle;
import javafx.scene.paint.Color;
import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.scene.paint.Stop;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.shape.Ellipse;
import javafx.scene.paint.*;
import javafx.scene.effect.*;
import javafx.scene.transform.*;
import javafx.scene.shape.Line;


var mediaUrl:String = "http://sun.edgeboss.net/download/sun/media/1460825906/1460825906_2956241001_big-buck-bunny-640x360.flv";
if(FX.getArgument("mediaURL")!=null) {
mediaUrl = FX.getArgument("mediaURL").toString();
}

def vidWidth = 1024;
def vidHeight = 576;

var X=0;
var Y=1;
var Z=0;
var VX=230; // perspectiva horizontal para video
var VY=376; // perspectiva vertical para video
var S:Number;

Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse: true
keyFrames: [
at (0s) {X => 100},
at (2.3s) {X => 924 tween Interpolator.LINEAR},
]
}.play();


Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse: true
keyFrames: [
at (0s) {Y => 100},
at (0.5s) {Y => 476 tween Interpolator.EASEIN},
]
}.play();


Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse: true
keyFrames: [
at (0s) {Z => 40},
at (10s) {Z => 100 tween Interpolator.LINEAR},
]
}.play();


Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse: true
keyFrames: [
at (0s) {S => 0.5},
at (0.5s) {S => 1 tween Interpolator.EASEIN},
]
}.play();


var mediaBox:MediaComponent = MediaComponent {
// set the media and make the component visible
mediaSourceURL : mediaUrl
visible:true
// the position and size of the media on screen
translateX: 0
translateY: 0
mediaViewWidth : vidWidth
mediaViewHeight: vidHeight
// determines if the control bar is below the media or on top with a fade in
staticControlBar: false
// make the movie play as soon as it's loaded
mediaPlayerAutoPlay: true
// set the volume
volume: 1.0

effect: PerspectiveTransform {
ulx: bind VX uly: bind VY
urx: bind 1024-VX ury: bind VY
lrx: 1024 lry: 576
llx: 0 lly: 576
}
};


// Currently these variables have to be set after the component is created

// Description to be displayed when the mouse is over the component
mediaBox.mediaDescriptionStr = "A well-tempered rabbit finds his day spoiled by the rude actions of the...";
// Duration to be displayed when the mouse is over the component
mediaBox.mediaDurationStr = "9:56";
// Title to be displayed when the mouse is over the component
mediaBox.mediaTitleStr = "Big Buck Bunny";


Stage {

title: "Simple Media Player"

scene: Scene{

width: vidWidth
height: vidHeight

stylesheets: [
MediaComponent.css_skins
]

content: [

mediaBox,

Line {
strokeWidth: 3.0
stroke: Color.BLACK
startX: bind VX
startY: bind VY
endX: 0
endY: 576
}

Line {
strokeWidth: 3.0
stroke: Color.BLACK
startX: bind VX
startY: bind VY
endX: bind VX
endY: 0
}

Line {
strokeWidth: 3.0
stroke: Color.BLACK
startX: bind VX
startY: bind VY
endX: bind 1024-VX
endY: bind VY
}

Line {
strokeWidth: 3.0
stroke: Color.BLACK
startX: bind 1024-VX
startY: bind VY
endX: bind 1024-VX
endY: 0
}

Line {
strokeWidth: 3.0
stroke: Color.BLACK
startX: bind 1024-VX
startY: bind VY
endX: 1024
endY: 576
}


Ellipse {
centerX: 0, centerY: 0 radiusX: 100 radiusY: 30
fill: Color.BLACK
opacity: bind S - 0.2
fill: RadialGradient {
proportional: false radius: 100
centerX: 0 centerY: 0
focusX: 0 focusY: 0
stops: [
Stop { offset: 0.0 color: Color.web("#333333", 1.0) },
Stop { offset: 0.4 color: Color.web("#444444", 1.0) },
Stop { offset: 1.0 color: Color.web("#FFFFFF", 0.0) }
]
}
transforms: bind [
Transform.translate(X , 476 + Z - 100 + Z / 2)
Transform.scale(S * Z / 100.0, S * Z / 100.0)
]
}

Circle {
centerX: 0 centerY: 0 radius: 100
stroke: Color.BLACK
fill: RadialGradient {
proportional: false radius: 100
centerX: 0 centerY: 0
focusX: 20 focusY: -50
stops: [
Stop { offset: 0.0 color: Color.WHITE },
Stop { offset: 0.85 color: Color.RED },
Stop { offset: 1.0 color: Color.BLACK }
]
}
transforms: bind [
Transform.translate(X , Y + Z - 100)
Transform.scale(Z/100.0, Z/100.0)
]

}

]

}

}

viernes, 5 de diciembre de 2008

Mi primer post sobre JavaFX... se va la bolita


La verdad es que no tengo mucha idea sobre JavaFX, mi primer contacto con este nuevo lenguaje de programación fue ayer, así que podrán imaginar cuanto sé del tema.

Mi intención es sólo compartir lo que vaya descubriendo, de manera de facilitarle la vida a otros que vengan después de mi ;D

De lo que ví, creo que hay cosas que me han gustado bastante. Para comenzar, me sorprendió un par de cosas:

1) El poco código que se requiere para hacer "algo" que haga algo ;D ... si saben programar en Java o C# sabrán cuanto código se necesita sólo para escribir el típico "Hola Mundo!!". Con JavaFX es tan corto como:

println("Hola Mundo");

¿Van captando los beneficios? ;D

2) Descubrí la instrucción BIND, algo que por lo menos no tiene ningún otro lenguaje de programación que conozca.

¿Qué hace esta instrucción?

En palabras simples, permite "amarrar" los cambios de estados (por ejemplo) de las variables y disparar "eventos" a través del código, sin hacer nada. Aquí les va un ejemplo muy básico:




package myfirstjavafx;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.ext.swing.SwingButton;
import javafx.scene.shape.Circle;
import javafx.scene.paint.Color;

var count=0;
var colors = [Color.RED, Color.BLUE, Color.YELLOW];
var newcolors = [Color.PURPLE, Color.GREEN, Color.CYAN];

Stage {
title: "Application title"
width:250
height:250

scene: Scene {
content: [

Circle {
centerX: 100
centerY: 100
radius: 100
fill: bind colors[count];
},

SwingButton {
text: "Rota colores"
rotate:20
translateX:100
translateY:100

action: function() {
if (++count == sizeof colors)
count=0;
}
},

SwingButton {
text: "Agrega nuevo color"
rotate:20
translateX:100
translateY:130

action: function() {
if (sizeof newcolors > 0) {
var col=newcolors[0];
delete newcolors[0];
insert col into colors;
println("Adding {col} to the current list")
}
}
}

]
}
}

De este ejemplo se pueden aprender un par de cosas: ¿Qué hace?

Esta "aplicación" tiene tres elementos visibles. Un CIRCULO y dos BOTONES. Cuenta además con dos listas de colores (arreglos o SECUENCIAS). Al oprimir el primer botón, rota los colores aplicados al círculo, contenidos en la primera secuencia (COLORS). Cuando llega al último color disponible, regresa al primero. Si se oprime el segundo botón, "trasvasija" el primer color de la secuencia NEWCOLORS a la lista de colores aplicados al circulo (COLORS). Hace eso mientras haya nuevos colores disponibles en la secuencia (NEWCOLORS).

1) Al definir el escenario (Stage), se está creando una ventana o area de ejecución de la aplicación. Con Width y Height se declara el tamaño de esa ventana o espacio de ejecución (en caso que sea un applet, supongo)

2) La escena (Scene) viene siendo el interior de la ventana que alberga los contenidos (content).

3) Define el círculo con su tamaño y radio, y aquí viene algo interesante. DEFINE con BIND que... el color usado para rellenar el círculo, es el color en la secuencia COLORS al que apunta COUNT. Lo interesante es que luego basta que en alguna parte del programa cambies el valor de COUNT para que actualice el círculo con el nuevo color. No es necesario hacer nada adicional.

4) Define el primer botón (el rotate sólo GIRA el botón en 20º, lo hice únicamente para probar que las interfaces pueden ser mas "divertidas" y de manera muy sencilla), y le asigna una ACCION al botón (action), que se ejecutará al ser oprimido.

¿Qúe hace este botón?

Simplemente incrementa COUNT hasta alcanzar el número máximo de colores disponibles en la lista COLORS. Al hacerlo, el color del círculo cambia (si te fijas, al incrementar COUNT no ejecuta ninguna rutina adicional o método, basta con cambiar el puntero y este actualiza el color del círculo [obteniendo el nuevo color al que apunta COUNT en la lista COLORS]).

Una vez que llega al último color de la lista, regresa al primer color (COUNT=0).

5) Define el segundo botón y le asigna la siguiente acción: primero, verifica si hay nuevos colores disponibles en la segunda lista (secuencia NEWCOLORS), con un simple "sizeof newcolors> 0", es decir, valida si hay un color disponible en la lista newcolors antes de proseguir.

A continuación, obtiene el primer color disponible en la lista newcolors (var col=newcolors[0];) y lo borra de la lista (delete newcolors[0];). Acto seguido, agrega el color obtenido de newcolors y lo agrega al final de la lista COLORS (insert col into colors;)

Listo. Funciona.... sólo tienes que oprimir el botón PLAY (>) en la barra de Netbeans.

PD: Si da algún error, anda al nombre del proyecto en la columna izquierda (arriba) y haz click con el botón derecho del mouse y selecciona SET AS MAIN PROJECT (en caso que tengas otro proyecto abierto).