Archivo

Archivo para la categoría ‘javafx’

¿Qué hacer mientras esperamos que Halo Reach beta arranque?

Lunes, 3 de mayo de 2010

Downloading Halo Reach Beta
Bajándome el Beta, como otro de los millones de fanáticos de este juego

Bueno, ya a esta hora el beta de Halo Reach está en pleno apogeo. Los gráficos simplemente no decepcionan, aunque como buen beta tiene varias verrugas (el servidor se cayó en más de una ocasión, los controles son un poco distintos, etc). Pero en general, se ve prometedor.

Para celebrar, les invito a que corran esta aplicación escrita en JavaFX, la cual escribí para la ocasión: HaloWeb.

Si les gusto les puedo explicar el código a continuación:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
package haloweb;
 
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.image.ImageView;
import javafx.scene.image.Image;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.scene.shape.Rectangle;
import javafx.scene.effect.DropShadow;
import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.scene.control.Button;
import javafx.scene.control.Tooltip;
import javafx.scene.Cursor;
 
var isPlaying = false;
 
var currentCursor = Cursor.WAIT;
 
/**
 * Simple application that plays a Halo Reach video while doing other eye candy
 * effects, or an excuse to do something while Bungie keeps rebooting the Halo
 * Reach Beta every 10 minutes :)
 * @author josevnz at kodegeek dot com, http://kodegeek.com/
 * License: BSD
 */
 
 
def const = Constants {
    videoUrl: "http://download.halowaypoint.com/content/waypoint/assets/videos/web/032373081197/Halo_Reach_Multiplayer_Trailer_ESRB_360p_ST.wmv";
    backgroundUrl: [
        "http://www.bungie.net/images/Games/Reach/images/visualID/REACH_KeyArt_Horizontal_1920x1080.jpg",
        "http://www.bungie.net/images/Games/Reach/images/concept_art/ReachConcept_NobleTeam.jpg"
        ];
    width:800;
    height: 600;
}
 
 
def videoWidth = 640;
def videoHeight = 360;
 
def backImage1 = Image {
		url: const.backgroundUrl[0]
};
 
def backImage2 = Image {
		url: const.backgroundUrl[1]
};
 
var backOpacity: Number = 0.0;
 
// Animation to show transition between the two images
var backgroundTimeline = Timeline {
    keyFrames: [
        at(0s) {
        backOpacity => 0.0;
        },
        at(1s) {
        backOpacity => 1.0 tween Interpolator.LINEAR;
        }
    ]
};
 
// First background
var backgroundView1 = ImageView {
	image: backImage1
        fitHeight: const.height
        fitWidth: const.width
        preserveRatio: true
        opacity: 1.0
        clip: Rectangle {
            height:const.height
            width: const.width
            arcWidth: 50,
            arcHeight: 50
        }
        effect: DropShadow {
            offsetX:10
            offsetY:10
            color: Color.BLACK
        }
 
}
 
// Background with rollover
var backgroundView2 = ImageView {
	image: backImage2
        fitHeight: const.height
        fitWidth: const.width
        preserveRatio: true
        opacity: bind backOpacity
        clip: Rectangle {
            height:const.height 
            width: const.width
            arcWidth: 50,
            arcHeight: 50
        }
        effect: DropShadow {
            offsetX:10
            offsetY:10
            color: Color.BLACK
        }
 
        // The second image is added last, run transition from there
        onMouseEntered: function(event):Void {
            backgroundTimeline.rate = 1; // Forward
            backgroundTimeline.play();
        }
 
        onMouseExited: function(event):Void {
            backgroundTimeline.rate = -1; // Backwards
            backgroundTimeline.play();
        }
}
 
def video = Media {
		source: const.videoUrl
                onError: function(error):Void {
                    println("No se pudo cargar el video: {error.message}");
                }
	}
 
def videoPlayer = MediaPlayer {
	media : video
        autoPlay:false
        volume:0.6
        repeatCount: MediaPlayer.REPEAT_NONE
}
 
var isVisible: Boolean = false;
 
def haloVideoView = MediaView {
	preserveRatio: true
	mediaPlayer : videoPlayer
        visible:bind isVisible
        fitHeight: videoHeight;
        fitWidth: videoWidth;
        translateX: (const.width - videoWidth) / 2
        translateY: ((const.height - videoHeight) / 2) - 60 // Little hack here
 
        // Control the video replay
        onMousePressed: function(event):Void {
            if (isPlaying) {
                videoPlayer.pause();
            } else {
                videoPlayer.play();
            }
            isPlaying = not isPlaying;
            currentCursor = Cursor.DEFAULT
        }
 
        onMouseEntered:function(event): Void {
            if (not isPlaying) {
                currentCursor = Cursor.CROSSHAIR
            }
        }
 
        onMouseExited:function(event): Void {
            currentCursor = Cursor.DEFAULT
        }
 
}
 
// Prepare the glowing text
def kodegeekText = GlowingText {
    height: const.height
    width: const.width
    text: "http://KodeGeek.com/"
}
 
def exitButton = Button {
	text: "!Salir¡"
        translateX: const.width - (const.width * .10)
        translateY: 20
        visible: bind isVisible
	action: function() {
		FX.exit();
	}
        tooltip: Tooltip {
           text: "Haga click aquí para salir de esta aplicación"
        }
 
}
 
var videoTimeline = Timeline {
    keyFrames: [
        at (5s) {
            isVisible => true;
            currentCursor => Cursor.DEFAULT;
        }
    ]
}
 
 
// Show the final scene
Stage {
 
    title: "KodeGeek está emocionado con Halo Reach!"
    scene: Scene {
        width: const.width
        height: const.height
        fill: Color.TRANSPARENT
        content: [
            backgroundView1,
            backgroundView2,
            haloVideoView,
            kodegeekText,
            exitButton
        ]
        cursor: bind currentCursor;
    }
 
}
 
videoTimeline.play();

Básicamente tenemos 2 fondos (backgroundView1, backgroundView2) de los cuales uno de ellos tiene opacidad variable, la cual cambia en cuando el usuario pone el cursor del ratón en el área de la aplicación. Los otros elementos son el vídeo (haloVideoView, el cual se puede parar o arrancar al hacer click en este), un texto “que brilla” (kodegeekText, con super publicidad :) ), y botón que mata la aplicación (exitButton). Si usted tiene familiaridad con otros lenguajes como Flash o JavaScript entonces no debería tener problemas leyendo el código.

¿Como hacemos que el texto brille? Es sencillo, creamos un nodo a la medida el cual corre una animación infinita la cual tiene un efecto especial (Glow) sobre el texto (la variable del efecto cambia ya que le hacemos un ‘bind’ con el nivel de brillo):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package haloweb;
import javafx.scene.CustomNode;
import javafx.scene.Node;
import javafx.scene.effect.Glow;
import javafx.scene.effect.Lighting;
import javafx.scene.effect.light.DistantLight;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
 
/**
 * Simple glowing text node
 * @author josevnz at kodegeek dot com, http://kodegeek.com/
 * License: BSD
 */
 
var glowLevel: Number = 1.0;
 
var glowTimeline: Timeline = Timeline {
    repeatCount: Timeline.INDEFINITE
    keyFrames: [
        KeyFrame {
            time: 600ms
            values: [
                glowLevel => .7 tween Interpolator.EASEBOTH
            ]
        }
    ]
}
 
public class GlowingText extends CustomNode {
 
    public-init var width: Number;
    public-init var height: Number;
    public-init var text: String;
 
    public override function create() :Node {
 
        glowTimeline.play();
 
        Text {
            font : Font {
		size: 24
            }
            translateX: width - (width * 0.30)
            translateY: height - (height * 0.25)
            content: text
            fill: Color.DARKORANGE
            effect: Glow {
                level: bind glowLevel
                input: Lighting {
                    light: DistantLight {azimuth: -135}
                    surfaceScale: 3
                    diffuseConstant: 1.8
                }
            }
        }
   }
 
}

Y una clase de constantes que escribí para este programa. Inútil, en algún momento puliré más la aplicación:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package haloweb;
 
/**
 * Useful constant for the HaloWeb project
 * @author josevnz at kodegeek dot com, http://kodegeek.com/
 * License: BSD
 */
 
public class Constants {
    public-init var videoUrl: String;
    public-init var backgroundUrl: String [];
    public-init var width: Number;
    public-init var height: Number;
    public-init var backgroundWidth: Number;
    public-init var backgroundHeight: Number;
}

No me tomó mucho tiempo escribir el código, pero debo decir que NeBeans 6.9 es una porquería. A cada rató se caia, me daba errores por todos lados y su creador de interfaces gráficas tiene demasiados problemas. A la final armé la aplicación a pedal y bomba, como los machos :) . Por ahora no tengo intenciones de meter este código en Subversion, me da flojera. Avisenme si de verdad quieren este código y con gusto hago el esfuerzo ;)

Ahora los dejo, vamos a ver si el servidor de Bungie está funcionando para continuar jugando el nuevo beta :D

–Jose

java, javafx , , , ,

Aventuras con JavaFX 1.3 y NetBean 6.9

Sábado, 1 de mayo de 2010

Bueno, después de una larga espera Oracle nos trae JavaFX 1.3. El entorno de ejecución promete muchas mejoras, lo único es que el entorno de desarrollo es NetBeans 6.9 BETA.

Si, beta. Tiene una lista de problemas (entre esos la depuración de programas). Sin embargo, si escribo mis ‘unit tests’ debería estar más o menos bien, ¿no es así?

Lo primero que intenté hacer es convertir el método ‘main’ de una de mis clases en Java (no JavaFX) a Junit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*
 * Helper class used to parse and process CVS data comming from an InputStream
 * License: GPL
 * @author josevnz at kodegeek dot com
 */
 
package com.kodegeek.blog.javafx.workout.app;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;
 
 
/**
 *
 * @author josevnz
 */
final public class CsvDataParser {
 
private List <list <Object>>data;
private static final Pattern splitPattern = Pattern.compile(",\\s*");
private SimpleDateFormat format;
private Pattern skipNonNumberPatter = Pattern.compile("^\\D+");
 
/**
 * Expected default number of data observations
 */
public static final int DEFAULT_DAY_COUNT = 365;
 
/**
 * Expected default number of series to display on the same chart
 */
public static final int DEFAULT_SERIES = 50;
 
    /**
     * Constructor
     * @param numSeries expected number of series on the stream
     */
    public CsvDataParser(int numSeries) {
 
        /*
         * Ddata is a list of lists where:
         * data(0): List of dates
         * data(n): Values of each series.
         * data is a rectangular array, every value matches a date
         */
        data = new ArrayList</list><list <Object>>();
        List<object> dates = new ArrayList</object><object>();
        data.add(0, dates);
        for (int i = 1; i < numSeries; i++) {
            List<Object> values =
                    new ArrayList</object><object>();
            data.add(i, values);
        }
 
        format = new SimpleDateFormat("MM/dd/yy");
    }
 
    /**
     * Parse a given input stream for data. Not thread safe. It is assumed than
     * the number of tokens on the stream is constant and there
     * are no data holes. The caller is responsible from closing the stream
     * @param io The data source
     * @throws Exception If there is a problem reading the stream
     */
    public void parse(InputStream io) throws Exception {
        BufferedReader lineRdr = new BufferedReader(new InputStreamReader(io));
        String line = null;
 
        try {
 
            while ((line = lineRdr.readLine()) != null) {
 
                if (skipNonNumberPatter.matcher(line).find()) {
                    continue;
                }
 
                String[] tokens = splitPattern.split(line, -1);
                int numSeries = tokens.length;
                if (numSeries ==0 || tokens[0] == null) {
                    return;
                }
 
                if (numSeries != data.size()) {
                    String error = String.format(
                            "Number of expected tokens is %s, got %s",
                            data.size(),
                            numSeries);
                    throw new Exception(error);
                }
 
                // Store dates as numbers, let the caller do any conversions
                List</object><object> dates = (List</object><object>) data.get(0);
                dates.add(format.parse(tokens[0]).getTime());
 
                // Get the values for each series now (rest of the tokens)
                for (int i = 1; i < numSeries; i++) {
                    List<Object> values = (List</object><object>) data.get(i);
                    values.add(Float.parseFloat(tokens[i]));
                }
 
            }
        } catch (Exception exp) {
            throw exp;
        }
 
    }
 
    /**
     * Get the collected data as a List of Lists. First element of the returned
     * list contains the dates as Epoch long dates, after that the series
     * @return Array of dates as epoch long
     */
    public Long [] getDates() {
        return data.get(0).toArray(new Long[0]);
    }
 
    /**
     * Get the values as a list of
     * @param index Valid ranges go from 1 to n
     * @return Array of float series values
     */
    public Float [] getSeriesValues(int index) {
        if (index < 1 || index > data.size()) {
            throw new IllegalArgumentException(
                    String.format("Invalid series index %d", index));
        }
        return data.get(index).toArray(new Float[0]);
    }
 
 
    /**
     * Unit test, show how the parser works on a CSV file. THIS SHOULD BE A
     * JUNIT TEST INSTEAD!!!
     * Program expects the property.
     * @param args List of files to parse. Each one like: numTokens:fullpathFile
     * @throws Exception If there are any problems
     */
    public static void main(final String [] tokens) throws Exception {
        for (String token: tokens) {
            String [] parts = token.split(":", -1);
            InputStream in = null;
            try {
                int nTokens = Integer.parseInt(parts[0]);
                File f = new File(parts[1]);
                if (f.canRead() && f.isFile()) {
                    in = new FileInputStream(f);
                    CsvDataParser instance = new CsvDataParser(nTokens);
                    instance.parse(in);
                    in.close();
                    Long [] dates = instance.getDates();
                    Float [] values = instance.getSeriesValues(1);
                    for (int i = 0; i < dates.length; i++) {
                        System.out.println(
                                String.format("%s -> %s",
                                new Date(dates[i]), values[i]));
                    }
                }
            } catch (NumberFormatException nfe) {
                System.err.println(
                        String.format(
                        "Invalid expected token value: '%s'", parts[0]));
            } catch (Exception exp) {
                throw exp;
            } finally {
                if (in != null) {
                    in.close();
                }
            }
        }
    }
 
}
</object></list>

Sin embargo la interfaz gráfica me abofetea con este error: No tests root folder was found

Que fastidio. No soy un experto en NetBeans (me defiendo mucho más con Eclipse), así que en vez de estar echando código ando resolviendo estos problemas. ¡Que perdida de tiempo!

Me pregunto si Oracle piensa competir en serio con Adobe Flash si su editor principal aún tiene este tipo de problemas.

Amanecerá y veremos, por los momentos ando tratando de resolver el asunto yo mismo, al mismo tiempo pedí ayuda en los foros oficiales. Una vez resuelto, podré correr Junit para JavaFX como lo indica este excelente blog.

java, javafx, kodegeek, oracle

¡JavaPassion ahora es un curso pago!

Miércoles, 3 de febrero de 2010

El autor del curso decidió convertirlo en un curso pago:

On 2/3/2010 4:33 PM, Alex Ruiz wrote:
Pedro and Dante,

From the javapassion website, I can see that the course will be
subscription based, but I never says it requires you to pay money (I
cannot speak for Mr. Shing,) so I think you are jumping into conclusions
too quickly (unless I’m missing something.)

Actually it will be indeed “paid service”. I am not sure how
successful it will be. But I decided to try mainly because
the other choice is “killing javapassion.com” all together.

The price point I am thinking about is $89 per year for unlimited
access to all the courses of “javapassion.com”. (My market research
shows that this is much less expensive compared to other similar
sites which offer “less quality” contents in my opinio. And frankly
my suspicion is that it will be barely enough to sustain “javapassion.com” and me.)


Even if Mr. Shing decides to charge. So what? Mr. Shing already made
public that he left Oracle, and now he is independent. He is completely
free to charge for his knowledge and time, isn’t he? After all, we all
have to make a living.

Thanks for your clarification and support.

-Sang Shin

El está en todo su derecho. Por esa misma razón decidí retirar las soluciones que había publicado a todos los ejercicios anteriores de Source Forge para incentivar a la gente a que pague por este excelente recurso.

¡Los mejores deseos a Sang Shin en esta nueva iniciativa!

-Jose

javafx, programación , ,

Se acabó Sun, ¿qué viene después?

Viernes, 29 de enero de 2010

Esta semana ha sido una semana interesante para la gente que alguna vez utilizó productos de Sun Microsystems. Para no repetir lo obvio los dejo con una lista preparada por James Weaver (un fanático de JavaFX) el cual nos dá su opinión de las cosas que vienen.

Como toda transacción de este tipo, hay ganadores y perdedores. Las cosas cambian y la gente decide moverse para hacer cosas distintas. Por ejemplo, Sang Shin ,el creador e instructor de JavaPassion, mandó un correo de despedida en el cual nos cuenta que piensa seguir otras oportunidades fuera de Sun:

As a result of recent Sun/Oracle merger, I’ve decided to
leave Sun/Oracle and decided to pursue a career of teaching
and consulting.

What this means is that the “javafxhomeworks@sun.com”
homework alias will not work anymore from tomorrow.

A new homework alias has been created and it is

javafxhomeworks@javapassion.com (same address with
different domain name)

Please send your homework to the new address above from
now on.

If you already submitted all the homeworks and personal
information to the old homework address, there is NO
need to resubmit since I have the copies.

Thanks.

-Sang Shin

Otra que me pegó de cerca es que Oracle planea descontinuar a Kenai.com. Para mi eso se traduce en que debo migrar el código de StupidZombie a otro sitio, probablemente Source Forge (pese a sus verrugas):

The Future of Kenai.com
With Sun now a wholly owned subsidiary of Oracle, the acquisition is triggering a consolidation process. Part of this process is the phasing out of the public-facing domain used for the Project Kenai Beta site. This action is being undertaken to provide the best project hosting solution for all of our customers into one location. Minimizing the number of current project hosting sites is a start in this direction. The consolidation process is underway and we will post notices about the plans and timeline as they become publicly available. The end-goal is to ensure we provide even more useful resources for all of the Oracle and Java developer communities.

Stay tuned as we work things through.

Ya hay gente que comenzó a irse debido a la adquisición de Sun por parte de Oracle. Charles Nutter (desarrollador lider de JRuby) nos cuenta desde Twitter:

Sounds like the post-merger bloodletting has begun at Sun/Oracle. Already hearing about some friends getting laid off :(

Al mismo tiempo que Jonathan Schwarts se despide de Sun:
As for where life takes me next, you should follow me via Twitter at openjonathan to find out. I’ll also be rehosting this blog (and again, stay tuned to Twitter by following me here). I expect to do my part to keep things interesting.

Thank you for your support and commitment. I wish you all the best of luck building, taking advantage of (and likely wearing) the future!

Jonathan Schwartz
CEO, Sun Microsystems, Inc.
A Wholly Owned Subsidiary of Oracle Corporation.

No todo es malo, el solapamiento de areas entre Sun y Oracle es mínimo y por los menos Java, JavaFX y NetBeans tienen un futuro promisorio. MySQL tampoco se puede quejar (al menos por los momentos) y este año viene un JavaOne.

Tiempos interesantes, los dejo con un enlace de mi experiencia en Java One del 2008. Me pone algo nostálgico :)

java, javafx, opensource, programación , , , ,

Aprendiendo JavaFX: Animaciones

Domingo, 24 de enero de 2010

Más código del curso de JavaFX. Aún sigo retrazado, la entrega final es el 3 de Febrero pero aún tengo esperanzas de llegar a tiempo :) .

En este caso, estuve trabajando en la lección de animaciones; La idea es hacer que un par de tiburones se paseen por la pantalla de manera indefinida:

Animación de tiburones en JavaFX

Animación de tiburones en JavaFX

El código como siempre se lo pueden bajar desde la página de KodeGeek en Source Forge.

Las gracias a Sebastian (mi hijo de 3 años) ya que me dio una pista sobre como resolver el problema. Hasta que la animación no salió no se fué a jugar con sus juguetes :D

¡Que lo disfruten!

java, javafx, kodegeek, programación , ,

Aprendiendo JavaFX: Lección de interfaz gráfica avanzada

Sábado, 16 de enero de 2010

Sigo progresando con el curso de JavaFX. En esta ocasión habia dos tareas y me decidí ha jugar con una aplicación que muestra localización usando el lenguaje.

El código se lo pueden bajar desde acá, y la aplicación se ve así:

Localización en JavaFX - Español Venezuela

La bandera animada de Venezuela es cortesía de Animated flag images by 3DFlags.com.

¡Que lo disfruten!

java, javafx, programación , , , ,

Aprendiendo JavaFX: GUI basics II

Domingo, 10 de enero de 2010

Aqui les dejó el código de la tarea de esta sección del curso de JavaFX en JavaPassion . El resultado se ve como esto:

En este ejercicio el circulo se mueve en sentido contrario al cuadrado proporcionalmente (sólo en el eje X).

Que lo disfruten, pienso seguir agregando respuestas del curso (admito que estoy muy retrasado con respecto a la entrega final, pero no pienso rendirme :) )

java, javafx, kodegeek, opensource, programación , ,