tag:blogger.com,1999:blog-71145259404954267732024-02-08T06:56:23.087+01:00JPereda's Coding BlogYet another blog about coding in Java, JavaFX, HTML5, REST, servlets, Arduino, Raspberry Pi & IoT, Leap Motion, PLCs, Mobile Apps and whatever new stuff may appearAnonymoushttp://www.blogger.com/profile/15247383133952423416noreply@blogger.comBlogger14125tag:blogger.com,1999:blog-7114525940495426773.post-18944778116159988532015-03-19T11:58:00.000+01:002015-05-15T16:07:16.851+02:00JavaFX on mobile, a dream come true!<span style="font-family: Georgia, "Times New Roman", serif;">Hi there!</span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">It seems is time for a new post, but not a long and boring one as usual. I'll post briefly about my first experience bringing JavaFX to Google Play store.</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">Yes, I made it with 2048FX, a JavaFX version of the original game <a href="http://gabrielecirulli.github.io/2048/">2048</a>, by Gabriel Cirulli, that Bruno <a href="https://twitter.com/brunoborges">Borges</a> and I started last year.</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">Now, thanks to the outstanding work of JavaFXPorts project, I've adapted that version so it could be ported to Android. </span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">And with the very last version of their plugin, I've managed to succesfully submit it to Google Play store.</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">After a week in beta testing mode, today the app is in production, so you can go and download it to your Android device, and test it for yourself.</span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">For those of you eager to get the app, this is the <a href="https://play.google.com/store/apps/details?id=org.jpereda.game2048">link</a>. Go and get it, and add a nice review ;)</span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">If you want to read about the process to make it possible, please keep on reading. </span><span style="font-family: Georgia, "Times New Roman", serif;">These are the topics I'll cover in this post:</span><br />
<ul>
<li><span style="font-family: Georgia, "Times New Roman", serif;">2048FX, the game</span></li>
<li><span style="font-family: Georgia, "Times New Roman", serif;">JavaFXPorts and their mobile plugin</span></li>
<li><span style="font-family: Georgia, "Times New Roman", serif;">New Gluon plugin for NetBeans</span></li>
<li><span style="font-family: Georgia, "Times New Roman", serif;">2048FX on Android</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Google Play Store</span></li>
</ul>
<span style="font-family: Georgia, "Times New Roman", serif;"></span><br />
<h3>
<span style="font-family: Georgia, "Times New Roman", serif;">2048FX, the game</span></h3>
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">Many of you will now for sure about the 2048 game by Gabriel <a href="https://twitter.com/gabrielecirulli">Cirulli</a>. Last year it was a hit. </span><br />
<span style="font-family: Georgia,"Times New Roman",serif;">Many of us got really addicted to it...</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwxQXDXuJDvMIASUNz1Wofv5heW0JusjOjPTVKjZlZOiZm455VzYUksAEuXXY089M9FDNWI1jMzQ3z7tLrjpjuTkSSnwwcUk8V_9Z3cyxrdJ0AlToePV1d4fC4PtQpRxrtAkPBYeHoJJk/s1600/cirulli.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwxQXDXuJDvMIASUNz1Wofv5heW0JusjOjPTVKjZlZOiZm455VzYUksAEuXXY089M9FDNWI1jMzQ3z7tLrjpjuTkSSnwwcUk8V_9Z3cyxrdJ0AlToePV1d4fC4PtQpRxrtAkPBYeHoJJk/s1600/cirulli.png" width="211" /></a></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">In case you are not one of those, the game is about moving numbers in a 4x4 grid, and when equal numbers clash while moving the blocks (up/down/left/right), they merge and numbers are added up. The goal is reaching the 2048 tile (though you can keep going on looking for bigger ones!). </span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">At that time, Bruno started a Java/JavaFX 8 version, given that the game was open <a href="https://github.com/gabrielecirulli/2048">sourced</a>. I jumped in inmediately, and in a few weeks we had a nice JavaFX working <a href="https://github.com/brunoborges/fx2048">version</a>. </span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2_4EhgrvLq3E9F9PWzbifek7eTiWIK8KSlS0-K_MymgZz0JHi_HQBXmRQrSmTjWZjaWLyXdrhAFPNw2cG2Y4eu4biUAl0za09GcVPZWgkWNfuWnbexZD_PifGt2hgMaLyBz99Pqkh4bc/s1600/2048.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2_4EhgrvLq3E9F9PWzbifek7eTiWIK8KSlS0-K_MymgZz0JHi_HQBXmRQrSmTjWZjaWLyXdrhAFPNw2cG2Y4eu4biUAl0za09GcVPZWgkWNfuWnbexZD_PifGt2hgMaLyBz99Pqkh4bc/s1600/2048.png" width="271" /></a></div>
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">Since we used (and learned) the great new features of Java 8, we thought it was a good proposal for JavaOne, and we end up presenting it in a <a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=2710&tclass=popup">talk</a> (<a href="https://www.parleys.com/talk/how-build-game-2048-javafx-java-8-lessons-learned">video</a>) and doing a Hands on Lab <a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=3244">session</a>.</span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">And we'll talk about it again next week at JavaLand. If you happen to be there, don't miss our <a href="https://www.doag.org/konferenz/konferenzplaner/konferenzplaner_details.php?locS=0&id=483801&vid=491330">talk</a>! </span><br />
<br />
<h3>
<span style="font-family: Georgia, "Times New Roman", serif;"><span style="font-family: Georgia, "Times New Roman", serif;">JavaFXPorts and their mobile plugin</span></span></h3>
<br />
<span style="font-family: Georgia, "Times New Roman", serif;"><span style="font-family: Georgia, "Times New Roman", serif;">Since the very beginning of JavaFX (2+), going mobile has been on the top of list of the most wanted features. We've dreamed with the possibility of making true the <i>WORA</i> slogan, and it's only recently since the appearance of the JavaFXPorts <a href="http://javafxports.org/page/home">project</a>, that this dream has come true. </span></span><br />
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: Georgia, "Times New Roman", serif;"><span style="font-family: Georgia, "Times New Roman", serif;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSoTky1toK6GOvdRhgdByef4Wv01Zz3AVBuFDr9e8aTcXV6VOPatlMoPJ42GCZSqJ1hbBOOy4vhMngEGzRS2IUGqk_usCclVIwKuLOLmbe6MCmmRFKXKxksTCNc0iQQcAH5nPlmFTOW-o/s1600/jfxmobile2.1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="145" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSoTky1toK6GOvdRhgdByef4Wv01Zz3AVBuFDr9e8aTcXV6VOPatlMoPJ42GCZSqJ1hbBOOy4vhMngEGzRS2IUGqk_usCclVIwKuLOLmbe6MCmmRFKXKxksTCNc0iQQcAH5nPlmFTOW-o/s1600/jfxmobile2.1.png" width="400" /></a></span></span></div>
<br />
<span style="font-family: Georgia, "Times New Roman", serif;"><span style="font-family: Georgia, "Times New Roman", serif;">Led by Johan <a href="https://twitter.com/johanvos">Vos</a>, he and his team have given to the community the missing piece, so now we can jump to mobile devices with the (almost) same projects we develop for desktop.</span></span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">While Johan started this adventure at the end of <a href="http://mail.openjdk.java.net/pipermail/openjfx-dev/2013-November/011523.html">2013</a>, his work on porting JavaFX to Android, based on the <a href="https://wiki.openjdk.java.net/display/OpenJFX/Main">OpenJFX</a> project, has been evolving constantly during 2014, until recently in Febrary 2015 he <a href="https://www.voxxed.com/blog/2015/02/writing-javafx-mobile-levels-trillian-mobile-lodgon-partnership/">announced</a> a join effort between his company, LodgOn, and Trillian Mobile, the company behind RoboVM, the open source project to port JavaFX to iOS.</span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">As a result, <b><i>jfxmobile-plugin</i></b>, the one and only gradle JavaFX plugin for mobile was created and freely available through the JavaFXPorts <a href="https://bitbucket.org/javafxports/javafxmobile-plugin">repository</a>.</span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">With this plugin you can target from one single project three different platforms: Desktop, Android and iOS.</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">An it's as simple as this sample of <span style="font-family: "Courier New",Courier,monospace;">build.gradle</span>:</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"> </span>
<br />
<pre class="brush:java">buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.javafxports:jfxmobile-plugin:1.0.0-b5'
}
}
apply plugin: 'org.javafxports.jfxmobile'
repositories {
jcenter()
}
mainClassName = 'org.javafxports.project.MainApplicationFX'
jfxmobile {
ios {
forceLinkClasses = [ 'org.javafxports.**.*' ]
}
}
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">For Android devices, it is required Android <a href="http://developer.android.com/sdk/index.html">SDK</a>, and Android build-tools as you can read <a href="http://javafxports.org/page/Getting_Started">here</a>. The rest of the depencencies (like Dalvik SDK and the Retrolambda plugin) are taking care by the plugin itself.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Note the plugin version 1.0.0-b5 is constantly evolving, and at the time of this writting 1.0.0-b7 is now available. Check <a href="https://bitbucket.org/javafxports/javafxmobile-plugin/overview">this</a> frequently to keep it updated.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">With this plugin, several tasks are added to your project, and you can run any of them. Among others, these are the main ones: </span><br />
<ul>
<li><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: "Courier New",Courier,monospace;">./gradlew android</span> creates an Android package</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: "Courier New",Courier,monospace;">./gradlew androidInstall </span>installs your Android application on an Android device that is connected to your development system via a USB cable.</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: "Courier New",Courier,monospace;">./gradlew launchIOSDevice</span> launches your application on an iOS device that is connected to your development system</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: "Courier New",Courier,monospace;">./gradlew run</span> launches your application on your development system. </span></li>
</ul>
<br />
<h3>
<span style="font-family: Georgia, "Times New Roman", serif;">New Gluon plugin for NetBeans</span></h3>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Setting up a complex project, with three different platforms can be a hard task. Until now, the best approach (at least the one I followed) was cloning the HelloPlatform <a href="https://bitbucket.org/javafxports/samples/src">sample</a>, and changing the project and package names.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">But recently, a new company called <a href="http://gluonhq.com/">Gluon</a>, with Johan as one of his founding fathers, has released a NetBeans <a href="http://gluonhq.com/gluon-plugin-for-netbeans/">plugin</a> that extremelly simplifies this task.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Once you have installed the plugin, just create a JavaFX new project, and select Basic Gluon Application.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqA3_3EkYousmB3hHId_iwUFsjHPhmOQLrlpNAPIi2tChOWmBOU6Qq1BNdT99q1jM1qtLTgY-Mt4nq4zvX_fFu7SQVUy3213JVgUaqn8MqUUo1gCN7Q_08O1m5Tt01Pu1kQAjoESh3umc/s1600/gluon.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="275" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqA3_3EkYousmB3hHId_iwUFsjHPhmOQLrlpNAPIi2tChOWmBOU6Qq1BNdT99q1jM1qtLTgY-Mt4nq4zvX_fFu7SQVUy3213JVgUaqn8MqUUo1gCN7Q_08O1m5Tt01Pu1kQAjoESh3umc/s1600/gluon.png" width="400" /></a></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Select valid names for project, packages and main class, and you will find a bunch of folders in your new project:</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"></span><br />
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: Georgia,"Times New Roman",serif;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg39HBZHml67YaKU642acyXGVxBV4tF_gyxkn7U4Z2YhfoegYutHSZ-uJKj9WwZnKmivCJG_pKdoSbvT_2DA6MUgrPijp-O91xKFGc1bQwmdrCC78nOT_ghz_M0803rTMOb5T4oYk-OQvY/s1600/gluon3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg39HBZHml67YaKU642acyXGVxBV4tF_gyxkn7U4Z2YhfoegYutHSZ-uJKj9WwZnKmivCJG_pKdoSbvT_2DA6MUgrPijp-O91xKFGc1bQwmdrCC78nOT_ghz_M0803rTMOb5T4oYk-OQvY/s1600/gluon3.png" width="640" /></a></span></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Change the jfxmobile-plugin version to 1.0.0-b5 (or the most recent one), select one of the tasks mentioned before, and see for yourself.</span><br />
<br />
<h3>
<span style="font-family: Georgia,"Times New Roman",serif;"> </span><span style="font-family: Georgia, "Times New Roman", serif;">2048FX on Android</span></h3>
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">I've been using previous versions of the plugin, and it was a hard task to get everything working nicely. In fact, I had a working version of 2048FX on Android before the announcement of the jfxmobile-plugin. But it was a separated project from the desktop one.</span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">Now with the plugin, everything binds together magically. So I have a single project with the desktop and the android version of the game.</span><br />
<br />
<h4>
<span style="font-family: Georgia, "Times New Roman", serif;">Java 8?</span></h4>
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">There's a main drawback in all this process: Dalvik VM doesn't support Java 8 new features. For Lambdas, we can use the Retrolambda plugin, that takes care of converting them to Java 6 compatible bytecode. But <span style="font-family: "Courier New",Courier,monospace;">Streams </span>or <span style="font-family: "Courier New",Courier,monospace;">Optional </span>are not supported. This means that you have to manually backport them to Java 6/7 compatible version. </span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">While the primary object of the 2048FX project was basically learning these features, for the sake of going mobile, I backported the project, though this didn't change its structure or its appearance.</span><br />
<br />
<h4>
<span style="font-family: Georgia, "Times New Roman", serif;">The project: Desktop and Android altogether</span></h4>
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">This is how the project structure looks like:</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR9B-N1fGCqwcmZufJ-tJ51JV93xtE0wXBSCU8E9xrerc_7nGAROK1KsfVoZnZRKv3BkROjX1eGvLOFY_umWydnZN26rBlRepaqhubijVxbfAHkalA9zI-j9aSHz4l0xPs-ebtOX3CWDU/s1600/project.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="512" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR9B-N1fGCqwcmZufJ-tJ51JV93xtE0wXBSCU8E9xrerc_7nGAROK1KsfVoZnZRKv3BkROjX1eGvLOFY_umWydnZN26rBlRepaqhubijVxbfAHkalA9zI-j9aSHz4l0xPs-ebtOX3CWDU/s1600/project.png" width="640" /></a></div>
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">A <span style="font-family: "Courier New",Courier,monospace;">PlatformProvider </span>interface allows us to find out in which platform we are running the project, which is extremely useful to isolate pieces of code that are natively related to that plaftorm.</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">For instance, to save the game session in a local file in the Android device, I need to access to an internal folder where the apk is installed, and for that I use an <span style="font-family: "Courier New",Courier,monospace;">FXActivity</span> instance, the bridge between JavaFX and Dalvik runtime, that extends Android <span style="font-family: "Courier New",Courier,monospace;">Context</span>. This <span style="font-family: "Courier New",Courier,monospace;">Context </span>can be used to lookup Android services. </span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">One example of this is <span style="font-family: "Courier New",Courier,monospace;">FileManager</span> class, under Android packages: </span><br />
<br />
<pre class="brush:java">import javafxports.android.FXActivity;
import android.content.Context;
import java.io.File;
public class FileManager {
private final Context context;
public FileManager(){
context = FXActivity.getInstance();
}
public File getFile(String fileName){
return new File(context.getFilesDir(), fileName);
}
}
</pre>
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">Now the PlatformProvider will call this implementation when running on Android, or the usual one for desktop.</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">After a few minor issues I had a working project in both desktop and Android.</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_n71j9sYmW0-pJUI-O6E4hgdo5FKZmTLgp3JssJptAyi0bRkBaiVRq8dRSSS_on1kVKjgYLI-9I60eXdD6ubDbA6axD4wzOpwlui5b4rnOsfxtRdGJskhxbG8YXmp7AguKOPeoe_dGsw/s1600/Screenshot_2015-02-01-22-49-57.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_n71j9sYmW0-pJUI-O6E4hgdo5FKZmTLgp3JssJptAyi0bRkBaiVRq8dRSSS_on1kVKjgYLI-9I60eXdD6ubDbA6axD4wzOpwlui5b4rnOsfxtRdGJskhxbG8YXmp7AguKOPeoe_dGsw/s1600/Screenshot_2015-02-01-22-49-57.png" width="225" /></a></div>
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<br />
<h3>
<span style="font-family: Georgia,"Times New Roman",serif;">Google Play Store</span></h3>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Bruno asked me once to go with this app to Google Play store, but at that time the project wasn't mature enough. But last weekend I decided to give it a try, so I enrolled myself in Google Play <a href="https://play.google.com/apps/publish/">Developers</a>, filled a form with the description and several screenshots, and finally submitted the apk of the game... what could go wrong, right?</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Well, for starters, I had a first error: the apk had debug options enabled, and that was not allowed.</span><br />
<br />
<h4>
<span style="font-family: Georgia,"Times New Roman",serif;">The AndroidManifest.xml</span></h4>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"> This hidden file, created automatically by the plugin, contains important <a href="http://developer.android.com/guide/topics/manifest/manifest-intro.html">information</a> of the apk. You can retrieve it after a first built, and modify it to include or modify different options. Then you have to refer this file in the build.gradle file.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">On the <span style="font-family: "Courier New",Courier,monospace;">application </span>tag is where you have to add <span style="font-family: "Courier New",Courier,monospace;">android:debuggable="false"</span>.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">There you can add also the icon of your app: <span style="font-family: "Courier New",Courier,monospace;">android:icon="@mipmap/ic_launcher"</span>, where mipmap-* are image folders with several resolutions.</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgk77OkZ1Vo4OWKYn4mPndWubtO2VBmvy_A66s4ImSuFOFgIFBYdtohqTmgYVa2sxTjfp792K9mx9lUJPIEUQoqqDE4O2sNgq0EoYYbPRXytCWir-4MrTFFTFn0CWuurekYR_KTwbhqy7k/s1600/ic_launcher.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgk77OkZ1Vo4OWKYn4mPndWubtO2VBmvy_A66s4ImSuFOFgIFBYdtohqTmgYVa2sxTjfp792K9mx9lUJPIEUQoqqDE4O2sNgq0EoYYbPRXytCWir-4MrTFFTFn0CWuurekYR_KTwbhqy7k/s1600/ic_launcher.png" /></a></div>
<br />
<h4>
<span style="font-family: Georgia,"Times New Roman",serif;">Signing the apk</span></h4>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Well, that part was easy. Second attempt, second error... The apk must be signed for release. "Signed" means you need a private key, and for that we can use <a href="http://developer.android.com/tools/publishing/app-signing.html#signing-manually">keytool</a>.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">And "release" means that we need to add to build.gradle the signing configuration... and that was not possible with the current plugin version b5.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">So I asked Johan (on Friday night) about this, and he answered me (Saturday afternoon) that they've been working precisely on that but it was not ready yet. Later that evening, Joeri <a href="https://twitter.com/tiainen">Sykora</a> from LodgON, told me that it was in a branch... so with the invaluable help of John <a href="https://twitter.com/john_sirach">Sirach</a> (the <a href="https://pidome.wordpress.com/">PiDome</a> guy) we spent most of the Saturday night trying to build locally the plugin to add the signing configuration. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">It end up being something like this:</span><br />
<br />
<pre class="brush:java">jfxmobile {
android {
signingConfig {
storeFile file("path/to/my-release-key.keystore")
storePassword 'STORE_PASSWORD'
keyAlias 'KEY_ALIAS'
keyPassword 'KEY_PASSWORD'
}
manifest = 'lib/android/AndroidManifest.xml'
resDirectory = 'src/android/resources'
}
}
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">It was done! It was almost 2 a.m., but I tried uploading the signed apk for the third time, and voilà!! No more errors. The app when to submission and in less than 10 hours, on Sunday morning it was already published!!</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiF_rU4jQc2QiiW7fUxx8tJ5s3yry_7tz4k5r-45BW5j5unFATHKTv2l9S3OCtSRiwgIvqhybU_BYqjcmQXojUBaPh8wKMlSRZwf0kKYijp7CHYfGYnlUxbwJVWZMNhkW-mrkfGF-NksSE/s1600/Screenshot_2015-03-15-12-59-59.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiF_rU4jQc2QiiW7fUxx8tJ5s3yry_7tz4k5r-45BW5j5unFATHKTv2l9S3OCtSRiwgIvqhybU_BYqjcmQXojUBaPh8wKMlSRZwf0kKYijp7CHYfGYnlUxbwJVWZMNhkW-mrkfGF-NksSE/s1600/Screenshot_2015-03-15-12-59-59.png" width="225" /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;"> </span><span style="font-family: Georgia,"Times New Roman",serif;"> </span><br />
<h4>
<span style="font-family: Georgia,"Times New Roman",serif;">Beta Testing Program</span></h4>
<span style="font-family: Georgia,"Times New Roman",serif;"></span>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Instead of going into production I chose the Beta Testing program, so during this week only a few guys have been able to access to Google Play to download and test the application.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Thanks to their feedback I've made a few upgrades, like fixing some issues with fonts and Samsung devices (thanks John) or changing the context menu to a visible toolbar (thanks Bruno).</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: Georgia,"Times New Roman",serif;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEho_iFOpm9x2nk4Ow4PODRyYGU06HzKCRIVIW_kOXFjU9ZyAnpwxA5lTvOxE_5Qr9-berb2JEm_7SsFKkYnFmtQSfEAu_Ke1Bjtpfd72xgL-yIbZuLM0Rn8xki9z8prXCZ28onahOWdCak/s1600/Screenshot_2015-03-15-23-41-33.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEho_iFOpm9x2nk4Ow4PODRyYGU06HzKCRIVIW_kOXFjU9ZyAnpwxA5lTvOxE_5Qr9-berb2JEm_7SsFKkYnFmtQSfEAu_Ke1Bjtpfd72xgL-yIbZuLM0Rn8xki9z8prXCZ28onahOWdCak/s1600/Screenshot_2015-03-15-23-41-33.png" width="225" /></a></span></div>
<br />
<br />
<h4>
<span style="font-family: Georgia,"Times New Roman",serif;">2048FX on Google Play Store</span></h4>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">And the beta testing time is over. As of now, the application is on production. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">What are you waiting for? Go and get <a href="https://play.google.com/store/apps/details?id=org.jpereda.game2048">it</a>!! </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Download it, play with it, enjoy, and if you have any issue, any problem at all, please report it, so we can work on improving its usability in all kind of devices.</span><br />
<br />
<h3>
<span style="font-family: Georgia,"Times New Roman",serif;">Final Thanks</span></h3>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Let me finish this post taking the word of the whole JavaFX community out there, saying out loud:</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<div style="text-align: center;">
<b><span style="font-family: Georgia,"Times New Roman",serif;">THANK YOU, JavaFXPorts !!!</span></b></div>
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Without you all of this wouldn't be possible at all.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Big thanks to all the guys already mentioned in this post, and also to Eugene <a href="https://twitter.com/eryzhikov">Ryzhikov</a>, Mark <a href="https://twitter.com/MkHeck">Heckler</a> and Diego <a href="https://twitter.com/ciruman">Cirujano</a>, for helping along the way.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">And finally, thanks to the OpenJFX project and the JavaFX team.</span><br />
<br />
<h3>
<span style="font-family: Georgia,"Times New Roman",serif;">UPDATE </span></h3>
<span style="font-family: Georgia,"Times New Roman",serif;">Thanks to the work of Jens <a href="https://twitter.com/Jerady">Deters</a>, 2048FX has made it to Apple Store too!</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://raw.githubusercontent.com/jperedadnr/Game2048FX/master/src/ios/assets/AppIcon.appiconset/Icon-76%402x.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://raw.githubusercontent.com/jperedadnr/Game2048FX/master/src/ios/assets/AppIcon.appiconset/Icon-76%402x.png" /></a></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;">Go and install it from <a href="https://itunes.apple.com/de/app/2048fx/id989966696?mt=8">here</a>!</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">And since today (15th May 2015), we are open sourcing all the project, so anyone can have a look at it and find out about the last final details required to put it all together and make it successfully to Google Play or Apple Store:</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"><a href="https://github.com/jperedadnr/Game2048FX">https://github.com/jperedadnr/Game2048FX</a> </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Enjoy! </span><br /><span style="font-family: Georgia,"Times New Roman",serif;"> </span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
Anonymoushttp://www.blogger.com/profile/15247383133952423416noreply@blogger.com59tag:blogger.com,1999:blog-7114525940495426773.post-13670334410252330312015-01-08T13:17:00.002+01:002015-01-08T13:29:06.501+01:00Creating and Texturing JavaFX 3D Shapes<span style="font-family: Georgia,"Times New Roman",serif;">Hi there!</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">It's been a while since my last post, and it seems I've just said the same on my last one... but you know, many stuff in between, and this blog has a tradition of long posts, those that can't be delivered on a weekly/monthly basis. But if you're a regular reader (thank you!) you know how this goes.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">This post is about my last developments in JavaFX 3D, after working in close collaboration with a bunch of incredible guys for the last months.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">For those of you new to this blog, I've already got a few posts talking about JavaFX 3D. My last recent one about the Rubik's Cube: <a href="http://jperedadnr.blogspot.com.es/2014/04/rubikfx-solving-rubiks-cube-with-javafx.html">RubikFX: Solving the Rubik's Cube with JavaFX 3D</a>, and other about the Leap Motion controller: <a href="http://jperedadnr.blogspot.com.es/2013/06/leap-motion-controller-and-javafx-new.html">Leap Motion Controller and JavaFX: A new touch-less approach</a>.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">I'll cover in this post the following topics:</span><br />
<ul>
<li><a href="https://www.blogger.com/blogger.g?blogID=7114525940495426773#LeapMotionSkeletal"><span style="font-family: Georgia,"Times New Roman",serif;">Leap Motion Skeletal Tracking Model</span></a></li>
<li><a href="https://www.blogger.com/blogger.g?blogID=7114525940495426773#SkinningMeshes">
<span style="font-family: Georgia,"Times New Roman",serif;">Skinning Meshes and Leap Motion </span></a></li>
<li><a href="https://www.blogger.com/blogger.g?blogID=7114525940495426773#TweetWallFX"><span style="font-family: Georgia,"Times New Roman",serif;">TweetWallFX</span></a></li>
<li><a href="https://www.blogger.com/blogger.g?blogID=7114525940495426773#CreatingNew3D"><span style="font-family: Georgia,"Times New Roman",serif;">Creating new 3D shapes</span></a></li>
<li><a href="https://www.blogger.com/blogger.g?blogID=7114525940495426773#PlayingWithTextures"><span style="font-family: Georgia,"Times New Roman",serif;">Playing with textures </span></a></li>
</ul>
<span style="font-family: Georgia,"Times New Roman",serif;">Before getting started, did I mention my article "<i>Building castles in the Sky. Use JavaFX 3D to model historical treasures and more</i>" has been published in the current issue of <a href="http://www.oraclejavamagazine-digital.com/javamagazine/november_december_2014#pg56">Java Magazine</a>?</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">In a nutshell, this article describes a multi model JavaFX 3D based application, developed for the virtual immersion in Cultural Heritage Buildings, through models created by Reverse Engineering with Photogrammetry Techniques. The 3D model of the Menéndez Pelayo Library in Santander, Spain, is used throughout the article as an example of a complex model.</span><br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: Georgia,"Times New Roman",serif;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3QD9zl99dMx8aVVc2f5VyFN4jRGV90MZCuwksfTnNOtiTQhwMNptwOLC6onjwGtd3dTLSnyMi_-_xWLEVnQ3uQkG_gQY-wtX4_Bl7hbSEk1sZCw5Txf5K3nCBTqRUFa2uRaxESeCpMn0/s1600/3DModel.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3QD9zl99dMx8aVVc2f5VyFN4jRGV90MZCuwksfTnNOtiTQhwMNptwOLC6onjwGtd3dTLSnyMi_-_xWLEVnQ3uQkG_gQY-wtX4_Bl7hbSEk1sZCw5Txf5K3nCBTqRUFa2uRaxESeCpMn0/s1600/3DModel.png" height="321" width="400" /></a></span></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">You can find this application and, t</span><span style="font-family: Georgia,"Times New Roman",serif;">hanks to Óscar <a href="https://twitter.com/Oscar_Cosido">Cosido</a>, </span><span style="font-family: Georgia,"Times New Roman",serif;">a free model of the Library <a href="https://bitbucket.org/JPereda/multimodel3dfx">here</a>.</span><br />
<br />
<h3>
<a href="https://www.blogger.com/null" name="LeapMotionSkeletal"></a>
<span style="font-family: Georgia,"Times New Roman",serif;">Leap Motion Skeletal Tracking Model </span></h3>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Since my first post about Leap Motion, I've improved the 3D version after Leap Motion released their <a href="https://developer.leapmotion.com/">version 2</a> that includes an skeletal tracking model. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">I haven't had the chance to blog about it, but this early video shows my initial work. You can see that the model now includes bones, so a more realistic hand can be built.</span><br />
<br />
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="315" src="//www.youtube.com/embed/im3uqoMwmk0" width="560"></iframe> </div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">I demoed a more advanced version at one of my <a href="https://oracleus.activeevents.com/2014/connect/sessionDetail.ww?SESSION_ID=1993">JavaOne</a> </span>
<span style="font-family: Georgia,"Times New Roman",serif;">talks with the incredibles James <a href="https://twitter.com/JavaFXpert">Weaver</a>, Sean <a href="https://twitter.com/SeanMiPhillips">Phillips</a> and Zoran <a href="https://twitter.com/neuroph">Sevarac</a>. Sadly, Jason <a href="https://twitter.com/jdub1581">Pollastrini</a> couldn't make it, but he was part of the 3D-team. </span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgt6QewO_KvxRtIQ9NXwROkVpYS1u9ROEMsK0AUMxID9R3nKyUY6WhXIdTV6s74tpqtXP_idrNwLaxr9AqWA096M0MrqcU1q2h4PTWPZGaLOoRwUkhvmtBSYquo1Eg7Q0suUMGcuDkbFMo/s1600/handLM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgt6QewO_KvxRtIQ9NXwROkVpYS1u9ROEMsK0AUMxID9R3nKyUY6WhXIdTV6s74tpqtXP_idrNwLaxr9AqWA096M0MrqcU1q2h4PTWPZGaLOoRwUkhvmtBSYquo1Eg7Q0suUMGcuDkbFMo/s1600/handLM.png" height="175" width="200" /></a>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8cD3gaoGpL7opFrQ5yHJ3tHMZgOC503-xfIb3A1y5DnluYom9i04ViUFcklbcQEM2hbI6SIKv8rERzcxh1trdKrkla5fOGRwlfqMF-mqOJnETJsTnyEPQ8Emsbx25zBHFhudngq-mR_s/s1600/handLM2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8cD3gaoGpL7opFrQ5yHJ3tHMZgOC503-xfIb3A1y5DnluYom9i04ViUFcklbcQEM2hbI6SIKv8rERzcxh1trdKrkla5fOGRwlfqMF-mqOJnETJsTnyEPQ8Emsbx25zBHFhudngq-mR_s/s1600/handLM2.png" height="249" width="320" /></a></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">If you are interested, a</span><span style="font-family: Georgia,"Times New Roman",serif;">ll the code is available <a href="https://github.com/jperedadnr/LeapV2">here</a>. Go, fork it and play with it if you have a Leap Motion controller.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"> Yes, we did have a great time there.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFaJylTB85q8vdTaqa6kt9LCsuHkCZJmDL_lOUO0kS7gqpn8whYU15F24CdcH8goGNjOI8B2uwWCs6rP2TCjkul7Fzo-BB5EjiJJ_P75Q67wy5TO6sNWaayZCnuRpk2d0qUG3IXqhVKuQ/s1600/IMG_5272.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFaJylTB85q8vdTaqa6kt9LCsuHkCZJmDL_lOUO0kS7gqpn8whYU15F24CdcH8goGNjOI8B2uwWCs6rP2TCjkul7Fzo-BB5EjiJJ_P75Q67wy5TO6sNWaayZCnuRpk2d0qUG3IXqhVKuQ/s1600/IMG_5272.JPG" height="298" width="400" /></a></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">The session was great. In fact you can watch it now at <a href="https://www.parleys.com/play/543f9f7ce4b06e1184ae41c1/about">Parleys</a>.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">We had even a special guest: John Yoon, a.k.a. <a href="https://twitter.com/javafx3d">@JavaFX3D</a>. </span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4NB3EfjeDmx_7er6zQE9ysgouJiJWkT9hquntQStiERQ27T2_PkQ1Wb07_SnQQgzsLpJdgkjp1YO24kNmJFvJX5aJnA9mtmpvCCWzDWIocbyFTk0BTj9WWZivNXhVDsCH_aqC1qH1pSI/s1600/IMG_5289-2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4NB3EfjeDmx_7er6zQE9ysgouJiJWkT9hquntQStiERQ27T2_PkQ1Wb07_SnQQgzsLpJdgkjp1YO24kNmJFvJX5aJnA9mtmpvCCWzDWIocbyFTk0BTj9WWZivNXhVDsCH_aqC1qH1pSI/s1600/IMG_5289-2.jpg" height="263" width="400" /></a></div>
<br />
<h3>
<a href="https://www.blogger.com/null" name="SkinningMeshes"></a>
<span style="font-family: Georgia,"Times New Roman",serif;">Skinning Meshes and Leap Motion </span></h3>
<span style="font-family: Georgia,"Times New Roman",serif;">And then I met Alexander <a href="https://twitter.com/mrkam2">Kouznetsov</a>. </span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgu-iQnw6IJ7n0ZwnbUOYg_TcU_iWEU_e6LDoAQhi0bLVvDnJb2DEPUgpCfUq3glTTq87yBFzQAzvkdbz4dVuc1f1qrqTHMOzxSSUKLzZUpA2HcfvlxF6Pctz3jQccPBusoX1YlGWKEs20/s1600/IMG_5296.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgu-iQnw6IJ7n0ZwnbUOYg_TcU_iWEU_e6LDoAQhi0bLVvDnJb2DEPUgpCfUq3glTTq87yBFzQAzvkdbz4dVuc1f1qrqTHMOzxSSUKLzZUpA2HcfvlxF6Pctz3jQccPBusoX1YlGWKEs20/s1600/IMG_5296.JPG" height="239" width="320" /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;">It was during the Hackergarten 3D session, where Sven <a href="https://twitter.com/SvenNB">Reimers</a> and I were hacking some JavaFX 3D stuff, when he showed up, laptop in backpack, ready for some hacking. There's no better trick than asking a real developer: <i>I bet you're not able to hack this...</i> to get it done!</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">So the challenge was importing a rigged hand in JSON format to use a <a href="http://hg.openjdk.java.net/openjfx/8u-dev/rt/file/688a5f6a294d/apps/samples/3DViewer/src/main/java/com/javafx/experiments/shape3d/SkinningMesh.java"><span style="font-family: "Courier New",Courier,monospace;">SkinningMesh</span></a> in combination with the new Leap Motion skeletal tracking model. As the one and only John Yoon would show later in his <a href="https://oracleus.activeevents.com/2014/connect/search.ww?eventRef=javaone#loadSearch-event=null&searchPhrase=yoon&searchType=session&tc=0&sortBy=&p=&i%2810009%29=10111">talk:</a> </span><br />
<blockquote class="tr_bq">
<span style="font-family: Georgia,"Times New Roman",serif;">"<i>In order to animate a 3D model, you need a transform hierarchy to which the 3D geometry is attached. </i></span></blockquote>
<blockquote class="tr_bq">
<div class="separator" style="clear: both; text-align: center;">
<i><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoIgKu81PSdFmqxrTz40eVUQqPuYSJkKWLzSyN1YKescrOYt82SGmFQNsa9q8MIQQnC_03CV7W10qiFSriM03DkHzXWByu-CK8OTy-xcEEGVf6fjFBFa8gE9520XZ5450W4QXOnt6wEdk/s1600/rigging.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoIgKu81PSdFmqxrTz40eVUQqPuYSJkKWLzSyN1YKescrOYt82SGmFQNsa9q8MIQQnC_03CV7W10qiFSriM03DkHzXWByu-CK8OTy-xcEEGVf6fjFBFa8gE9520XZ5450W4QXOnt6wEdk/s1600/rigging.png" height="158" width="400" /></a></i></div>
<i><span style="font-family: Georgia,"Times New Roman",serif;">The general term for this part of the pipeline is “rigging” or “character setup”.<br />Rigging is the process of setting up your static 3D model for computer-generated animation, to make it animatable."</span></i></blockquote>
<span style="font-family: Georgia,"Times New Roman",serif;">He was in charge of animating the Duke for the chess demo shown at the Keynote of JavaOne 2013. As shown in the above picture, this required a mesh, a list of joints, weights and transformations, binding the inner 'bones' with the surrounding external mesh, so when the former were moved the latter was deformed, creating the desired animation effect.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">The SkinningMesh class in the 3DViewer project was initially designed for Maya, and we had a <a href="https://github.com/leapmotion/leapjs-rigged-hand/tree/master/src/models">rigged hand</a> in <a href="https://github.com/mrdoob/three.js/wiki/JSON-Model-format-3">Three.js</a> model in JSON format.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">So out of the blue Alex built an importer, and managed </span><span style="font-family: Georgia,"Times New Roman",serif;">to </span><span style="font-family: Georgia,"Times New Roman",serif;">get the mesh of the hand </span><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">by reverse engineering. </span>Right after that he solved the rest of the components of the skinningMesh. The most important part was the binding of the transformations between joints.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<pre class="brush: java"> Affine[] bindTransforms = new Affine[nJoints];
Affine bindGlobalTransform = new Affine();
List<Joint> joints = new ArrayList<>(nJoints);
List<Parent> jointForest = new ArrayList<>();
for (int i = 0; i < nJoints; i++) {
JsonObject bone = object.getJsonArray("bones").getJsonObject(i);
Joint joint = new Joint();
String name = bone.getString("name");
joint.setId(name);
JsonArray pos = bone.getJsonArray("pos");
double x = pos.getJsonNumber(0).doubleValue();
double y = pos.getJsonNumber(1).doubleValue();
double z = pos.getJsonNumber(2).doubleValue();
joint.t.setX(x);
joint.t.setY(y);
joint.t.setZ(z);
bindTransforms[i] = new Affine();
int parentIndex = bone.getInt("parent");
if (parentIndex == -1) {
jointForest.add(joint);
bindTransforms[i] = new Affine(new Translate(-x, -y, -z));
} else {
Joint parent = joints.get(parentIndex);
parent.getChildren().add(joint);
bindTransforms[i] = new Affine(new Translate(
-x - parent.getLocalToSceneTransform().getTx(),
-y - parent.getLocalToSceneTransform().getTy(),
-z - parent.getLocalToSceneTransform().getTz()));
}
joints.add(joint);
joint.getChildren().add(new Axes(0.02));
}
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">This was the first </span><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">animation</span> with the model:</span><br />
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<video autoplay="autoplay" controls="" height="300" poster="https://dl.dropboxusercontent.com/u/27006338/rigged0.png" src="https://dl.dropboxusercontent.com/u/27006338/Rigged0.webm" width="400">
</video> </div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">The axes are shown at every joint. Observe how easy is to deform a complex mesh just by rotating two joints:</span><br />
<br />
<pre class="brush: java"> Timeline t = new Timeline(new KeyFrame(Duration.seconds(1),
new KeyValue(joints.get(5).rx.angleProperty(), 90),
new KeyValue(joints.get(6).rx.angleProperty(), 90)));
t.setCycleCount(Timeline.INDEFINITE);
t.play();
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">With a working <span style="font-family: "Courier New",Courier,monospace;">SkinningMesh</span>, it was just time for adding the skeletal tracking model from Leap Motion. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">First, we needed to match <span style="font-family: "Courier New",Courier,monospace;"><a href="https://developer.leapmotion.com/documentation/java/api/Leap.Bone.html">Bones</a></span> to joints, and then we just needed to apply the actual orientation of every bone to the corresponding joint transformation. </span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<pre class="brush: java"> listener = new LeapListener();
listener.doneLeftProperty().addListener((ov,b,b1)->{
if(b1){
List<finger$gt; fingersLeft=listener.getFingersLeft();
Platform.runLater(()->{
fingersLeft.stream()
.filter(finger -> finger.isValid())
.forEach(finger -> {
previousBone=null;
Stream.of(Bone.Type.values()).map(finger::bone)
.filter(bone -> bone.isValid() && bone.length()>0)
.forEach(bone -> {
if(previousBone!=null){
Joint joint = getJoint(false,finger,bone);
Vector cross = bone.direction().cross(previousBone.direction());
double angle = bone.direction().angleTo(previousBone.direction());
joint.rx.setAngle(Math.toDegrees(angle));
joint.rx.setAxis(new Point3D(cross.getX(),-cross.getY(),cross.getZ()));
}
previousBone=bone;
});
});
((SkinningMesh)skinningLeft.getMesh()).update();
});
}
});
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">The work was almost done! Back from JavaOne I had the time to finish the model, adding hand movements and drawing the joints:</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisezoE3kyi0eT2Na8kbGpEdD1ypk0pNS_QOkB7fdYSSSVB1xoCY-2wszXlRf0gkHapE4VPFM_nklf936-YgeSPwss6U4txu7bN7Zz92Fip3lAwh7YtHyW6ivYLWPFc3bDMY8pZUyPLQp0/s1600/BzcpiSaCAAAdruw.jpg+large.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisezoE3kyi0eT2Na8kbGpEdD1ypk0pNS_QOkB7fdYSSSVB1xoCY-2wszXlRf0gkHapE4VPFM_nklf936-YgeSPwss6U4txu7bN7Zz92Fip3lAwh7YtHyW6ivYLWPFc3bDMY8pZUyPLQp0/s1600/BzcpiSaCAAAdruw.jpg+large.jpg" height="318" width="400" /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">This video sums up most of what we've accomplished:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="315" src="//www.youtube.com/embed/8_xiv1pV3tI" width="560"></iframe>
</div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">If you are interested in this project, all the code is <a href="https://github.com/jperedadnr/RiggedHand">here</a>. Feel free to clone or fork it. Pull requests will be very wellcome.</span><br />
<h3>
<a href="https://www.blogger.com/null" name="TweetWallFX"></a>
<span style="font-family: Georgia,"Times New Roman",serif;"> TweetWallFX</span></h3>
<span style="font-family: Georgia,"Times New Roman",serif;">One thing leads to another... And Johan <a href="https://twitter.com/johanvos">Vos</a> and Sven asked me to join them in a project to create a Tweet Wall with JavaFX 3D for Devoxx 2014. JavaFX 3D? I couldn't say no even if I wasn't attending!</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Our first proposal (not the one Sven finally <a href="http://nighthacking.com/sven-reimers-on-the-devoxx-tweetwall/">accomplished</a>) was based on the <a href="https://github.com/Birdasaur/FXyz">F(X)yz</a> library from Sean and Jason: a <span style="font-family: "Courier New",Courier,monospace;">SkyBox </span>as a container, with several tori inside, where tweets were rotating over them:</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"></span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjURbsDDRrvQCnBWwEpDBMDj3rc3gBgOf_T2LORvOUcwN8eznwXO4BC78hnwHPH0p-5pIEnHIqVlqCH-AtgHKqKahU__jPUnu8nWlYl2_kKSpxmKD8QjBXJaLX8H_2PXeFhHRJppk9oDS4/s1600/tori.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjURbsDDRrvQCnBWwEpDBMDj3rc3gBgOf_T2LORvOUcwN8eznwXO4BC78hnwHPH0p-5pIEnHIqVlqCH-AtgHKqKahU__jPUnu8nWlYl2_kKSpxmKD8QjBXJaLX8H_2PXeFhHRJppk9oDS4/s1600/tori.png" height="291" width="400" /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;"></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;">Needless to say, we used the great <a href="http://twitter4j.org/en/index.html">Twitter4J</a> API for retrieving new tweets with the hashtag #Devoxx.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">The first challenge here was figuring out how to render the tweets over each torus. The solution was based on the use of an snapshot of the tweet (rendered in a background scene) that would serve as the diffuse map image of the PhongMaterial assigned to the torus.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">To second was creating a banner effect rotating the tweets over they tori. To avoid artifacts, a segmented torus was built on top of the first one, cropping the faces of a regular torus, so the resulting mesh will be textured with the image.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">This is our desired segmented torus. </span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEik82cjHVpLAkk-iEgTLC3U2MQpa-ESz0LXQk1VZmO_s2gaYGO2XrURTF2PU60gEV7-QNLv8M8_OxBNy6557MPUfMsWEC7De5HX7ILNDrkTBeKawBKk2l2GDWSLYqdBEHvv4G7FPX6go60/s1600/torus.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEik82cjHVpLAkk-iEgTLC3U2MQpa-ESz0LXQk1VZmO_s2gaYGO2XrURTF2PU60gEV7-QNLv8M8_OxBNy6557MPUfMsWEC7De5HX7ILNDrkTBeKawBKk2l2GDWSLYqdBEHvv4G7FPX6go60/s1600/torus.png" height="400" width="385" /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;"></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;">In the next section, we'll go into details of how we could accomplish this shape.</span><br />
<h3>
<a href="https://www.blogger.com/null" name="CreatingNew3D"></a>
<span style="font-family: Georgia,"Times New Roman",serif;">Creating new 3D shapes</span></h3>
<i><span style="font-family: Georgia,"Times New Roman",serif;">Note to beginners: For an excelent introduction to JavaFX 3D, have a look to the 3D chapters on these books: <a href="http://www.apress.com/9781430264606">JavaFX 8 Introduction by Example</a> an <a href="http://www.apress.com/9781430265740">JavaFX 8 Pro: A Definitive Guide to Building Desktop, Mobile, and Embedded Java Clients</a>.</span></i><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">To create this mesh in JavaFX 3D we use a <a href="https://docs.oracle.com/javase/8/javafx/api/javafx/scene/shape/TriangleMesh.html"><span style="font-family: "Courier New",Courier,monospace;">TriangleMesh</span></a> as a basis for our mesh, where we need to provide float arrays of vertices and texture coordinates and one int array of vertex and texture indices for defining every triangle face.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Since a torus can be constructed from a rectangle, by gluting both pairs of opposite edges together with no twists, we could use a 2D rectangular grid in a local system ($\theta$,$\phi$), and map every point with these equations:</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">\[X=(R+r \cos\phi) \cos\theta\\Z=(R+r \cos\phi) \sin\theta\\Y=r \sin\phi\]</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">So based on this grid (with colored borders and triangles for clarity):</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIyL_opc11RztzlwqscMYxpLj8naSTs_163jlxGONU_gCXlKt-LDl3lLohYCSGihdcQ4hya965XINzMf-N8Dm3d16EF_P4WOx6v34sYR4cIsfpQjVdeIXyoBhWxGtUGeFfmF7aQCQxvPw/s1600/grid0.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIyL_opc11RztzlwqscMYxpLj8naSTs_163jlxGONU_gCXlKt-LDl3lLohYCSGihdcQ4hya965XINzMf-N8Dm3d16EF_P4WOx6v34sYR4cIsfpQjVdeIXyoBhWxGtUGeFfmF7aQCQxvPw/s1600/grid0.png" height="276" width="400" /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;">we could create this torus (observe how the four corners of the rectangle are joinned together in one single vertex):</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7X_aXvP8wSjkyf8cWJD0_PA1_3pn6sIgzL_iQyP_OtQFvmTq1iAYw-bFZturKojTisvXJkkjb_Qea-IbM9UVADcUkCJ8rtIjTZS7IXLba9S2Zz5jXqWq99yYxoKVaaZYryxfpEsjsj7c/s1600/grid1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7X_aXvP8wSjkyf8cWJD0_PA1_3pn6sIgzL_iQyP_OtQFvmTq1iAYw-bFZturKojTisvXJkkjb_Qea-IbM9UVADcUkCJ8rtIjTZS7IXLba9S2Zz5jXqWq99yYxoKVaaZYryxfpEsjsj7c/s1600/grid1.png" height="363" width="400" /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Now if we want to segment the mesh, we can get rid of a few elements from the borders. From the red inner grid, we could have a segmented torus now:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh28-o3OJd4w1rc8hV5zNqzNaCdQ4Mqo1Rb4cUzGjUata6QdQGQUjEoIWKmn2s3nHAFAnU2Nl3l-w-xxx_tRgtanCs7-kh4o7j1GeQBoZd1u8vBKb7OEj3OSVUrsjR48gHtSnfhc_x1QC4/s1600/grid2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh28-o3OJd4w1rc8hV5zNqzNaCdQ4Mqo1Rb4cUzGjUata6QdQGQUjEoIWKmn2s3nHAFAnU2Nl3l-w-xxx_tRgtanCs7-kh4o7j1GeQBoZd1u8vBKb7OEj3OSVUrsjR48gHtSnfhc_x1QC4/s1600/grid2.png" height="214" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzmNgrlqt6dnOyum231ZgJUGfksG5m7onA_4zOEQxbH-Yw2-xXlr-65495pND2JqtXjtvMtEb6ubsbZKJTyURUbvbqSmljcOJF4SQH1kNgYNbr_y3k72BCSrcJwa4FMwSV_M-saIPwxao/s1600/grid3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzmNgrlqt6dnOyum231ZgJUGfksG5m7onA_4zOEQxbH-Yw2-xXlr-65495pND2JqtXjtvMtEb6ubsbZKJTyURUbvbqSmljcOJF4SQH1kNgYNbr_y3k72BCSrcJwa4FMwSV_M-saIPwxao/s1600/grid3.png" height="281" width="320" /></a></div>
<br />
<h4>
<span style="font-family: Georgia,"Times New Roman",serif;">Vertices coordinates</span></h4>
<span style="font-family: Georgia,"Times New Roman",serif;">As we can see in the <a href="https://github.com/Birdasaur/FXyz/blob/master/src/org/fxyz/shapes/primitives/SegmentedTorusMesh.java">SegmentedTorusMesh</a> class from the <a href="https://github.com/Birdasaur/FXyz">F(x)yz</a> library, generating the vertices for the mesh is really easy, based in the above equations, the desired number of subdivisions (20 and 16 in the figures) and the number of elements cropped in both directions (4):</span><br />
<br />
<pre class="brush: java">
private TriangleMesh createTorus(int subDivX, int subDivY, int crop, float R, float r){
TriangleMesh triangleMesh = new TriangleMesh();
// Create points
List<Point3D> listVertices = new ArrayList<>();
float pointX, pointY, pointZ;
for (int y = crop; y <= subDivY-crop; y++) {
float dy = (float) y / subDivY;
for (int x = crop; x <= subDivX-crop; x++) {
float dx = (float) x / subDivX;
if(crop>0 || (crop==0 && x<subDivX && y<subDivY)){
pointX = (float) ((R+r*Math.cos((-1d+2d*dy)*Math.PI))*Math.cos((-1d+2d*dx)*Math.PI));
pointZ = (float) ((R+r*Math.cos((-1d+2d*dy)*Math.PI))*Math.sin((-1d+2d*dx)*Math.PI));
pointY = (float) (r*Math.sin((-1d+2d*dy)*Math.PI));
listVertices.add(new Point3D(pointX, pointY, pointZ));
}
}
}
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Note that we have to convert this collection to a float array. Since there is no such thing as <span style="font-family: "Courier New",Courier,monospace;">FloatStream</span>, trying to use Java 8 streams, I asked a question at <a href="http://stackoverflow.com/questions/26951431/how-to-get-an-observablefloatarray-from-a-stream">StackOverflow</a>, and as result now we use a very handy <span style="font-family: "Courier New",Courier,monospace;"><a href="https://github.com/Birdasaur/FXyz/blob/master/src/org/fxyz/utils/FloatCollector.java">FloatCollector</a> </span>to do the conversion:</span><br />
<br />
<pre class="brush: java"> float[] floatVertices=listVertices.stream()
.flatMapToDouble(p->new DoubleStream(p.x,p.y,p.z))
.collect(()->new FloatCollector(listVertices.size()*3), FloatCollector::add, FloatCollector::join)
.toArray();
triangleMesh.getPoints().setAll(floatVertices);
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">In case anybody is wondering why we don't use plain <span style="font-family: "Courier New",Courier,monospace;">float[]</span>, using collections instead of simple float arrays allow us to perform mesh coloring (as we'll see later), subdivisions, ray tracing,...using streams and, in many of these cases, parallel streams.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Well, in Jason's words: <i> why TriangleMesh doesn't provide a format that incorporates the use of streams by default...??</i></span><br />
<h4>
<span style="font-family: Georgia,"Times New Roman",serif;">
Texture coordinates</span></h4>
<span style="font-family: Georgia,"Times New Roman",serif;">In the same way, we can create the texture coordinates. We can use the same grid, but now mapping (u,v) coordinates, from (0.0,0.0) on the left top corner to (1.0,1.0) on the right bottom one.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyGqlBaV1CeyxY915z3khyphenhyphenwTU8CwDhJh1UHh5Ex7x920thFCRVBgknAuxyClrCbDvXIlxvCnBcVmsY0xg7_LPD0T9a1LKtK8kUDSp1JX_9DSbVdlx-gHz3j5gg9kWfS4S6Pk2xauXgngo/s1600/grid4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyGqlBaV1CeyxY915z3khyphenhyphenwTU8CwDhJh1UHh5Ex7x920thFCRVBgknAuxyClrCbDvXIlxvCnBcVmsY0xg7_LPD0T9a1LKtK8kUDSp1JX_9DSbVdlx-gHz3j5gg9kWfS4S6Pk2xauXgngo/s1600/grid4.png" height="218" width="320" /></a></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">We need extra points for the borders.</span><br />
<br />
<pre class="brush: java"> int index=0;
int width=subDivX-2*crop;
int height=subDivY-2*crop;
float[] textureCoords = new float[(width+1)*(height+1)*2];
for (int v = 0; v <= height; v++) {
float dv = (float) v / ((float)(height));
for (int u = 0; u <= width; u++) {
textureCoords[index] = (float) u /((float)(width));
textureCoords[index + 1] = dv;
index+=2;
}
}
triangleMesh.getTexCoords().setAll(textureCoords);
</pre>
<h4>
<span style="font-family: Georgia,"Times New Roman",serif;">Faces</span></h4>
<span style="font-family: Georgia,"Times New Roman",serif;">Once we have defined the coordinates we need to create the faces. From JavaDoc: </span><br />
<blockquote class="tr_bq">
<i><span style="font-family: Georgia,"Times New Roman",serif;">The term face is used to indicate 3 set of interleaving points and texture coordinates that together represent the geometric topology of a single triangle.</span></i></blockquote>
<span style="font-family: Georgia,"Times New Roman",serif;">One face is defined by 6 indices: p0, t0, p1, t1, p2, t2, where p0, p1 and p2 are indices into the points array, and t0, t1 and t2 are indices into the texture coordinates array.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">For convenience, we'll use two splitted collections of points indices and texture indices.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Based on the above figures, we go triangle by triangle, selecting the three indices position in specific order. This is critical for the surface orientation. Also note that for vertices we reuse indices at the borders to avoid the formation of seams.</span> <br />
<br />
<pre class="brush: java"> List<Point3D> listFaces = new ArrayList<>();
// Create vertices indices
for (int y =crop; y<subDivY-crop; y++) {
for (int x=crop; x<subDivX-crop; x++) {
int p00 = (y-crop)*((crop>0)?numDivX:numDivX-1) + (x-crop);
int p01 = p00 + 1;
if(crop==0 && x==subDivX-1){
p01-=subDivX;
}
int p10 = p00 + ((crop>0)?numDivX:numDivX-1);
if(crop==0 && y==subDivY-1){
p10-=subDivY*((crop>0)?numDivX:numDivX-1);
}
int p11 = p10 + 1;
if(crop==0 && x==subDivX-1){
p11-=subDivX;
}
listFaces.add(new Point3D(p00,p10,p11));
listFaces.add(new Point3D(p11,p01,p00));
}
}
List<Point3D> listTextures = new ArrayList<>();
// Create textures indices
for (int y=crop; y<subDivY-crop; y++) {
for (int x=crop; <subDivX-crop; x++) {
int p00 = (y-crop) * numDivX + (x-crop);
int p01 = p00 + 1;
int p10 = p00 + numDivX;
int p11 = p10 + 1;
listTextures.add(new Point3D(p00,p10,p11));
listTextures.add(new Point3D(p11,p01,p00));
}
}
</pre>
<span style="font-family: Georgia,"Times New Roman",serif;">Though now we have to join them. The adventages of this approach will be shown later.</span><br />
<br />
<pre class="brush: java"> // create faces
AtomicInteger count=new AtomicInteger();
int faces[] = return listFaces.stream()
.map(f->{
Point3D t=listTexture.get(count.getAndIncrement());
int p0=(int)f.x; int p1=(int)f.y; int p2=(int)f.z;
int t0=(int)t.x; int t1=(int)t.y; int t2=(int)t.z;
return IntStream.of(p0, t0, p1, t1, p2, t2);
}).flatMapToInt(i->i).toArray();
triangleMesh.getFaces().setAll(faces);
// finally return mesh
return triangleMesh;
}
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">This picture shows how we create the first and last pairs of faces. Note the use of counterclockwise winding to define the front faces, so we have the normal of every surface pointing outwards (to the outside of the screen).</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHWB077qfixKRBikbR5Y-P_TXySgWUq5pAFNwW11sdoHrbvN51RK0frqCnxZVa7Cf88Vg00cWprHJk-mIP2J1F4SVbHZ2Yd1BMP_ltuAacJb_oJ_facWwKxDMG7SitmsqCkC9OfXS_OOs/s1600/grid5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHWB077qfixKRBikbR5Y-P_TXySgWUq5pAFNwW11sdoHrbvN51RK0frqCnxZVa7Cf88Vg00cWprHJk-mIP2J1F4SVbHZ2Yd1BMP_ltuAacJb_oJ_facWwKxDMG7SitmsqCkC9OfXS_OOs/s1600/grid5.png" height="198" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjOYM3gG8_rrFZWr-viKcfwjTKQFMJMND-GKCIBFOHi_Bsq_4oRmbR_CAj7tjQggNnkSXI6esGSkb_-igc8NcHD1ckoPPyi0cvekKfiYXlXfXSNA7LEKEMlbDJReu7icKciKVAehwFAuM/s1600/grid5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;">Finally, we can create our banner effect, adding two tori, both solid (<span style="font-family: "Courier New",Courier,monospace;">DrawMode.FILL</span>) and one of them segmented and textured with an image. This snippet shows the basics: </span><br />
<br />
<pre class="brush: java"> SegmentedTorusMesh torus = new SegmentedTorusMesh(50, 42, 0, 500d, 300d);
PhongMaterial matTorus = new PhongMaterial(Color.FIREBRICK);
torus.setMaterial(matTorus);
SegmentedTorusMesh banner = new SegmentedTorusMesh(50, 42, 14, 500d, 300d);
PhongMaterial matBanner = new PhongMaterial();
matBanner.setDiffuseMap(new Image(getClass().getResource("res/Duke3DprogressionSmall.jpg").toExternalForm()));
banner.setMaterial(matBanner);
Rotate rotateY = new Rotate(0, 0, 0, 0, Rotate.Y_AXIS);
torus.getTransforms().addAll(new Rotate(0,Rotate.X_AXIS),rotateY);
banner.getTransforms().addAll(new Rotate(0,Rotate.X_AXIS),rotateY);
Group group.getChildren().addAll(torus,banner);
Group sceneRoot = new Group(group);
Scene scene = new Scene(sceneRoot, 600, 400, true, SceneAntialiasing.BALANCED);
primaryStage.setTitle("F(X)yz - Segmented Torus");
primaryStage.setScene(scene);
primaryStage.show();
final Timeline bannerEffect = new Timeline();
bannerEffect.setCycleCount(Timeline.INDEFINITE);
final KeyValue kv1 = new KeyValue(rotateY.angleProperty(), 360);
final KeyFrame kf1 = new KeyFrame(Duration.millis(10000), kv1);
bannerEffect.getKeyFrames().addAll(kf1);
bannerEffect.play();
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">to get this animation working:</span><br />
<br />
<div style="text-align: center;">
<video autoplay="autoplay" controls="" height="300" poster="https://dl.dropboxusercontent.com/u/27006338/torus.jpg" src="https://dl.dropboxusercontent.com/u/27006338/Torus2.webm" width="400">
</video> </div>
<br />
<h3>
<a href="https://www.blogger.com/null" name="PlayingWithTextures"></a>
<span style="font-family: Georgia,"Times New Roman",serif;">Playing with textures</span></h3>
<span style="font-family: Georgia,"Times New Roman",serif;">The last section of this long post will show you how we </span><span style="font-family: Georgia,"Times New Roman",serif;">can </span><span style="font-family: Georgia,"Times New Roman",serif;"> hack the textures from a <span style="font-family: "Courier New",Courier,monospace;">TriangleMesh </span>to display more advances images over the 3D shape. This will include:</span><br />
<ul>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Coloring meshes (vertices or faces) </span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Creating contour plots </span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Using patterns</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Animating textures</span></li>
</ul>
<span style="font-family: Georgia,"Times New Roman",serif;">This work is inspired by a question from Álvaro <a href="https://twitter.com/AlximiaXtreme">Álvarez</a> on <a href="http://stackoverflow.com/questions/26831871/coloring-individual-triangles-in-a-triangle-mesh-on-javafx/26834319#26834319">StackOverflow</a>, about coloring individual triangles or individual vertices from a mesh. The inmediate answer would be: no, you can't easily, since for one mesh there's one material with one diffuse color, and it's not possible to assing different materials to different triangles of the same mesh. You could create as many meshes and materials as colors, if this number were really small.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Using textures, was the only way, but for that, following the standard procedure, you will need to color precisely your texture image, to match each triangle with each color. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">In convex polihedra there's at least one <a href="http://en.wikipedia.org/wiki/Net_%28polyhedron%29">net</a>, a 2D arrangement of polygons that can be folded into the faces of the 3D shape. Based on an icosahedron (20 faces), we could use its net to color every face:</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ6c6k73x56Meqn8lV-kqeLz4w4ps0wGeRCoTOpKNydCwZ1LOgp7PK7X4QI68FY_cwxjPFTkurFFM3OGZZDO5m3-eL1ZJHIdy0ziqHm-5VAAD4eIzjmYsEfodzmZgalS2WS7k1nufiyek/s1600/0ZKMx.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ6c6k73x56Meqn8lV-kqeLz4w4ps0wGeRCoTOpKNydCwZ1LOgp7PK7X4QI68FY_cwxjPFTkurFFM3OGZZDO5m3-eL1ZJHIdy0ziqHm-5VAAD4eIzjmYsEfodzmZgalS2WS7k1nufiyek/s1600/0ZKMx.png" /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">And then use the image as texture for the 3D shape:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizmq937nKBLoUzUerGIBcjIclQrBZ94CaoM_1K9JHIwS1aXUsv51642sNLODSXCaoqVD1CAWFRF0ICaLRhvyF2JAFlRraVyBZS_-mtLTFW9HQcnNRDBMWbdH6a_PFt1iSKn0Vayb9CmMY/s1600/icosahedron.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizmq937nKBLoUzUerGIBcjIclQrBZ94CaoM_1K9JHIwS1aXUsv51642sNLODSXCaoqVD1CAWFRF0ICaLRhvyF2JAFlRraVyBZS_-mtLTFW9HQcnNRDBMWbdH6a_PFt1iSKn0Vayb9CmMY/s1600/icosahedron.jpg" height="320" width="308" /></a></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"></span>
<span style="font-family: Georgia,"Times New Roman",serif;">This was my first answer, but I started thinking about using another approach. What if instead of the above colored net we could create on runtime a small image of colored rectangles, like this:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjo7ct3DZa32HDnLiDf2ZK8BMAar1JfGVphyphenhyphenotqbfDMKfVjlMmsNhH37yxoc6tEBOpoM_Ulu-35RSaNGmtdwxdCfHnZ9UUAE1A737__raDam9_U7Z9YTIiTDrxRdDKDyg1xj4a_335WF7w/s1600/colors.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjo7ct3DZa32HDnLiDf2ZK8BMAar1JfGVphyphenhyphenotqbfDMKfVjlMmsNhH37yxoc6tEBOpoM_Ulu-35RSaNGmtdwxdCfHnZ9UUAE1A737__raDam9_U7Z9YTIiTDrxRdDKDyg1xj4a_335WF7w/s1600/colors.png" /></a></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">and trick the texture coordinates and texture indices to find their values in this image instead? Done! The result was this more neat picture:</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKY4f8zXD3TFK4L3NqEnECvRvPpkaKqTC_tRfnRBzLo3B1uyi5818-KMaKmgzGyI7E5MS8r4O9s9JsrpnaJio1m9kYmKqw2xJbifZGV0Bp3OvNTLqp1HxT0v5v3IedI25LoBGXKagd58E/s1600/9FwSJ.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKY4f8zXD3TFK4L3NqEnECvRvPpkaKqTC_tRfnRBzLo3B1uyi5818-KMaKmgzGyI7E5MS8r4O9s9JsrpnaJio1m9kYmKqw2xJbifZGV0Bp3OvNTLqp1HxT0v5v3IedI25LoBGXKagd58E/s1600/9FwSJ.png" height="320" width="308" /></a></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">(The required code to do this is in my <a href="http://stackoverflow.com/a/26834319/3956070">answer</a>, so I won't post it here). </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">And going a little bit further, if we could create one palette image, with one color per pixel, we could also assign one color to each vertex, and the texture for the rest of the triangle will be interpolated by the scene graph! This was part of a second <a href="http://stackoverflow.com/a/26886358/3956070">answer</a>:</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEil0tjeH1EP7PqqgtykuuRtOEslAxDvyWZNuL-J9gMUEffcuwDd6zzYoT9YaEBXvFbn_AMjXNi5CwGyZrFqTSaKROIBV6xRPSd4Qc4MpIsdSRbL-HR0qfwoa2R4yzbD6SrMzlvUldiJai4/s1600/icosahedron.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEil0tjeH1EP7PqqgtykuuRtOEslAxDvyWZNuL-J9gMUEffcuwDd6zzYoT9YaEBXvFbn_AMjXNi5CwGyZrFqTSaKROIBV6xRPSd4Qc4MpIsdSRbL-HR0qfwoa2R4yzbD6SrMzlvUldiJai4/s1600/icosahedron.png" height="320" width="308" /></a></div>
<h4>
<span style="font-family: Georgia,"Times New Roman",serif;">Color Palette</span></h4>
<span style="font-family: Georgia,"Times New Roman",serif;">With this small <a href="https://github.com/Birdasaur/FXyz/blob/master/src/org/fxyz/utils/Palette.java">class</a> we can create small images with up to 1530 unique colors. The most important thing is they are correlative, so we'll have smooth contour-plots, and there won't be unwanted bumps when intermediate values are interpolated.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyXCG3fTrNJ7HXpfYvpi5S3Oid3RGY5nsi6G8K3TGHz-OpSpBDdTzJsBO0cMD-tQivRtWAsm3gp-p9XhDFs9zNpJEk7tiA9MuzCEWzzEwzZonyFtkkwVDCLZYohMA6qJBRquZVS5wo8jE/s1600/palette_1600.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyXCG3fTrNJ7HXpfYvpi5S3Oid3RGY5nsi6G8K3TGHz-OpSpBDdTzJsBO0cMD-tQivRtWAsm3gp-p9XhDFs9zNpJEk7tiA9MuzCEWzzEwzZonyFtkkwVDCLZYohMA6qJBRquZVS5wo8jE/s1600/palette_1600.png" height="200" width="200" /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">To generate on runtime this 40x40 image (2 KB) we just use this short snippet:</span><br />
<br />
<pre class="brush: java"> Image imgPalette = new WritableImage(40, 40);
PixelWriter pw = ((WritableImage)imgPalette).getPixelWriter();
AtomicInteger count = new AtomicInteger();
IntStream.range(0, 40).boxed()
.forEach(y->IntStream.range(0, 40).boxed()
.forEach(x->pw.setColor(x, y, Color.hsb(count.getAndIncrement()/1600*360,1,1))));
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">With it, we can retrieve the texture coordinates for a given point from this image and update the texture coordinates on the mesh:</span><br />
<br />
<pre class="brush: java"> public DoubleStream getTextureLocation(int iPoint){
int y = iPoint/40;
int x = iPoint-40*y;
return DoubleStream.of((((float)x)/40f),(((float)y)/40f));
}
public float[] getTexturePaletteArray(){
return IntStream.range(0,colors).boxed()
.flatMapToDouble(palette::getTextureLocation)
.collect(()->new FloatCollector(2*colors), FloatCollector::add, FloatCollector::join)
.toArray();
}
mesh.getTexCoords().setAll(getTexturePaletteArray());
</pre>
<br />
<h4>
<span style="font-family: Georgia,"Times New Roman",serif;">Density Maps</span></h4>
<span style="font-family: Georgia,"Times New Roman",serif;">Half of the work is done. The other half consists in assigning a color to every vertex or face in our mesh, based on some criteria. By using a mathematical function that for any $(x,y,z)$ coordinates we'll have a value $f(x,y,z)$ that can be scaled within our range of colors.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">So let's have a function:</span><br />
<br />
<pre class="brush: java"> @FunctionalInterface
public interface DensityFunction<T> {
Double eval(T p);
}
private DensityFunction<Point3D> density;
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Let's find the extreme values, by evaluating the given function in all the vertices, using parallel streams:</span><br />
<br />
<pre class="brush: java"> private double min, max;
public void updateExtremes(List<Point3D> points){
max=points.parallelStream().mapToDouble(density::eval).max().orElse(1.0);
min=points.parallelStream().mapToDouble(density::eval).min().orElse(0.0);
if(max==min){
max=1.0+min;
}
}
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Finally, we assign the color to every vertex in every face, by evaluating the given function in all the vertices, using parallel streams:
</span><br />
<pre class="brush: java">
public int mapDensity(Point3D p){
int f=(int)((density.eval(p)-min)/(max-min)*colors);
if(f<0){
f=0;
}
if(f>=colors){
f=colors-1;
}
return f;
}
public int[] updateFacesWithDensityMap(List<Point3D> points, List<Point3D> faces){
return faces.parallelStream().map(f->{
int p0=(int)f.x; int p1=(int)f.y; int p2=(int)f.z;
int t0=mapDensity(points.get(p0));
int t1=mapDensity(points.get(p1));
int t2=mapDensity(points.get(p2));
return IntStream.of(p0, t0, p1, t1, p2, t2);
}).flatMapToInt(i->i).toArray();
}
mesh.getFaces().setAll(updateFacesWithDensityMap(listVertices, listFaces));
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Did I say I love Java 8??? You can see now how the strategy of using lists for vertices, textures and faces has clear adventages over the float arrays.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Let's run some example, using the <a href="https://github.com/Birdasaur/FXyz/blob/master/src/org/fxyz/shapes/primitives/IcosahedronMesh.java"><span style="font-family: "Courier New",Courier,monospace;">IcosahedronMesh</span></a> classs from F(X)yz:
</span><br />
<pre class="brush: java">
IcosahedronMesh ico = new IcosahedronMesh(5,1f);
ico.setTextureModeVertices3D(1600,p->(double)p.x*p.y*p.z);
Scene scene = new Scene(new Group(ico), 600, 600, true, SceneAntialiasing.BALANCED);
primaryStage.setScene(scene);
primaryStage.show();
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">This is the result:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLE6gyBUMLJEfbt2Xh4dbOmtQ4qUIqKbGcL9OtMfUYGFmk1rn5c4zr1HipeKFS2GO3joPLJm9d5rWswYLV3ZKmHm2e-CD-_N4JPQMxzSc1asfvMucvRk8d3qIXWsalh8YPAmKW7Yh9WoY/s1600/icosphere.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLE6gyBUMLJEfbt2Xh4dbOmtQ4qUIqKbGcL9OtMfUYGFmk1rn5c4zr1HipeKFS2GO3joPLJm9d5rWswYLV3ZKmHm2e-CD-_N4JPQMxzSc1asfvMucvRk8d3qIXWsalh8YPAmKW7Yh9WoY/s1600/icosphere.png" height="329" width="640" /></a></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Impressive, right? After a long explanation, we can happily say: yes! we can color every single triangle or vertex on the mesh!</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">And we could even move the colors, creating an smooth animation. For this we only need to update the faces (vertices and texture coordinates are the same). This video shows one:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="315" src="//www.youtube.com/embed/CMGQrcm8RV0" width="560"></iframe>
</div>
<br />
<h3>
<span style="font-family: Georgia,"Times New Roman",serif;">More features</span></h3>
<span style="font-family: Georgia,"Times New Roman",serif;">More? In this post? No! I won't extend it anymore. I just post this picture:</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtXfsKPIH-RXBBKl2sNBtH1Wve2KM2-OxweaACLv367DFcpq0QE2IrDJjV41Mzb2pjsIS_sjZ6T9zB6bAmX2bQoOX1udauAOW1INxC54W1hqDP3kD8NUv1WsTZbWNUxRjxfoOxEzmsH10/s1600/3Dshapes.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtXfsKPIH-RXBBKl2sNBtH1Wve2KM2-OxweaACLv367DFcpq0QE2IrDJjV41Mzb2pjsIS_sjZ6T9zB6bAmX2bQoOX1udauAOW1INxC54W1hqDP3kD8NUv1WsTZbWNUxRjxfoOxEzmsH10/s1600/3Dshapes.png" height="360" width="640" /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">And refer you to all these available 3D shapes and more at F(X)yz <a href="https://github.com/Birdasaur/FXyz">repository</a>. If I have the time, I'll try to post about them in a second part.</span><br />
<h3>
<span style="font-family: Georgia,"Times New Roman",serif;">Conclusions</span></h3>
<span style="font-family: Georgia,"Times New Roman",serif;">JavaFX 3D API in combination with Java 8 new features has proven really powerful in terms of rendering complex meshes. The API can be easily extended to create libraries or frameworks that help the developer in case 3D features are required.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">We are far from others (Unity 3D, Three.js, ... to say a few), but with the collaboration of the great JavaFX community we can shorten this gap.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Please, clone the repository, test it, create pull requests, issues, feature requests, ... get in touch with us, help us to keep this project alive and growing.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Also visit StackOverflow and ask questions there </span><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">using these tags: <span style="font-family: "Courier New",Courier,monospace;">javafx</span>, <span style="font-family: "Courier New",Courier,monospace;">javafx-8</span> and the new <span style="font-family: "Courier New",Courier,monospace;">javafx-3d</span>). You never know where a good question may take you! And</span> the answers will help others developers too.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">A final word to give a proper shout-out to Sean Phillips and Jason Pollastrini, founders of the F(x)yz library, for starting an outstanding project.</span>
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
Anonymoushttp://www.blogger.com/profile/15247383133952423416noreply@blogger.com6tag:blogger.com,1999:blog-7114525940495426773.post-90302071298142861042014-04-14T13:40:00.001+02:002014-05-20T21:12:26.815+02:00RubikFX: Solving the Rubik's Cube with JavaFX 3D<span style="font-family: Verdana,sans-serif;">Hi all!</span><br />
<br />
<span style="font-family: Verdana,sans-serif;">It's really been a while since my last post... But in the middle, three major conferences kept me away from my blog. I said to myself I had to blog about the topics of my talks, but I had no chance at all. </span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<span style="font-family: Verdana,sans-serif;">Now, with JavaOne still far in the distance, and after finishing my collaboration in the book</span><span style="font-family: Verdana,sans-serif;">, soon to be published,</span><span style="font-family: Verdana,sans-serif;"> <a href="http://www.apress.com/9781430264606" target="_blank">JavaFX 8, Introduction by Example</a>, with my friends Carl Dea, Gerrit Grunwald, Mark Heckler and Sean Phillips, I've had the chance to play with Java 8 and JavaFX 3D</span> <span style="font-family: Verdana,sans-serif;">for a few weeks, and this post is the result of my findings.</span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<span style="font-family: Verdana,sans-serif;">It happens that my kids had recently at home a <a href="http://eu.rubiks.com/" target="_blank">Rubik's cube</a>, and we also built the Lego Mindstorms EV3 solver thanks to <a href="http://mindcuber.com/mindcub3r/mindcub3r.html" target="_blank">this</a> incredible project from David Gilday, the guy behind the <a href="https://www.youtube.com/watch?v=X0pFZG7j5cE" target="_blank">CubeStormer 3</a> with the world record of fastest solving.</span><br />
<br />
<span style="font-family: Verdana,sans-serif;"></span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIUnx9-YJ3SkySEoLhbNGp9i80UorfDQQB2uWUw3JPnD_s_jbCebnlcmUs6JHuy9s0eMmRu72e6z_qXS92zXlDe-P8HYnDvBZ77174D4-8N6iGW-gZnKvPaY-cKe5k8NLsA35V2RLDDiM/s1600/IMG_4196.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIUnx9-YJ3SkySEoLhbNGp9i80UorfDQQB2uWUw3JPnD_s_jbCebnlcmUs6JHuy9s0eMmRu72e6z_qXS92zXlDe-P8HYnDvBZ77174D4-8N6iGW-gZnKvPaY-cKe5k8NLsA35V2RLDDiM/s1600/IMG_4196.png" height="476" width="640" /></a></div>
<br />
<br />
<span style="font-family: Verdana,sans-serif;">After playing with the cube for a while, I thought about the possibility of creating a JavaFX application for solving the cube, and that's how RubikFX was born.</span><br />
<br />
<span style="font-family: Verdana,sans-serif;">If you're eager to know what this is about, <a href="https://www.youtube.com/watch?v=ZVPIBkDgZV4" target="_blank">here</a> is a link to a video in YouTube which will show you most of it. </span><br />
<br />
<span style="font-family: Verdana,sans-serif;">Basically, in this post
I'll talk about importing 3D models in a JavaFX scene, with the ability
to zoom, rotate, scale them, add lights, move the camera,... Once we have a nice 3D model of the Rubik's cube, we will try to create a way of moving layers independently from the rest of the model, keeping track of the changes made. Finally, we'll add mouse picking for selecting faces and rotating layers.</span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<span style="font-family: Verdana,sans-serif;">Please read <a href="http://eu.rubiks.com/solving-guide/3x3" target="_blank">this</a> if you're not familiar with Rubik's cube notation and the basic steps for solving it.</span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<span style="font-family: Verdana,sans-serif;"><b>Before we start</b></span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<span style="font-family: Verdana,sans-serif;">By now you should know that Java 8 is GA since the 18th of March, so the code in this project is based on this version. In case you haven't done it yet, please download from <a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html" target="_blank">here</a> the new SDK and update your system. Also, I use NetBeans 8.0 for its support for Java 8 including lambdas and the new Streams API, among other things. You can update your IDE from <a href="https://netbeans.org/downloads/" target="_blank">here</a>.</span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<span style="font-family: Verdana,sans-serif;">I use two external dependencies. One for importing the model, from a experimental project called <a href="http://hg.openjdk.java.net/openjfx/8/master/rt/file/f89b7dc932af/apps/experiments/3DViewer" target="_blank">3DViewer</a>, which is part of the <a href="http://openjdk.java.net/projects/openjfx/" target="_blank">OpenJFX</a> project. So we need to <a href="http://hg.openjdk.java.net/openjfx/8/master/rt/file/f89b7dc932af/apps/experiments/3DViewer" target="_blank">download</a> it and build it. The second one is from the <a href="http://fxexperience.com/controlsfx/" target="_blank">ControlsFX</a> project, for adding cool dialogs to the application. Download it from <a href="http://fxexperience.com/downloads/controlsfx-8.0.5.zip" target="_blank">here</a>.</span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<span style="font-family: Verdana,sans-serif;">Finally, we need a 3D model for the cube. You can build it yourself or use a free model, like <a href="http://tf3dm.com/3d-model/rubik39s-cube-79189.html" target="_blank">this</a>, submitted by 3dregenerator, which you can download in 3ds or OBJ formats.</span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<span style="font-family: Verdana,sans-serif;">Once you've got all this ingredients, it's easy to get this picture:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNSCfDkJWs3YtsVP-o8sULEvYnd0sQ3hdXDhNjOuHD-e75hlm5OqD4PGwwb9ILMhWGfPYBBi520WVpCC_wnbxevSKJF1RpvD0pq6ozUry9CQ-CmeqRq1nl-dRZ40HE-5xOy9TmFwWHWAU/s1600/3dViewerCube.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNSCfDkJWs3YtsVP-o8sULEvYnd0sQ3hdXDhNjOuHD-e75hlm5OqD4PGwwb9ILMhWGfPYBBi520WVpCC_wnbxevSKJF1RpvD0pq6ozUry9CQ-CmeqRq1nl-dRZ40HE-5xOy9TmFwWHWAU/s1600/3dViewerCube.png" height="448" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVSVIa0u6E1Rot3z6JZZtrDdk2yNUDs_YtUQs-ch4jlLgKs3-NoFEA0dSB5a7dFS0m9melZxo3m-ftKaU8W9RSpkhUHqfx37UWAPLj5X8gW12F3z1XM91E2Py7C2U7xnDxan9xG2Zpwm4/s1600/3dViewerCube.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<span style="font-family: Verdana,sans-serif;"></span><br />
<span style="font-family: Verdana,sans-serif;">For that, just extract the files, rename</span><span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;"> </span><span style="font-family: Verdana,sans-serif;">'Rubik's Cube.mtl' to 'Cube.mtl' and</span> </span><span style="font-family: Verdana,sans-serif;">'Rubik's Cube.obj' to 'Cube.obj', edit this file and change the third line to 'mtllib Cube.mtl', and save the file.</span><br />
<br />
<span style="font-family: Verdana,sans-serif;">Now run the 3DViewer application, and drag 'Cube.obj' to the viewer. Open Settings tab, and select Lights, turn on the ambient light with a white color, and off the puntual Light 1. You can zoom in or out (mouse wheel, right button or navigation bar), rotate the cube with the left button (modifying rotation speed with Ctrl or Shift), or translate the model with both mouse buttons pressed.</span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<span style="font-family: Verdana,sans-serif;">Now select Options and click Wireframe, so you can see the triangle meshes used to build the model.</span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCkIAvefwO9gvUJ66YPzwbMzIRzLyNoYxZmBdblJIPhU6qTq5xn3okR0BXOGlBhFCy9sUWfKWKsLdMLbxtNIKO0L-UN9XHF-IH072hoW51QrNMKbHb1SJSZzcPs3hBh_64imqdnNv9_NA/s1600/3dViewerCubeMesh.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCkIAvefwO9gvUJ66YPzwbMzIRzLyNoYxZmBdblJIPhU6qTq5xn3okR0BXOGlBhFCy9sUWfKWKsLdMLbxtNIKO0L-UN9XHF-IH072hoW51QrNMKbHb1SJSZzcPs3hBh_64imqdnNv9_NA/s1600/3dViewerCubeMesh.png" height="448" width="640" /></a></div>
<span style="font-family: Verdana,sans-serif;"><br /></span>
<span style="font-family: Verdana,sans-serif;"></span><br />
<span style="font-family: Verdana,sans-serif;">Each one of the 27 <i>cubies </i>is given a name in the obj file, like 'Block46' and so on. All of its triangles are grouped in meshes defined by the material assigned, </span><span style="font-family: Verdana,sans-serif;">so each cubie is made of 1 to 6 meshes, with names like 'Block46', 'Block46 (2)' and so on, and there are a total number of 117 meshes.</span><br />
<span style="font-family: Verdana,sans-serif;"></span><br />
<span style="font-family: Verdana,sans-serif;">The color of each cubie meshes is asigned in the 'Cube.mtl' file with the Kd constant relative to the <a href="http://en.wikipedia.org/wiki/Wavefront_.obj_file" target="_blank">diffuse</a> color. </span><br />
<br />
<span style="color: blue;"><span style="font-family: Verdana,sans-serif;"><span style="font-size: small;"><b>1. The Rubik's Cube - Lite Version</b></span> </span></span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<span style="font-family: Verdana,sans-serif;"><b>Importing the 3D model</b></span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<span style="font-family: Verdana,sans-serif;">So once we know how is our model, we need to construct the <span style="font-family: "Courier New",Courier,monospace;">MeshView </span>nodes for each mesh. The <span style="font-family: "Courier New",Courier,monospace;">ObjImporter </span>class from 3DViewer provide a <span style="font-family: "Courier New",Courier,monospace;">getMeshes() </span>method that returns a <span style="font-family: "Courier New",Courier,monospace;">Set</span> of names of the blocks for every mesh. So we will define a <span style="font-family: "Courier New",Courier,monospace;">HashMap </span>to bind every name to its <span style="font-family: "Courier New",Courier,monospace;">MeshView</span>. For each mesh name <span style="font-family: "Courier New",Courier,monospace;">s</span> we get the <span style="font-family: "Courier New",Courier,monospace;">MeshView </span>object with <span style="font-family: "Courier New",Courier,monospace;">buildMeshView(s)</span> method. </span><br />
<br />
<span style="font-family: Verdana,sans-serif;">By design, the cube materials in this model don't reflect light (<span style="font-family: "Courier New",Courier,monospace;">Ns=0</span>), so we'll change this to allow interaction with puntual lights, by modifying the material specular power property, defined in the <span style="font-family: "Courier New",Courier,monospace;"><a href="http://docs.oracle.com/javase/8/javafx/api/javafx/scene/paint/PhongMaterial.html" target="_blank">PhongMaterial</a></span> class.</span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<span style="font-family: Verdana,sans-serif;">Finally, we will rotate the original model so we have the white face on the top, and the blue one on the front. </span><br />
<br />
<pre class="brush: java">public class Model3D {
// Cube.obj contains 117 meshes, marked as "Block46",...,"Block72 (6)" in this set:
private Set<String> meshes;
// HashMap to store a MeshView of each mesh with its key
private final Map<String,Meshview> mapMeshes=new HashMap<>();
public void importObj(){
try {// cube.obj
ObjImporter reader = new ObjImporter(getClass().getResource("Cube.obj").toExternalForm());
meshes=reader.getMeshes(); // set with the names of 117 meshes
Affine affineIni=new Affine();
affineIni.prepend(new Rotate(-90, Rotate.X_AXIS));
affineIni.prepend(new Rotate(90, Rotate.Z_AXIS));
meshes.stream().forEach(s-> {
MeshView cubiePart = reader.buildMeshView(s);
// every part of the cubie is transformed with both rotations:
cubiePart.getTransforms().add(affineIni);
// since the model has Ns=0 it doesn't reflect light, so we change it to 1
PhongMaterial material = (PhongMaterial) cubiePart.getMaterial();
material.setSpecularPower(1);
cubiePart.setMaterial(material);
// finally, add the name of the part and the cubie part to the hashMap:
mapMeshes.put(s,cubiePart);
});
} catch (IOException e) {
System.out.println("Error loading model "+e.toString());
}
}
public Map<String, MeshView> getMapMeshes() {
return mapMeshes;
}
}
</pre>
<br />
<span style="font-family: Verdana,sans-serif;">Since the model is oriented with white to the right (X axis) and red in the front (Z axis) (see picture above), two rotations are required: first rotate -90 degrees towards X axis, to put blue in the front, and then rotate 90 degrees arount Z axis to put white on top.</span><br />
<br />
<span style="font-family: Verdana,sans-serif;">Mathematically, the second rotation matrix in Z must by multiplied on the left to the first matrix in X. But according to this <a href="http://hg.openjdk.java.net/openjfx/8/master/rt/file/f89b7dc932af/modules/graphics/src/main/java/javafx/scene/transform/Affine.java" target="_blank">link</a> if we use <span style="font-family: "Courier New",Courier,monospace;">add </span>or <span style="font-family: "Courier New",Courier,monospace;">append</span> matrix rotations are operated on the right, and this will be wrong:</span><br />
<br />
<pre class="brush: java">cubiePart.getTransforms().addAll(new Rotate(-90, Rotate.X_AXIS),
new Rotate(90, Rotate.Z_AXIS));
</pre>
<br />
<span style="font-family: Verdana,sans-serif;">as it will perform a first rotation in Z and a second one in X, putting red on top and yellow on front. </span><span style="font-family: Verdana,sans-serif;">Also this is wrong too:</span><br />
<br />
<pre class="brush: java">cubiePart.getTransforms().addAll(new Rotate(90, Rotate.Z_AXIS),
new Rotate(-90, Rotate.X_AXIS));
</pre>
<br />
<span style="font-family: Verdana,sans-serif;">Though it does the right rotations, then it will require for further rotations of any cubie to be rotated from its original position, which is quite more complicated than rotating always from the last state.<br /> <br />So <span style="font-family: "Courier New",Courier,monospace;">prepend </span>is the right way to proceed here, and we just need to prepend the last rotation matrix to the <span style="font-family: "Courier New",Courier,monospace;">Affine </span>matrix of the cubie with all the previous rotations stored there.</span><br />
<br />
<b><span style="font-family: Verdana,sans-serif;">Handling the model</span></b><br />
<br />
<span style="font-family: Verdana,sans-serif;">After importing the obj file, we can figure out which is the number of each cubie, and once the cube it's well positioned (white face top, blue face front), the scheme we're going to use is a <span style="font-family: "Courier New",Courier,monospace;">List<Integer></span> with 27 items:</span><br />
<ul>
<li><span style="font-family: Verdana,sans-serif;">first 9 indexes are the 9 cubies in the (F)Front face, from top left (R/W/B) to down right (Y/O/B).</span></li>
<li><span style="font-family: Verdana,sans-serif;">second 9 indexes are from the (S)Standing face, from top left (R/W) to down right (Y/O).</span></li>
<li><span style="font-family: Verdana,sans-serif;">last 9 indexes are from (B)Back face, from top left (G/R/W) to down right (G/Y/O). </span></li>
</ul>
<span style="font-family: Verdana,sans-serif;">But for performing rotations of these cubies, the best way is the internal use of a 3D array of integers:</span><br />
<br />
<pre class="brush: java"> private final int[][][] cube={{{50,51,52},{49,54,53},{59,48,46}},
{{58,55,60},{57,62,61},{47,56,63}},
{{67,64,69},{66,71,70},{68,65,72}}};
</pre>
<br />
<span style="font-family: Verdana,sans-serif;">where 50 is the number of the R/W/B cubie and 72 is the number for the G/Y/O.</span><br />
<br />
<span style="font-family: Verdana,sans-serif;">The <span style="font-family: "Courier New",Courier,monospace;">Rotations </span>class will take care of any face rotation.</span><br />
<br />
<pre class="brush: java"> // This is the method to perform any rotation on the 3D array just by swapping indexes
// first index refers to faces F-S-B
// second index refers to faces U-E-D
// third index refers to faces L-M-R
public void turn(String rot){
int t = 0;
for(int y = 2; y >= 0; --y){
for(int x = 0; x < 3; x++){
switch(rot){
case "L": tempCube[x][t][0] = cube[y][x][0]; break;
case "Li": tempCube[t][x][0] = cube[x][y][0]; break;
case "M": tempCube[x][t][1] = cube[y][x][1]; break;
case "Mi": tempCube[t][x][1] = cube[x][y][1]; break;
case "R": tempCube[t][x][2] = cube[x][y][2]; break;
case "Ri": tempCube[x][t][2] = cube[y][x][2]; break;
case "U": tempCube[t][0][x] = cube[x][0][y]; break;
case "Ui": tempCube[x][0][t] = cube[y][0][x]; break;
case "E": tempCube[x][1][t] = cube[y][1][x]; break;
case "Ei": tempCube[t][1][x] = cube[x][1][y]; break;
case "D": tempCube[x][2][t] = cube[y][2][x]; break;
case "Di": tempCube[t][2][x] = cube[x][2][y]; break;
case "F": tempCube[0][x][t] = cube[0][y][x]; break;
case "Fi": tempCube[0][t][x] = cube[0][x][y]; break;
case "S": tempCube[1][x][t] = cube[1][y][x]; break;
case "Si": tempCube[1][t][x] = cube[1][x][y]; break;
case "B": tempCube[2][t][x] = cube[2][x][y]; break;
case "Bi": tempCube[2][x][t] = cube[2][y][x]; break;
}
}
t++;
}
save();
}
</pre>
<br />
<span style="font-family: Verdana,sans-serif;">Similar rotations can be performed to the whole cube (X, Y or Z).</span><br />
<br />
<span style="font-family: Verdana,sans-serif;"><b><span style="font-family: Verdana,sans-serif;">The content model</span></b></span><br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;"></span></span><br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;">Once we have our model, we need a scene to display it. For that we'll use a <span style="font-family: "Courier New",Courier,monospace;"><a href="http://docs.oracle.com/javase/8/javafx/api/javafx/scene/SubScene.html" target="_blank">SubScene</a> </span>object as content container, wrapped in a <span style="font-family: "Courier New",Courier,monospace;">ContentModel </span>class, where camera, lights and orientation axis are added, which is b</span>ased in the </span><span style="font-family: "Courier New",Courier,monospace;">ContentModel </span><span style="font-family: Verdana,sans-serif;">class from 3DViewer application:</span><br />
<span style="font-family: Verdana,sans-serif;"> </span>
<br />
<pre class="brush: java">public class ContentModel {
public ContentModel(double paneW, double paneH, double dimModel) {
this.paneW=paneW;
this.paneH=paneH;
this.dimModel=dimModel;
buildCamera();
buildSubScene();
buildAxes();
addLights();
}
private void buildCamera() {
camera.setNearClip(1.0);
camera.setFarClip(10000.0);
camera.setFieldOfView(2d*dimModel/3d);
camera.getTransforms().addAll(yUpRotate,cameraPosition,
cameraLookXRotate,cameraLookZRotate);
cameraXform.getChildren().add(cameraXform2);
cameraXform2.getChildren().add(camera);
cameraPosition.setZ(-2d*dimModel);
root3D.getChildren().add(cameraXform);
// Rotate camera to show isometric view X right, Y top, Z 120º left-down from each
cameraXform.setRx(-30.0);
cameraXform.setRy(30);
}
private void buildSubScene() {
root3D.getChildren().add(autoScalingGroup);
subScene = new SubScene(root3D,paneW,paneH,true,javafx.scene.SceneAntialiasing.BALANCED);
subScene.setCamera(camera);
subScene.setFill(Color.CADETBLUE);
setListeners(true);
}
private void buildAxes() {
double length = 2d*dimModel;
double width = dimModel/100d;
double radius = 2d*dimModel/100d;
final PhongMaterial redMaterial = new PhongMaterial();
redMaterial.setDiffuseColor(Color.DARKRED);
redMaterial.setSpecularColor(Color.RED);
final PhongMaterial greenMaterial = new PhongMaterial();
greenMaterial.setDiffuseColor(Color.DARKGREEN);
greenMaterial.setSpecularColor(Color.GREEN);
final PhongMaterial blueMaterial = new PhongMaterial();
blueMaterial.setDiffuseColor(Color.DARKBLUE);
blueMaterial.setSpecularColor(Color.BLUE);
Sphere xSphere = new Sphere(radius);
Sphere ySphere = new Sphere(radius);
Sphere zSphere = new Sphere(radius);
xSphere.setMaterial(redMaterial);
ySphere.setMaterial(greenMaterial);
zSphere.setMaterial(blueMaterial);
xSphere.setTranslateX(dimModel);
ySphere.setTranslateY(dimModel);
zSphere.setTranslateZ(dimModel);
Box xAxis = new Box(length, width, width);
Box yAxis = new Box(width, length, width);
Box zAxis = new Box(width, width, length);
xAxis.setMaterial(redMaterial);
yAxis.setMaterial(greenMaterial);
zAxis.setMaterial(blueMaterial);
autoScalingGroup.getChildren().addAll(xAxis, yAxis, zAxis);
autoScalingGroup.getChildren().addAll(xSphere, ySphere, zSphere);
}
private void addLights(){
root3D.getChildren().add(ambientLight);
root3D.getChildren().add(light1);
light1.setTranslateX(dimModel*0.6);
light1.setTranslateY(dimModel*0.6);
light1.setTranslateZ(dimModel*0.6);
}
}
</pre>
<br />
<span style="font-family: Verdana,sans-serif;">For the camera, a <span style="font-family: "Courier New",Courier,monospace;">Xform </span>class from 3DViewer is used to change easily its rotation values. This also allows the initial rotation of the camera to show an isometric view:</span><br />
<br />
<pre class="brush: java"> cameraXform.setRx(-30.0);
cameraXform.setRy(30);
</pre>
<br />
<span style="font-family: Verdana,sans-serif;">Other valid ways to perform these rotations could be based on obtaining the vector and angle of rotation to combine two rotations, which involve calculate the rotation matrix first and then the vector and angle (as I explained <a href="http://jperedadnr.blogspot.com.es/2013/06/leap-motion-controller-and-javafx-new.html" target="_blank">here</a>):</span><br />
<br />
<pre class="brush: java"> camera.setRotationAxis(new Point3D(-0.694747,0.694747,0.186157));
camera.setRotate(42.1812);
</pre>
<br />
<span style="font-family: Verdana,sans-serif;">Or prepending the two rotations to all the previous transformations, by appending all of them in a single <span style="font-family: "Courier New",Courier,monospace;">Affine </span>matrix before prepending these two rotations:</span><br />
<br />
<pre class="brush: java"> Affine affineCamIni=new Affine();
camera.getTransforms().stream().forEach(affineCamIni::append);
affineCamIni.prepend(new Rotate(-30, Rotate.X_AXIS));
affineCamIni.prepend(new Rotate(30, Rotate.Y_AXIS));
camera.getTransforms().setAll(affineCamIni);
</pre>
<br />
<span style="font-family: Verdana,sans-serif;">Then we add the listeners to the subscene, so the camera can be easily rotated.</span><br />
<br />
<pre class="brush: java"> private void setListeners(boolean addListeners){
if(addListeners){
subScene.addEventHandler(MouseEvent.ANY, mouseEventHandler);
} else {
subScene.removeEventHandler(MouseEvent.ANY, mouseEventHandler);
}
}
private final EventHandler<MouseEvent> mouseEventHandler = event -> {
double xFlip = -1.0, yFlip=1.0; // y Up
if (event.getEventType() == MouseEvent.MOUSE_PRESSED) {
mousePosX = event.getSceneX();
mousePosY = event.getSceneY();
mouseOldX = event.getSceneX();
mouseOldY = event.getSceneY();
} else if (event.getEventType() == MouseEvent.MOUSE_DRAGGED) {
double modifier = event.isControlDown()?0.1:event.isShiftDown()?3.0:1.0;
mouseOldX = mousePosX;
mouseOldY = mousePosY;
mousePosX = event.getSceneX();
mousePosY = event.getSceneY();
mouseDeltaX = (mousePosX - mouseOldX);
mouseDeltaY = (mousePosY - mouseOldY);
if(event.isMiddleButtonDown() || (event.isPrimaryButtonDown() && event.isSecondaryButtonDown())) {
cameraXform2.setTx(cameraXform2.t.getX() + xFlip*mouseDeltaX*modifierFactor*modifier*0.3);
cameraXform2.setTy(cameraXform2.t.getY() + yFlip*mouseDeltaY*modifierFactor*modifier*0.3);
}
else if(event.isPrimaryButtonDown()) {
cameraXform.setRy(cameraXform.ry.getAngle() - yFlip*mouseDeltaX*modifierFactor*modifier*2.0);
cameraXform.setRx(cameraXform.rx.getAngle() + xFlip*mouseDeltaY*modifierFactor*modifier*2.0);
}
else if(event.isSecondaryButtonDown()) {
double z = cameraPosition.getZ();
double newZ = z - xFlip*(mouseDeltaX+mouseDeltaY)*modifierFactor*modifier;
cameraPosition.setZ(newZ);
}
}
};
</pre>
<br />
<b><span style="font-family: Verdana,sans-serif;">Handling the model</span></b><br />
<br />
<span style="font-family: Verdana,sans-serif;">Now we can put all together and create the <span style="font-family: "Courier New",Courier,monospace;">Rubik</span> class, where the 3D model is imported, all the meshviews are created and grouped in <span style="font-family: "Courier New",Courier,monospace;">cube</span>, which is added to the <span style="font-family: "Courier New",Courier,monospace;">content</span> subscene. At the same time, <span style="font-family: "Courier New",Courier,monospace;">rot </span>is instantiated with the original position of the cubies.</span><br />
<br />
<pre class="brush: java">public class Rubik {
public Rubik(){
// Import Rubik's Cube model and arrows
Model3D model=new Model3D();
model.importObj();
mapMeshes=model.getMapMeshes();
cube.getChildren().setAll(mapMeshes.values());
dimCube=cube.getBoundsInParent().getWidth();
// Create content subscene, add cube, set camera and lights
content = new ContentModel(800,600,dimCube);
content.setContent(cube);
// Initialize 3D array of indexes and a copy of original/solved position
rot=new Rotations();
order=rot.getCube();
// save original position
mapMeshes.forEach((k,v)->mapTransformsOriginal.put(k, v.getTransforms().get(0)));
orderOriginal=order.stream().collect(Collectors.toList());
// Listener to perform an animated face rotation
rotMap=(ov,angOld,angNew)->{
mapMeshes.forEach((k,v)->{
layer.stream().filter(l->k.contains(l.toString()))
.findFirst().ifPresent(l->{
Affine a=new Affine(v.getTransforms().get(0));
a.prepend(new Rotate(angNew.doubleValue()-angOld.doubleValue(),axis));
v.getTransforms().setAll(a);
});
});
};
}
}
</pre>
<br />
<span style="font-family: Verdana,sans-serif;">Finally we create a listener for rotating layers of cubies in a <span style="font-family: "Courier New",Courier,monospace;">Timeline </span>animation. As the rotations are prepended to the actual affine matrix of the cubies, to perform a smooth animation we'll change the angle between 0 and 90º, and listen how the timeline internally interpolate it, making small rotations between <span style="font-family: "Courier New",Courier,monospace;">angNew </span>and <span style="font-family: "Courier New",Courier,monospace;">angOld </span>angles.</span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<span style="font-family: Verdana,sans-serif;">So the method to perform the rotation could be like this:</span><br />
<br />
<pre class="brush: java"> public void rotateFace(final String btRot){
if(onRotation.get()){
return;
}
onRotation.set(true);
// rotate cube indexes
rot.turn(btRot);
// get new indexes in terms of blocks numbers from original order
reorder=rot.getCube();
// select cubies to rotate: those in reorder different from order.
AtomicInteger index = new AtomicInteger();
layer=order.stream()
.filter(o->!Objects.equals(o, reorder.get(index.getAndIncrement())))
.collect(Collectors.toList());
// add central cubie
layer.add(0,reorder.get(Utils.getCenter(btRot)));
// set rotation axis
axis=Utils.getAxis(btRot);
// define rotation
double angEnd=90d*(btRot.endsWith("i")?1d:-1d);
rotation.set(0d);
// add listener to rotation changes
rotation.addListener(rotMap);
// create animation
Timeline timeline=new Timeline();
timeline.getKeyFrames().add(
new KeyFrame(Duration.millis(600), e->{
// remove listener
rotation.removeListener(rotMap);
onRotation.set(false);
}, new KeyValue(rotation,angEnd)));
timeline.playFromStart();
// update order with last list
order=reorder.stream().collect(Collectors.toList());
}
</pre>
<br />
<b><span style="font-family: Verdana,sans-serif;">RubikFX, Lite Version</span></b><br />
<br />
<span style="font-family: Verdana,sans-serif;">Later on we'll add more features, but for now let's create a JavaFX application, with a <span style="font-family: "Courier New",Courier,monospace;">BorderPane</span>, add <span style="font-family: "Courier New",Courier,monospace;">content </span>to the center of the pane, and a few toolbars with buttons to perform rotations.</span><br />
<br />
<pre class="brush: java">public class TestRubikFX extends Application {
private final BorderPane pane=new BorderPane();
private Rubik rubik;
@Override
public void start(Stage stage) {
rubik=new Rubik();
// create toolbars
ToolBar tbTop=new ToolBar(new Button("U"),new Button("Ui"),new Button("F"),
new Button("Fi"),new Separator(),new Button("Y"),
new Button("Yi"),new Button("Z"),new Button("Zi"));
pane.setTop(tbTop);
ToolBar tbBottom=new ToolBar(new Button("B"),new Button("Bi"),new Button("D"),
new Button("Di"),new Button("E"),new Button("Ei"));
pane.setBottom(tbBottom);
ToolBar tbRight=new ToolBar(new Button("R"),new Button("Ri"),new Separator(),
new Button("X"),new Button("Xi"));
tbRight.setOrientation(Orientation.VERTICAL);
pane.setRight(tbRight);
ToolBar tbLeft=new ToolBar(new Button("L"),new Button("Li"),new Button("M"),
new Button("Mi"),new Button("S"),new Button("Si"));
tbLeft.setOrientation(Orientation.VERTICAL);
pane.setLeft(tbLeft);
pane.setCenter(rubik.getSubScene());
pane.getChildren().stream()
.filter(n->(n instanceof ToolBar))
.forEach(tb->{
((ToolBar)tb).getItems().stream()
.filter(n->(n instanceof Button))
.forEach(n->((Button)n).setOnAction(e->rubik.rotateFace(((Button)n).getText())));
});
rubik.isOnRotation().addListener((ov,b,b1)->{
pane.getChildren().stream()
.filter(n->(n instanceof ToolBar))
.forEach(tb->tb.setDisable(b1));
});
final Scene scene = new Scene(pane, 880, 680, true);
scene.setFill(Color.ALICEBLUE);
stage.setTitle("Rubik's Cube - JavaFX3D");
stage.setScene(scene);
stage.show();
}
}
</pre>
<br />
<span style="font-family: Verdana,sans-serif;">Now this is what we have already accomplished:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="315" src="//www.youtube.com/embed/dtLG4CrxXpM?rel=0" type="text/html" width="420"></iframe></div>
<br />
<span style="font-family: Verdana,sans-serif;">If you're interested in having a deeper look at the application, you can find the source code in my GitHub <a href="https://github.com/jperedadnr/LiteRubikFX" target="_blank">repository</a>. Note you'll need to add the 3DViewer.jar. </span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span><b>
</b><span style="font-family: Verdana,sans-serif;"><b>How does it work?</b> </span><br />
<br />
<span style="font-family: Verdana,sans-serif;">Take, for instance, a initial "F" rotation. We apply it to <span style="font-family: "Courier New",Courier,monospace;">rot</span>:</span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<br />
<pre class="brush: java"> // rotate cube indexes
rot.turn(btRot);
// get new indexes in terms of blocks numbers from original order
reorder=rot.getCube();
</pre>
<br />
<span style="font-family: Verdana,sans-serif;">Using <span style="font-family: "Courier New",Courier,monospace;">rot.printCube()</span> we can see the numbers of cubies for the solved cube (<span style="font-family: "Courier New",Courier,monospace;">order</span>) and for the new one, with the frontal layer rotated clockwise (<span style="font-family: "Courier New",Courier,monospace;">reorder</span>):</span><br />
<br />
<span style="font-family: Verdana,sans-serif; font-size: xx-small;"><span style="font-family: "Courier New",Courier,monospace;">order: 50 51 52 49 54 53 59 48 46 || 58 55 60 57 62 61 47 56 63 || 67 64 69 66 71 70 68 65 72<br /><span style="color: red;"><span style="color: black;">reorder: </span>59 49 50 48 <span style="color: black;">54</span> 51 46 53 52</span> || 58 55 60 57 62 61 47 56 63 || 67 64 69 66 71 70 68 65 72</span></span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<span style="font-family: Verdana,sans-serif;">By comparing both lists and getting the different items, we know which cubies must be rotated, though we have to add the number of the central cubie (54), as it is the same in both lists, but it should be rotated too. So we create the list <span style="font-family: "Courier New",Courier,monospace;">layer </span>with these nine cubies:</span><br />
<br />
<pre class="brush: java"> // select cubies to rotate: those in reorder different from order.
AtomicInteger index = new AtomicInteger();
layer=order.stream()
.filter(o->!Objects.equals(o, reorder.get(index.getAndIncrement())))
.collect(Collectors.toList());
// add central cubie
layer.add(0,reorder.get(Utils.getCenter(btRot)));
// set rotation axis
axis=Utils.getAxis(btRot);
</pre>
<br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">Utils </span>is a class that manage the values for each type of rotation. For this case:</span><br />
<br />
<pre class="brush: java"> public static Point3D getAxis(String face){
Point3D p=new Point3D(0,0,0);
switch(face.substring(0,1)){
case "F":
case "S": p=new Point3D(0,0,1);
break;
}
return p;
}
public static int getCenter(String face){
int c=0;
switch(face.substring(0,1)){
case "F": c=4; break;
}
return c;
}
</pre>
<br />
<span style="font-family: Verdana,sans-serif;"></span>
<span style="font-family: Verdana,sans-serif;">Once we've got the cubies and the axis of rotation, now it's worth noticing how the rotation listener works. With the timeline, the angle goes from 0 to 90º with an <span style="font-family: "Courier New",Courier,monospace;">EASE_BOTH </span>interpolation
(by default), so the angle increments are smaller at the beginning,
bigger in the middle and smaller again at the end. This could be a
possible list of increments: </span><span style="font-family: Verdana,sans-serif;">0.125º-3º-4.6º-2.2º-2.48º-...-2.43º-4.78º-2.4º-2.4º-0.55º.</span><br />
<br />
<span style="font-family: Verdana,sans-serif;">For every value in <span style="font-family: "Courier New",Courier,monospace;">angNew</span>, the listener <span style="font-family: "Courier New",Courier,monospace;">rotMap </span>applies a small rotation to a layer of cubies. For that we look in our <span style="font-family: "Courier New",Courier,monospace;">HashMap </span>which meshviews belongs to these cubies, and prepend a new rotation to their previous affine matrix:</span><br />
<br />
<pre class="brush: java"> // Listener to perform an animated face rotation
rotMap=(ov,angOld,angNew)->{
mapMeshes.forEach((k,v)->{
layer.stream().filter(l->k.contains(l.toString()))
.findFirst().ifPresent(l->{
Affine a=new Affine(v.getTransforms().get(0));
a.prepend(new Rotate(angNew.doubleValue()-angOld.doubleValue(),axis));
v.getTransforms().setAll(a);
});
});
};
</pre>
<br />
<span style="font-family: Verdana,sans-serif;">So in 600 ms we apply around 30 to 40 small rotations to a bunch of around 40 meshviews. </span><br />
<br />
<span style="font-family: Verdana,sans-serif;">Finally, after the rotation is done, we just need to update <span style="font-family: "Courier New",Courier,monospace;">order </span>with the last list of cubies, so we can start all over again with a new rotation.</span><br />
<span style="font-family: Verdana,sans-serif;"></span><br />
<br />
<span style="color: blue;"><span style="font-family: Verdana,sans-serif;"><span style="font-size: small;"><b>2. The Rubik's Cube - Full Version</b></span></span></span><br />
<br />
<span style="font-family: Verdana,sans-serif;"><b>Adding more features</b></span><br />
<br />
<span style="font-family: Verdana,sans-serif;">Now that we've got a working but pretty basic Rubik's cube JavaFX application, it's time for adding a few extra features, like graphic arrows and preview rotations </span><span style="font-family: Verdana,sans-serif;">to show the direction of rotation before they're performed.</span><br />
<br />
<b><span style="font-family: Verdana,sans-serif;">Scramble and Sequences</span></b><br />
<br />
<span style="font-family: Verdana,sans-serif;">Let's start by adding a scramble routine, to scramble the cubies before start solving the cube. To do that we generate a sequence of 25 random moves from a list of valid rotations.</span><br />
<br />
<pre class="brush: java"> private static final List<String> movements =
Arrays.asList("F", "Fi", "F2", "R", "Ri", "R2",
"B", "Bi", "B2", "L", "Li", "L2",
"U", "Ui", "U2", "D", "Di", "D2");
private String last="V", get="V";
public void doScramble(){
StringBuilder sb=new StringBuilder();
IntStream.range(0, 25).boxed().forEach(i->{
while(last.substring(0, 1).equals(get.substring(0, 1))){
// avoid repeating the same/opposite rotations
get=movements.get((int)(Math.floor(Math.random()*movements.size())));
}
last=get;
if(get.contains("2")){
get=get.substring(0,1);
sb.append(get).append(" ");
}
sb.append(get).append(" ");
});
doSequence(sb.toString().trim());
}
</pre>
<br />
<span style="font-family: Verdana,sans-serif;">Then we have to perform this sequence, by rotating each movement. First we extract the rotations from the string, converting other notations (like lower letters or ' instead of 'i' for counter clockwise rotations) to the one used.</span><br />
<br />
<span style="font-family: Verdana,sans-serif;">A listener is added to <span style="font-family: "Courier New",Courier,monospace;">onRotation</span>, so only when the last rotation finishes, a new rotation starts. By adding a second listener to the index property, </span><span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;">when the end of the list plus one is reached, this listener</span> is stopped, allowing for the last rotation to finish properly, and saving the rotations for a further replay option. </span><br />
<br />
<pre class="brush: java"> public void doSequence(String list){
onScrambling.set(true);
List<String> asList = Arrays.asList(list.replaceAll("’", "i").replaceAll("'", "i").split(" "));
sequence=new ArrayList<>();
asList.stream().forEach(s->{
if(s.contains("2")){
sequence.add(s.substring(0, 1));
sequence.add(s.substring(0, 1));
} else if(s.length()==1 && s.matches("[a-z]")){
sequence.add(s.toUpperCase().concat("i"));
} else {
sequence.add(s);
}
});
System.out.println("seq: "+sequence);
IntegerProperty index=new SimpleIntegerProperty(1);
ChangeListener<boolean> lis=(ov,b,b1)->{
if(!b1){
if(index.get()<sequence.size()){
rotateFace(sequence.get(index.get()));
} else {
// save transforms
mapMeshes.forEach((k,v)->mapTransformsScramble.put(k, v.getTransforms().get(0)));
orderScramble=reorder.stream().collect(Collectors.toList());
}
index.set(index.get()+1);
}
};
index.addListener((ov,v,v1)->{
if(v1.intValue()==sequence.size()+1){
onScrambling.set(false);
onRotation.removeListener(lis);
count.set(-1);
}
});
onRotation.addListener(lis);
rotateFace(sequence.get(0));
}
</pre>
<br />
<span style="font-family: Verdana,sans-serif;">Note that we use a <span style="font-family: "Courier New",Courier,monospace;">Dialog </span>from ControlsFX to prevent losing previous moves. </span><span style="font-family: Verdana,sans-serif;"><br /></span><br />
<br />
<pre class="brush: java"> Button bSc=new Button("Scramble");
bSc.setOnAction(e->{
if(moves.getNumMoves()>0){
Action response = Dialogs.create()
.owner(stage)
.title("Warning Dialog")
.masthead("Scramble Cube")
.message( "You will lose all your previous movements. Do you want to continue?")
.showConfirm();
if(response==Dialog.Actions.YES){
rubik.doReset();
doScramble();
}
} else {
doScramble();
}
});
</pre>
<br />
<span style="font-family: Verdana,sans-serif;">If you want to load a sequence, like any of <a href="http://ruwix.com/the-rubiks-cube/rubiks-cube-patterns-algorithms/" target="_blank">these</a>, another <span style="font-family: "Courier New",Courier,monospace;">Dialog </span>with input allowed is used.</span><br />
<br />
<pre class="brush: java"> Button bSeq=new Button("Sequence");
bSeq.setOnAction(e->{
String response;
if(moves.getNumMoves()>0){
response = Dialogs.create()
.owner(stage)
.title("Warning Dialog")
.masthead("Loading a Sequence").lightweight()
.message("Add a valid sequence of movements:\n(previous movements will be discarded)")
.showTextInput(moves.getSequence());
} else {
response = Dialogs.create()
.owner(stage)
.title("Information Dialog")
.masthead("Loading a Sequence").lightweight()
.message( "Add a valid sequence of movements")
.showTextInput();
}
if(response!=null && !response.isEmpty()){
rubik.doReset();
rubik.doSequence(response.trim());
}
});
</pre>
<br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;">The </span>results of scrambling a cube or adding a sequence of rotations can be seen in this video. </span><br />
<br />
</span><br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="315" src="//www.youtube.com/embed/mxmJKNQlO7g?rel=0" type="text/html" width="420"></iframe></div>
<span style="font-family: Verdana,sans-serif;">
<br />
<span style="font-family: Verdana,sans-serif;"><b></b></span><span style="font-family: Verdana,sans-serif;"><b>Timer and moves counter</b></span><br />
<br />
<span style="font-family: Verdana,sans-serif;">Let's add now a timer using the new Date and Time API for Java 8. You may have noticed the timer in the bottom toolbar in the previous video. </span><br />
<br />
<span style="font-family: Verdana,sans-serif;">For that, we use the following code in <span style="font-family: "Courier New",Courier,monospace;">RubikFX </span>class:</span><br />
<br />
</span><br />
<pre class="brush: java"> private LocalTime time=LocalTime.now();
private Timeline timer;
private final StringProperty clock = new SimpleStringProperty("00:00:00");
private final DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm:ss").withZone(ZoneId.systemDefault());
@Override
public void start(Stage stage) {
...
Label lTime=new Label();
lTime.textProperty().bind(clock);
tbBottom.getItems().addAll(new Separator(),lTime);
timer=new Timeline(new KeyFrame(Duration.ZERO, e->{
clock.set(LocalTime.now().minusNanos(time.toNanoOfDay()).format(fmt));
}),new KeyFrame(Duration.seconds(1)));
timer.setCycleCount(Animation.INDEFINITE);
rubik.isSolved().addListener((ov,b,b1)->{
if(b1){
timer.stop();
}
});
time=LocalTime.now();
timer.playFromStart();
}
</pre>
<span style="font-family: Verdana,sans-serif;">
<br />
<span style="font-family: Verdana,sans-serif;">For the counter, we'll add two classes. <span style="font-family: "Courier New",Courier,monospace;">Move </span>is a simple POJO class, with a string for the name of the rotation and a long for the timestamp of the movement. <span style="font-family: "Courier New",Courier,monospace;">Moves </span>class will contain a list of moves.</span><br />
<br />
</span><br />
<pre class="brush: java">public class Moves {
private final List<Move> moves=new ArrayList<>();
public Moves(){
moves.clear();
}
public void addMove(Move m){ moves.add(m); }
public List<Move> getMoves() { return moves; }
public Move getMove(int index){
if(index>-1 && index<moves.size()){
return moves.get(index);
}
return null;
}
public String getSequence(){
StringBuilder sb=new StringBuilder("");
moves.forEach(m->sb.append(m.getFace()).append(" "));
return sb.toString().trim();
}
}
</pre>
<span style="font-family: Verdana,sans-serif;">
<br />
<span style="font-family: Verdana,sans-serif;">For adding the number of rotations, we use the following code in <span style="font-family: "Courier New",Courier,monospace;">RubikFX </span>class:</span><br />
<br />
</span><br />
<pre class="brush: java"> private Moves moves=new Moves();
@Override
public void start(Stage stage) {
...
rubik.getLastRotation().addListener((ov,v,v1)->{
if(!v1.isEmpty()){
moves.addMove(new Move(v1, LocalTime.now().minusNanos(time.toNanoOfDay()).toNanoOfDay()));
}
});
Label lMov=new Label();
rubik.getCount().addListener((ov,v,v1)->{
lMov.setText("Movements: "+(v1.intValue()+1));
});
tbBottom.getItems().addAll(new Separator(),lMov);
}
</pre>
<span style="font-family: Verdana,sans-serif;">
<br />
<b><span style="font-family: Verdana,sans-serif;">Replay</span></b><br />
<br />
<span style="font-family: Verdana,sans-serif;">We can also replay the list of movements the user has done stored in <span style="font-family: "Courier New",Courier,monospace;">moves</span>. For that we need to restore first the state of the cube right after the scramble, and performe one by one all the rotations from the list. </span><br />
<br />
</span><br />
<pre class="brush: java"> public void doReplay(List<Move> moves){
if(moves.isEmpty()){
return;
}
content.resetCam();
//restore scramble
if(mapTransformsScramble.size()>0){
mapMeshes.forEach((k,v)->v.getTransforms().setAll(mapTransformsScramble.get(k)));
order=orderScramble.stream().collect(Collectors.toList());
rot.setCube(order);
count.set(-1);
} else {
// restore original
doReset();
}
onReplaying.set(true);
IntegerProperty index=new SimpleIntegerProperty(1);
ChangeListener<boolean> lis=(ov,v,v1)->{
if(!v1 && moves.size()>1){
if(index.get()<moves.size()){
timestamp.set(moves.get(index.get()).getTimestamp());
rotateFace(moves.get(index.get()).getFace());
}
index.set(index.get()+1);
}
};
index.addListener((ov,v,v1)->{
if(v1.intValue()==moves.size()+1){
onReplaying.set(false);
onRotation.removeListener(lis);
acuAngle=0;
}
});
onRotation.addListener(lis);
timestamp.set(moves.get(0).getTimestamp());
rotateFace(moves.get(0).getFace());
}
</pre>
<span style="font-family: Verdana,sans-serif;">
<br />
<b><span style="font-family: Verdana,sans-serif;">Rotation direction preview</span></b><span style="font-family: Verdana,sans-serif;"><br /> </span><br />
<span style="font-family: Verdana,sans-serif;">Time for a new feature: 3D arrows will be shown in the rotating face or axis, to show the direction.</span><br />
<br />
<span style="font-family: Verdana,sans-serif;">Actually, JavaFX 3D API doesn't supply any way of building 3D complex models. There's an impressive ongoing work by Michael <a href="https://twitter.com/mihosoft" target="_blank">Hoffer</a> to provide a way by using Constructive Solid Geometry (CSG) </span><span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;"><a href="https://github.com/miho/JavaFXScad" target="_blank">here</a></span>, kudos Michael!!</span><br />
<br />
<span style="font-family: Verdana,sans-serif;">By u</span><span style="font-family: Verdana,sans-serif;">sing primitives and boolean operations with CSG you can build a model, and even export it with STL format.</span></span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: Verdana,sans-serif;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMTI_x0F8V3-H80lXwMzxILcAn1VJPHzL-qj9oG18eAOHP5uVkUqb8vW3WaiIrRwn3HeA4UwzmOwX4bkqEn1bCWlDbFG7R1Lh0X7uUEcTzyz8Y_K9yY3Y9_YOCeegx5N7Jj_fzYNsQlpw/s1600/arrowFX.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMTI_x0F8V3-H80lXwMzxILcAn1VJPHzL-qj9oG18eAOHP5uVkUqb8vW3WaiIrRwn3HeA4UwzmOwX4bkqEn1bCWlDbFG7R1Lh0X7uUEcTzyz8Y_K9yY3Y9_YOCeegx5N7Jj_fzYNsQlpw/s1600/arrowFX.png" height="292" width="400" /></a></span></div>
<span style="font-family: Verdana,sans-serif;">
<br />
<br />
<span style="font-family: Verdana,sans-serif;">You can use free or commercial 3D software for this task too. I designed these arrows with <a href="http://www.sketchup.com/products/sketchup-make" target="_blank">SketchUp Make</a> and exported them to OBJ format so I could import them as we did with the cube using ObjImporter from 3DViewer. </span><br />
<br />
</span><br />
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: Verdana,sans-serif;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgy9K67A9f4usSi5_50iTPQVLKyWHRNPaASQye4uZws-ZvGX2lPd5kGAYiTJ8Th9oOhgXPzG79fyCFZFoOjjggH5OE97P9CpFELbWi49Cxls2JmCDJtA13BA1nRKA6P6FRVSQdPsR0m8b4/s1600/arrow.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgy9K67A9f4usSi5_50iTPQVLKyWHRNPaASQye4uZws-ZvGX2lPd5kGAYiTJ8Th9oOhgXPzG79fyCFZFoOjjggH5OE97P9CpFELbWi49Cxls2JmCDJtA13BA1nRKA6P6FRVSQdPsR0m8b4/s1600/arrow.png" height="320" width="279" /></a></span></div>
<span style="font-family: Verdana,sans-serif;">
<br />
<span style="font-family: Verdana,sans-serif;">While the design is fast, it requires manual editting of the created obj file to convert long faces of more than 4 vertixes as they are not properly imported. </span><br />
<br />
</span><br />
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: Verdana,sans-serif;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9hdESkzzDBV1i3R2_WmgDlBXToAQ1I_bDKTe5wmxf9EmTX-C6aFma0mOAM4Sxx8-BnSeUJLLy0AsybOxUxVs3w83hoW9X_jXQT3T1lJb02aiSiOcnsOZqpLUPGkvj3R13fyo8elyOlH8/s1600/arrowObj.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9hdESkzzDBV1i3R2_WmgDlBXToAQ1I_bDKTe5wmxf9EmTX-C6aFma0mOAM4Sxx8-BnSeUJLLy0AsybOxUxVs3w83hoW9X_jXQT3T1lJb02aiSiOcnsOZqpLUPGkvj3R13fyo8elyOlH8/s1600/arrowObj.png" height="320" width="400" /></a></span></div>
<span style="font-family: Verdana,sans-serif;">
<br />
<span style="font-family: Verdana,sans-serif;">Other approach could be exporting the file to *.3ds and use the proper <a href="http://www.interactivemesh.org/models/jfx3dimporter.html" target="_blank">importer</a> from August Lammersdorf. </span></span><br />
<br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;"><i><span style="font-family: Verdana,sans-serif;"><b>Edit</b>: Michael Hoffer kindly added an option to export to OBJ format, so now it would be possible to import the arrow model generated with CSG in <a href="https://github.com/miho/JavaFXScad" target="_blank">JavaFXScad</a> </span>in our scene. Thanks Michael!</i></span></span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<span style="font-family: Verdana,sans-serif;">Once we have the model, we have to add it, scale and rotate it, so we can show the arrow in the rotating face.</span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
<br />
</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2vhI10BBubDr2chftC8V6hO3VKtzHG3uhhkFn5SC1sSn9FWfALUkQnOcZHPi2pHdf5BU8bjTadH8gDcFtxkL7NCyZX9r7evnDbb01H5mdk9nFGwLQ41UDgMpj-DGO7jY5GgmAoDhG4u0/s1600/rotUi.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2vhI10BBubDr2chftC8V6hO3VKtzHG3uhhkFn5SC1sSn9FWfALUkQnOcZHPi2pHdf5BU8bjTadH8gDcFtxkL7NCyZX9r7evnDbb01H5mdk9nFGwLQ41UDgMpj-DGO7jY5GgmAoDhG4u0/s1600/rotUi.png" height="320" width="400" /></a></div>
<br />
<span style="font-family: Verdana,sans-serif;">For a rotation like 'Ui':</span><br />
<span style="font-family: Verdana,sans-serif;"> </span>
<br />
<pre class="brush: java"> public void updateArrow(String face, boolean hover){
boolean bFaceArrow=!(face.startsWith("X")||face.startsWith("Y")||face.startsWith("Z"));
MeshView arrow=bFaceArrow?faceArrow:axisArrow;
if(hover && onRotation.get()){
return;
}
arrow.getTransforms().clear();
if(hover){
double d0=arrow.getBoundsInParent().getHeight()/2d;
Affine aff=Utils.getAffine(dimCube, d0, bFaceArrow, face);
arrow.getTransforms().setAll(aff);
arrow.setMaterial(Utils.getMaterial(face));
if(previewFace.get().isEmpty()) {
previewFace.set(face);
onPreview.set(true);
rotateFace(face,true,false);
}
} else if(previewFace.get().equals(face)){
rotateFace(Utils.reverseRotation(face),true,true);
} else if(previewFace.get().equals("V")){
previewFace.set("");
onPreview.set(false);
}
}
</pre>
<br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;">where the affine is calculated in the <span style="font-family: "Courier New",Courier,monospace;">Utils </span>class for the current face:</span></span><br />
<br />
<pre class="brush: java"> public static Affine getAffine(double dimCube, double d0, boolean bFaceArrow, String face){
Affine aff=new Affine(new Scale(3,3,3));
aff.append(new Translate(0,-d0,0));
switch(face){
case "U":
case "Ui": aff.prepend(new Rotate(face.equals("Ui")?180:0,Rotate.Z_AXIS));
aff.prepend(new Rotate(face.equals("Ui")?45:-45,Rotate.Y_AXIS));
aff.prepend(new Translate(0,dimCube/2d,0));
break;
}
return aff;
}
</pre>
<br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;">To trigger the drawing of the arrow, we set a listener to the buttons on the toolbars based on the mouse hovering.</span></span><br />
<br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;">We can also add a small rotation (5º) as preview of the full rotation (90º) in the face selected, by calling <span style="font-family: "Courier New",Courier,monospace;">rotateFace </span>again, with <span style="font-family: "Courier New",Courier,monospace;">bPreview=true</span> at this point.</span></span><br />
<br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;"></span></span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFKGexzAgs0q1BPSREbu21ewEP4bNhi0FWCYrwt5ODut6FTHe5tQ_RIIN-_gc5MevabLwRCnENG-ZjddK8GDysl0WKWDEBa5iEOWz6ieJTKk-U6Fv484_ljSINuzzILO2Dmm0wCYwb_Lc/s1600/rotUi2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFKGexzAgs0q1BPSREbu21ewEP4bNhi0FWCYrwt5ODut6FTHe5tQ_RIIN-_gc5MevabLwRCnENG-ZjddK8GDysl0WKWDEBa5iEOWz6ieJTKk-U6Fv484_ljSINuzzILO2Dmm0wCYwb_Lc/s1600/rotUi2.png" height="320" width="400" /></a></div>
<br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;">If the user clicks on the button, the rotation is completed (from 5º to 90º). Otherwise the rotation is cancelled (from 5º to 0º). In both cases, with a smooth animation.</span></span><br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;"><br /></span></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: Verdana,sans-serif;"><iframe allowfullscreen="" frameborder="0" height="315" src="//www.youtube.com/embed/rhNeI1ROHTg?rel=0" type="text/html" width="420"></iframe></span></div>
<br />
<b><span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;">Select rotation by picking</span></span></b><br />
<br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;">Finally, the rotation could be performed based on the mouse picking of a cubie face, with visual aid showing the arrow and performing a small rotation of 5º. If the mouse is dragged far enough the full rotation will be performed after it is released. If the mouse is released close to the origin, the rotation is cancelled.</span></span><br />
<br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;">For this feature, the critical part is being able to know which mesh we are selecting with the mouse click. And for that, the API provides <span style="font-family: "Courier New",Courier,monospace;">MouseEvent.getPickResult().getIntersectedNode()</span>, which returns one of the meshviews on the cube.</span></span><br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;"><br /></span></span>
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;">So the next step is find which is this meshview and what cubie does it belongs to. As all the meshes have a name, like 'Block46 (2)', looking at the number of block we identify the cubie.</span></span><br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;"><br /></span></span>
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;">Now we need to find which of the faces we have selected. For that we use the triangles coordinates of the mesh, as for the faces they define a plane, so with the cross product we know the normal direction of this plane. Note we must update the operations with the actual set of transformations applied.</span></span><br />
<br />
<pre class="brush: java"> private static Point3D getMeshNormal(MeshView mesh){
TriangleMesh tm=(TriangleMesh)mesh.getMesh();
float[] fPoints=new float[tm.getPoints().size()];
tm.getPoints().toArray(fPoints);
Point3D BA=new Point3D(fPoints[3]-fPoints[0],fPoints[4]-fPoints[1],fPoints[5]-fPoints[2]);
Point3D CA=new Point3D(fPoints[6]-fPoints[0],fPoints[7]-fPoints[1],fPoints[8]-fPoints[2]);
Point3D normal=BA.crossProduct(CA);
Affine a=new Affine(mesh.getTransforms().get(0));
return a.transform(normal.normalize());
}
public static String getPickedRotation(int cubie, MeshView mesh){
Point3D normal=getMeshNormal(mesh);
String rots=""; // Rx-Ry
switch(cubie){
case 0: rots=(normal.getZ()>0.99)?"Ui-Li":
((normal.getX()<-0.99)?"Ui-F":((normal.getY()>0.99)?"Ui-Li":""));
break;
}
return rots;
}
</pre>
<span style="font-family: Verdana,sans-serif;">
<br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;">Once we have the normal, we can provide the user with two possible rotations (and their possible two directions). To select which one to perform, </span>we'll look how the user moves the mouse while it's being dragged. Note the mouse coordinates are 2D.</span><br />
<br />
</span><br />
<pre class="brush: java"> public static String getRightRotation(Point3D p, String selFaces){
double radius=p.magnitude();
double angle=Math.atan2(p.getY(),p.getX());
String face="";
if(radius>=radMinimum && selFaces.contains("-") && selFaces.split("-").length==2){
String[] faces=selFaces.split("-");
// select rotation if p.getX>p.getY
if(-Math.PI/4d<=angle && angle<Math.PI/4d){ // X
face=faces[0];
} else if(Math.PI/4d<=angle && angle<3d*Math.PI/4d){ // Y
face=faces[1];
} else if((3d*Math.PI/4d<=angle && angle<=Math.PI) ||
(-Math.PI<=angle && angle<-3d*Math.PI/4d)){ // -X
face=reverseRotation(faces[0]);
} else { //-Y
face=reverseRotation(faces[1]);
}
System.out.println("face: "+face);
} else if(!face.isEmpty() && radius<radMinimum){ // reset previous face
face="";
}
return face;
}
</pre>
<span style="font-family: Verdana,sans-serif;">
<br />
<span style="font-family: Verdana,sans-serif;">Now that we have the layer to rotate, we make a small rotation as a preview of rotation if the mouse is dragged far from the initial click point, with a minimum distance. Then if the user releases the mouse and the distance from the initial point is greater than a distance <span style="font-family: "Courier New",Courier,monospace;">radClick</span>, the rotation is completed. But if the distance is lower or the mouse </span><span style="font-family: Verdana,sans-serif;">is dragged under the distance <span style="font-family: "Courier New",Courier,monospace;">radMinimum</span>, the rotation is cancelled.</span><br />
<br />
<span style="font-family: Verdana,sans-serif;">The next listing shows an <span style="font-family: "Courier New",Courier,monospace;">EventHandler<MouseEvent></span> implemented to provide this behaviour. Note that we have to stop the camera rotations while we are picking a face and rotating a layer. </span><br />
<br />
</span><br />
<pre class="brush: java"> public EventHandler<MouseEvent> eventHandler=(MouseEvent event)->{
if (event.getEventType() == MouseEvent.MOUSE_PRESSED ||
event.getEventType() == MouseEvent.MOUSE_DRAGGED ||
event.getEventType() == MouseEvent.MOUSE_RELEASED) {
mouseNewX = event.getSceneX();
mouseNewY = -event.getSceneY();
if (event.getEventType() == MouseEvent.MOUSE_PRESSED) {
Node picked = event.getPickResult().getIntersectedNode();
if(null != picked && picked instanceof MeshView) {
mouse.set(MOUSE_PRESSED);
cursor.set(Cursor.CLOSED_HAND);
stopEventHandling();
stopEvents=true;
pickedMesh=(MeshView)picked;
String block=pickedMesh.getId().substring(5,7);
int indexOf = order.indexOf(new Integer(block));
selFaces=Utils.getPickedRotation(indexOf, pickedMesh);
mouseIniX=mouseNewX;
mouseIniY=mouseNewY;
myFace="";
myFaceOld="";
}
} else if (event.getEventType() == MouseEvent.MOUSE_DRAGGED) {
if(stopEvents && !selFaces.isEmpty()){
mouse.set(MOUSE_DRAGGED);
Point3D p=new Point3D(mouseNewX-mouseIniX,mouseNewY-mouseIniY,0);
radius=p.magnitude();
if(myFaceOld.isEmpty()){
myFace=Utils.getRightRotation(p,selFaces);
if(!myFace.isEmpty() && !onRotation.get()){
updateArrow(myFace, true);
myFaceOld=myFace;
}
if(myFace.isEmpty()){
myFaceOld="";
}
}
// to cancel preselection, just go back to initial click point
if(!myFaceOld.isEmpty() && radius<Utils.radMinimum){
myFaceOld="";
updateArrow(myFace, false);
myFace="";
}
}
} else if (stopEvents && event.getEventType() == MouseEvent.MOUSE_RELEASED) {
mouse.set(MOUSE_RELEASED);
if(!onRotation.get() && !myFace.isEmpty() && !myFaceOld.isEmpty()){
if(Utils.radClick<radius){
// if hand is moved far away do full rotation
rotateFace(myFace);
} else {
// else preview cancellation
updateArrow(myFace, false);
}
}
myFace=""; myFaceOld="";
stopEvents=false;
resumeEventHandling();
cursor.set(Cursor.DEFAULT);
}
}
};
</pre>
<span style="font-family: Verdana,sans-serif;">
<br />
<span style="font-family: Verdana,sans-serif;">Finally, we add this <span style="font-family: "Courier New",Courier,monospace;">EventHandler </span>to the scene:</span><br />
<br />
</span><br />
<pre class="brush: java">scene.addEventHandler(MouseEvent.ANY, rubik.eventHandler);
</pre>
<span style="font-family: Verdana,sans-serif;">
<br />
<span style="font-family: Verdana,sans-serif;">This video shows how this event handling works.</span></span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="315" src="//www.youtube.com/embed/f92mbCwnQDc?rel=0" type="text/html" width="420"></iframe>
</div>
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;"> </span><br />
<br />
</span><br />
<b><span style="font-family: Verdana,sans-serif;">Check if cube is solved</span></b><br />
<br />
<span style="font-family: Verdana,sans-serif;">Finally, let's add a check routine. We know the initial solved order of cubies, but we need to take into account any of the 24 possible orientations of the faces, which can be acchieved with up to two rotations.</span><br />
<br />
<pre class="brush: java"> private static final List<String> orientations=Arrays.asList("V-V","V-Y","V-Yi","V-Y2",
"X-V","X-Z","X-Zi","X-Z2",
"Xi-V","Xi-Z","Xi-Zi",
"X2-V","X2-Z","X2-Zi",
"X-Y","X-Yi","X-Y2",
"Xi-Y","Xi-Yi","X2-Y","X2-Yi",
"Z-V","Zi-V","Z2-V");
public static boolean checkOrientation(String r, List<Integer> order){
Rotations rot=new Rotations();
for(String s:r.split("-")){
if(s.contains("2")){
rot.turn(s.substring(0,1));
rot.turn(s.substring(0,1));
} else {
rot.turn(s);
}
}
return order.equals(rot.getCube());
}
</pre>
<span style="font-family: Verdana,sans-serif;">
<br />
<span style="font-family: Verdana,sans-serif;">So after any movement we have to check if the actual order matches any of </span><span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;">these 24 solutions. </span>For that we can use <span style="font-family: "Courier New",Courier,monospace;">parallelStream() </span>with a filter in which we rotate a new cube to one of the possible orientations and check if that matches the actual one:</span><br />
<br />
</span><br />
<pre class="brush: java"> public static boolean checkSolution(List<Integer> order) {
return Utils.getOrientations().parallelStream()
.filter(r->Utils.checkOrientation(r,order)).findAny().isPresent();
}
</pre>
<span style="font-family: Verdana,sans-serif;">
<b><span style="font-family: Verdana,sans-serif;">Conclusions</span></b><br />
<br />
<span style="font-family: Verdana,sans-serif;">All along this post, we've been discussing the new JavaFX 3D API. Powerfull enough, but with lack of some usual tools in the 3D modelling world. Maybe they will come soon...</span><br />
<br />
<span style="font-family: Verdana,sans-serif;">We've used the new lambdas and Stream API. I hope by now you've got a clear view of what you can do with them. For sure, they will definetely change the way we write code.</span><br />
<br />
<span style="font-family: Verdana,sans-serif;">The Rubik's cube application has proven to be a nice way of testing these new capabilities, while enjoying playing, humm, I mean, developing the code.</span><br />
<br />
<span style="font-family: Verdana,sans-serif;">This final video shows most of what we've
accomplished in this post. As I said, it's for begginers like me with the Rubik's cube...</span><br />
</span><br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="315" src="//www.youtube.com/embed/ZVPIBkDgZV4?rel=0" type="text/html" width="420"></iframe><span style="font-family: Verdana,sans-serif;">
</span></div>
<span style="font-family: Verdana,sans-serif;">
<br /><span style="font-family: Verdana,sans-serif;">In my <a href="https://github.com/jperedadnr/RubikFX" target="_blank">repo</a> you can find all the code for this full version. Fill
free to fork it and play with it. There're tons of improvements to make,
so any pull request will be welcome!</span></span><br />
<br />
<br />
<span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;"><span style="font-family: Verdana,sans-serif;"><b>Edit</b>: And if you just want to try it before having a look at the code, here you can download a single executable jar. <a href="https://github.com/jperedadnr/RubikFX/releases/download/1.0/RubikFX.jar" target="_blank">Download</a> it and run it with Java 8 installed.</span><br />
<br />
<span style="font-family: Verdana,sans-serif;">As always, thanks for reading me! Please, try it for yourself and share any comment you may have. </span><br />
<span style="font-family: Verdana,sans-serif;"><br /></span>
</span></span>Anonymoushttp://www.blogger.com/profile/15247383133952423416noreply@blogger.com12tag:blogger.com,1999:blog-7114525940495426773.post-21821385105309382542013-06-09T13:34:00.000+02:002013-06-10T19:04:45.237+02:00Leap Motion Controller and JavaFX: A new touch-less approach<span style="font-family: Georgia,"Times New Roman",serif;">Hi, it's been a while since my last post, but this first half of the year I've been quite busy at work. So I had to put on hold most of my JavaFX, Raspberry Pi and Arduino projects.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">In all this time I could (even) afford only one distraction, because the device really deserve it!</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">In April I had the chance to get involved in the <a href="https://www.leapmotion.com/">Leap Motion</a> Developer program (thanks for that to <a href="https://twitter.com/JavaFXpert">Jim</a> Weaver and <a href="https://twitter.com/speakjava">Simon</a> Ritter, and of course, to the Leap Motion staff), and since I received the little but powerful device at home, I've been playing around with it in several JavaFX based projects. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">So this post is a little briefing of the few projects I've done with the Leap Motion controller and JavaFX, most of them just as a Proof of Concept.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">As the Leap SDK is private for now (though they intend to make it public soon), I won't release any code, just snippets, few screenshots and short videos.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">At a glance, this is what I'll cover:</span><br />
<ul>
<li><span style="font-family: Georgia,"Times New Roman",serif;">The Leap Motion Controller, what you can expect: hands, fingers, tools and basic gestures.</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">The JavaFX and the Leap threads, change listeners to the rescue.</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">POC #1. Moving 2D shapes on a JavaFX scene, trapping basic gestures with the Leap.</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">POC #2. Physics 2D worlds and Leap, a JavaFX approach.</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">POC #3. JavaFX 3D and Leap, with JDK8 early preview and openJFX Project.</span></li>
</ul>
<span style="font-family: Georgia,"Times New Roman",serif;">I won't go in much detail regarding the Leap itself. There're plenty of videos out there. If you don't know about it yet, check these, for instance:</span><br />
<ul>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Official <a href="https://www.youtube.com/watch?feature=player_embedded&v=3b4w749Tud8">presentation</a></span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Google Earth and Leap <a href="http://www.youtube.com/watch?v=Q0ZdDD0d2rg">integration</a>.</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Controlling physical <a href="http://www.youtube.com/watch?v=ZOdxlrQj_ZU">devices</a>.</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Augmented reality and Leap <a href="http://www.youtube.com/watch?v=GiiPcsoOFfQ">interaction</a></span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">OS <a href="https://www.youtube.com/watch?v=21LtA5-wiwU">integration</a>.</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;"><a href="http://www.youtube.com/watch?v=c4338Emvl2o">Games</a></span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">... or just google "Leap Motion"!</span></li>
</ul>
<span style="font-family: Georgia,"Times New Roman",serif;">For those of you already on the pre-order list, the 22nd of July it's already there... be (just a little bit more) patient! For those who haven't decided to buy one yet, maybe this reading will help you make up your mind.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Let's go!</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<b><span style="font-family: Georgia,"Times New Roman",serif;">1. </span><span style="font-family: Georgia,"Times New Roman",serif;">The Leap Motion Controller </span></b><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">After you plug in the Leap Motion device in your USB port (Windows, Mac and Linux OS), and download and install its sofware, you can try the Leap Visualizer, a bundled application which allows you to learn and discover the magic of the Leap.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQVNecrHDv-otjYF7P-_5Fe2Q_T4ctNe1-1oxQ4VwlonefQoTzxJH7fONM_zaCRlOy0NvlBDl2OBaR-JKRDDqmDO0hCxRL7QE6BWJAF35ORdNPGYVRjus0wV_z_wK0I_vLgECWFfnr67k/s1600/leapVisualizer.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQVNecrHDv-otjYF7P-_5Fe2Q_T4ctNe1-1oxQ4VwlonefQoTzxJH7fONM_zaCRlOy0NvlBDl2OBaR-JKRDDqmDO0hCxRL7QE6BWJAF35ORdNPGYVRjus0wV_z_wK0I_vLgECWFfnr67k/s640/leapVisualizer.jpg" height="411" width="640" /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">As soon as you launch it, you can <i>virtually </i>see your hands and fingers moving around the screen. It's really impressive because of the high precision of the movements due to the high frequency in which the Leap scans.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Activating hands and fingers visualization you can realize those are the basics of the model provided: the Leap will detect none, one or several hands, and several fingers in each one of them. For each hand, it will show, for instance, its position, where it points at (hand direction) and its palm normal. For fingers, you'll get their position and where they point at. Also you can get hand or fingers velocity.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">These positions, directions and velocities of <i>your real hands and fingers </i>are 3D vectors, refered to a right-handed Cartesian coordinate system, with the origin <i>at the center of the device</i> and the X and Z axes lying in the horizontal plane and the Y axis is vertical.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjExo_0GXEa7XMfSiLOK9QwEu9AJnsJ0nl4WLIQ4p0K9g2HcJNBaDxHpOZmDjDgPtnBCpVyZKfnheocLLcP28sdtdKS9kRG2majfQEQVbu1QJec1lOM5TgLE6lI4T6nO7tg5zwZeimnPRM/s1600/Leap_Axes.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjExo_0GXEa7XMfSiLOK9QwEu9AJnsJ0nl4WLIQ4p0K9g2HcJNBaDxHpOZmDjDgPtnBCpVyZKfnheocLLcP28sdtdKS9kRG2majfQEQVbu1QJec1lOM5TgLE6lI4T6nO7tg5zwZeimnPRM/s1600/Leap_Axes.png" height="197" width="320" /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;">It's important to notice you'll have to convert these coordinates to the ones of the screen if you want to display and move anything on it. For that, you need to calculate where the vector of the hand or finger direction intersects with the plane of the screen. </span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">The Leap device performs complete scans of its surroundings, with an effective range approximately from 25 to 600 millimeters above the device (1 inch to 2 feet). Each scan defines a <i>frame</i>, with all the data associated.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">The scan rate is really high, that's what makes the Leap so impressive and accurate compared to similar devices. Depending on your CPU and the amount of data analyzed, the range of processing latency goes from 2 ms to 33 ms, giving rates from 30 to 500 fps.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi49iWzGBuwOKFhgSMqhph9k4ztehntQXvAJd2qC887GM_jbVmHX4U6PnY4o9FhyphenhypheneJRdU5WB_qCdsSmMiGQeF7AP154GK4QEINMZxIkFuFTBE-SlyzQyixu1bHVoKzFWVFqkIHvTrVq9Ys/s1600/leap_rate.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi49iWzGBuwOKFhgSMqhph9k4ztehntQXvAJd2qC887GM_jbVmHX4U6PnY4o9FhyphenhypheneJRdU5WB_qCdsSmMiGQeF7AP154GK4QEINMZxIkFuFTBE-SlyzQyixu1bHVoKzFWVFqkIHvTrVq9Ys/s1600/leap_rate.jpg" height="244" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFLAnCRY7iygT1qUajhqQmvzE9NL7opWEt8Niofm7jSW787zr9BXrwoyrSdCruvATeuVgnudSkul6UIz03GKff2dd-6E0OmYHoIRb3xHlVr5-MTEwYGGWIFzQtjzMrfqfRztXBA2kQMJM/s1600/leap_rate.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicx0VuBwf76FZbPJ96bsg86YpvhPICjMv2Yd6yNHH_V3l6vFrbOWJMb6SzPkTQPqqhH1IQb-S1yZB_dMrwNyJHENYY9nsGgsLp5qUnvrAJgrgO9YK4YyX9Bol-DvX0M68xqCk5wmxWZ48/s1600/leap_rate.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;">Besides directions, a basic collection of gestures is also provided: key tap or screen tap, swipe and circle gestures are tracked by comparing the finger movements through different frames.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpcXapjWqbh_70qTsQ-kCYK9o11GGb3_q1hp_SmD1VHd7dI5y2HdokaIOLfILAlGoO4MkSuNMeteWFGGnw68vvJERF6L4XFCuwQW1LPzL4WdB5r4Aj6e0_P38NXbePpMuMk1l-T2Jf2YU/s1600/gestures.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpcXapjWqbh_70qTsQ-kCYK9o11GGb3_q1hp_SmD1VHd7dI5y2HdokaIOLfILAlGoO4MkSuNMeteWFGGnw68vvJERF6L4XFCuwQW1LPzL4WdB5r4Aj6e0_P38NXbePpMuMk1l-T2Jf2YU/s1600/gestures.jpg" height="145" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhA_qdWtiJv19OEzXa-wixCMDepwmpSax5Bgk2pQ32xJ2wZzL1KCbWAkkXv9iYC8ynUM3QbhQG3fgHwqYODQDvWPxoR2QP04sxtEjpT4pKOoA9vosD5cmaQlfg3uzjk_YeqsLgTYgYFKi4/s1600/gestures.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;">The good thing of having access to all frames data is that you can define your custom gestures, and try to find them analyzing a relative short collection of frames, all over again.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">To end this brief intro to the great Leap Motion device, let's say that being on the Developer Program you can get your SDK for many programming languages, such as Java, JavaScript, C++, C#, Objetive C or Phyton. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">In terms of Java code, all you need to do is extend the <span style="font-family: "Courier New",Courier,monospace;">Listener</span> class provided by the SDK and basically </span><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">override </span>the <span style="font-family: "Courier New",Courier,monospace;">onFrame </span>method, <i>and let the magic begin</i>.</span><br />
<br />
<b><span style="font-family: Georgia,"Times New Roman",serif;">2. The JavaFX and the Leap threads</span>
</b><span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;"> </span><br />
<span style="font-family: Georgia,"Times New Roman",serif;">Having a Leap Motion Controller means you can interact with your applications in a very different way you're used to. For that, you just need to integrate the Leap events, in terms of movement or actions, in your apps.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">In a JavaFX based application, one easy way to do this is by adding <span style="font-family: "Courier New",Courier,monospace;">ObjectProperty<T></span> objects to the LeapListener class in order to set desired values at every frame using <span style="font-family: "Courier New",Courier,monospace;">Vector</span>, <span style="font-family: "Courier New",Courier,monospace;">Point2D</span>, <span style="font-family: "Courier New",Courier,monospace;">Point3D</span>, <span style="font-family: "Courier New",Courier,monospace;">CircleGesture</span>,... and then implement their related public <span style="font-family: "Courier New",Courier,monospace;">ObservableValue<T></span> methods. </span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Then, in the JavaFX thread, an anonimous <span style="font-family: "Courier New",Courier,monospace;">ChangeListener<T> </span></span><span style="font-family: Georgia,"Times New Roman",serif;">class can be added to listen for any change in the <span style="font-family: "Courier New",Courier,monospace;">ObservableValue</span>. Special care must be taken here, as anything related to the UI must be deal by <span style="font-family: "Courier New",Courier,monospace;">Platform.runLater()</span>.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">The next proof of concept samples will try to explain this. </span><br />
<br />
<b><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">3. POC #1. Moving 2D shapes on a JavaFX scene</span> </span></b><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Let's say we want to move a node in the scene with our hand as a first simple POC. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">We create two classes: <span style="font-family: "Courier New",Courier,monospace;">SimpleLeapListener </span>class, that </span><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">extends <span style="font-family: "Courier New",Courier,monospace;">Listener</span>, where </span>we just set at every frame the coordinates of the screen where the hand points at:</span><br />
<br />
<pre class="brush: java">public class SimpleLeapListener extends Listener {
private ObjectProperty<Point2D> point=new SimpleObjectProperty<>();
public ObservableValue<Point2D> pointProperty(){ return point; }
@Override
public void onFrame(Controller controller) {
Frame frame = controller.frame();
if (!frame.hands().empty()) {
Screen screen = controller.calibratedScreens().get(0);
if (screen != null && screen.isValid()){
Hand hand = frame.hands().get(0);
if(hand.isValid()){
Vector intersect = screen.intersect(hand.palmPosition(),hand.direction(), true);
point.setValue(new Point2D(screen.widthPixels()*Math.min(1d,Math.max(0d,intersect.getX())),
screen.heightPixels()*Math.min(1d,Math.max(0d,(1d-intersect.getY())))));
}
}
}
}
}
</pre>
<span style="font-family: Georgia,"Times New Roman",serif;"><br />And <span style="font-family: "Courier New",Courier,monospace;">LeapJavaFX</span>, our JavaFX class, that listen to changes in this point and reflect them on the scene:</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"> </span>
<br />
<pre class="brush: java">public class LeapJavaFX extends Application {
private SimpleLeapListener listener = new SimpleLeapListener();
private Controller leapController = new Controller();
private AnchorPane root = new AnchorPane();
private Circle circle=new Circle(50,Color.DEEPSKYBLUE);
@Override
public void start(Stage primaryStage) {
leapController.addListener(listener);
circle.setLayoutX(circle.getRadius());
circle.setLayoutY(circle.getRadius());
root.getChildren().add(circle);
final Scene scene = new Scene(root, 800, 600);
listener.pointProperty().addListener(new ChangeListener<point2d>(){
@Override
public void changed(ObservableValue ov, Point2D t, final Point2D t1) {
Platform.runLater(new Runnable(){
@Override
public void run() {
Point2D d=root.sceneToLocal(t1.getX()-scene.getX()-scene.getWindow().getX(),
t1.getY()-scene.getY()-scene.getWindow().getY());
double dx=d.getX(), dy=d.getY();
if(dx>=0d && dx<=root.getWidth()-2d*circle.getRadius() &&
dy>=0d && dy<=root.getHeight()-2d*circle.getRadius()){
circle.setTranslateX(dx);
circle.setTranslateY(dy);
}
}
});
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
@Override
public void stop(){
leapController.removeListener(listener);
}
}
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Pretty simple, isn't it? This short video shows the result.</span><br />
<br />
<div style="text-align: center;">
<span style="font-family: Verdana,sans-serif;"><video controls="controls" loop="loop" poster="https://dl.dropboxusercontent.com/u/27006338/leapPOC1.mp4" tabindex="0" width="500">
<source src="https://dl.dropboxusercontent.com/u/27006338/leapPOC1.mp4" type="video/mp4; codecs="avc1.42E01E, mp4a.40.2""></source>
<source src="https://dl.dropboxusercontent.com/u/27006338/leapPOC1.webm" type="video/webm; codecs="vp8, vorbis""></source>
Your browser does not support the video tag, view this video <a href="https://dl.dropboxusercontent.com/u/27006338/leapPOC1.mp4">here</a>.<br />
</video></span></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Here goes a second sample, based on the same idea, one circle per detected hand it's displayed, with its radius growing or shrinking according the Z distance of the hand to the Leap. When key tap kind of gestures are detected, a shadow circle is shown where the tap occurs, moved from the previous tap location with an animation of the changes in both transition and scale properties.</span><span style="font-family: Georgia,"Times New Roman",serif;"> </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">Here you can see it in action:</span></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"> </span> </span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/eJDxq38Sa7U" width="560"></iframe></span><br />
<br />
<b><span style="font-family: Georgia,"Times New Roman",serif;">4. POC #2. Physics 2D worlds and Leap, a JavaFX approach</span></b><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">When <a href="https://twitter.com/monacotoni">Toni </a>Epple saw this video, he suggested me to add some physics to the mix, so I started learning from his blog <a href="http://jayskills.com/blog/2012/04/06/javafx-and-jbox2d/">posts</a> about JavaFX and JBox2D, the Java port of the popular Box2D physics engine. Using his</span><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"> amazing </span> <a href="https://github.com/eppleton/FXGameEngine">work</a> I was able to create a simple <span style="font-family: "Courier New",Courier,monospace;">World</span>, add some dynamic bodies and static walls to the boundaries, and a static big circle which I could move with the Leap, as in the previous samples. Thank you, Toni, your work is really inspiring!</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Here is a code snippet of the JavaFX class.</span><br />
<br />
<pre class="brush: java">public class PhysicsLeapJavaFX extends Application {
private SimpleLeapListener listener = new SimpleLeapListener();
private Controller leapController = new Controller();
private Button button=new Button("Add Ball");
private AnchorPane root = new AnchorPane();
private AnchorPane pane = new AnchorPane();
private Body myCircle=null;
private World world=null;
private WorldView worldView=null;
private final float worldScale=50f;
private final float originX=4f, originY=8f;
private final float radius=1f;
@Override
public void start(Stage primaryStage) {
leapController.addListener(listener);
world = new World(new Vec2(0, 0f)); // No gravity
// 200x400 -> world origin->(4f, 8f), Y axis>0 UP
worldView=new WorldView(world, originX*worldScale, originY*worldScale, worldScale);
AnchorPane.setBottomAnchor(pane, 20d); AnchorPane.setTopAnchor(pane, 50d);
AnchorPane.setLeftAnchor(pane, 20d); AnchorPane.setRightAnchor(pane, 20d);
// root: 800x600, pane: 760x530, worldScale= 50 -> world dimensions: 15.2f x 10.6f
pane.getChildren().setAll(worldView);
NodeManager.addProvider(new MyNodeProvider());
button.setLayoutX(30); button.setLayoutY(15);
button.setOnAction(new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent t) {
Body ball=new CircleShapeBuilder(world).userData("ball")
.position(0f, 4f)
.type(BodyType.DYNAMIC).restitution(1f).density(0.4f)
.radius(0.5f).friction(0f)
.build();
ball.setLinearVelocity(new Vec2(4,2));
ball.setLinearDamping(0f);
}
});
myCircle=new CircleShapeBuilder(world).userData("hand1").position(0f, 2f)
.type(BodyType.STATIC).restitution(1f).density(1)
.radius(radius).friction(0f)
.build();
new BoxBuilder(world).position(3.6f, 8f).restitution(1f).friction(0f)
.halfHeight(0.1f).halfWidth(7.7f).build();
new BoxBuilder(world).position(3.6f, -2.6f).restitution(1f).friction(0f)
.halfHeight(0.1f).halfWidth(7.7f).build();
new BoxBuilder(world).position(-4f, 2.7f).restitution(1f).friction(0f)
.halfHeight(5.4f).halfWidth(0.1f).build();
new BoxBuilder(world).position(11.2f, 2.7f).restitution(1f).friction(0f)
.halfHeight(5.4f).halfWidth(0.1f).build();
root.getChildren().addAll(button, pane);
final Scene scene = new Scene(root, 800, 600);
listener.pointProperty().addListener(new ChangeListener<point2D>(){
@Override
public void changed(ObservableValue<? extends Point2D> ov, Point2D t, final Point2D t1) {
Platform.runLater(new Runnable(){
@Override
public void run() {
Point2D d=pane.sceneToLocal(t1.getX()-scene.getX()-scene.getWindow().getX()-root.getLayoutX(),
t1.getY()-scene.getY()-scene.getWindow().getY()-root.getLayoutY());
double dx=d.getX()/worldScale, dy=d.getY()/worldScale;
if(dx>=0.1 && dx<=pane.getWidth()/worldScale-2d*radius-0.1 &&
dy>=0.1 && dy<=pane.getHeight()/worldScale-2d*radius-0.1){
myCircle.setTransform(new Vec2((float)(dx)-(originX-radius),
(originY-radius)-(float)(dy)),
myCircle.getAngle());
}
}
});
}
});
listener.keyTapProperty().addListener(new ChangeListener<Boolean>(){
@Override public void changed(ObservableValue<? extends Boolean> ov, Boolean t, final Boolean t1) {
if(t1.booleanValue()){
Platform.runLater(new Runnable(){
@Override public void run() {
button.fire();
}
});
}
}
});
primaryStage.setTitle("PhysicsLeapJavaFX Sample");
primaryStage.setScene(scene);
primaryStage.show();
}
@Override
public void stop(){
leapController.removeListener(listener);
}
}
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">I've added some gesture recognition to fire the button when a key tap gesture it's done. Besides, i</span>t's quite convinient to smooth the readings from the Leap, taking the average of the last positions instead of the position for every frame. So let's modify the <span style="font-family: "Courier New",Courier,monospace;">SimpleLeapListener</span> class adding a <i>size limited </i><span style="font-family: "Courier New",Courier,monospace;">LinkedList </span>collection to store the last 30 positions, and also enable the key tap gestures:</span><br />
<br />
<pre class="brush: java">public class SimpleLeapListener extends Listener {
private ObjectProperty<Point2D> point=new SimpleObjectProperty<>();
public ObservableValue<Point2D> pointProperty(){ return point; }
private LimitQueue<Vector> positionAverage = new LimitQueue<Vector>(30);
private BooleanProperty keyTap= new SimpleBooleanProperty(false);
public BooleanProperty keyTapProperty() { return keyTap; }
@Override
public void onFrame(Controller controller) {
Frame frame = controller.frame();
if (!frame.hands().empty()) {
Screen screen = controller.calibratedScreens().get(0);
if (screen != null && screen.isValid()){
Hand hand = frame.hands().get(0);
if(hand.isValid()){
Vector intersect = screen.intersect(hand.palmPosition(),hand.direction(), true);
positionAverage.add(intersect);
Vector avIntersect=Average(positionAverage);
point.setValue(new Point2D(screen.widthPixels()*Math.min(1d,Math.max(0d,avIntersect.getX())),
screen.heightPixels()*Math.min(1d,Math.max(0d,(1d-avIntersect.getY())))));
}
}
}
keyTap.set(false);
GestureList gestures = frame.gestures();
for (int i = 0; i < gestures.count(); i++) {
if(gestures.get(i).type()==Gesture.Type.TYPE_KEY_TAP){
keyTap.set(true); break;
}
}
}
private Vector Average(LimitQueue<Vector> vectors)
{
float vx=0f, vy=0f, vz=0f;
for(Vector v:vectors){
vx=vx+v.getX(); vy=vy+v.getY(); vz=vz+v.getZ();
}
return new Vector(vx/vectors.size(), vy/vectors.size(), vz/vectors.size());
}
private class LimitQueue<E> extends LinkedList<E> {
private int limit;
public LimitQueue(int limit) {
this.limit = limit;
}
@Override
public boolean add(E o) {
super.add(o);
while (size() > limit) { super.remove(); }
return true;
}
}
}
</pre>
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">And finally, here you can see it in action:</span><br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/VvBfymztcVY" width="560"></iframe>
<b><span style="font-family: Georgia,"Times New Roman",serif;"> </span></b><br />
<br />
<b><span style="font-family: Georgia,"Times New Roman",serif;">5. POC #3. JavaFX 3D and Leap, with JDK8</span></b><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">The last part of this post will cover my experiments with the recent early access releases of JDK8, build <a href="https://jdk8.java.net/download.html">b92</a> on the time of this writing, as JavaFX 3D is enabled since b77. <a href="https://wikis.oracle.com/display/OpenJDK/3D+Features">Here</a> you can read about the 3D features planned for JavaFX 8. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Installing JDK8 is easy, and so is </span><span style="font-family: Georgia,"Times New Roman",serif;">creating a JavaFX scene with 3D primitives like spheres, boxes or cylinders, or even user-defined shapes by meshes, defined by a set of points, texture coordinates and faces.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0nhyphenhyphenKz8cbH0pnHHYuWPwYvEfz2C7XQjO8ujeRLj-Jet9QSTbpgq-6_rTXXKwD3og5aiG_ygnR3ziZwcTkeqIC1NTluP39uzCrB0vrLEj1osNnwfTxPsYDgMYtyZWKNz8gtdmKKXRMLaI/s1600/javafx3d.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0nhyphenhyphenKz8cbH0pnHHYuWPwYvEfz2C7XQjO8ujeRLj-Jet9QSTbpgq-6_rTXXKwD3og5aiG_ygnR3ziZwcTkeqIC1NTluP39uzCrB0vrLEj1osNnwfTxPsYDgMYtyZWKNz8gtdmKKXRMLaI/s1600/javafx3d.jpg" height="400" width="393" /></a></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">There are no loaders for existing 3D file formats (obj, stl, Maya, 3D Studio, ...). So if you want to import a 3D model, you need one. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">The first place to start looking is in <a href="http://openjdk.java.net/projects/openjfx/">OpenJFX</a>, the open source code home of JavaFX development.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">You'll find in their repository between their <a href="http://hg.openjdk.java.net/openjfx/8/master/rt/file/018368ab7327/apps/experiments/"><i>experiments</i></a>, as they call them, a 3D Viewer. So download it from their <a href="http://hg.openjdk.java.net/openjfx/8/master/rt/archive/018368ab7327.zip">repository</a>, build it and see what you can do!</span><br />
<div style="text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: Georgia,"Times New Roman",serif;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtA3Bni4RRL3_ER-_UDiYFVFJ8HEs1vMXc1KGhz-4flF-uzejwJzujd3XCbCB1TIidnjA1gBADSXmqBzZ9gKsv-nHMS6NUFsUYLF45zhgkrc21qeyNHffni7uJSgWOxJSUUm9-R55WArE/s1600/3dViewer.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtA3Bni4RRL3_ER-_UDiYFVFJ8HEs1vMXc1KGhz-4flF-uzejwJzujd3XCbCB1TIidnjA1gBADSXmqBzZ9gKsv-nHMS6NUFsUYLF45zhgkrc21qeyNHffni7uJSgWOxJSUUm9-R55WArE/s1600/3dViewer.jpg" height="392" width="640" /></a></span></div>
<br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">For instance, you can drag and drop an obj model. The one in the picture is a model of a Raspberry Pi, downloaded from <a href="http://www.youtube.com/watch?v=4f6GhBVa1Go">here</a>.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">For other formats not yet supported, you can go to <a href="http://www.interactivemesh.org/models/jfx3dimporter.html">InteractiveMesh.org</a>, where August Lammersdorf has released several importers (3ds, obj and stl) for JDK8 b91+. Kudos to him for his amazing work and contributions!</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">I'll use his 3ds importer and the Hubble Space Telescope model from <a href="http://www.nasa.gov/multimedia/3d_resources/assets/HST_3DS.html">NASA</a>, to add this model to a JavaFX scene, and then I'll try to add touch-less rotation and scaling options.</span>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;">First of all, we need a little mathematical background here, as rotating a 3D model in JavaFX requires a rotation axis and angle. If we have several rotations to make at the same time we need to construct a rotation matrix, and after that get the rotation axis and its angle. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">As the Leap provides three rotations from a hand: pitch (around its X axis) , yaw (around its Y axis) and </span><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">roll (around its Z axis)</span>, providing the model is already well orientated (otherwise we'll need to add previous rotations too), the <a href="http://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions">rotation matrix</a> will be: </span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDbENZmJs_8-8nbpBBNX12KqICnVotVQrcqNKGaEWfzgsO-Z-n0RCALbDNmHGllvgoxOJJFU7tq25C1KvHhwpBLIyhUgq-6-nYOcLBt9999Smj_PE9k2oDB-kNwNgjeGV9vN-l5mqpMqk/s1600/8373efb2bc0b884cc83f8db70f618b3a.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDbENZmJs_8-8nbpBBNX12KqICnVotVQrcqNKGaEWfzgsO-Z-n0RCALbDNmHGllvgoxOJJFU7tq25C1KvHhwpBLIyhUgq-6-nYOcLBt9999Smj_PE9k2oDB-kNwNgjeGV9vN-l5mqpMqk/s1600/8373efb2bc0b884cc83f8db70f618b3a.png" /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;"> where:</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8QEZBtfd2MJDzQdmpDGF-91fJPnI3oCThIU24Kzyv2FTNk1Db8Avv3o83WRr8k60y79YDfhOuju_vZQz1bemeYOZK4q1NocziIVEhRxheT6SIo30hMwsr9eOh6AcIOt-KvQKcVVnETQU/s1600/8867b40e7d7d69e2db9cba8a7b42d2db.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8QEZBtfd2MJDzQdmpDGF-91fJPnI3oCThIU24Kzyv2FTNk1Db8Avv3o83WRr8k60y79YDfhOuju_vZQz1bemeYOZK4q1NocziIVEhRxheT6SIo30hMwsr9eOh6AcIOt-KvQKcVVnETQU/s1600/8867b40e7d7d69e2db9cba8a7b42d2db.png" /> </a> </div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;">So: </span></div>
<div style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdX-7d1yOVLs0xvx2ioo8tOzIgtFW4wqhFHEB8SfdJoXM6m0gFVvvhglDnycLyuyYfvW-7dVrej97-1v7DkZEuMVWQYlFMP_n9I3RRAGVUxiO8eS_TUsVG0_xJxfZTzPzpw2q21AsVDjI/s1600/d7d24840d643af424de6a73730aea6d9.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdX-7d1yOVLs0xvx2ioo8tOzIgtFW4wqhFHEB8SfdJoXM6m0gFVvvhglDnycLyuyYfvW-7dVrej97-1v7DkZEuMVWQYlFMP_n9I3RRAGVUxiO8eS_TUsVG0_xJxfZTzPzpw2q21AsVDjI/s1600/d7d24840d643af424de6a73730aea6d9.png" height="66" width="640" /> </a></div>
<div style="text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;">Then, the angle and the rotation unitary axis components can be easily computed from: </span></div>
<div style="text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6o8Uhje07IYKuuuc0T-x2g2_2AZqpu7kC0KU7Wf5HRpAej3d2liOkX2i7YsQDXukG9tlmRATX4zzhRBMmhrBH26H9q4ZaYwydNAD9mpeQ2C399J6JNWt5GrSkoQ9kYvIWhPYF76Rg4oc/s1600/f48d592326f1f53920b8ad155bf809ca.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6o8Uhje07IYKuuuc0T-x2g2_2AZqpu7kC0KU7Wf5HRpAej3d2liOkX2i7YsQDXukG9tlmRATX4zzhRBMmhrBH26H9q4ZaYwydNAD9mpeQ2C399J6JNWt5GrSkoQ9kYvIWhPYF76Rg4oc/s1600/f48d592326f1f53920b8ad155bf809ca.png" /></a></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Special care has to be taken when converting Leap roll, pitch and yaw angles values to those required for the JavaFX coordinate system (180º rotated from X axis).</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">With this equations, we just need to listen to hand rotation changes and compute the rotation axis and angle values on every change to rotate accordingly the 3D model. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">So now we're ready to try our POC 3D sample: import a 3ds model and perform rotations with our hand through the Leap Motion Controller.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"> </span><br />
<span style="font-family: Georgia,"Times New Roman",serif;">The following code snippet shows how it is done for the JavaFX class:</span><br />
<br />
<pre class="brush: java">public class JavaFX8 extends Application {
private AnchorPane root=new AnchorPane();
private final Rotate cameraXRotate = new Rotate(0,0,0,0,Rotate.X_AXIS);
private final Rotate cameraYRotate = new Rotate(0,0,0,0,Rotate.Y_AXIS);
private final Translate cameraPosition = new Translate(-300,-550,-700);
private SimpleLeapListener listener = new SimpleLeapListener();
private Controller leapController = new Controller();
@Override
public void start(Stage stage){
final Scene scene = new Scene(root, 1024, 800, true);
final Camera camera = new PerspectiveCamera();
camera.getTransforms().addAll(cameraXRotate,cameraYRotate,cameraPosition);
scene.setCamera(camera);
controller.addListener(listener);
TdsModelImporter model=new TdsModelImporter();
try {
URL hubbleUrl = this.getClass().getResource("hst.3ds");
model.read(hubbleUrl);
}
catch (ImportException e) {
System.out.println("Error importing 3ds model: "+e.getMessage());
return;
}
final Node[] hubbleMesh = model.getImport();
model.close();
final Group model3D = new Group(hubbleMesh);
final PointLight pointLight = new PointLight(Color.ANTIQUEWHITE);
pointLight.setTranslateX(800);
pointLight.setTranslateY(-800);
pointLight.setTranslateZ(-1000);
root.getChildren().addAll(model3D,pointLight);
listener.posHandLeftProperty().addListener(new ChangeListener<Point3D>(){
@Override public void changed(ObservableValue<? extends Point3D> ov, Point3D t, final Point3D t1) {
Platform.runLater(new Runnable(){
@Override public void run() {
if(t1!=null){
double roll=listener.rollLeftProperty().get();
double pitch=-listener.pitchLeftProperty().get();
double yaw=-listener.yawLeftProperty().get();
matrixRotateNode(model3D,roll,pitch,yaw);
}
}
});
}
});
}
private void matrixRotateNode(Node n, double alf, double bet, double gam){
double A11=Math.cos(alf)*Math.cos(gam);
double A12=Math.cos(bet)*Math.sin(alf)+Math.cos(alf)*Math.sin(bet)*Math.sin(gam);
double A13=Math.sin(alf)*Math.sin(bet)-Math.cos(alf)*Math.cos(bet)*Math.sin(gam);
double A21=-Math.cos(gam)*Math.sin(alf);
double A22=Math.cos(alf)*Math.cos(bet)-Math.sin(alf)*Math.sin(bet)*Math.sin(gam);
double A23=Math.cos(alf)*Math.sin(bet)+Math.cos(bet)*Math.sin(alf)*Math.sin(gam);
double A31=Math.sin(gam);
double A32=-Math.cos(gam)*Math.sin(bet);
double A33=Math.cos(bet)*Math.cos(gam);
double d = Math.acos((A11+A22+A33-1d)/2d);
if(d!=0d){
double den=2d*Math.sin(d);
Point3D p= new Point3D((A32-A23)/den,(A13-A31)/den,(A21-A12)/den);
n.setRotationAxis(p);
n.setRotate(Math.toDegrees(d));
}
}
}
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">And this code snippet shows how it is done for the Leap Listener class:</span><br />
<br />
<pre class="brush: java">public class SimpleLeapListener extends Listener {
private ObjectProperty<Point3D> posHandLeft=new SimpleObjectProperty<Point3D>();
private DoubleProperty pitchLeft=new SimpleDoubleProperty(0d);
private DoubleProperty rollLeft=new SimpleDoubleProperty(0d);
private DoubleProperty yawLeft=new SimpleDoubleProperty(0d);
private LimitQueue<Vector> posLeftAverage = new LimitQueue<Vector>(30);
private LimitQueue<Double> pitchLeftAverage = new LimitQueue<Double>(30);
private LimitQueue<Double> rollLeftAverage = new LimitQueue<Double>(30);
private LimitQueue<Double> yawLeftAverage = new LimitQueue<Double>(30);
public ObservableValue<Point3D> posHandLeftProperty(){ return posHandLeft; }
public DoubleProperty yawLeftProperty(){ return yawLeft; }
public DoubleProperty pitchLeftProperty(){ return pitchLeft; }
public DoubleProperty rollLeftProperty(){ return rollLeft; }
@Override
public void onFrame(Controller controller) {
Frame frame = controller.frame();
if (!frame.hands().empty()) {
Screen screen = controller.calibratedScreens().get(0);
if (screen != null && screen.isValid()){
Hand hand = frame.hands().get(0);
if(hand.isValid()){
pitchLeftAverage.add(new Double(hand.direction().pitch()));
rollLeftAverage.add(new Double(hand.palmNormal().roll()));
yawLeftAverage.add(new Double(hand.direction().yaw()));
pitchLeft.set(dAverage(pitchLeftAverage).doubleValue());
rollLeft.set(dAverage(rollLeftAverage).doubleValue());
yawLeft.set(dAverage(yawLeftAverage).doubleValue());
Vector intersect = screen.intersect(hand.palmPosition(),hand.direction(), true);
posLeftAverage.add(intersect);
Vector avIntersect=Average(posLeftAverage);
posHandLeft.setValue(new Point3D(screen.widthPixels()*Math.min(1d,Math.max(0d,avIntersect.getX())),
screen.heightPixels()*Math.min(1d,Math.max(0d,(1d-avIntersect.getY()))),
hand.palmPosition().getZ()));
}
}
}
}
private Double dAverage(LimitQueue<Double> vectors){
double vx=0;
for(Double d:vectors){
vx=vx+d.doubleValue();
}
return new Double(vx/vectors.size());
}
}
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">In the following video I've added a few more things, which aren't in the previous code: with the hand Z position we can scale the model, and we look for right hand circle gestures, to start an animation to rotate indefinitely the model, till another circle gesture is found, resuming hand rotations.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/TS5RvqDsEoU" width="560"></iframe>
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<b><span style="font-family: Georgia,"Times New Roman",serif;">Conclusions</span></b><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">With these few samples I think you've already shown the impressive potential of a device like the Leap Motion Controller. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">JavaFX as RIA platform can interact with the Leap Motion device nicely and take UI to the next level.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">We're all waiting eagerly to the public release of the device, and the opening of the <a href="https://www.leapmotion.com/apps"><i>Airspace</i></a> market, where we'll find all kind of applications to use the Leap with.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">This will change definitely the way we interact with our computers for ever.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Thank you for reading, as always, any comment will be absolutely welcome.</span><br />
<br />Anonymoushttp://www.blogger.com/profile/15247383133952423416noreply@blogger.com9tag:blogger.com,1999:blog-7114525940495426773.post-84001949274124931572013-01-08T13:30:00.000+01:002013-10-24T17:57:49.743+02:00NXTBeeFX: A JavaFX based app for Raspberry Pi to control a Lego NXT robot wirelessly<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Hi there, and happy new year! </span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">After my last series of posts about </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><a href="http://jperedadnr.blogspot.com.es/2012/12/arduinofx-javafx-gui-for-home.html">ArduinoFX</a> (Java Embedded on the Raspberry Pi with sensors, Arduino and XBee, and a <a href="http://jperedadnr.blogspot.com.es/2012/12/arduinofx-javafx-gui-for-home_17.html">JavaFX</a> based client app, monitoring the measures), </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">it happened that Oracle released <a href="http://jdk8.java.net/fxarmpreview/javafx-arm-developer-preview.html">JDK 8 (with JavaFX) for ARM</a> Early Access, and finally we all could try JavaFX on our <a href="http://www.raspberrypi.org/">Rasperry Pi</a>. </span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">It also happened that I left the <a href="http://jperedadnr.blogspot.com.es/2012/11/nxtlegofx-javafx-based-application-to.html">NXTLegoFX post</a> pending...</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">But, now, with JavaFX running on the Raspberry Pi I had to give it a try, so I decided to use again my </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">kid's </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><a href="http://mindstorms.lego.com/en-us/Default.aspx">Lego Mindstorms NXT 2.0</a> to have something real to interact with, and resume in part the pending post. </span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">And just at the time of finishing this post, Lego unveiled their new version <a href="http://mindstorms.lego.com/en-us/News/ReadMore/Default.aspx?id=476243">EV3</a> for the summer of 2013... so before ditching the NXT, let's have some fun with it!</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">A few months ago I bought a <a href="http://www.dexterindustries.com/NXTBee_Naked.html">NXTBee</a>, the naked version without XBee, from <a href="http://www.dexterindustries.com/">Dexter Industries</a>. Please, read their <a href="http://dexterindustries.com/manual/nxtbee/">wiki</a> with further explanations, and their downloads section, to find </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">NXT-G <a href="http://www.dexterindustries.com/download.html#NXTBee">blocks</a></span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> for the official NXT firmware.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVvH-4BQ_hFAvU9X7lzFLZEtioGqTP_vHCUkdV-hkpCxGyPwIZyxlzLXFqQthdM5_M8wemsrj-hgrRL6Ksk2VijdFBJLlew9GbB_lQ2eXMW9rY5QO_K2VEDpPErcRG1fb6vUbGShyphenhyphenNzq4/s1600/xBee_2.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVvH-4BQ_hFAvU9X7lzFLZEtioGqTP_vHCUkdV-hkpCxGyPwIZyxlzLXFqQthdM5_M8wemsrj-hgrRL6Ksk2VijdFBJLlew9GbB_lQ2eXMW9rY5QO_K2VEDpPErcRG1fb6vUbGShyphenhyphenNzq4/s1600/xBee_2.gif" height="122" width="200" /></a></div>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"></span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">It's like the XBee shield for Arduino, but for the NXT. So the project was clear to me: connect wirelessly the NXT with the Raspberry Pi via serial communication, and develop a JavaFX based application for the Pi. No server required this time. </span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Here you have a little overview:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCoU2mT-H1yKV8XPV1OnLy284xWHIWNYb5qm36G5FBGj4c4l4ZljC8tGI7MeLVqFvS5bgK_t0lzdlHbWjpukCVeqhgOAUopOkBPM57PzY8qRIdRecpsCy6HJ-uUtNSUQLxocZDjiuGMbI/s1600/nxtbeefx+overview.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCoU2mT-H1yKV8XPV1OnLy284xWHIWNYb5qm36G5FBGj4c4l4ZljC8tGI7MeLVqFvS5bgK_t0lzdlHbWjpukCVeqhgOAUopOkBPM57PzY8qRIdRecpsCy6HJ-uUtNSUQLxocZDjiuGMbI/s1600/nxtbeefx+overview.png" height="226" width="640" /></a></div>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">And this is the bill of materials:</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhohBx3pSsPljD7ErDQlGFPmk1b6n20bi3wVaHYBkIduzKewAyIR-iWD8gxxMhWBWVAs24bgMYouTCj7yoEa_Om_kDw2fU7aug9smYReJfUJAp0JhceaHlHsBGFT7OZN2xxJaqSCLmjxAY/s1600/listMaterials2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhohBx3pSsPljD7ErDQlGFPmk1b6n20bi3wVaHYBkIduzKewAyIR-iWD8gxxMhWBWVAs24bgMYouTCj7yoEa_Om_kDw2fU7aug9smYReJfUJAp0JhceaHlHsBGFT7OZN2xxJaqSCLmjxAY/s1600/listMaterials2.png" height="560" width="640" /></a></div>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> </span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><i>Disclaimer:
I'm not responsible in any way for what you do on your spare time with
what I say here! The following procedures are provided without any
warranty. Also, you should know by now that I'm not related to any of
the companies mentioned in this blog.</i> </span></span><br />
<br />
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"> </span>1. LeJOS and NXTBee</span></b><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">I've already talked about <a href="http://lejos.sourceforge.net/">leJOS</a>, </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">so you'll know by now it's a </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">firmware that replace the NXT one, including a tiny Java Virtual Machine, so it allows you to program the robot with Java. Please, check </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><a href="http://jperedadnr.blogspot.com.es/2012/11/nxtlegofx-javafx-based-application-to.html">this</a></span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> for detailed explanation.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">The official distribution (by now <a href="http://sourceforge.net/projects/lejos/files/lejos-NXJ/">0.9.1 beta 3</a>) was released in February 2012 and it didn't have the classes required to interact with the NXTbee. </span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Fortunately, <a href="http://mastincrosbie.com/Marks_LEGO_projects/leJOS_driver_for_NXTBee.html">Mark Crosbie</a> has developed <span style="font-family: "Courier New",Courier,monospace;">NXTBee</span>, </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">a leJOS class to interact with the NXTBee, along with some sample Java code in leJOS showing how to send and receive data. Basically, the NXTBee, attached to Port 4 of the NXT, uses a RS485 serial link. The <span style="font-family: "Courier New",Courier,monospace;">NXTBee</span> class uses a thread as a data pump, continually polling the RS485 port and storing data received on the port in an internal circular buffer, and reading data from the circular buffer and writing it onto the RS485 port. It returns an <span style="font-family: "Courier New",Courier,monospace;">InputStream </span>and an <span style="font-family: "Courier New",Courier,monospace;">OutputStream </span>object which can be used by programs to read and write data to and from the NXTBee.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">His work has been included for future versions of leJOS, and you can find it in their <a href="http://sourceforge.net/p/lejos/code/6936/tree/trunk/classes/src/lejos/nxt/addon/">repository</a>. For now, in our projects, we just need to include NXTBee.java and CircularByteBuffer.java with the rest of our classes, before compiling and downloading to the NXT.</span><br />
<br />
<span style="color: blue;"><b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Test #1. Read from and write to NXT</span></b></span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">So as a first test we'll just check communication to and from the NXT with the NXTBee and a XBee, to other XBee plugged to the PC via XBee Explorer, with the help of <a href="http://www.digi.com/support/productdetail?pid=3352&type=utilities">X-CTU</a> software (or equivalent hyperterminal like program).</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Please, first read how to set the proper configuration of the XBee antennas <a href="http://jperedadnr.blogspot.com.es/2012/12/arduinofx-javafx-gui-for-home.html#xbee">here</a>, in case you haven't done it yet, and change the DL parameter in the Coordinator XBee #1:</span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Addressing. DL: Destination Address Low: <i>FFFF</i></span></li>
</ul>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">so it will be able to read and write to the XBee #2.</span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Now, in Netbeans, we'll make a new project, apart from the leJOS samples project. For that, follow these steps:</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">1. In your usual Java projects folder, create a new folder named <span style="font-family: "Courier New",Courier,monospace;">NXTBeeTest1</span>, and copy <span style="font-family: "Courier New",Courier,monospace;">build.properties </span>and <span style="font-family: "Courier New",Courier,monospace;">build.xml</span> files you'll find in the folder <span style="font-family: "Courier New",Courier,monospace;">LeJOS NXJ Samples\org.lejos.example</span>. Also, create a <span style="font-family: "Courier New",Courier,monospace;">src </span>folder inside.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">2.
In Netbeans, choose New Project->Java->Java Free Form Project. In
Name and Location step, browse to the <span style="font-family: "Courier New",Courier,monospace;">NXTBeeTest1 </span>folder. Change project name to
<span style="font-family: "Courier New",Courier,monospace;">NXTBeeTest1</span>. </span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhX6N0gcWGFyWZYf-DwCM4bttss0PBFvR1f_vukH3cTDpmsiSMO7aEBagqr6VI2ktLI5BH-ck4g3AwIG_24rptRp10ZXPR-AaJciQX70oZEuyYKGf5A8yqgPkrZSJDYVsm-GF4j28CoIEo/s1600/nxtTes1-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhX6N0gcWGFyWZYf-DwCM4bttss0PBFvR1f_vukH3cTDpmsiSMO7aEBagqr6VI2ktLI5BH-ck4g3AwIG_24rptRp10ZXPR-AaJciQX70oZEuyYKGf5A8yqgPkrZSJDYVsm-GF4j28CoIEo/s1600/nxtTes1-1.png" height="224" width="640" /></a></div>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> </span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">In Build and Run Actions step, in Run Project, select <i>
uploadandrun</i>. In Source Package Folders step, press Add Folder, and select <span style="font-family: "Courier New",Courier,monospace;">
src </span>folder. Finally, in Java Sources Classpath step, press Add Jar/Folder,
and browse to C:\Program Files\leJOS NXJ\lib\nxt\classes.jar. Click
finish.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNLo01EDeA8eTiODVZmoyL2vvALUo23TvTnYudT-1uBHv3M3QyvmAp79zWRP3Gh9m4S0RXD6VMfspPiVhWGrzpsOUUNP2f9oUvTCkESy0zrsTpVL3QacXbMHS2RK40FUX4-uLQ_15cYu4/s1600/nxtTes1-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNLo01EDeA8eTiODVZmoyL2vvALUo23TvTnYudT-1uBHv3M3QyvmAp79zWRP3Gh9m4S0RXD6VMfspPiVhWGrzpsOUUNP2f9oUvTCkESy0zrsTpVL3QacXbMHS2RK40FUX4-uLQ_15cYu4/s1600/nxtTes1-3.png" height="229" width="640" /></a></div>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"></span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">3. </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">In the src folder, create package <span style="font-family: "Courier New",Courier,monospace;">lejos.nxt.addon</span>, download <span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">NXTBee.java</span> from <a href="http://sourceforge.net/p/lejos/code/6936/tree/trunk/classes/src/lejos/nxt/addon/NXTBee.java?format=raw">here</a> a</span>nd add it there</span>. Also, create package </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">lejos.internal.io </span></span>and download <span style="font-family: "Courier New",Courier,monospace;">CircularByteBuffer.java</span> from <a href="http://sourceforge.net/p/lejos/code/6936/tree/trunk/classes/src/lejos/internal/io/CircularByteBuffer.java?format=raw">here</a> to that folder. </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Create package <span style="font-family: "Courier New",Courier,monospace;">org.lejos.jpl.nxtbee</span> and add a new class, <span style="font-family: "Courier New",Courier,monospace;">NXTBeeTest1</span> with the </span>following code:</span><br />
<br />
<pre class="brush: java">public static void main(String[] args) {
NXTBee nb = new NXTBee(9600, true, true);
Thread t = new Thread(nb);
t.setDaemon(true);
t.start();
//
// PART 1. SEND DATA FROM XBEE #1, RECEIVE IT IN XBEE #2 WITH NXTBEE
// Press RIGHT on the NXT to finish Part 1
//
InputStream is = nb.getInputStream();
DataInputStream dis = new DataInputStream(is);
Delay.msDelay(1000);
LCD.clear();
LCD.drawString("NXTBee Receiving", 0, 0);
byte[] b = new byte[20];
try {
while(Button.RIGHT.isUp()){
if(dis.available() > 0) {
int bytesRead = dis.read(b);
LCD.drawString("Read " + bytesRead + " bytes",0, 3);
String s = new String(b);
LCD.drawString(s, 0, 5);
}
Delay.msDelay(1000);
}
} catch(Exception e) {}
try {
dis.close();
} catch (IOException ex) {}
//
// PART 2. SEND DATA FROM XBEE #2 WITH NXTBEE, RECEIVE IT IN XBEE #1
// Press ENTER on the NXT to finish Part 2
//
OutputStream os = nb.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
SensorPort sp = SensorPort.getInstance(0);
TouchSensor touch = new TouchSensor(sp);
LCD.clear();
LCD.drawString("NXTBee Sending", 0, 1);
try {
while(Button.ENTER.isUp()){
if (touch.isPressed()){
LCD.drawString("Touch on ", 0, 2);
dos.writeBytes("Touch on");
} else {
LCD.drawString("Touch off ", 0, 2);
dos.writeBytes("Touch off");
}
dos.writeByte(13); dos.writeByte(10); // CRLF
Delay.msDelay(2000);
}
} catch(Exception e) { }
try {
dos.close();
} catch (IOException ex) {}
}
</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">4.
Edit <span style="font-family: "Courier New",Courier,monospace;">build.properties</span> and change these two lines:</span><br />
<br />
<pre class="brush: java">main.class=org.lejos.jpl.nxtbee.NXTBeeTest1
output.basename=NXTBeeTest1</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Edit <span style="font-family: "Courier New",Courier,monospace;">build.xml</span> and change description:</span><br />
<br />
<pre class="brush: java"><project name="NXTBeeTest1" default="uploadandrun"></pre>
<pre class="brush: java"><description>org.lejos.jpl.nxtbee.NXTBeeTest1 build file</description>
</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Now, plug XBee #2 to the NXTBee, and this one to sensor port #4 on the NXT brick. In port #1 attach also the Touch sensor.</span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"></span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Finally connect the NXT brick to your PC by USB or by Bluetooth and switch on the NXT. Run project. If everything is alright, it should build and download <span style="font-family: "Courier New",Courier,monospace;">NXTBeeTest1 </span>to the NXT, and start the first part: In the NXT LCD screen you should see "NXTBee Receiving". </span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieHNfdRrR0MK7cz9ImBDJds1ljNuRRPDlcUIZP0iwxvpg97Dqve7_aAkYdZeANKO6Og45N0634nfYjgJGxXwjIh9ru9c8uPd2aJUVMUMPhH0Le5w_qZ4U8cm4BuQu5aLE4oSGhRYI5o9M/s1600/test1-4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieHNfdRrR0MK7cz9ImBDJds1ljNuRRPDlcUIZP0iwxvpg97Dqve7_aAkYdZeANKO6Og45N0634nfYjgJGxXwjIh9ru9c8uPd2aJUVMUMPhH0Le5w_qZ4U8cm4BuQu5aLE4oSGhRYI5o9M/s1600/test1-4.png" height="269" width="320" /></a></div>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Now, plug the XBee #1 to your PC, via XBee Explorer, and open X-CTU. Read the XBee and go to Terminal tab. Write something, like "Test1". It should appear on the NXT LCD display:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLGCSQJDN7BQmU8QH_zTVfNDOTdAbPHEP7XQcSTWrrbXi5N7S-vymHncyz2sInp4TMQf3gcjQTEHErJbNN90v5SQseobrt6HMEEnUp2NZv_2K1rvRS_cUdKZ7UjlMF626RLBwoTEYy1_8/s1600/test1-5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLGCSQJDN7BQmU8QH_zTVfNDOTdAbPHEP7XQcSTWrrbXi5N7S-vymHncyz2sInp4TMQf3gcjQTEHErJbNN90v5SQseobrt6HMEEnUp2NZv_2K1rvRS_cUdKZ7UjlMF626RLBwoTEYy1_8/s1600/test1-5.png" height="252" width="640" /></a></div>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Press firmly the right button on your NXT, and now you should see on the terminal tab the status of the touch sensor (press and release a few times).</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAE11V5opkjA_hzdqEi1yZR1hhO5e2W4Fs1hvtpBgCmiTVvu7zgcfrdT0iNM7ptGMjTyTwEWuhcdn6EqJUjM2d8ccdL7X7bzGCjchgAqx7oX4dgU7EUt4FMyhjtm3c5kyysYuAuTf0v1Q/s1600/test-6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAE11V5opkjA_hzdqEi1yZR1hhO5e2W4Fs1hvtpBgCmiTVvu7zgcfrdT0iNM7ptGMjTyTwEWuhcdn6EqJUjM2d8ccdL7X7bzGCjchgAqx7oX4dgU7EUt4FMyhjtm3c5kyysYuAuTf0v1Q/s1600/test-6.png" height="219" width="640" /></a></div>
<br />
<br />
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">2. Robotics: Arbitrator and Behavior</span></b><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">To introduce some robotic way of thinking in our project we can examine the sample <span style="font-family: "Courier New",Courier,monospace;">BumperCar </span>distributed with the samples bundle. It requieres a wheeled vehicle with two independently controlled motors connected to motor ports A and C, and a touch sensor connected to sensor port 1 and an ultrasonic sensor connected to port 3.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">So we build this simple robot. You can follow this fantastic <a href="http://www.nxtprograms.com/NXT2/castor_bot/steps.html">guide</a>, and finally add the ultrasonic sensor.</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZAE2iDVJkCHRTxwnfKW82cO4V6tfISrvMHqB6lBX-RQsJj1E6ThRjXovSbc2YIKEjU5KldH-GTNvcbHe3ST4F3UHD_hky2rGeS9-SG-1P9naErq1sJ5rwNSZrPSxV18tSD9fmtlAl09w/s1600/robot.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZAE2iDVJkCHRTxwnfKW82cO4V6tfISrvMHqB6lBX-RQsJj1E6ThRjXovSbc2YIKEjU5KldH-GTNvcbHe3ST4F3UHD_hky2rGeS9-SG-1P9naErq1sJ5rwNSZrPSxV18tSD9fmtlAl09w/s1600/robot.png" height="313" width="320" /></a></div>
<br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">As we can read <a href="http://lejos.sourceforge.net/nxt/nxj/tutorial/Behaviors/BehaviorProgramming.htm">here</a>, the concepts of Behavior Programming as implemented in leJOS NXJ are very simple:</span><br />
<ul>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> Only one behavior can be active and in control of the robot at any time.</span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> Each behavior has a fixed priority.</span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> Each behavior can determine if it should take control.</span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> The active behavior has higher priority than any other behavior that should take control.</span></li>
</ul>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Basically, for each task the robot must perform, a behavior class is defined. This class will override the three public methods from <span style="font-family: "Courier New",Courier,monospace;">Behavior</span> interface:</span><br />
<ul>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">boolean takeControl()</span> indicates if this behavior should become active, returning quickly without performing long calculations.</span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">void action()</span> performs its task when the behavior becomes active. A behavior is active as long as its action() method is running, so the action() method should exit when its task is complete, or promptly when <span style="font-family: "Courier New",Courier,monospace;">suppress() </span>is called. </span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">void suppress()</span> immediately terminates the code running in the action() method. It also should exit quickly.</span>
</li>
</ul>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Once all the behaviors are created, they are given to an <span style="font-family: "Courier New",Courier,monospace;">Arbitrator </span>class, to regulate which behavior should be activated at any time. The order in the array of behaviors determines the priority of each one: 0 index means lowest priority. </span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Whent its <span style="font-family: "Courier New",Courier,monospace;">start()</span> method is called, it begins arbitrating: deciding which behavior will become active. For that, it calls the <span style="font-family: "Courier New",Courier,monospace;">takeControl()</span> method on each Behavior object, starting with the object with the highest index number in the array, till it finds a behavior that wants to take control. If the priority index of this behavior is greater than that of the current active behavior, the active behavior is suppressed.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">In the bumpercar sample, there are two behaviors. The first one defines its primary task, drive forward, and the second orders the robot to reverse and turn whenever the touch sensor strikes an object, or the ultrasonic sensor gets an echo from a close object, with priority over the first behavior:</span><br />
<br />
<pre class="brush: java">Behavior b1 = new DriveForward(); // low priority
Behavior b2 = new DetectWall(); // high priority
Behavior[] behaviorList = {b1, b2};
Arbitrator arbitrator = new Arbitrator(behaviorList);
arbitrator.start();</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="color: blue;"><b>Test #2 The BumperCar</b></span></span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">This test is quite simple: if you have built the bumpercar, just run samples project, type "bumpercar" in the dialog, wait till it's downloaded, press a button on your NXT to start, and test if it behaves as expected. </span><br />
<br />
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">3. Serial communication with the NXT</span></b><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Now let's insert a new behavior in our robot, one that takes care of remote communication with the NXT, so we can override its autonomous behavior, providing manual control. A rear touch sensor will be added.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">We create a new Java Free Form Project, named NXTBeeNXT, in the very same way as in the previous test1 with NXTBeeTest1.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">First
of all we'll add <span style="font-family: "Courier New",Courier,monospace;">Brick</span>, a singleton class to wrap the NXT motors and
sensors, so they can be accessed from any behavior. Also, from this
class any change in the robot status will be notified to the PC or
Raspberry Pi, via XBee.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Then we add three behaviors: <span style="font-family: "Courier New",Courier,monospace;">DriveForward</span>, <span style="font-family: "Courier New",Courier,monospace;">Remote </span>and <span style="font-family: "Courier New",Courier,monospace;">DetectWall</span>.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Finally we define the main class, <span style="font-family: "Courier New",Courier,monospace;">NXTBeeNXT</span>.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Here you can see part of the <span style="font-family: "Courier New",Courier,monospace;">Remote </span>code. It just listens for commands from the inputstream and takes control or perform actions accordingly. In case an obstacle is found, this behavior will be suppressed and its action will be stopped:</span><br />
<br />
<pre class="brush: java">public class Remote implements Behavior
{
private DataInputStream dis;
private boolean _suppressed = false;
private String s;
public Remote(DataInputStream dis){
this.dis=dis;
}
public boolean takeControl()
{
if(Brick.getInstance().isStopped()){
// cancel all behaviors, stop the arbitrator
return false;
}
if(Brick.getInstance().getBehState()==Brick.state.MANUAL){
// Take control if we're in Manual mode, don't read remote orders here
return true;
}
// Read remote orders from PC/Raspi
byte[] b = new byte[20];
try {
if(dis.available() > 0) {
dis.read(b);
s = new String(b);
}
} catch(Exception e) {}
// Take control if orders are Stop or change from Auto (driving forward) to Manual mode
return s!=null && (s.startsWith(Brick.STOP) || s.startsWith(Brick.MANUAL));
}
public void suppress()
{
_suppressed = true;
}
public void action()
{
_suppressed=false;
LCD.clearDisplay();
if(s.startsWith(Brick.STOP)){
// Notify Stop order
Brick.getInstance().setBehState(Brick.state.STOPPED);
LCD.drawString("Bumper Car STOP",0,1);
} else if(s.startsWith(Brick.MANUAL)){
// Notify order to enter in Manual Mode
Brick.getInstance().setBehState(Brick.state.MANUAL);
// Start reading serial port and process the orders
byte[] b = new byte[20];
try {
// This action will be suppressed if the robot finds an obstacle
while(!_suppressed) {
if(dis.available() > 0) {
dis.read(b);
s = new String(b);
if(s.startsWith(Brick.LEFT)){
LCD.drawString("LEFT ",0,3);
// start Motor.C rotating forward, with A stopped, so
// the robot turns left
Brick.getInstance().getLeftMotor().stop();
Brick.getInstance().getRightMotor().rotate(360, true);
} else if(s.startsWith(Brick.RIGHT)){
...
} else if(s.startsWith(Brick.AUTO)){
LCD.drawString("AUTO ",0,3);
// Return to Auto mode. Motors are stopped
Brick.getInstance().getLeftMotor().stop();
Brick.getInstance().getRightMotor().stop();
// Notify forward (auto) state
Brick.getInstance().setBehState(Brick.state.FORWARD);
// ends the action
_suppressed=true;
}
}
Delay.msDelay(500);
}
} catch(Exception e) { }
}
s="";
}
}
</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">All the code for this project can be found in my GitHub repository <a href="https://github.com/jperedadnr/NXTBeeNXT">here</a>.</span><br />
<br />
<span style="color: blue;"><b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Test #3 The remotely controlled BumperCar</span></b></span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">We add to the bumpercar a second touch sensor, to look for obstacles when the robot moves backward in Manual mode, plugged to port #2. The XBee #2 must be plugged to the NXTBee and this should be plugged to port #4 on the NXT. </span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifH_8n3W6OfcMs5ZcDUCGV8NiukB35B-E4jajwJb1YpNJlp-RoSdaHAUcWujYQsbK4HOdF5cGIhVJi2ii4gHoh0mrp6WU6Zp9HmXRNaRMHGxRshwL18dsatAN4L8dX6BWJa4o8I3WtTXw/s1600/bumpercar.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifH_8n3W6OfcMs5ZcDUCGV8NiukB35B-E4jajwJb1YpNJlp-RoSdaHAUcWujYQsbK4HOdF5cGIhVJi2ii4gHoh0mrp6WU6Zp9HmXRNaRMHGxRshwL18dsatAN4L8dX6BWJa4o8I3WtTXw/s1600/bumpercar.png" height="400" width="283" /></a></div>
<br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">If you've just clonned the code from the repository, open the project, and run it, with your NXT switched on. After it has been compiled, built and downloaded, it will start and you'll see in the NXT display "NXTBee waiting...". Leave the NXT on the floor, in a relative clear wide area.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Now plug XBee #1 in the USB port of your PC, via XBee Explorer, and open X-CTU, read the modem configuration and go to Terminal tab. Now press 'S' to start Auto mode in the NXT. Let the BumperCar find some obstacles and react to them. Press 'P' to Stop in case something is wrong, and 'Q' to quit the program. On the NXT, from files menu, select <span style="font-family: "Courier New",Courier,monospace;">NXTBeeNXT.nxj</span> and click Enter. Now press again 'S' and enter in manual mode with 'M'. Now you can press:</span><br />
<ul>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">'F' to move forward,</span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">'B' to move backward,</span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">'L' to turn left,</span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">'R' to turn right,</span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">'V' to speed up,</span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">'W' to speed down and</span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">'A' to go to Auto mode</span></li>
</ul>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">The red numbers appering on the terminal screen after commands are typed are the echo back from the NXT notifying its status: 0 means stopped, 1 driving forward, 2 wall detected and 3 manual mode.</span></span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Note that if you drive manually against any obstacle, DetectWall behavior will override your control, trying to avoid the obstacle and returning to auto mode. </span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3LvOS2z_WRaHWUlEynmJxSdRW7SYi86QTCL8fvK4jY5Nmx6crbkjf9UMyTDYs8W8SaUJRjFsoEbrMP3dQb767hs_JgDYHtjmZ4QnOH1tEzvHS0zUv0kE6fNJwhi7P3LrIe-2HqT92YrE/s1600/bumpercar2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3LvOS2z_WRaHWUlEynmJxSdRW7SYi86QTCL8fvK4jY5Nmx6crbkjf9UMyTDYs8W8SaUJRjFsoEbrMP3dQb767hs_JgDYHtjmZ4QnOH1tEzvHS0zUv0kE6fNJwhi7P3LrIe-2HqT92YrE/s1600/bumpercar2.png" height="400" width="310" /></a></div>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> </span><b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> </span></b><br />
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">4. JavaFX for Raspberry Pi</span></b><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">To run JavaFX on the Raspberry Pi, you'll need a hard float Raspbian version.</span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> I covered <a href="http://jperedadnr.blogspot.com.es/2012/12/arduinofx-javafx-gui-for-home.html">here</a> how to install the soft float version, so follow the same instructions again, but now with this hard float version </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">
<a href="http://downloads.raspberrypi.org/images/raspbian/2012-12-16-wheezy-raspbian/2012-12-16-wheezy-raspbian.zip">2012-12-16-wheezy-raspbian.zip</a>. When the Pi boots for the first time, in the config menu option </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">memory_split, now you should give 128 MB to video.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">At the first login, edit <span style="font-family: "Courier New",Courier,monospace;">/boot/config.txt</span> file and uncomment these two lines, and select the resolution of your display:</span><br />
<span style="font-family: "Courier New",Courier,monospace;"><br /></span>
<span style="font-family: "Courier New",Courier,monospace;">framebuffer_width=1280<br />framebuffer_height=1024</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">There are already several blogs out there covering how to install Java in a hard float Raspbian Wheezy version, like <a href="http://jdk8.java.net/fxarmpreview/javafx-arm-developer-preview.html#req">this</a> or <a href="http://www.savagehomeautomation.com/projects/raspberry-pi-installing-oracle-java-se-8-with-javafx-develop.html">this</a>. Basically you have to follow these steps:</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">1. Download Oracle JDK 8 (with JavaFX) for ARM Early Access from <a href="http://jdk8.java.net/fxarmpreview/">here</a> in your PC. With ssh, copy the file to a folder in your Pi, like /home/pi/Downloads/. In a terminal window in your PC, or in the Pi, run these commands:</span><br />
<br />
<pre><span style="font-family: "Courier New",Courier,monospace;">mkdir -p /opt</span></pre>
<pre><span style="font-family: "Courier New",Courier,monospace;">cd /home/pi/Downloads
sudo tar zxvf jdk-8-ea-b36e-linux-arm-hflt-29_nov_2012.tar.gz -C /opt
rm jdk-8-ea-b36e-linux-arm-hflt-29_nov_2012.tar</span></pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">If you type<span style="font-family: "Courier New",Courier,monospace;"> /opt/jdk1.8.0/bin/java -version</span>, you should see "java version '1.8.0-ea'"<tt>.</tt></span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">2. Download JavaFX samples from <a href="http://www.java.net/download/JavaFXarm/javafx_samples-8_0_0-ea-linux.zip">here</a>. Copy the file to /home/pi/Downloads by ssh. Run in a terminal window:</span><br />
<br />
<pre><span style="font-family: "Courier New",Courier,monospace;">cd /home/pi/Downloads
unzip javafx_samples-8_0_0-ea-linux.zip
rm javafx-samples-8.0.0-ea.zip</span></pre>
<pre><span style="font-family: "Courier New",Courier,monospace;">mv </span><span style="font-family: "Courier New",Courier,monospace;"><span style="font-family: "Courier New",Courier,monospace;">javafx-samples-8.0.0-ea /home/pi/javafx</span></span></pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">And that's all!</span><br />
<br />
<span style="color: blue;"><b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Test #4. JavaFX sample </span></b></span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Now we can test several of the included samples. Note that, for the moment, the application takes the whole screen and you can't quit if it doesn't have a button for it. </span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">So one way to do it is running your sample from a terminal window in a VNC session. You'll see the application in your display</span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> connected with HDMI to the Pi, and </span>all the text output (System.out, System.err) in your terminal window. And you can kill the application anytime with Ctrl+C, or with <span style="font-family: "Courier New",Courier,monospace;">ps -a</span>, find the id of the java application and type <span style="font-family: "Courier New",Courier,monospace;">kill <<i>javaid</i>></span>.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">To run the sample, plug a mouse in your Pi, and type this line in the terminal window: </span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<span style="font-family: "Courier New",Courier,monospace;">sudo /opt/jdk1.8.0/bin/java -Djavafx.platform=eglfb -cp /opt/jdk1.8.0/jre/lib/jfxrt.jar:/home/pi/javafx/StopWatch.jar stopwatch.MainScreen</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJyUGD7axz2DMiB4II6Vj1nJR6sYEYxnaAY5AY1awqGa1PhBsm66tntgroq9Q0zxSHNC9WUYyBsJvnXuPyChC1Uk0bot58oAd0u89ctumBGY1a3AJJ1sJIHFXOKOUxNo5rzdhCiGJBx7w/s1600/test4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJyUGD7axz2DMiB4II6Vj1nJR6sYEYxnaAY5AY1awqGa1PhBsm66tntgroq9Q0zxSHNC9WUYyBsJvnXuPyChC1Uk0bot58oAd0u89ctumBGY1a3AJJ1sJIHFXOKOUxNo5rzdhCiGJBx7w/s1600/test4.png" height="286" width="400" /></a></div>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Press Ctrl+C to finish. </span><br />
<br />
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">5</span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">. </span></b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><b>Pi4J library</b> </span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">The <a href="http://pi4j.com/">Pi4j</a> project </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">is intended to provide a bridge between the native libraries and Java for full access to the Raspberry Pi, so you can easily access to GPIO pins for your Java project.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">To install the library, follow these steps:</span><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">sudo wget http://pi4j.googlecode.com/files/pi4j-0.0.5-SNAPSHOT.deb<br />sudo dpkg -i pi4j-0.0.5-SNAPSHOT.deb</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">It will install in <span style="font-family: "Courier New",Courier,monospace;">/opt/pi4j/lib</span> four jars. Also, in <span style="font-family: "Courier New",Courier,monospace;">/opt/pi4j/examples</span> you'll find several samples.</span><br />
<br />
<span style="color: blue;"><b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Test #5. Control GPIO</span></b></span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Before testing the first sample, please review the GPIO pins labelling <a href="http://pi4j.com/images/gpio-control-example.png">here</a>. </span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Now add a LED with a 330 Ω pull-up resistor in a breadboard, and connect anode to pin #1 and cathode to GND.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisey2XqVVpL5OzoKOZmaqiHfj9wje1a9e7vt1wad9YNQpdRNaqc7HN0aJ071jvn5CeM6bFH-XryLN1PWysFJTADiS7WD6BiucZi0a6YX0JHc6QOGMGeu4PWgn1ST7dzCE1WHhv03xOaXI/s1600/RaspiGPIO_bb.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisey2XqVVpL5OzoKOZmaqiHfj9wje1a9e7vt1wad9YNQpdRNaqc7HN0aJ071jvn5CeM6bFH-XryLN1PWysFJTADiS7WD6BiucZi0a6YX0JHc6QOGMGeu4PWgn1ST7dzCE1WHhv03xOaXI/s1600/RaspiGPIO_bb.png" height="307" width="400" /></a></div>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">On your Pi, compile the sample </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">first</span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">:</span><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">cd /opt/pi4j/examples</span><br />
<span style="font-family: "Courier New",Courier,monospace;">sudo /opt/jdk1.8.0/bin/javac -classpath .:classes:/opt/pi4j/lib/'*' ControlGpioExample.java</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">And now run it:</span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /><span style="font-family: "Courier New",Courier,monospace;">sudo /opt/jdk1.8.0/bin/java -classpath .:classes:/opt/pi4j/lib/'*' ControlGpioExample</span> </span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_F12tqFOGHnVu0hsi7So1K8D61giUzMPzJlQF1qlTwMnF9S61v_AcbYs8Ovwj56jdLkeKUnbI0IIknOBkbzIxXMOuxmCP7ABS5F9SMbdbwEcuW4qY0skFd8XDtn06F0U0D1n70S6x-Cg/s1600/pi4j.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_F12tqFOGHnVu0hsi7So1K8D61giUzMPzJlQF1qlTwMnF9S61v_AcbYs8Ovwj56jdLkeKUnbI0IIknOBkbzIxXMOuxmCP7ABS5F9SMbdbwEcuW4qY0skFd8XDtn06F0U0D1n70S6x-Cg/s1600/pi4j.png" height="285" width="400" /></a></div>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> </span><b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> </span></b><br />
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">6. The JavaFX application</span></b><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Finally, we'll design the JavaFX GUI application to remotely control the BumperCar from the Raspberry Pi.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">The first thing we should do is install jdk1.8.0-ea in our PC, so we can use our favourite IDE to develope and build the project. Then, we only need to send the jar to the Pi and test it. For that:</span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"></span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">1. Unzip </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">jdk-8-ea-b36e-linux-arm-hflt-29_nov_2012.tar.gz</span><span style="font-family: "Courier New",Courier,monospace;"> </span>in a folder, like C:\Java.</span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">2. </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">In Netbeans go to Tools, select Ant Variables, click Add, and define <span style="font-family: "Courier New",Courier,monospace;">J8_HOME</span> and browse to the folder "C:\Java\jdk1.8.0". </span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">3. Create a new JavaFX FXML Application, named <span style="font-family: "Courier New",Courier,monospace;">NXTBeeFX</span>, and open <span style="font-family: "Courier New",Courier,monospace;">build.xml</span>. Add at the end this target:</span><br />
<br />
<pre class="brush: java"><target depends="-pre-init,-init-private" name="-init-user">
<property file="${user.properties.file}"/>
<property name="javac.compilerargs" value="-bootclasspath ${var.J8_HOME}/jre/lib/rt.jar"/>
<property name="javac.classpath" value="${var.J8_HOME}/jre/lib/jfxrt.jar:
${var.J8_HOME}/jre/lib/ext/RXTXcomm-2.2pre2.jar:
${var.J8_HOME}/jre/lib/ext/pi4j-core.jar"/>
<!-- The two properties below are usually overridden -->
<!-- by the active platform. Just a fallback. -->
<property name="default.javac.source" value="1.6"/>
<property name="default.javac.target" value="1.6"/>
</target>
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">4. Add RXTXcomm-2.2pre2.jar and pi4j-core.jar to the project. Copy them from your Pi to your PC, by ssh, from <span style="font-family: "Courier New",Courier,monospace;">/usr/share/java</span> and <span style="font-family: "Courier New",Courier,monospace;">/opt/pi4j/lib</span>.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Now edit <span style="font-family: "Courier New",Courier,monospace;">NXTBeeFX.fxml</span> in the JavaFX Scene Builder, and add the buttons and labels required:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDDD_SXZJn60jIK9Ht8jgAMfcUS44yuF9iomCEDAAENz-maKvCuF49MIXHiiMm7PWGfHOq_qK4kCzLj5UOGtQ0Q5Tyk3aVo90B0aAFOdBrL4Zc9zOIKMwWrJEtiExUMeWTnwJlLPZ8yRk/s1600/nxtFxml.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDDD_SXZJn60jIK9Ht8jgAMfcUS44yuF9iomCEDAAENz-maKvCuF49MIXHiiMm7PWGfHOq_qK4kCzLj5UOGtQ0Q5Tyk3aVo90B0aAFOdBrL4Zc9zOIKMwWrJEtiExUMeWTnwJlLPZ8yRk/s1600/nxtFxml.png" height="424" width="640" /></a></div>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">There'is one issue I've found while creating this app: you can't apply InnerShadow or DropShadows effects. If you do, you'll get a RuntimeException. A bug has already been filed: <a href="http://javafx-jira.kenai.com/browse/RT-27464">http://javafx-jira.kenai.com/browse/RT-27464</a>.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Now, in NXTBeeFXController, we create the methods for the buttons, and initialize those from the manual panel to change the text label to a graphic icon: </span><br />
<br />
<pre class="brush: java">btnRight.setId("key-button");
icon = new Label();
icon.getStyleClass().add("arrowRight");
btnRight.setText(null);
btnRight.setGraphic(icon);
btnRight.setPrefSize(50, 50);
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">taken from a SVG path, defined in the css file:</span><br />
<br />
<pre class="brush: java">#key-button.button .arrowRight {
-fx-shape: "M 14.007057,41.909369 C 2.3562491,41.605509 12.432093,7.29355
31.877087,12.49765 l 0,-9.3754104 16.648482,14.5865794
-16.648482,15.29753 0,-9.66838 c -15.93811,-5.71097
-9.177528,18.43258 -17.87003,18.5714 z";
-fx-translate-x: -6;
-fx-translate-y: -4;
}
</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Another issue I've found is that the icon is centered in the button at it's expected when running on my PC, but on the Raspberry Pi it appears to the right and to the bottom of the button, so that's the reason for translating it back to the center.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">So this is how the manual panel looks like after a little bit of styling: </span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggEulUBP7ZjuxI1RE3yGB2DBRXJ5AyOYWzwKsaot6fpzu7htZeRGY3HgrgG6NoYWO3VyA6Yu2D2fiFUA4dyh7eigPwq1NzAYlNXeB2hJEl3jd9WWD6i1WBekPy8cXBeW7Z-xwTlkYZY74/s1600/NXTBeeFX-man.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggEulUBP7ZjuxI1RE3yGB2DBRXJ5AyOYWzwKsaot6fpzu7htZeRGY3HgrgG6NoYWO3VyA6Yu2D2fiFUA4dyh7eigPwq1NzAYlNXeB2hJEl3jd9WWD6i1WBekPy8cXBeW7Z-xwTlkYZY74/s1600/NXTBeeFX-man.png" /></a></div>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">In the initialize method, we also start a thread to read the serial port (via XBee) and get all the responds from the NXT.</span><br />
<br />
<pre class="brush: java">// Initialize Serial Port, with the XBee #1 connected on the USB port
serial=new Serial();
try {
System.out.println("Connecting to serial port...");
serial.connect( "/dev/ttyUSB0" );
} catch( Exception e ) {
System.out.println("Error connecting to serial port: "+e.getMessage());
}
//
// Service to start reading serial port for NXT Status
// It will stop and close when requested
//
serviceSerial=new Service<Void>(){
@Override
protected Task<Void> createTask() {
return new Task<Void>(){
@Override
protected Void call() throws Exception {
System.out.println("start reading...");
serial.read();
return null;
}
@Override protected void cancelled() {
System.out.println("cancelling...");
serial.disconnectInput();
super.cancelled();
}
};
}
};
serviceSerial.start();
</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Also, the GPIO pines are initialized, to set an alarm in case the NXT finds an obstacle.</span><br />
<br />
<pre class="brush: java"> // create gpio controller
final GpioController gpio = GpioFactory.getInstance();
// provision gpio pin #01 as an output pin
final GpioPinDigitalOutput pinRed =
gpio.provisionDigitalOutputPin(RaspiPin.GPIO_01, "MyLEDRed", PinState.LOW);
// provision gpio pin #07 as an output pin
final GpioPinDigitalOutput pinGreen =
gpio.provisionDigitalOutputPin(RaspiPin.GPIO_07, "MyLEDGreen", PinState.LOW);
public void setAlarmOn() {
pinRed.high();
pinGreen.low();
}
public void setAlarmOff() {
pinRed.low();
pinGreen.high();
}
</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Note that a second LED is added to pin 7.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy_RdVdt39rAMx9MJGX4pQ7ARSPKJ9_mjzvyB4Vv8o7Webo77CNScmTOd6yOnfkxZKbNycTfGAmLp9e7jWqj7txUB4En7llaGrUmNKJpa8PQ5Zoi6KDm3DaxzwLIkW5P7o4bM3IW1TRQ0/s1600/RaspiGPIO_bb2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy_RdVdt39rAMx9MJGX4pQ7ARSPKJ9_mjzvyB4Vv8o7Webo77CNScmTOd6yOnfkxZKbNycTfGAmLp9e7jWqj7txUB4En7llaGrUmNKJpa8PQ5Zoi6KDm3DaxzwLIkW5P7o4bM3IW1TRQ0/s1600/RaspiGPIO_bb2.png" height="307" width="400" /></a></div>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">All the code for this project can be found in my GitHub repository <a href="https://github.com/jperedadnr/NXTBeeFX">here</a>. Clone it, build it, and then you must send the jars to the Pi, by ssh:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFoUHQdVvIvLvcXT-1_YsfXLWxqmUEqy7_Bzp_vzXgZjKt5GVOeF7plQCcrjibVN3wfq-TBUaNT8Jj0lcF7OOG2-ebbgTGRtN32VR1eIQWuXGlQzwzUP0EbffgeGalpLmJI0qMgBquUBY/s1600/nxt_ssh.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFoUHQdVvIvLvcXT-1_YsfXLWxqmUEqy7_Bzp_vzXgZjKt5GVOeF7plQCcrjibVN3wfq-TBUaNT8Jj0lcF7OOG2-ebbgTGRtN32VR1eIQWuXGlQzwzUP0EbffgeGalpLmJI0qMgBquUBY/s1600/nxt_ssh.png" height="257" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguMqCwrxlt_FTp998MFQQELSzpl8XtZV-gVS7HvanzYLhAQwOT3StpL4pT0usoymaSs3w30HhZBEwmzCz9Ak6wxZ6MCQBPtH2kfOYLjFn0l_v0FCHL80YA8WdMrD2gySrqFJtvUGJg3LY/s1600/nxt_ssh.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">To run the project you can create a file typing <span style="font-family: "Courier New",Courier,monospace;">nano bash.sh<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">. There you should type:</span></span></span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">#!/bin/bash<br /><br />sudo /opt/jdk1.8.0/bin/java -Djavafx.platform=eglfb -Djava.library.path="/usr/lib/jni" -cp /opt/jdk1.8.0/jre/lib/jfxrt.jar:/home/pi/javafx/NXT/RXTXcomm-2.2pre2.jar:/home/pi/javafx/NXT/pi4j-core.jar:/home/pi/javafx/NXT/NXTBeeFX.jar nxtbeefx.NXTBeeFX</span></span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Save (Ctrl+O) and exit (Ctrl+X). Now <span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">check all is in place and </span>you can run it:</span><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">/home/pi/Downloads/javafx/NXT/bash.sh</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgB3DZX5uu3kAAuj4F17gqHCDel8OIOwlgtkcicIxz08liLFNYG_wAPuJfkjRQNxfOvdc1TyqlzB5f-7ZKv8lgzrMG8QRlQtTI65lNZCLBY7_ePF0nXYhyxbGHJ2P_haSCL-Oq-QADYNyI/s1600/run+NXT.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgB3DZX5uu3kAAuj4F17gqHCDel8OIOwlgtkcicIxz08liLFNYG_wAPuJfkjRQNxfOvdc1TyqlzB5f-7ZKv8lgzrMG8QRlQtTI65lNZCLBY7_ePF0nXYhyxbGHJ2P_haSCL-Oq-QADYNyI/s1600/run+NXT.png" height="303" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihdyplQFQVdBY0_Guza3G4aUt2WKfVJWb0Ms1_o6h-2IzTxsZBW6uoHuhrysA-ht0GwFkZDHoywI7AivpEnc6qqMFyNy0k6XI3PSk0a3LoWGNUoHRuQfUpSzv_-67ZeaBkp0LRolpS3Js/s1600/nxt_run.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihdyplQFQVdBY0_Guza3G4aUt2WKfVJWb0Ms1_o6h-2IzTxsZBW6uoHuhrysA-ht0GwFkZDHoywI7AivpEnc6qqMFyNy0k6XI3PSk0a3LoWGNUoHRuQfUpSzv_-67ZeaBkp0LRolpS3Js/s1600/nxt_run.png" height="283" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhX91sGWcMZlSWUpJEeBoquGvEdrwvX2knuxFqvvjP0xpv233vXap7bMzhOE_Wc4MKGipze_5rG9_BUawr1C0Rk1IIM7Uqr9hy0n28futU-Rcgp1P9-U6WVx30RNXua2mT7aYHZpNsbz_s/s1600/nxt_run.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><b>CONCLUSION</b></span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">As a short conclusion after this long post, let me just say that JavaFX on the Raspberry Pi performs really well, being just an Early Access release. It will be improved and several issues will be fixed, and things that were not included, like media, will be in.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">So if you have a Raspberry Pi, don't wait any longer and start testing your JavaFX apps.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Again, leJOS has proved to be a really mature platform, enabling a really easy integration between Java/JavaFX and robotics </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">applications for</span> Lego Mindstorms NXT. </span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Finally, in this <a href="http://www.youtube.com/watch?v=o0rUtgv7HmE&feature=youtu.be">video</a> you'll find most of the details I've been talking about in this post. </span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">If you have the chance, take your NXT, grab the code, and give it a try! Any comment will be absolutely welcome.</span>
Anonymoushttp://www.blogger.com/profile/15247383133952423416noreply@blogger.com6tag:blogger.com,1999:blog-7114525940495426773.post-13667504886587054542012-12-17T20:56:00.000+01:002013-11-09T14:46:08.468+01:00ArduinoFX: A JavaFX GUI for Home Automation with Raspberry Pi and Arduino. Part II<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Hi There!</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">If you haven't read the <a href="http://jperedadnr.blogspot.com.es/2012/12/arduinofx-javafx-gui-for-home.html">first part</a> of this series, please take your time, and read about the basic configuration required for the application I'm blogging about in this second part.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Briefly, as a recap, we have an Arduino with a RHT03 temperature and relative humidity sensor and a XBee antenna, plugged in an Arduino shield. The measures are sent wirelessly to a second XBee antenna, plugged via a XBee Explorer to a Raspberry Pi USB port.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">In the Raspberry Pi, with soft-float Debian wheezy distro, we have installed Java Embedded Suite 7.0. For now, we are just reading the measures.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Now, if you keep on reading this second part, we are going to create the embedded server in the Raspberry Pi, with the following main tasks: </span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Back-end task: it will repeatedly read the serial port to get the measures from the Arduino and store them in a database.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Web services: it will respond to web requests returning the current values measured.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">RESTful web services: it will respond to REST requests returning lists in json format with the data measured between two dates.</span></li>
</ul>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">And then we will create the JavaFX based client application, that will mainly:</span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Connect to the server to get last measures and show them in a LED Matrix Panel control.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Connect to the server to get a list of measures between two dates, showing them in a chart. </span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Show the status of the connection anytime.</span></li>
</ul>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Embedded application development is fundamentally cross-platform work. The target device on which the application will be deployed, the </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Raspberry Pi in this case</span>, doesn't have the hardware resources to support development tools such as Netbeans. Therefore, we must build on our host computer, copy the required build artifacts to the target, and debug and tune the application running on the target. </span><br />
<br />
<b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">1. Setting the Environment</span></b><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Basically, we should recreate the embedded environment in our PC, so we can create the project in our favourite Java IDE, compile and build it, and then move the jar/war files by ssh to the Raspberry Pi, where we'll use the provided scripts by the </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Java EmbeddedSuite to run the application. </span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">As pointed out <a href="http://www.oracle.com/technetwork/java/embedded/resources/java-embedded-suite/index.html#platforms">here</a>, we need at least JDK 1.7, NetBeans 7.1 and Ant 1.8. In that case, follow the next steps to add the required Ant variable <span style="font-family: "Courier New",Courier,monospace;">JES_HOME</span>:</span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Download JES from <a href="http://download.oracle.com/otn-pub/java/JES/7.0-b11/jes-7.0-ga-bin-b11-linux-arm-runtime-15_nov_2012.zip">here</a>, in case you haven't done it yet following Part I of this series.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Unzip <code class="java plain">jes-</code><code class="java value">7.0</code><code class="java plain">-ga-bin-b11-linux-arm-runtime-15_nov_2012<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"> </span></code>and move all its content to a local folder like C:\Java</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">In Netbeans go to Tools, select Ant Variables, click Add, and define <span style="font-family: "Courier New",Courier,monospace;">JES_HOME</span> and browse to the folder "jes7.0", and click Ok. </span></li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCKoEZ1XMNbc72_yW3Dac8RrYQq9bkEx69UNK4bsieDQiTcrBnHqoyR1Od7wpaPN5sUR-5llCBfuclaStqI0xs6sE5mBTVtiCo49idyY4p0cbkTIUfX5JPkl_l8iUFJVf0e9QjQEJi770/s1600/ant.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCKoEZ1XMNbc72_yW3Dac8RrYQq9bkEx69UNK4bsieDQiTcrBnHqoyR1Od7wpaPN5sUR-5llCBfuclaStqI0xs6sE5mBTVtiCo49idyY4p0cbkTIUfX5JPkl_l8iUFJVf0e9QjQEJi770/s1600/ant.png" width="320" /></a></div>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Now we can open the embedded <a href="http://download.oracle.com/otn-pub/java/JES/7.0-b11/jes-7.0-ga-b11-linux-samples-15_nov_2012.zip">samples</a> projects on NetBeans and build them. We can make any change, rebuild again, and send them by ssh to the Pi, and run them.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">2. Modifying HelloService sample</span></b><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Open this project and check the content of the Ant file <span style="font-family: "Courier New",Courier,monospace;">build.xml</span>. You will see several customized targets. The first one is used to set the javac compiler arguments, specifying the rt.jar from JES_HOME instead from the regular JAVA_HOME.</span><br />
<br />
<pre class="brush: java"><target depends="-pre-init,-init-private" name="-init-user">
<property file="${user.properties.file}"/>
<property name="javac.compilerargs" value="-bootclasspath ${var.JES_HOME}/jre/lib/rt.jar"/>
<property name="default.javac.source" value="1.7"/>
<property name="default.javac.target" value="1.7"/>
</target>
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Now I'm going to make a slight change in the Message string of HelloService.java:</span><br />
<br />
<pre class="brush: java">@Path("/")
public class HelloService {
public static final String MESSAGE = "Hello World from <b>ArduinoFX</b> Embedded Server!";
@GET
@Produces({MediaType.TEXT_HTML, MediaType.TEXT_PLAIN})
public String getText() {
return MESSAGE;
}
@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public HelloBean getTextAsBean() {
return new HelloBean(MESSAGE);
}
}
</pre>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Build the project, and send jar/war files to <span style="font-family: "Courier New",Courier,monospace;">/usr/java/jes7.0/samples/helloservice/dist/</span> in your Pi, by ssh. Please check <a href="http://jperedadnr.blogspot.com.es/2012/12/arduinofx-javafx-gui-for-home.html#installation">Installation</a> in the first post in case you haven't installed Java Embedded Suite on your Raspberry Pi yet.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwLqUDg4OPgPcXVET2QHf2ubiezHIT0pU7QyYjK50LZfslIkple2iZb83O_1JIvo8imP4XLrPGYbKpFQyyyBlSdM1EmjufXEM1JuXkVz-T5CPlURfoRCQ7A8XNExOEUm9BwMsqbgZosYA/s1600/ssh.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="275" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwLqUDg4OPgPcXVET2QHf2ubiezHIT0pU7QyYjK50LZfslIkple2iZb83O_1JIvo8imP4XLrPGYbKpFQyyyBlSdM1EmjufXEM1JuXkVz-T5CPlURfoRCQ7A8XNExOEUm9BwMsqbgZosYA/s1600/ssh.png" width="400" /></a></div>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Now on your Pi run the sample with the <span style="font-family: "Courier New",Courier,monospace;">gfhost </span>script and the path to the war file:</span><br />
<br />
<pre class="brush: java">cd /usr/java/jes7.0/samples/dist/run/
./gfhost.sh ../../helloservice/dist/helloservice.war
</pre>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzV3J7_EqObBnxj_hlBIWQtxW-wW92qvc24lFlVY0hcePUH7v0-7D46qlBE2O0vRm6YiAPRxJ_MlBEUaeYHLXK2GxTAxFF7TDf9tOFpVv7PKbmj8-Q1lHwZONTAEVihNZtFLasN18-ogU/s1600/piSample.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="387" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzV3J7_EqObBnxj_hlBIWQtxW-wW92qvc24lFlVY0hcePUH7v0-7D46qlBE2O0vRm6YiAPRxJ_MlBEUaeYHLXK2GxTAxFF7TDf9tOFpVv7PKbmj8-Q1lHwZONTAEVihNZtFLasN18-ogU/s1600/piSample.png" width="640" /></a></div>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Note it takes around one minute to deploy, so be patient.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Now invoke the service from your web browser. The first time it's called, takes around 15 seconds to respond, but subsequent calls are faster. </span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"></span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTE6nTzCuLp-REZv1lLXiWavbgDNiZZUn1_d_ImE_6YUpB6m7erG7jnFzTwx1_qJmUuGN-8VP-GhrWeUhfMepUd7UpkCza3418dBELe9tVmuuZpJVHxuRr_jaN4DGt9I5HuhFk_bImV5I/s1600/rest.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="131" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTE6nTzCuLp-REZv1lLXiWavbgDNiZZUn1_d_ImE_6YUpB6m7erG7jnFzTwx1_qJmUuGN-8VP-GhrWeUhfMepUd7UpkCza3418dBELe9tVmuuZpJVHxuRr_jaN4DGt9I5HuhFk_bImV5I/s1600/rest.png" width="320" /></a></div>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"></span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Finally, on the server, click Enter to undeploy.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"> </span><b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"> </span></b><br />
<b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">3. Embedded Server</span></b><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"></span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Now it's time to create from the scratch our server, with a little help of the provided samples.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">These are the steps you need to follow carefully in order for the server to run in your Pi with Java Embedded Suite 7.0.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">1. Copy </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">RXTXcomm-2.2pre2.jar</span> to </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">JES_HOME/jre/lib/ext</span> local folder. Do the same in the Pi, copying it to <span style="font-family: "Courier New",Courier,monospace;">/usr/java/jes7.0/jre/lib/ext</span>.</span></span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">2. In your Pi, edit <span style="font-family: "Courier New",Courier,monospace;">/usr/java/jes7.0/samples/dist/run/config.sh</span> and create this variable:</span><br />
<br />
<span style="font-family: "Courier New",Courier,monospace;">JES_EXT_CLASSPATH="$JES_HOME/jre/lib/ext/RXTXcomm-2.2pre2.jar"</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">and add it to the classpath:</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /><span style="font-family: "Courier New",Courier,monospace;">JES_CLASSPATH="$JES_JAVADB_CLASSPATH:$JES_GLASSFISH_CLASSPATH:</span></span><br />
<span style="font-family: "Courier New",Courier,monospace;"> $JES_JERSEY_SERVLET_CLASSPATH:$JES_EXT_CLASSPATH"</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">3. Now in Netbeans create a new Java SE application. I'll name it <span style="font-family: "Courier New",Courier,monospace;">Embedded</span>. Uncheck Create main class.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">4. Edit project properties. In Sources, change Source Packages to src/java. In Libraries, add the following jars: </span><br />
<ul>
<li><span style="font-family: "Courier New",Courier,monospace;">JES_HOME/javadb/lib/derby.jar</span></li>
<li><span style="font-family: "Courier New",Courier,monospace;">JES_HOME/glassfish/lib/glassfish-jes.jar</span></li>
<li><span style="font-family: "Courier New",Courier,monospace;">JES_HOME/jersey/lib/jsr311-api.jar</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">JES_HOME/jre/lib/ext/RXTXcomm-2.2pre2.jar</span></span></li>
</ul>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Finally, in Build/Packaging, uncheck Copy Dependency Libraries</span></span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">5. Create <span style="font-family: "Courier New",Courier,monospace;">Embedded/src/webapp</span> and </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">Embedded/src/webapp/WEB-INF</span> folders, and copy <span style="font-family: "Courier New",Courier,monospace;">web.xml</span> from <span style="font-family: "Courier New",Courier,monospace;">HelloSuite/src/webapp/WEB-INF</span>.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"> </span> </span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">6. Edit <span style="font-family: "Courier New",Courier,monospace;">build.xml</span> from HelloSuite sample, copy the four last targets, and paste them at the end of <span style="font-family: "Courier New",Courier,monospace;">Embedded/build.xml</span>. In this way <span style="font-family: "Courier New",Courier,monospace;">rt.jar</span> will be selected </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">from <span style="font-family: "Courier New",Courier,monospace;">JES_HOME</span> </span>to compile the project, and the war will be created. In the last two targets change <span style="font-family: "Courier New",Courier,monospace;">hellosuite.jar</span> for <span style="font-family: "Courier New",Courier,monospace;">embedded.jar</span> and <span style="font-family: "Courier New",Courier,monospace;">hellosuite.war</span> for <span style="font-family: "Courier New",Courier,monospace;">embedded.war</span>.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">7. Add a class with all the required JAX-RS web services, <span style="font-family: "Courier New",Courier,monospace;">com.jpl.embedded.EmbeddedREST</span>. Basically it should have two services: </span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Return the last reading of the sensor, </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">in json format.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Return an array of readings between two dates, in json format.</span></li>
</ul>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">8. Add a class <span style="font-family: "Courier New",Courier,monospace;">com.jpl.embedded.JAXRXConfig</span> to register this latter class:</span><br />
<br />
<pre class="brush: java">@ApplicationPath("/")
public class JAXRXConfig extends Application {
@Override public Set<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<Class<?>>();
// register root resource the first time there is a REST request
classes.add(EmbeddedREST.class);
return classes;
}
}
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">This servlet must be registered in the web.xml file, modifying the servlet from HelloSuite:</span><br />
<br />
<pre class="brush: java"><servlet>
<servlet-name>com.jpl.embedded.JAXRXConfig</servlet-name>
</servlet>
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">9. Finally, add a servlet <span style="font-family: "Courier New",Courier,monospace;">com.jpl.embedded.ConfigServlet</span>. It must be loaded just after deploying the project, so the database is created (only the first time) and the connection is established. Also we start a scheduled task to read the serial port, and storing the measures periodically. When the project is undeployed, it should stop this task and close the serial port.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">In order to load RXTXcomm, which uses native code, a system property must be set before starting the task:</span><br />
<br />
<pre class="brush: java">@WebServlet(urlPatterns={"/ArduinoOnline"}, loadOnStartup=0)
public class ConfigServlet extends HttpServlet {
static {
System.setProperty( "java.library.path", "/usr/lib/jni" );
}
private XBee xbee;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
// Initialize BDD
CStore.getInstance();
// Initialize Serial communication, starts reading port
xbee=new XBee();
}
@Override public void destroy() {
xbee.disconnect();
}
}
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">10. The rest of auxiliary classes we'll use are:</span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">com.jpl.embedded.comms.Serial</span>. Connect to serial port and read measures from Arduino, via XBee antennas.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">com.jpl.embedded.model.BeanHT</span>. JavaBean with temperature and humidity values and time of reading.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">com.jpl.embedded.model.CSensor</span>. Singleton with the most recent BeanHT read.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">com.jpl.embedded.service.CStore</span>. Singleton to open and close the connection to the database, and to write and read BeanHT values.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Courier New",Courier,monospace;">com.jpl.embedded.service.XBee</span>. Open and close the connection to the serial port, and start a scheduled task to record BeanHT values every 30 seconds in the database.</span></li>
</ul>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">All the source code for java classes mentioned in points 7 to 10 can be found in my <a href="https://github.com/jperedadnr/ArduinoFX-server">GitHub repository</a>. Feel free to clone it and use the code to test your own embedded server.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">So finally, we can compile and build the project. If everything is in place, we could send embedded.jar and war to <span style="font-family: "Courier New",Courier,monospace;">/usr/java/jes7.0/samples/embedded/dist</span> in the Pi by ssh. </span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">11. To deploy the server, in your Raspberry Pi: </span><br />
<br />
<pre class="brush: java">cd /usr/java/jes7.0/samples/dist/run/
./gfhost.sh ../../embedded/dist/embedded.war
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Note the deploy takes around one minute. After that, the servlet is initialized, so the database connection is established and the serial port connection is opened. The background task starts and the measures coming from the Arduino are read and stored in the database.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6_a-xJRtHw6xAoydmefzWxhP6dXmdlVpaNQ9GKIhMeVOYvcmpw-MMuRiMoBO253tS5E39t-7qISi2VindBkQ6BJoYCPslrxXm0iWBGXkO5RAaRoAAkft_XbG07jMe4kpAmO898vOABBg/s1600/embedded2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="435" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6_a-xJRtHw6xAoydmefzWxhP6dXmdlVpaNQ9GKIhMeVOYvcmpw-MMuRiMoBO253tS5E39t-7qISi2VindBkQ6BJoYCPslrxXm0iWBGXkO5RAaRoAAkft_XbG07jMe4kpAmO898vOABBg/s1600/embedded2.png" width="640" /></a></div>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">12. Now we can test the different web services, from a browser. </span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Test the servlet. It takes around 18 seconds to load, but only the first time. </span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjP4livtdsMEmC9Xknsax2ZiTf0gStWw0_G9-4qtCnddlKYf6_NOa5asC9yqOOcZyhJ0YPRNFuyj9H1tqKx98j-mLuDX3oDbpj-1y0v_AL0GIzXHy0cWFLuLh5FLjZob5vT2x0iOn9AIbw/s1600/embedded3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="282" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjP4livtdsMEmC9Xknsax2ZiTf0gStWw0_G9-4qtCnddlKYf6_NOa5asC9yqOOcZyhJ0YPRNFuyj9H1tqKx98j-mLuDX3oDbpj-1y0v_AL0GIzXHy0cWFLuLh5FLjZob5vT2x0iOn9AIbw/s1600/embedded3.png" width="400" /></a></div>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Test the RESTful web services. The first run takes around 80 seconds to complete.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgA7fFl9rwf7u5qlx4DrNDy-9FxQMndKbeJUOz027yqO9Li17PR-qbW_L0tkeGsVAZZ_4Plaz_X_VT2FFe3jwnWzp0VRweyR4V6sZLkjAw6-UboE6PZaJz8SumS9uMlkSGh69YJkNe5L9s/s1600/embedded4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="109" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgA7fFl9rwf7u5qlx4DrNDy-9FxQMndKbeJUOz027yqO9Li17PR-qbW_L0tkeGsVAZZ_4Plaz_X_VT2FFe3jwnWzp0VRweyR4V6sZLkjAw6-UboE6PZaJz8SumS9uMlkSGh69YJkNe5L9s/s1600/embedded4.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYGpSkZaqOevePwD8vLfV0Ag4i_4MLyyQvS_WKaYO3WT1wsvH1je_HlCMbSCw3DNasbQ9VGoDvshV898sXXDsxlPfnJG4iZq_XxPJL8KVVBOAMSSpQcyh-zyHKgyQH4Jk2LNTZ4H3O0Rs/s1600/embedded5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="84" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYGpSkZaqOevePwD8vLfV0Ag4i_4MLyyQvS_WKaYO3WT1wsvH1je_HlCMbSCw3DNasbQ9VGoDvshV898sXXDsxlPfnJG4iZq_XxPJL8KVVBOAMSSpQcyh-zyHKgyQH4Jk2LNTZ4H3O0Rs/s1600/embedded5.png" width="320" /></a></div>
<br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Finally, to undeploy the server, just press <Enter> twice on the terminal. All the tasks will be smoothly stopped, and serial port and database connections will be closed.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTADFrc6LUiOJW-OYtbfmcqjCdyHgOw6QvJALY-A9GD-BwfayBQ2NiZNt87kCd3CrA-DPEfctgnH8wDgezwB8tEbmJpZ-rE48wt6x-N27moVtevMu6g8JUqbbh52C2Sd6CIccPOVjM8ME/s1600/embedded6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="435" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTADFrc6LUiOJW-OYtbfmcqjCdyHgOw6QvJALY-A9GD-BwfayBQ2NiZNt87kCd3CrA-DPEfctgnH8wDgezwB8tEbmJpZ-rE48wt6x-N27moVtevMu6g8JUqbbh52C2Sd6CIccPOVjM8ME/s1600/embedded6.png" width="640" /></a></div>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">4. The JavaFX GUI</span></b><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Now we are going to design the JavaFX based client, a simple UI with three main tasks:</span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"> </span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Connect to the embedded server to get last measures and show them in a MatrixPanel control.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Show the status of the connection anytime.</span> </span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Connect to the server to get a list of measures between two dates, showing them in a chart. </span></li>
</ul>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><b>User Interface</b> </span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">First, let's start by describing the user interface. With the help of the JavaFX Scene Builder the basic layout is done.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCXbNWgWOBdHb8jXBfJyVavvgBUEBGi-ITETpVDOHe_LRJ9ReLOWyLw9E8S_2YlJrbBqXEWNuT49U2yHvyhn2yImfLnVtqfYqMQW0ouNEP-Vc0PmxmCgukrCrqbB8QHuwvqi2U_AHg1ZU/s1600/fxml.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="401" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCXbNWgWOBdHb8jXBfJyVavvgBUEBGi-ITETpVDOHe_LRJ9ReLOWyLw9E8S_2YlJrbBqXEWNuT49U2yHvyhn2yImfLnVtqfYqMQW0ouNEP-Vc0PmxmCgukrCrqbB8QHuwvqi2U_AHg1ZU/s1600/fxml.png" width="640" /></a></div>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">There're three main boxes, ready to hold several custom controls </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">from the <a href="http://jfxtras.org/">JFXtras</a> project</span>, that will be added from the controller:</span><br />
<ul>
<li>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">A top VBox for the <b>MatrixPanel </b>control. This LED panel like control will show the last values of temperature and relative humidity read from the server every 30 seconds.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">A central HBox for the <b>CalendarTextField</b> and ChoiceBox controls. These controls will allow the user to choose an initial date and an ending date to ask for the values stored in the database in the server. The ChoiceBox allows you to select the number of items readed between these two dates.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">A bottom HBox for the <b>SimpleIndicator</b>. It will simply show if the connection is established to the server (green) or not (red).</span></li>
</ul>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">In the controller class, these controls are first created, and later are added when the initialize method is called.</span><br />
<pre class="brush: java">
private final MatrixPanel animatedPanel =
MatrixPanelBuilder.create()
.ledWidth(192).ledHeight(18)
.prefWidth(650.0).prefHeight(400.0)
.frameDesign(FrameDesign.DARK_GLOSSY)
.frameVisible(true)
.build();
private final CalendarTextField lStartCalendarTextField =
new CalendarTextField().withShowTime(true);
private final CalendarTextField lEndCalendarTextField =
new CalendarTextField().withShowTime(true);
private final SimpleIndicator indicator = SimpleIndicatorBuilder.create()
.prefHeight(40).prefWidth(40)
.innerColor(Color.rgb(0,180,0).brighter())
.outerColor(Color.rgb(0,180,0).darker())
.build();
@Override
public void initialize(URL url, ResourceBundle rb) {
vbox.getChildren().add(0,animatedPanel);
hbox.getChildren().addAll(lStartCalendarTextField,lEndCalendarTextField);
hbox.getChildren().add(sizeChoiceBox);
hboxStatus.getChildren().add(0, indicator);
...
}
</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">So when the application is launched, it will look like this:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6jUk-VBPMDMzCm53GB0HRkTb9ig64V1hwditOck4auqa_b6cxfsF-Vg-STZcAw9iOeMGtslfKxw5ohny4T_uBnLwbd_f6Bh_dJjZtWQccz5pMyfN9Ab5mBO1QwhVgir8TZ96KSvkku1I/s1600/fxml2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="499" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6jUk-VBPMDMzCm53GB0HRkTb9ig64V1hwditOck4auqa_b6cxfsF-Vg-STZcAw9iOeMGtslfKxw5ohny4T_uBnLwbd_f6Bh_dJjZtWQccz5pMyfN9Ab5mBO1QwhVgir8TZ96KSvkku1I/s1600/fxml2.png" width="640" /></a></div>
<br />
<b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Rest services</span></b><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">When the application starts, a scheduled task is launched with the purpose of repeatedly ask the server for the last values of temperature and humidity. This values are then passed to the content of the matrixpanel.</span><br />
<br />
<pre class="brush: java"> private long EVENT_CYCLE = 30000; // ms
private final ScheduledExecutorService scheduler =
Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture scheduleAtFixedRate = null;
@Override
public void start(Stage stage) throws Exception {
scheduleAtFixedRate = scheduler.scheduleAtFixedRate(new LastHT(), 0,
EVENT_CYCLE, TimeUnit.MILLISECONDS);
...
}
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Where <span style="font-family: "Courier New",Courier,monospace;">LastHT </span>class perform a web request to <span style="font-family: "Courier New",Courier,monospace;">http://<IP>:<PORT>/embedded/last</span> REST service and reads the last measured values from response in json format, deserializing it to a <span style="font-family: "Courier New",Courier,monospace;">BeanHT</span> object.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">This object is then stored, and if any of temperature or humidity have changed, the matrixpanel content is updated. For that, a JavaFX thread must be used.</span><br />
<pre class="brush: java">
Platform.runLater(new Runnable() {
@Override
public void run() {
Content contentTemp = ContentBuilder.create()
.color(MatrixColor.GREEN)
.type(Type.TEXT)
.txtContent("Temperature: " + String.format("%.1f",
MonitoringServiceStub.getInstance().getLastMeasure().getTemp()) + " ºC")
.font(MatrixFont.FF_8x16).fontGap(Gap.SIMPLE)
.origin(0, 1).area(0, 0, 191, 18)
.align(Align.RIGHT).effect(Effect.SCROLL_LEFT)
.lapse(20).postEffect(PostEffect.PAUSE)
.pause(3000).order(RotationOrder.FIRST)
.build();
...
animatedPanel.setContents(Arrays.asList(contentTemp, contentHum));
}
});
</pre>
<br />
<a name="service"></a><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">When the user clicks on the Evaluation button, the request for a list of values between two dates starts. But since the server is a tiny computer with low CPU power, it will take some time to be performed. For that reason, we need to set this as a task from a service.</span><br />
<br />
<pre class="brush: java">Service<void> chartService=new Service<void>(){
@Override
protected Task<void> createTask() {
return new Task<void>(){
@Override
protected Void call() throws Exception {
ChartHT ht=new ChartHT(
lStartCalendarTextField.getValue().getTimeInMillis(),
lEndCalendarTextField.getValue().getTimeInMillis(),
sizeChoiceBox.getSelectionModel().getSelectedItem().toString());
ht.run();
return null;
}
};
}
};
@FXML
public void getEvolution(ActionEvent a){
chartService.restart();
}
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">The way we will know the task has been completed (maybe a minute later) is looking for changes in the <span style="font-family: "Courier New",Courier,monospace;">stateProperty</span> of the service. </span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">When its value reach <span style="font-family: "Courier New",Courier,monospace;">State.SUCCEEDED</span>, we can proceed with creating the chart and plotting the data deserialized from the json array returned by the server, again in a new JavaFX thread:</span><br />
<br />
<pre class="brush: java">chartService.stateProperty().addListener(new ChangeListener<state>(){
@Override
public void changed(ObservableValue ov, State t, State t1) {
if(t1==State.SUCCEEDED) {
Platform.runLater(new Runnable() {
@Override
public void run() {
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
final LineChart<String,Number> lineChart = new
LineChart<String,Number>(xAxis,yAxis);
XYChart.Series series1 = new XYChart.Series();
series1.setName("Temperature (ºC)");
XYChart.Series series2 = new XYChart.Series();
series2.setName("Relative Humidity (%)");
for(IObservableMeasure d :
MonitoringServiceStub.getInstance().getChartMeasures()){
series1.getData().add(new XYChart.
Data(d.getTime(),d.getTemp()));
series2.getData().add(new XYChart.
Data(d.getTime(),d.getHum()));
}
lineChart.getData().addAll(series1,series2);
hbox2.getChildren().add(1,lineChart);
}
});
}
}
});
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">All the source code of this project is in my <a href="https://github.com/jperedadnr/ArduinoFX-client">GitHub repository</a>. Feel free to clone it, and test it. Once you have installed the embedded server in your Raspberry Pi, you will need to set the IP address and port in the client:</span><br />
<br />
<pre class="brush: java">public class MonitoringServiceStub implements MonitoringService {
/* SET YOUR SERVER IP AND PORT HERE */
public static final String urlServer="http://192.168.0.39:8080";
...
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Finally, this is a short video of the JavaFX client in action.</span><br />
<br />
<div style="text-align: center;">
<span style="font-family: Verdana,sans-serif;"><video controls="controls" poster="https://dl.dropbox.com/u/27006338/ArduinoFX2.mp4" tabindex="0" width="500">
<source src="https://dl.dropbox.com/u/27006338/ArduinoFX2.mp4" type="video/mp4; codecs="avc1.42E01E, mp4a.40.2""></source>
<source src="https://dl.dropbox.com/u/27006338/ArduinoFX2.webm" type="video/webm; codecs="vp8, vorbis""></source>
Your browser does not support the video tag, view this video <a href="https://dl.dropbox.com/u/27006338/ArduinoFX2.mp4">here</a>.<br />
</video></span></div>
<br />
<b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Conclusion</span></b><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Here I conclude this new proof of concept. As
a recap, we have communicated an Arduino with a RHT03 temperature and relative
humidity sensor and a XBee antenna, wirelessly to a second XBee antenna, plugged via a
XBee Explorer to a Raspberry Pi USB port.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">In
the Raspberry Pi, with soft-float Debian wheezy distro, we have
installed Java Embedded Suite 7.0. We have created an embedded server in the Pi, with the following main tasks: </span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Back-end task: it repeatedly reads the serial port to get the measures from the Arduino and store them in a database.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Web services: it responds to web requests returning the current values measured.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">RESTful web services: it responds to REST requests returning lists in json format with the data measured between two dates.</span></li>
</ul>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">And then we have created the JavaFX based client application, that mainly:</span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Connects to the server to get last measures and show them in a LED Matrix Panel control.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Connects to the server to get a list of measures between two dates, showing them in a chart. </span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Shows the status of the connection anytime.</span></li>
</ul>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">If
you have followed this two part series post, I hope it has caught your
attention, and you're willing to give it a try. Let me say here:<b> PLEASE, DO IT AT HOME!!</b> </span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">I'd love to hear from you if you have any questions regarding the setup of this project, or any part of it. </span><br />
<br />Anonymoushttp://www.blogger.com/profile/15247383133952423416noreply@blogger.com5tag:blogger.com,1999:blog-7114525940495426773.post-21386627495009835092012-12-13T19:15:00.000+01:002013-10-24T17:53:59.450+02:00ArduinoFX: A JavaFX GUI for Home Automation with Raspberry Pi and Arduino. Part I<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Hi there! </span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">I'll make a little break to the <a href="http://jperedadnr.blogspot.com.es/2012/11/nxtlegofx-javafx-based-application-to.html">NXTLegoFX</a> series posts, and blog about a very similar application I've made the last week: <b>ArduinoFX</b>. It's a JavaFX based simple GUI for remote monitoring and control of sensors, intended mainly for home automation. Though, to be fair, it's mainly a proof of concept.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">More or less, both JavaFX applications share the same architecture, though the difference is now the back-end: </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">instead of</span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"> the Mindstorms Lego NXT now we've got an <a href="http://www.arduino.cc/en/Main/arduinoBoardUno">Arduino Uno</a> with a low cost digital humidity and temperature sensor, </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><a href="https://www.sparkfun.com/products/10167"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">RHT03</span></a>.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Also, as a server, instead of a regular computer, I'll be using </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">a <a href="http://www.raspberrypi.org/">Raspberry Pi</a></span></span>, a single-board tiny computer with an ARM1176JZF-S CPU, powered with the new <a href="http://www.oracle.com/us/technologies/java/embedded/suite/overview/index.html">Java Embedded Suite 7.0</a>, just released by <a href="http://www.oracle.com/index.html">Oracle</a> in October 2012.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">And for comunnications, instead of leJOS and Bluetooth, Arduino and Raspberry Pi will be connected wirelessly by two <a href="http://www.digi.com/products/wireless-wired-embedded-solutions/zigbee-rf-modules/zigbee-mesh-module/xbee-zb-module#overview">XBee</a> antennas, using <a href="http://www.zigbee.org/">ZigBee</a> protocol.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">I'll try to describe briefly each and everyone of this components, focusing in how to install what's needed, and with each one ot them I'll make a simple test, so this could serve as a guide for thouse of you who want to give it a try.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">This first part deals with all the hardware stuff, and the basic software required to work out, and I leave for Part II the JavaFX application and the embedded server development. </span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><i>Disclaimer: I'm not responsible in any way for what you do on your spare time with what I say here! The following procedures are provided without any warranty. Also, you should know by now that I'm not related to any of the companies mentioned in this blog.</i> </span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><b>Bill of Materials</b></span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Main components requiered to follow this guide:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU6GmqnraVm1viK031IkS7bn09A_5Ju0WE3Es9hIKU24xw8vxpPgNLFBXx0FQXhgvMisn0tNIAuzLo9F-Lu3WOiboDMl2FP8j32D1t1rrkCk4L68lW8pOUGOFFyZ3b7YtIGUX7X6_58U8/s1600/listMaterials.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU6GmqnraVm1viK031IkS7bn09A_5Ju0WE3Es9hIKU24xw8vxpPgNLFBXx0FQXhgvMisn0tNIAuzLo9F-Lu3WOiboDMl2FP8j32D1t1rrkCk4L68lW8pOUGOFFyZ3b7YtIGUX7X6_58U8/s1600/listMaterials.png" height="560" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_kkBHideOc_5o0Trz1HOvVm2nCy8kwMb19UdnoLNFNgjtI0Mts3W2oacmJ9PFPfcy9BrnbXOPgFZbUpTOej8Jp-WhkMtr3IPCIOob6d8Vs_e7_ktOvAGZPbPN5gKFQ_fk9bkM9JkiiXw/s1600/listMaterials.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"></span><br />
<b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">1. Arduino</span></b><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">I'm using an Arduino Uno, revision 3, a microcontroller board based on the ATmega328, with 14 digital input/output pins, 6 analog</span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"> input pins, USB connection, power jack, operating at 5 V, with 32 kB of flash memory and a 16 MHz clock speed. </span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">It can be programmed easily with its own <a href="http://arduino.cc/en/Main/Software">IDE</a>. Its language is an implementation of <a href="http://wiring.org.co/">Wiring</a>. Programs are written in a simplified version of the C++ language, although only two main functions are needed to make a runnable program: <span style="font-family: "Courier New",Courier,monospace;">setup(), </span>a function run once at the start of a program, and <span style="font-family: "Courier New",Courier,monospace;">loop(), </span>a function called repeatedly until the board is powered off. </span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">For monitoring temperature and relative humidity values in my room, I have a DHT22 sensor, also known as RHT03. You can find <a href="http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Sensors/Weather/RHT03.pdf">here</a> the schematics and <a href="https://github.com/nethoncho/Arduino-DHT22">here</a> an open source library to read these values. You must install it in the <span style="font-family: "Courier New",Courier,monospace;">libraries</span> folder of Arduino IDE, before compiling your project.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Using a breadboard, connect pin 1 of the sensor to the 5 V pin of Arduino board. Pin 2 goes to Arduino pin 7, using a 1 kΩ pull-up resistor. Pin 4 goes to Arduino board ground. There're few blogs out there where you can find more detailed instructions, like <a href="http://garagelab.com/profiles/blogs/tutorial-humidity-and-temperature-sensor-with-arduino">this</a>. </span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Let's
add now a XBee antenna, so the data can be send remotely to a second
antenna plugged in another Arduino or in a XBee Explorer plugged in the
USB port of a computer. To connect the antenna with the Arduino I'll use an Arduino XBee Shield, from <a href="http://www.cooking-hacks.com/index.php/shop/arduino/shields/communication-shield-xb-bt-rfid.html">Libellium</a>, though you can use other similar shields, or even plug the XBee on the breadboard through a breakout board.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Here it's the schematics done with the open-source </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">tool </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><a href="http://www.fritzing.org/">Fritzing</a>. The XBee is plugged on to the shield, and this is plugged on Arduino (according to the dotted purple line in the picture below). Note also the wire from pin 2 is reconnected through pin 7 on the shield.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Important tips (that work for me, at least): </span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Never plug/unplug the shield with the Arduino powered on. </span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Always power off, and then unplug the shield <i>before</i> loading the code to the Arduino. </span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Then plug the board to the USB port, download the code, and unplug the USB. </span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Finally, plug the shield and then power on. </span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Remember that the jumpers on the shield must be on the XBee pins (on the two pins towards the interior of the board, see the picture below).</span></li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOYAT1jvUvERzkqknB_Sh4IznXOzYIeer6Z_eFpVrseGRpIAHbPS4hjngnrVV7qMtZFX5qoIBapU6Dl3IM9KVR-GxgxvvLNUYCJask34FMDhOfn6SyEgXXdEhSOejI3PDx8bqk1vH56rw/s1600/RHT03_bb.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOYAT1jvUvERzkqknB_Sh4IznXOzYIeer6Z_eFpVrseGRpIAHbPS4hjngnrVV7qMtZFX5qoIBapU6Dl3IM9KVR-GxgxvvLNUYCJask34FMDhOfn6SyEgXXdEhSOejI3PDx8bqk1vH56rw/s1600/RHT03_bb.png" height="330" width="400" /></a></div>
<div style="text-align: center;">
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
</div>
<div style="text-align: left;">
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">And this is how I did it.</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhp8W-b-6gkoWQA8-2R6UFj88HD_qlwIT_5ecysy22W2-YbieFBbZvlifVjIFmVJfvEOnxsJC86LTMEMZcOigitR4FO6tjbxnogHd8qZ6lDnVlj64wph9sYNfmjBXfyG64z0fLolOvfTYg/s1600/arduinoXBee.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhp8W-b-6gkoWQA8-2R6UFj88HD_qlwIT_5ecysy22W2-YbieFBbZvlifVjIFmVJfvEOnxsJC86LTMEMZcOigitR4FO6tjbxnogHd8qZ6lDnVlj64wph9sYNfmjBXfyG64z0fLolOvfTYg/s1600/arduinoXBee.png" height="300" width="400" /></a></div>
<br />
<b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Code </span></b><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">This is the code to make the Arduino microcontroller print the values of the measures taken by the sensor through the serial port:</span><br />
<br />
<pre class="brush: java">#include <dht22.h>
#include <stdio.h>
// Data wire is plugged into port 7 on the Arduino
#define DHT22_PIN 7
// Setup a DHT22 instance
DHT22 myDHT22(DHT22_PIN);
char buf[128];
void setup(void)
{
Serial.begin(9600);
Serial.println("DHT22 Library Loaded");
}
void loop(void)
{
DHT22_ERROR_t errorCode;
// minimum 2s warm-up after power-on, then read every 2 seconds.
delay(2000);
errorCode = myDHT22.readData();
switch(errorCode){
case DHT_ERROR_NONE:
sprintf(buf, "{%hi.%01hi,%i.%01i}",myDHT22.getTemperatureCInt()/10,
abs(myDHT22.getTemperatureCInt()%10),
myDHT22.getHumidityInt()/10, myDHT22.getHumidityInt()%10);
Serial.println(buf);
break;
case DHT_ERROR_CHECKSUM: Serial.print("check sum error "); break;
case DHT_BUS_HUNG: Serial.println("BUS Hung "); break;
case DHT_ERROR_NOT_PRESENT: Serial.println("Not Present "); break;
case DHT_ERROR_ACK_TOO_LONG: Serial.println("ACK time out "); break;
case DHT_ERROR_SYNC_TIMEOUT: Serial.println("Sync Timeout "); break;
case DHT_ERROR_DATA_TIMEOUT: Serial.println("Data Timeout "); break;
case DHT_ERROR_TOOQUICK: Serial.println("Polled to quick "); break;
}
}
}
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">To download the compiled code, just open the Arduino IDE, create a new sketch, paste the code and verify it. Then plug the Arduino without the shield, and without any wires connected, and select your serial port in Tools->Serial Port. Then download the compiled code, and unplug the USB cable.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi78ld4_VVNCrYoCY40hqsH3PQ1DzIQbX9HRJAWb2jDH-pcPboktt5TUZucZmplSv2UYd-dZ1rtmby4rwbyb4ULQ_Ma5vDxZUyVzp0bFPKiz4kZ-FAP5Fe3O0P9SGEe1ms9JeYGvrMNMoQ/s1600/arduino1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi78ld4_VVNCrYoCY40hqsH3PQ1DzIQbX9HRJAWb2jDH-pcPboktt5TUZucZmplSv2UYd-dZ1rtmby4rwbyb4ULQ_Ma5vDxZUyVzp0bFPKiz4kZ-FAP5Fe3O0P9SGEe1ms9JeYGvrMNMoQ/s1600/arduino1.png" height="640" width="531" /></a></div>
<br />
<span style="color: blue;"><b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Test #1</span></b></span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Let's make a simple test, before going any further, to check the Arduino works properly. Connect the sensor but not the shield nor the XBee, so sensor's pin 2 goes straight to pin 7 in the Arduino board. Plug again the USB cable, and in the Arduino IDE go to Tools->Serie Monitor, and you should see the measures appearing on the screen:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1kV5OutOvPnm_KVjrEB_MpiwUVyR0_PzYjBcYNvseVIQzU9yCCghxti-HeQb20b8Gz6w8ITv0VF3kwqXvHN25x3VZbeOHVKhECtn9bKtKhfui5dN5XYyFoapVXshDtTBb4zfA2L-HfMw/s1600/arduino2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1kV5OutOvPnm_KVjrEB_MpiwUVyR0_PzYjBcYNvseVIQzU9yCCghxti-HeQb20b8Gz6w8ITv0VF3kwqXvHN25x3VZbeOHVKhECtn9bKtKhfui5dN5XYyFoapVXshDtTBb4zfA2L-HfMw/s1600/arduino2.png" height="283" width="400" /></a></div>
<br />
<b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">2. <a href="http://www.blogger.com/blogger.g?blogID=7114525940495426773" name="xbee">XBee</a></span></b></div>
<div style="text-align: left;">
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">In order for the antennas to be connected, it is neccessary to establish a ZigBee network between them. For that, Digi International software <a href="http://www.digi.com/support/productdetail?pid=3352&type=utilities">X-CTU</a> is required (only works in Windows, for other OS see <a href="https://github.com/sparkfun/xbeeconfigure">xbeeconfigure</a> sketch). </span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Also, it's recommended to use a <a href="https://www.sparkfun.com/products/8687">XBee Explorer</a>, so just by attaching a mini USB cable you will have direct access to the serial and programming pins on the XBee unit. Otherwise, you'll have to use the access through the Arduino board, but changing the jumpers in the shield to the USB position and removing the microcontroller, which is always a little bit risky.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcAv4k8_Xrpt7APkTO7Z_Ih0zSVvSy6Qdpja83wEUjFcoJpVTQb6Sz-sFEd4fcI7WFgz3XZ-NNFyyWlj8nEOUHSCU_2g747DalCJ4pl-lvLyyscLDdSIUNdpiOP3-bvwt0iwnIq5bq0do/s1600/explorerXBee.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcAv4k8_Xrpt7APkTO7Z_Ih0zSVvSy6Qdpja83wEUjFcoJpVTQb6Sz-sFEd4fcI7WFgz3XZ-NNFyyWlj8nEOUHSCU_2g747DalCJ4pl-lvLyyscLDdSIUNdpiOP3-bvwt0iwnIq5bq0do/s1600/explorerXBee.png" height="185" width="320" /></a></div>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Anyway, for our project we'll need this Explorer, as we intend to connect it to the Raspberry Pi USB port.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">There are quite good blogs out there showing how to configure both antennas (like <a href="http://www.cooking-hacks.com/index.php/documentation/tutorials/arduino-xbee-shield">this</a> or <a href="http://www.seeedstudio.com/wiki/Zigbee_Networking_with_XBee_Series_2_and_Seeed%27s_Products">this</a><span id="goog_448080465"></span><span id="goog_448080466"></span>), so I'm going to give just a short description of how I do it.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><b>Coordinator XBee #1</b></span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">First, with the XBee #1 in the Explorer, plugged to the USB port, </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">open the X-CTU application and select the correct USB serial port from the list displayed in the PC-Settings tab.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Then, go to <span style="font-family: "Courier New",Courier,monospace;">Modem Configuration</span> tab, uncheck <span style="font-family: "Courier New",Courier,monospace;">Always update firmware</span> and click on <span style="font-family: "Courier New",Courier,monospace;">Read</span> button and wait a few seconds. A valid name should appear under <span style="font-family: "Courier New",Courier,monospace;">Modem </span>label. Otherwise, click on <span style="font-family: "Courier New",Courier,monospace;">Download new versions</span> button, and try again.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Once you've got a right modem (in my case XB24-ZB), on the function set list, select Zigbee Coordinator AT. See left picture below. This one should be afterwards plugged to the Raspberry Pi.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">A few parameters must be set:</span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Networking, ID: Pan ID. The network ID, 0 to 0xFFFF. For instance, <i>1111</i></span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Addressing. NI: Node Identifier, for instance <i>COORDINATOR</i></span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Addressing. DL: Destination Address Low: <i>0</i> </span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Serial Interfacing. BD Baud Rate, <i>9600</i></span></li>
</ul>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"></span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">then click Write button, and these parameters will be stored in your XBee #1. Close X-CTU and unplug the Explorer.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzJhgRwSQjm0bsv4pVI5ozFIGpRel8M3BhWlRWA3yT67USLpG9qNY10_lQZLMzbZH2L8_gmhlDXtaFw8Q7fACO7JpFnNUYer_CH3JY7xw6YOFk_VD5M-UtuXYdryPWw5OnfEJQnABMyWc/s1600/xctu2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzJhgRwSQjm0bsv4pVI5ozFIGpRel8M3BhWlRWA3yT67USLpG9qNY10_lQZLMzbZH2L8_gmhlDXtaFw8Q7fACO7JpFnNUYer_CH3JY7xw6YOFk_VD5M-UtuXYdryPWw5OnfEJQnABMyWc/s1600/xctu2.png" height="404" width="640" /></a></div>
<div style="text-align: center;">
<br /></div>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"></span>
<b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Router XBee #2</span></b><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Plug on the Explorer the second XBee, and start again X-CTU. Choose the serial port and read the modem parameters. Now select Zigbee Router AT and set these parameters:</span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Networking, ID: Pan ID. The network ID, the same as #1: <i>1111</i></span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Addressing. NI: Node Identifier, for instance <i>ARDUINO</i></span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Addressing. DL: Destination Address Low: <i>0</i> </span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Serial Interfacing. BD Baud Rate, <i>9600</i></span></li>
</ul>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Check that the Operating Channel is the same for both (CH=<i>19</i>). T</span>hen
click Write button, and these parameters will be stored in your XBee
#2. See right picture above. Close X-CTU and plug this anntena on the Arduino shield.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Note: You can use also AT commands in the <span style="font-family: "Courier New",Courier,monospace;">Terminal </span>Tab, just by entering <span style="font-family: "Courier New",Courier,monospace;">+++</span> and waiting for an OK response. Then you can get or set the usual parameters, with AT and the parameter code: ATID, ATNI, ATDL, an so on.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Though this configuration is pretty simple, it gives you the idea of how you could set a mesh of nodes around the same coordinator, in case you have several arduinos around your house, with their own XBee antenna.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnW1-dGQsCAbpyxs5NlMYYg-E2a4LlzEue4VF-qOU_vzFV2NKtrYRW2C5h8ccx5utXOI6jtD5QMpFR-XG436UOUYuIZCauZlKMXppVB5-_iAOE6nzJL4jHJJ5G5V1ua1y1pqQ25U0ORj4/s1600/zigbee.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnW1-dGQsCAbpyxs5NlMYYg-E2a4LlzEue4VF-qOU_vzFV2NKtrYRW2C5h8ccx5utXOI6jtD5QMpFR-XG436UOUYuIZCauZlKMXppVB5-_iAOE6nzJL4jHJJ5G5V1ua1y1pqQ25U0ORj4/s1600/zigbee.png" height="315" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFSjmSjecBAwgBPZzuM_qUv5rDLiZFVYjCOoJx_SFsC-evKegeffEdLZtDyhLC1GGQbUp8Eh-l5c9EoawRHgIgk9wmXSs0b1UlM0l1OiqomDe2i6Vt8cuM-cldp9ij2u0eY-awi17qIM0/s1600/zigbee.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><br /></a></div>
<div style="text-align: center;">
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"> <i><span style="font-size: x-small;">Source: http://www.rfwireless-world.com/Tutorials/Zigbee_tutorial.html</span></i></span></div>
<span style="color: blue;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span><b>
</b></span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="color: blue;"><b>Test #2</b></span> </span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">So let's now check if the ZigBee network is working properly. Let's plug the shield with the XBee #2 on the Arduino board, check the RHT03 connections (its pin 2 now goes through pin 7 in the shield) and </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">power on the Arduino</span>. Then plug the XBee #1 via Explorer to the PC and start X-CTU, select serial port, read the modem and go to Terminal tab to see the readings that should appear:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-NppsFw5GM7C5lI972t5gjc_H3ZIXbW64HxSKNdFzX9Q9ucxo0Y8-hG-EnAuJHj2UBdxKVgtlVTcmNbj-JRIpoFAAM5NlHDfveYUF6pfXsofIXUy3J1UkLnKt1xaU8J0lH0VwNzVaFJ0/s1600/xctu4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-NppsFw5GM7C5lI972t5gjc_H3ZIXbW64HxSKNdFzX9Q9ucxo0Y8-hG-EnAuJHj2UBdxKVgtlVTcmNbj-JRIpoFAAM5NlHDfveYUF6pfXsofIXUy3J1UkLnKt1xaU8J0lH0VwNzVaFJ0/s1600/xctu4.png" height="180" width="320" /></a></div>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Otherwise, please check carefully the previous steps. Note that instead of X-CTU you can use any </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"></span>hyperterminal like software in your OS.</span><br />
<br />
<b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">3. <a href="http://www.blogger.com/null" name="raspi">Raspberry Pi</a></span></b><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">First of all, we need to set the Raspberry Pi. For that you can follow this <a href="http://elinux.org/RPi_Hardware_Basic_Setup">guide</a>, or <a href="http://www.raspberrypi.org/quick-start-guide">this</a>. Briefly, you need:</span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">A Raspberry Pi model B (with 256 or 512 MB RAM)</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">A USB keyboard and USB mouse (optional)</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">A display with HDMI, a HDMI cable, or a display with DVI and a HDMI to DVI conversor</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">A power supply of 5 V and minimum 0.7 A, with micro USB connector (some mobile chargers will do).</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">And a SD card, 4 or 8 GB, not-generic, class 6 to 10 recommended.</span></li>
</ul>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">In order to install Java Embedded, for now we must use soft-float Debian wheezy image. Download it from <strike><a href="http://downloads.raspberrypi.org/images/debian/7/2012-08-08-wheezy-armel/2012-08-08-wheezy-armel.zip">here</a></strike> <a href="http://www.mirrorservice.org/sites/downloads.raspberrypi.org/images/debian/7/2012-08-08-wheezy-armel/2012-08-08-wheezy-armel.zip">here</a>, extract it and write it to the SD with <a href="https://launchpad.net/win32-image-writer/+download">Win32DiskImager</a> for Windows. For other OS please check <a href="http://elinux.org/RPi_Easy_SD_Card_Setup#Easy_way">here</a>.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Once the SD card is ready, insert it in the socket below the Raspberry Pi, connect the display and the keyboard and power on. Then it should boot up, and enter in the raspi-config menu, where we are going to config a few things (look <a href="http://elinux.org/RPi_raspi-config">here</a> for a further explanation):</span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">expand-rootfs: Press ok to use the whole SD card space.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">change_locale: from en_GB.UTF8 to es_ES.UTF8 in my case, select yours.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">change_timezone: Select continent (Europe) and major city (Madrid, in my case). </span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">memory_split: you can give only 32MB to video, leaving the rest for the ARM, as in this case we're developing a server. You can change this later on.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">ssh: Press enable to allow ssh connections.</span></li>
</ul>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Select Finish and the Pi will reboot.</span><br />
<br />
<b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Network</span></b><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Now let's setup the network. My advise is that you set a static IP address. For that (look <a href="http://elinux.org/RPi_Setting_up_a_static_IP_in_Debian">here</a> for further instructions), edit with root privileges this file:</span><br />
<br />
<pre class="brush: java">sudo nano /etc/network/interfaces
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">And change dhcp for static, adding your IP and LAN data configuration. This is mine:</span><br />
<br />
<pre class="brush: java">auto lo
iface lo inet loopback
iface eth0 inet static
address 192.168.0.39
netmask 255.255.255.0
gateway 192.168.0.1
</pre>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
</div>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Save (Ctrl+O) and exit (Ctrl+X). Now edit this other file:</span><br />
<br />
<pre class="brush: java">sudo nano /etc/resolv.conf
</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">and add your DNS nameservers. This is my data:</span><br />
<br />
<pre class="brush: java">nameserver 192.168.0.1
nameserver 62.42.230.24
nameserver 62.42.63.52
</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Save and exit, reboot and plug in the ethernet cable. Now we're able to install software from the repository, like a VNC server, so we can access remotely to the Pi headless, without display, keyboard nor mouse:</span><br />
<br />
<pre class="brush: java">sudo apt-get install tightvncserver
</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">After it has been installed, type:</span><br />
<br />
<pre class="brush: java">sudo /usr/bin/tightvncserver
</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">When prompted, select a password for remote users. Allow it to start at boot time, editing the file: </span><br />
<br />
<pre class="brush: java">sudo nano /etc/rc.local
</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">At the end type:</span><br />
<br />
<pre class="brush: java">su -c "/usr/bin/tightvncserver -geometry 1280x1024" pi
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Save, exit and reboot. Now from your favourite VNC client try to connect to the Pi, using its IP address, </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">the port 5901, </span>and the specified password for remote users.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="color: blue;">Test #3</span></span></b><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">In order to read serial port communications install minicom:</span><br />
<br />
<pre class="brush: java">sudo apt-get install minicom
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Then plug in the XBee #1 via Explorer in the USB port of the Pi. Power on the Arduino, and start minicom:</span><br />
<br />
<pre class="brush: java">minicom -b 9600 -o -D /dev/ttyUSB0
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">You should see the readings appearing on the screen:</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"></span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSMvmTNPR4Pf8ZUKwPp5Y_xvWFSzwEd2T59TyJN_khTaBMr3qAtEBUpqhk1RqKDhbHUgFcTHFMl45WoIKzbeJzyng58ZHXHhyphenhyphenofbVgL1rERUwZYSggfSdWXJc58CpftcqqGN81E8xzkrI/s1600/minicom.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSMvmTNPR4Pf8ZUKwPp5Y_xvWFSzwEd2T59TyJN_khTaBMr3qAtEBUpqhk1RqKDhbHUgFcTHFMl45WoIKzbeJzyng58ZHXHhyphenhyphenofbVgL1rERUwZYSggfSdWXJc58CpftcqqGN81E8xzkrI/s1600/minicom.png" height="444" width="640" /></a></div>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">To close it, press Ctrl+A+Z+X. </span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">4. Java Embedded Suite 7.0</span></b><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">It was launched by Oracle at the end of September 2012. As you can read <a href="http://www.oracle.com/us/technologies/java/embedded/suite/overview/index.html">here</a>, </span><br />
<ul>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Oracle Java SE Embedded 7 provides <b>runtime for Java embedded applications</b></span></li>
<b>
</b>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Java DB provides <b>a database</b> to store local content securely</span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Glassfish for Embedded Suite provides <b>an application server</b> for Web pages</span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Jersey RESTful Web Services Framework for hosting and accessing <b>Web services</b>.</span></li>
</ul>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">with the main goal of optimization for embedded devices and server systems.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5OTtzcK3cpxBh4E8sQaHg0Teoe3D5ftNC3ha7ScTJQMjnq96IPo36mpLtOdBi5i4_00jbT2RosoE2DVk9TRmiyZu6TIhyphenhyphenvuRZQAlgUBUrBTW_rNrNrB946WfvGNAbUUuf8KyjhG-nqxA/s1600/javaEmbeddedSuite.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5OTtzcK3cpxBh4E8sQaHg0Teoe3D5ftNC3ha7ScTJQMjnq96IPo36mpLtOdBi5i4_00jbT2RosoE2DVk9TRmiyZu6TIhyphenhyphenvuRZQAlgUBUrBTW_rNrNrB946WfvGNAbUUuf8KyjhG-nqxA/s1600/javaEmbeddedSuite.png" height="320" width="213" /></a></div>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"> </span><br />
<b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><a href="http://www.blogger.com/blogger.g?blogID=7114525940495426773" name="installation">Installation</a></span></b><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">In your PC, download the distribution for Raspberry Pi (ARM v6 soft-float) from <a href="http://download.oracle.com/otn-pub/java/JES/7.0-b11/jes-7.0-ga-bin-b11-linux-arm-runtime-15_nov_2012.zip">here</a> and the samples from <a href="http://download.oracle.com/otn-pub/java/JES/7.0-b11/jes-7.0-ga-b11-linux-samples-15_nov_2012.zip">here</a>. Now connect by ssh and go to </span>directory<span style="font-family: "Courier New",Courier,monospace;"> /home/pi</span> and make a new directory there, <span style="font-family: "Courier New",Courier,monospace;">install</span>. Then copy both files to <span style="font-family: "Courier New",Courier,monospace;">/home/pi/install</span>.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"></span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Now on your Pi, go to<span style="font-family: "Courier New",Courier,monospace;"> /home/pi/install</span> and unzip the code bundle, which creates <span style="font-family: "Courier New",Courier,monospace;">/home/pi/install/jes7.0/</span>. </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Unzip also the samples bundle, which creates <span style="font-family: "Courier New",Courier,monospace;">/home/p<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">i</span>/install/jes7.0/samples/</span>. Then delete the bundles:</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<br />
<pre class="brush: java">unzip jes-7.0-ga-bin-b11-linux-arm-runtime-15_nov_2012
rm jes-7.0-ga-bin-b11-linux-arm-runtime-15_nov_2012
unzip jes-7.0-ga-b11-linux-samples-15_nov_2012.zip
rm jes-7.0-ga-b11-linux-samples-15_nov_2012.zip
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Now with root privileges create <span style="font-family: "Courier New",Courier,monospace;">/usr/java </span>directory and move <span style="font-family: "Courier New",Courier,monospace;">/home/pi/install/jes7.0</span> to<span style="font-family: "Courier New",Courier,monospace;"> /usr/java</span>:</span><br />
<br />
<pre class="brush: java">sudo mv jes7.0 ../../../usr/java
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Finally, we need to add to PATH the route for Java, and add a JAVA_HOME variable. For that edit the file:</span><br />
<br />
<pre class="brush: java">sudo nano /etc/bash.bashrc
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">And nearly at the end, add:</span><br />
<br />
<pre class="brush: java">export PATH=/usr/java/jes7.0/jre/bin:$PATH
JAVA_HOME="/usr/java/jes7.0/jre"
export JAVA_HOME</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Save and exit, and reboot. Login, and check if everything is fine by typing <span style="font-family: "Courier New",Courier,monospace;">java -version</span>:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ2Cfi_Qd1gJPMvG2PUzB4wcgDUx4MYUzevhlBVWybiYTvOSfPmf2VxYGJQqlG1DpOVTBYQuP_kL_xR_i2tbLdXHeLotu_-fbjx30XXi9RCqh0OiT8KpFrSKFpiX7OSOLIDpk6GWG1Byw/s1600/javaEmbeddedSuite1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ2Cfi_Qd1gJPMvG2PUzB4wcgDUx4MYUzevhlBVWybiYTvOSfPmf2VxYGJQqlG1DpOVTBYQuP_kL_xR_i2tbLdXHeLotu_-fbjx30XXi9RCqh0OiT8KpFrSKFpiX7OSOLIDpk6GWG1Byw/s1600/javaEmbeddedSuite1.png" height="248" width="640" /></a></div>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"></span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">You should try now all the samples from the Suite that come within the bundle, following this <a href="http://docs.oracle.com/javase/7/embedded/jes/7.0/docs/html/application/samples.htm">guide</a>, going to <span style="font-family: "Courier New",Courier,monospace;">/usr/java/jes7.0/samples/dist/run</span> directory.</span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><b><span style="color: blue;">Test #4</span></b><br /> </span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">The next step in our tests is check if we can read the serial port with Java Embedded. For that we'll make a small project to read the serial port. First of all, we need to install RXTXcomm library and test java embedded serial reading with xbee.jar.</span><br />
<br />
<pre class="brush: java">sudo apt-get install librxtx-java
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">It will install <span style="font-family: "Courier New",Courier,monospace;">/usr/share/java/RXTXcomm-2.2pre2.jar</span>. Back on your PC, create a new project in your Java IDE, with the class listed below, add this jar (copy it </span><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">by ssh </span>from your Pi), compile and build the project, and send it to the Pi by ssh (both jars), to <span style="font-family: "Courier New",Courier,monospace;">/home/pi/java</span>, for instance.</span><br />
<br />
<pre class="brush: java">public class EmbSerial {
private CommPort m_commPort=null;
private SerialPort m_serialPort=null;
public void connect( String portName ) throws Exception {
CommPortIdentifier portIdentifier =
CommPortIdentifier.getPortIdentifier(portName);
if(portIdentifier.isCurrentlyOwned()){
System.out.println( "Error: Port is currently in use" );
} else {
int timeout = 2000;
m_commPort = portIdentifier.open(this.getClass().getName(), timeout);
if( m_commPort instanceof SerialPort ) {
m_serialPort = (SerialPort)m_commPort;
m_serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8,
SerialPort.STOPBITS_1, SerialPort.PARITY_NONE );
final InputStream in = m_serialPort.getInputStream();
new Thread(){
@Override public void run() {
byte[] buffer=new byte[1024];
int len = -1;
try {
while((len = in.read(buffer))>-1) {
System.out.print(new String(buffer,0,len));
}
} catch( IOException e ) {
System.out.println("Error reading: " + e.getMessage());
}
}
}.start();
} else {
System.out.println( "Error: Not a serial port" );
}
}
}
public void disconnect(){
if(m_serialPort!=null){
m_serialPort.removeEventListener();
m_serialPort.close();
}
if(m_commPort!=null){
System.out.println("Port closed");
m_commPort.close();
}
}
public static void main(String[] args) {
final EmbSerial serial=new EmbSerial();
try {
serial.connect("/dev/ttyUSB0");
} catch(Exception e) {
System.out.println("Error connecting: " + e.getMessage());
return;
}
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override public void run() {
serial.disconnect();
}
}));
}
}
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Run it by:</span><br />
<br />
<pre class="brush: java">java -Djava.library. path=/usr/lib/jni -jar EmbSerial.jar
</pre>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">You should get the readings on the terminal. Press Ctrl+C to finish.</span><br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYuWNZ8TL5abxDwFubVp9OVBrkV_xdEi9Vhgxd5Dz1FDqCt-nZ9yvM6fQaPomzwGsaZocCM0EkQNtnEi-8k3ZXeVONMQ5rGMO65nkYIhfmgMg7AqxRFdTY-FjtjztN-sGMzn3FRQqzurY/s1600/javaEmbeddedSuite2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYuWNZ8TL5abxDwFubVp9OVBrkV_xdEi9Vhgxd5Dz1FDqCt-nZ9yvM6fQaPomzwGsaZocCM0EkQNtnEi-8k3ZXeVONMQ5rGMO65nkYIhfmgMg7AqxRFdTY-FjtjztN-sGMzn3FRQqzurY/s1600/javaEmbeddedSuite2.png" height="273" width="640" /></a></div>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;"><br /></span>
<b><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Part I conclusion</span></b><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Well, if you have followed closely the detailed procedures and performed successfully all the tests, congratulations! </span><br />
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Now you're ready for bigger adventures, so in the next part of this series we'll create the embedded server in the Raspberry Pi: </span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Back-end task: it will repeatedly read the serial port to get the measures from the Arduino and store them in a database.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Web services: it will respond to web requests returning the current values measured.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">RESTful web services: it will respond to REST requests returning lists in json format with the data measured between two dates.</span></li>
</ul>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">And we will create the JavaFX based client application, that will mainly:</span><br />
<ul>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Connect to the server to get last measures and show them in a LED Matrix Panel control.</span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Connect to the server to get a list of measures between two dates, showing them in a chart. </span></li>
<li><span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Show the status of the connection anytime.</span></li>
</ul>
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">This is a preview of this app:</span><br />
<br />
<div style="text-align: center;">
<span style="font-family: Verdana,sans-serif;"><video controls="controls" poster="https://dl.dropbox.com/u/27006338/ArduinoFX.mp4" tabindex="0" width="500">
<source src="https://dl.dropbox.com/u/27006338/ArduinoFX.mp4" type="video/mp4; codecs="avc1.42E01E, mp4a.40.2""></source>
<source src="https://dl.dropbox.com/u/27006338/ArduinoFX.webm" type="video/webm; codecs="vp8, vorbis""></source>
Your browser does not support the video tag, view this video <a href="https://dl.dropbox.com/u/27006338/ArduinoFX.mp4">here</a>.<br />
</video></span></div>
<br />
<span style="font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;">Please, have a breath after this really long post and, if you're ready, continue reading <a href="http://jperedadnr.blogspot.com.es/2012/12/arduinofx-javafx-gui-for-home_17.html">Part II</a>. In the meantime, I'd love to hear any comments from you. </span><br />
<br />Anonymoushttp://www.blogger.com/profile/15247383133952423416noreply@blogger.com20tag:blogger.com,1999:blog-7114525940495426773.post-18964983990521241332012-11-26T21:20:00.000+01:002013-10-24T18:00:03.466+02:00NXTLegoFX: JavaFX based application to play with a Lego Mindstorms NXT. Part I<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Hi there!</span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">This is the first of a series of posts related to a new application in JavaFX I'm developing with the initial intention of interacting with a Lego® Mindstorms® NXT.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">The first purpose of this app is, as a preliminary proof of concept, to show how easy is to integrate different software and hardware technologies. For that I'll start by introducing you briefly to the following topics:</span><br />
<ul>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><a href="http://mindstorms.lego.com/en-us/Default.aspx">Lego Mindstorms</a> NXT: </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">computational module, with graphical programming environment</span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">, initially intended for kids to play with robotics.</span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><a href="http://lejos.sourceforge.net/">leJOS</a>, firmware to replace the NXT one, includes a tiny Java Virtual Machine, and allows you to programm the robot with Java.</span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><a href="http://glassfish.org/">Glassfish</a>, application server to run Java apps and RESTful web services, implemented using HTTP and the principles of REST.</span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><a href="http://www.oracle.com/technetwork/java/javafx/overview/index.html">JavaFX</a>, the new Java platform for creating and delivering rich Internet applications.</span></li>
</ul>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">If you're already an expert in any of these items, please, feel free to skip to the last part of the post. If you're not, there're plenty of blogs out there covering deeply any of them, as this post pretends to be just a glance.</span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Please notice I don't consider myself in any way an expert in any of the topics I'm covering here. Quite on the contrary, this is an exercise of </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><i>traversal and shallow engineering</i>.</span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Before I begin, it's fair I mention the guy whose idea I've taken to start with all of this: Sébastien Stormacq <a href="https://twitter.com/sebsto">@Sebtso</a>. His entry blog <a href="http://www.stormacq.com/put-a-java-brain-into-your-lego-robot/">Put a Java Brain in your Lego Robot</a>, from 2010, opened my eyes, with his code I didn't need to start from the scratch. Also I want to thank the team members of leJOS <a href="http://lejos.sourceforge.net/forum/index.php">community</a>. They keep vey much alive this fantastic project.</span><br />
<br />
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">1. The schematics</span></b>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"></span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">For starters, let's see a simple overview of the intended project and the technologies involved.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQsjdevW48BuwGNSFqOsmRvAIrTt1fnGx98ZCEQFOzoQYTREYMbbAxPGOlRd2qsL76G6Ze34jHkoRk6i0k9RAcJPa37M11Mm9_Cq7nPwhAb_tLlD6Eo1nxWDy9VnR_EwVHoc0kcB-5y6A/s1600/overview.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQsjdevW48BuwGNSFqOsmRvAIrTt1fnGx98ZCEQFOzoQYTREYMbbAxPGOlRd2qsL76G6Ze34jHkoRk6i0k9RAcJPa37M11Mm9_Cq7nPwhAb_tLlD6Eo1nxWDy9VnR_EwVHoc0kcB-5y6A/s640/overview.png" height="425" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">2. Lego Mindstorms</span></b><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">As I said before, there are plenty of blogs out there describing in full detail what it is or what you can do with it, so just for the very beginners here it's in short the main specs.</span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><a href="http://mindstorms.lego.com/en-gb/history/default.aspx">Lego Mindstorms NXT</a> is a programmable robotics kit released by Lego in 2006. The NXT 2.0 was released in 2009. The kit includes 3 servo motors and 4 sensors (ultrasonic, 2 touch and color), 7 connection cables, a USB interface cable, and the NXT Intelligent Brick, which is the <i>brain</i> of a Mindstorms machine.</span><br />
<table style="width: 100%px;"><tbody>
<tr align="center"><td><div class="separator" style="clear: both;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid_wfUe0dOA-7PZFxx8V6CpB4WW87WsvIszNJsjR0PcOxxfuf1Le9NTpv8n15lU0DF_jQyRh32d9Ng8SfhiQboO8TTnyLtO2ksBBzgKJPF59An-yEcaz7R5ukAcLRRcFeKQmQ0IoD9Vjo/s1600/lego0.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid_wfUe0dOA-7PZFxx8V6CpB4WW87WsvIszNJsjR0PcOxxfuf1Le9NTpv8n15lU0DF_jQyRh32d9Ng8SfhiQboO8TTnyLtO2ksBBzgKJPF59An-yEcaz7R5ukAcLRRcFeKQmQ0IoD9Vjo/s320/lego0.jpg" height="299" width="320" /></a></div>
</td><td><div class="separator" style="clear: both;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiW2021YNBD7MeCAPB1Ir3WzAZ7wY2xAaYgQA5q1gZu7ZUj9DxiWDHCCNPd3BqPCCjvSZaxSveLjPUIktOrGB4Q67ojzmr6I9H_ABBIVmN3elXH74bTSn9zte6TKLqLieCPWWcXsKompdM/s1600/lego1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiW2021YNBD7MeCAPB1Ir3WzAZ7wY2xAaYgQA5q1gZu7ZUj9DxiWDHCCNPd3BqPCCjvSZaxSveLjPUIktOrGB4Q67ojzmr6I9H_ABBIVmN3elXH74bTSn9zte6TKLqLieCPWWcXsKompdM/s320/lego1.jpg" height="187" width="320" /></a></div>
</td></tr>
</tbody></table>
<div style="text-align: left;">
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">The <i>brick </i>is the </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">computational module:</span></span></div>
<div style="text-align: left;">
<ul>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Based on a 32-bit Atmel AT91SAM7S256 ARM7TDMI microcontroller (ARMv4) with 64 kB RAM, 256 kB flash and 48 MHz.</span></span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Attached to it a 8-bit Atmel AVR ATmega48 controller via I2C interface, 512 Bytes RAM, used to collect data from sensors and control motors.</span></span></li>
<li><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">With one USB 2.0 port, Bluetooth support and a 100x64 pixels black and white display.</span></span></li>
</ul>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBPN6fzEWCj_bu3pcj1T6-fVDs8jaJgLrlPIX32nfyrpp7NrSXK9atCjvJkviWCiD5B2WYsDa24KGVYeWcLBlczrX1Najlt9YKX42caAwRjm-adNcdSW8FT7CaIAJt_gEIqDWFv_yyRRA/s1600/NXT_system_architecture.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBPN6fzEWCj_bu3pcj1T6-fVDs8jaJgLrlPIX32nfyrpp7NrSXK9atCjvJkviWCiD5B2WYsDa24KGVYeWcLBlczrX1Najlt9YKX42caAwRjm-adNcdSW8FT7CaIAJt_gEIqDWFv_yyRRA/s400/NXT_system_architecture.JPG" height="272" width="400" /></a></div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Let me say here that, </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">relatively speaking,</span> the brick architecture could be compared to a quite underrated <a href="http://www.raspberrypi.org/">Raspberry Pi</a> (ARMv6 ARM1176JZF-S 32-bit, 512 MB RAM, 700 MHz) with and <a href="http://www.arduino.cc/">Arduino</a> (ATMega328, 8-bit, 2 kB RAM) attached. </span></div>
<div style="text-align: left;">
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span></div>
<div style="text-align: left;">
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">On the software side, bundled with the kit comes NXT-G, a graphical programming environment that enables the creation and downloading of programs to the NXT. It is </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">powered by </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><a href="http://www.ni.com/labview">LabVIEW</a> </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">from National Instruments. These programs make use of the standard <a href="http://mindstorms.lego.com/en-us/support/files/firmware.aspx">firmware</a> provided by Lego.</span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> </span></div>
<div style="text-align: left;">
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ1hyphenhyphenLtImcnZ4O_SXSI6bFDbWmoZyD-AqXwfyprnVgypQnfKQxuEP9_QRrtTBtMr_A-fRDahyECY_oEKzlJH35G4GtbvKGt03LO3-W9xbWYJ0ck46I1NJkKO9AzgjwdE_js1ibYWGuUTY/s1600/lego3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ1hyphenhyphenLtImcnZ4O_SXSI6bFDbWmoZyD-AqXwfyprnVgypQnfKQxuEP9_QRrtTBtMr_A-fRDahyECY_oEKzlJH35G4GtbvKGt03LO3-W9xbWYJ0ck46I1NJkKO9AzgjwdE_js1ibYWGuUTY/s400/lego3.jpg" height="227" width="400" /></a></div>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">This is intended for kids to start programming, very much like MIT's <a href="http://scratch.mit.edu/">Scratch</a>. It's open to new blocks related to sensors from third-party providers like <a href="http://www.hitechnic.com/">HiTechnic</a> or <a href="http://www.dexterindustries.com/">Dexter Industries</a>.</span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> </span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">While NXT-G provides a very user friendly approach to programming NXT robots, for more advanced programming with a text-based language, there is plenty of unofficial languages for the NXT out there. Three of the most popular of them are <a href="http://bricxcc.sourceforge.net/nbc/">NXC/NBC</a>, <a href="http://www.robotc.net/">RobotC</a> and <a href="http://lejos.sourceforge.net/">LeJOS NXJ</a>.</span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">3. <a href="http://www.blogger.com/blogger.g?blogID=7114525940495426773" name="lejos">LeJOS NXJ</a></span></b><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Well, why I've choosen LeJOS NXJ in the first place, a Java based language, instead of the others C based languages it should be clear to a regular reader of this blog. </span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">You can find a complete description of leJOS, the firmware replacement for the official one, and leJOS NXT, the language, <a href="http://en.wikipedia.org/wiki/LeJOS">here</a>. You can find a full tutorial <a href="http://lejos.sourceforge.net/nxt/nxj/tutorial/index.htm">here</a>.</span> <span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> </span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Download the latest version (at the time of this post,
0.9.1beta-3) </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><a href="http://sourceforge.net/projects/lejos/files/lejos-NXJ/0.9.1beta/">here</a></span></span>. Choose Windows (*_win32_setup.exe) or Linux and Mac OS X
version (*.tar.gz)</span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">In short, the firmware includes a tiny Java virtual machine, which allows Lego Mindstorms robots to be programmed with Java. So, first of all, you have to flash this firmware. </span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Note you'll lose every file stored in the NXT. This process is reversible, and you can restore Lego official firmware afterwards.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">To flash the NXT, please follow the detailed Getting Started instructions <a href="http://lejos.sourceforge.net/nxt/nxj/tutorial/Preliminaries/GettingStarted.htm">here</a> according to your operative system. At the end, you'll have your NXT with a brand new leJOS logo and a main menu, quite similar to the official one. <a href="http://lejos.sourceforge.net/nxt/nxj/tutorial/MenuSystem/MenuSystem.htm">Here</a> you'll find detailed instructions of how to use it. </span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">If you want to use bluetooth for communication, enter in the submenu, select Power On, Visibility On, and enter a 4 digit pin (1234 by default). In your PC, search the device and connect to it. In Windows (my case) this is quite simple and it works smoothly. <strike>But in Mac OS X, on the contrary, it seems bluetooth is not working, from what I've heard.</strike> <i>Edited: Please, check this <a href="http://jperedadnr.blogspot.com/2012/11/nxtlegofx-javafx-based-application-to.html?showComment=1354011598560#c6390440958485253839">Comment</a> from Philipp Dörfler below for instructions to use bluetooth with your NXT in Mac OS.</i><br /><strike><br /></strike></span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">On the other side, on your PC you'll find in the installation path a <i>bin </i>folder with a bunch of Swing based <a href="http://lejos.sourceforge.net/nxt/nxj/tutorial/PC_GUI/PCGUITools.htm">PC GUI tools</a>, which are batch files like <i>nxjbrowse</i>, to list the files in your NXT, the <i>nxjflashg</i>, to flash the NXT or <i>nxjcontrol</i>, that gives you full control of your NXT:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3hpFPcMG_3ghqkyUINwaQTLH0dc9sbVtNnqiHvZub-ldW3n5AhUEeuwBRZsw0YX-icA6PBmhQfIqw0K5yb8Q1Njkcwe8cLNAhaWVIAd87EGHusRRiR3wg3OBBKwdCdVBKi_rNC9ASl9o/s1600/lego4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3hpFPcMG_3ghqkyUINwaQTLH0dc9sbVtNnqiHvZub-ldW3n5AhUEeuwBRZsw0YX-icA6PBmhQfIqw0K5yb8Q1Njkcwe8cLNAhaWVIAd87EGHusRRiR3wg3OBBKwdCdVBKi_rNC9ASl9o/s400/lego4.png" height="248" width="400" /></a></div>
<div style="text-align: center;">
<br /></div>
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Programming the NXT</span></b><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">You can either write Java programs that run on the NXT or run on the PC and control the NXT remotely. You can use any Java IDE or just command line tools.</span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">In the installation path you'll find the <i>samples.zip</i> file, which contains samples to download and execute on the NXT (<i>samples </i>folder) or to run them on your PC (<i>pcsamples</i> folder). Ant build scripts are provided. </span><br />
<br />
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Running samples on the NXT</span></b><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">The samples require <i>classes.jar</i> in the classpath. When you run the project from the IDE, a dialog ask you to enter the name of the sample you want to download and run. The <i>view</i> sample, for instance, shows a menu in the NXT LCD and you can read sensors or control motors with the buttons.</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilsjyYSRhoSa9B8bJfayv8xouqwUPoH3ATNegDtBhjag0S_aTe7URMEDo9-N9R5oewG6thjWDENLdYBIS1d9yCZ640O8v50qHULUwk4YUVVm5K9JK_sANxOiT0xa8u6NNtKPFJ6ZgSknI/s1600/lego5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQizOYrvJMK641ny7ab6Me0ToYxSvdONclYMnhPCH0wVb6hJgwnGx3LuuR7DTvG2q0tKVMiZ0-hN-hTFhhkjQ8hP92eFLUaIz_d5z9HmCjWoCbD2FAi8bcL3zpzRWY7A9FGS_fKfFiiMI/s1600/lego6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQizOYrvJMK641ny7ab6Me0ToYxSvdONclYMnhPCH0wVb6hJgwnGx3LuuR7DTvG2q0tKVMiZ0-hN-hTFhhkjQ8hP92eFLUaIz_d5z9HmCjWoCbD2FAi8bcL3zpzRWY7A9FGS_fKfFiiMI/s1600/lego6.png" /></a></div>
<div style="text-align: center;">
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span></div>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Here you have a sneak peek of the code of this program:</span><br />
<br />
<pre class="brush: java">package org.lejos.sample.view;
import lejos.nxt.Battery;
import lejos.nxt.Button;
import lejos.nxt.LCD;
import lejos.util.TextMenu;
public class View {
public static void main (String[] aArg) throws Exception
{
String[] viewItems = {"System", "Sensors", "Motors", "Exit"};
TextMenu main = new TextMenu(viewItems, 1, "View Example");
...
int selection;
for(;;)
{
LCD.clear();
selection = main.select();
if (selection == 0) // System Info
{
LCD.clear();
LCD.drawString(sys, 0, 0);
LCD.drawString(batt, 0, 2);
LCD.drawInt(Battery.getVoltageMilliVolt(), 4, 10, 2);
LCD.drawString(tot, 0, 3);
LCD.drawInt((int)(Runtime.getRuntime().totalMemory()), 5, 10, 3);
LCD.drawString(free, 0, 4);
LCD.drawInt((int)(Runtime.getRuntime().freeMemory()), 5, 10,4);
LCD.refresh();
Button.ESCAPE.waitForPressAndRelease();
}
...
}
}
}
</pre>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">As you can see, it's pretty straightforward.</span><br />
<br />
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Running on your PC with remote control of the NXT </span></b><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">In this case, you must add the jar files from <i>lib/pc</i>, <i>pccomm.jar</i> and <i>pctools.jar</i>, and from <i>lib/pc/3rdparty</i> <i>bluecove.jar</i>. But you must not add <i>classes.jar</i>. </span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Here it's one of the samples:</span><br />
<br />
<pre class="brush: java">package org.lejos.pcsample.tachocount;
import lejos.nxt.Motor;
import lejos.nxt.Sound;
import lejos.nxt.remote.NXTCommand;
import lejos.pc.comm.NXTComm;
import lejos.pc.comm.NXTCommLogListener;
import lejos.pc.comm.NXTCommandConnector;
import lejos.pc.comm.NXTConnector;
/**
* @author Lawrie Griffiths and Brian Bagnall
*/
public class TachoCount {
public static void main(String [] args) throws Exception {
NXTConnector conn = new NXTConnector();
conn.addLogListener(new NXTCommLogListener() {
public void logEvent(String message) {
System.out.println(message);
}
public void logEvent(Throwable throwable) {
System.err.println(throwable.getMessage());
}
});
if(!conn.connectTo("btspp://NXT", NXTComm.LCP)) {
System.err.println("Failed to connect");
System.exit(1);
}
NXTCommandConnector.setNXTCommand(new NXTCommand(conn.getNXTComm()));
System.out.println("Tachometer A: " + Motor.A.getTachoCount());
System.out.println("Tachometer C: " + Motor.C.getTachoCount());
Motor.A.rotate(5000);
Motor.C.rotate(-5000);
System.out.println("Tachometer A: " + Motor.A.getTachoCount());
System.out.println("Tachometer C: " + Motor.C.getTachoCount());
conn.close();
}
}
</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Notice that the NXTConnector object uses Lego Mindstorms NXT Communications Protocol LCP to establish communication between PC and the NXT either via bluetooth or via USB. As valid URL for your specific NXT, you can set "btspp://<NXT name>", "btspp://<NXT address>", "usb://<NXT name>" or "usb://<NXT address>". To coonect to any device just use "btspp://" or "usb://".</span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Look at the other samples, to find out the rest of the possibilities.</span><br />
<br />
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">4. The Glassfish server</span></b><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">With the ability to communicate to the NXT from a PC, a further step is the installation of a server on the PC to allow the connection of </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">several devices </span>simultaneously to the same NXT(s). </span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">So I'll try to develop an application, deployed in a Glassfish application server, that will respond to REST requests from these devices, by establishing a synchronized connection with the NXT</span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">.</span><br />
<br />
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">The LegoNXT class </span></b><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Continuing the previous work of </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Sébastien Stormacq, based on a singleton object to guarantee one only access to the NXT, regardless the number of users trying to access to it, I'll add a blocking key, so only one request is processed at a time. We must keep in mind that we are targeting a very slow processor. I also add another key to know if the NXT is connected or not.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Here you can see part of the code:</span><br />
<br />
<pre class="brush: java">public class LegoNXT {
private static LegoNXT singleton = null;
private static boolean legoBlocked=false;
private static boolean legoConnected=false;
public static LegoNXT getSingleton() {
final Object lock = LegoNXT.class;
if (singleton == null) {
synchronized(lock) {
singleton = new LegoNXT();
}
}
return singleton;
}
private LegoNXT() {}
public void connect(String NXTName, String Protocol) {
final String connectString = Protocol.concat("://").concat(NXTName);
legoBlocked=true;
legoConnected=false;
try {
Logger.getLogger(getClass().getName()).log(Level.INFO, "Initializing communication with NXT");
NXTConnector cmd = new NXTConnector();
cmd.addLogListener(new NXTCommLogListener() {
@Override public void logEvent(String message) {
Logger.getLogger(getClass().getName()).log(Level.INFO, message);
}
@Override public void logEvent(Throwable throwable) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Exception while talking to NXT brick", throwable);
}
});
if(!cmd.connectTo(connectString, NXTComm.LCP)) {
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Failed to connect");
legoBlocked=false;
return;
}
NXTCommandConnector.setNXTCommand(new NXTCommand(cmd.getNXTComm()));
Logger.getLogger(getClass().getName()).log(Level.FINE, "Done");
legoConnected=true;
} catch (Exception e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Exception while initializing connection", e);
legoBlocked=false;
}
}
public void disconnect() {
try {
NXTCommandConnector.close();
} catch (IOException ex) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
}
legoConnected=false;
legoBlocked=false;
}
public boolean isLegoBlocked() { return legoBlocked; }
public boolean isLegoConnected(){ return legoConnected; }
...
/* NXT methods */
</pre>
<br />
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">The RESTful web services</span></b><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">The first RESTful web service will search for any NXT device connected to the server.</span><br />
<br />
<pre class="brush: java">@Path("search")
public class SearchResource {
@GET
@Produces("application/json")
public String getJson() {
NXTConnectionManager man= new NXTConnectionManager();
NXTInfo[] search = man.search();
JSONObject json = null;
try{
JSONArray arr=new JSONArray();
for(NXTInfo info:search){
JSONObject element = new JSONObject();
element.put(CData.JSON_RESPONSE_DEVICENAME, info.name);
element.put(CData.JSON_RESPONSE_DEVICEADDRESS, info.deviceAddress);
if(info.protocol==NXTCommFactory.USB){
element.put(CData.JSON_RESPONSE_PROTOCOL, "USB");
} else if(info.protocol==NXTCommFactory.BLUETOOTH){
element.put(CData.JSON_RESPONSE_PROTOCOL, "BLUETOOTH");
}
arr.put(element);
}
json = new JSONObject();
json.put(CData.JSON_RESPONSE_DEVICES, arr);
} catch(JSONException jse){
return "{}";
}
return json.toString();
}
}
</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">So when you start the Glassfish server, from any browser you can search for NXT devices connected, and it will answer with a JSON array. In my case, with just one device:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZaAysyg2aTE4fVBYjNaq7YcjT6PxbBoRIJMhDT4l4f1N2qopSUa-DxaOXfXzNnSqoW3gFiyOLinq_-IOW3X1SPu_geM48DOBJLLGbUfBynpdxXxslggcggVR5rKMk_pZ_91qKP1dVBEk/s1600/lego8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZaAysyg2aTE4fVBYjNaq7YcjT6PxbBoRIJMhDT4l4f1N2qopSUa-DxaOXfXzNnSqoW3gFiyOLinq_-IOW3X1SPu_geM48DOBJLLGbUfBynpdxXxslggcggVR5rKMk_pZ_91qKP1dVBEk/s640/lego8.png" height="121" width="640" /></a></div>
<div style="text-align: center;">
<br /></div>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">The second service, once you've selected the NXT to connect to, will process a request to ask the NXT for several of its system properties:</span><br />
<br />
<pre class="brush: java">@Path("device")
public class DeviceResource {
private LegoNXT nxt = null;
@GET
@Produces("application/json")
public String getJson(@QueryParam("NXTName") @DefaultValue("NXT") String NXTName,
@QueryParam("Protocol") @DefaultValue("usb") String Protocol) {
String result = null;
if (nxt == null) {
nxt = LegoNXT.getSingleton();
}
int cont=0;
while(nxt.isLegoBlocked()){
try{
Thread.sleep(100);
cont+=1;
} catch(InterruptedException ie) {}
if(cont>CData.TIME_OUT){
String msg = "Device was blocked";
Logger.getLogger(getClass().getName()).log(Level.WARNING, msg);
result = "{ \"status\" : \"" + msg + "\" }";
nxt = null;
return result;
}
}
try {
nxt.connect(NXTName,Protocol);
result = new JSONStringer().object()
.key(CData.JSON_RESPONSE_DEVICENAME)
.value(nxt.getDeviceName())
.key(CData.JSON_RESPONSE_BATTERYLEVEL)
.value((double)nxt.getDeviceBateryLevel())
.key(CData.JSON_RESPONSE_BLUETOOTHADDRESS)
.value(nxt.getDeviceBluetoothAddress())
.key(CData.JSON_RESPONSE_FIRMWAREVERSION)
.value(nxt.getDeviceFirmwareVersion())
.key(CData.JSON_RESPONSE_VOLUME)
.value(nxt.getVolume())
.key(CData.JSON_RESPONSE_STATUS)
.value("OK")
.key(CData.JSON_RESPONSE_TIME)
.value(((double)cont)/10d)
.endObject().toString();
nxt.disconnect();
} catch (Exception ex) {
String msg = "Can not get device information";
Logger.getLogger(getClass().getName()).log(Level.WARNING, msg, ex);
result = "{ \"status\" : \"" + msg + "\" }";
nxt = null;
}
return result;
}
}
</pre>
<div style="text-align: center;">
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"></span><br />
<div style="text-align: left;">
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Several other RESTful web services are also added to the server in this way. </span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">EDITED on the 1st of February of 2013: By request I've uploaded the server code to my GitHub repo, so if you're interested, grab the code <a href="https://github.com/jperedadnr/WebLego">here</a> and give it a try!</span></div>
</div>
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span></b>
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">5. The client requests</span></b><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Those RESTful web services must be called from the client side, and the returned json string must be processed to extract the contained information. For that I'll use the <a href="http://code.google.com/p/google-gson/">GSON</a> open source Java library, which allows deserialization of collections or nested classes. So I just need one POJO class with the properties expected to be read from the json string for one device, <i>EntSearch</i>, and one POJO class with an arraylist of the first one, <i>EntDeviceSearch</i>, to collect all NXT devices found and </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">their properties:</span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span><br />
<pre class="brush: java">public class EntSearch {
private String devicename;
private String deviceaddress;
private String deviceprotocol;
private int batterylevel;
private String firmwareversion;
private String status;
private int volume;
private double time;
/* getters and setters */
}
</pre>
<pre class="brush: java">public class EntDeviceSearch {
private ArrayList<entsearch> devices = null;
/* getters and setters */
}
</pre>
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">So the request to the server to search for NXT devices, and the deserialization of the response, can be done very easily:</span><br />
<pre class="brush: java">
public void search() {
URL theJsonUrl=null;
try {
theJsonUrl = new URL("http://localhost:8080/WebLego/NXT/search");
} catch (MalformedURLException ex) {
System.out.println("URL Error: " + ex.getMessage());
return;
}
String jSonTxt="";
try {
InputStream in=theJsonUrl.openStream();
jSonTxt=IOUtils.toString(in);
} catch (IOException ex) {
System.out.println("URL Error: " +theJsonUrl+" "+ ex.getMessage());
return;
}
EntDeviceSearch devices=null;
try{
devices=new Gson().fromJson(jSonTxt,EntDeviceSearch.class);
} catch(JsonSyntaxException jse){
System.out.println("GSON Error: "+jse.getMessage());
return;
}
if(devices!=null){
System.out.println("Found Devices: "+devices.getDevices().size());
for(EntSearch ent: devices.getDevices()){
... // process each entry accordingly
}
}
else{
System.out.println("Search Error");
}
}
</pre>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> </span><b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> </span></b><br />
<b><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">6. The JavaFX UI</span></b><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Finally we reach the User Interface part. Now we only need to desing a fancy scene and connect it to the server via the RESTful web services. As this is a proof of concept, well be adding just a few controls to the scene, in this first version.</span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">I'll use a Tab Pane with two tabs. The first one, labeled Communication, will have a TableView to list all NXT devices found, just by calling the <i>search </i>RESTful web service. Once a row is selected, the other service (<i>device</i>) is called to fetch the data from the picked device.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">You should know by now that there're quite fancy custom JavaFX controls in <a href="http://jfxtras.org/">jfxtras.org</a>. Just drop the last available jar (at the time of this writting, jfxtras-labs-2.2-r5-SNAPSHOT.jar) to your project. To show the data values, a Battery, several Leds and an Indicator from JFXtras project</span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"> will be used</span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">This is how it looks like the Communication tab, for now:</span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS7n7c-OM4ZWaWmyzxQac2Q-aaBN9Qtd-UQPWhBa-edbdEc5rH269yzMV5MJApzFp8fVGf6rb8TCErMu3Sk2ZAZR1p7-pknBSJlNzswIXJlK4UIvaYOqfIxt1tynN7uwm7OWKD8UPz3no/s1600/LegoFX.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS7n7c-OM4ZWaWmyzxQac2Q-aaBN9Qtd-UQPWhBa-edbdEc5rH269yzMV5MJApzFp8fVGf6rb8TCErMu3Sk2ZAZR1p7-pknBSJlNzswIXJlK4UIvaYOqfIxt1tynN7uwm7OWKD8UPz3no/s640/LegoFX.png" height="556" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHPO7lSNvgm1VBudGUzqaMsLHs0Am-9G4iATl8nH0VyCNJatqdf6dtclOYsBwO2MM01j5voL_O7DfFQCjE2l2txGrT7fSfo8ekLcZyl8Qq1-NZpZCiszgUbN0dbRTOoqtEqg8-3HACohM/s1600/LegoFX.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"></a></div>
<div style="text-align: center;">
<br /></div>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">Values from the NXT such as battery level (in mV), speaker volume level and connection status are read with the <i>device </i>REST service. To set the volume, a new REST service is used.</span><br />
<br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">I should explain here the model behind this application, as it connects the UI part (FXML, the controller, the controls) with the REST requests from the client to the server part. There're a few tricky things here, as the controls values (JavaFX thread) are bound to the readings of the REST responses (standard Java thread).</span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">But due to the clearly exceded length of this post, I must stop here, and leave it to a second part... So let me just add a short video showing what we've got of the NXTLegoFX application so far:</span><br />
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<br />
<div style="text-align: center;">
<span style="font-family: Verdana,sans-serif;"><video controls="controls" loop="loop" poster="https://dl.dropbox.com/u/27006338/LegoFX9.mp4" tabindex="0" width="500">
<source src="https://dl.dropbox.com/u/27006338/LegoFX9.mp4" type="video/mp4; codecs="avc1.42E01E, mp4a.40.2""></source>
<source src="https://dl.dropbox.com/u/27006338/LegoFX9.webm" type="video/webm; codecs="vp8, vorbis""></source>
Your browser does not support the video tag, view this video <a href="https://dl.dropbox.com/u/27006338/LegoFX9.mp4">here</a>.<br />
</video></span></div>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"><br /></span>
<span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">See you in the (hopefully coming </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">soon) next </span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;">part of this post! Love to hear any comments from you in the meantime.</span><span style="font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;"></span>Anonymoushttp://www.blogger.com/profile/15247383133952423416noreply@blogger.com1tag:blogger.com,1999:blog-7114525940495426773.post-77994822953306590802012-09-22T14:47:00.000+02:002012-09-23T18:03:25.097+02:00A multi-touching experience with a WeTab<span style="font-family: Georgia, "Times New Roman", serif;">Hi there!</span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">This time my post won't be strictly code related. Instead I'll share with you my recent experience with a <a href="http://wetab.mobi/en/">WeTab</a>, a tablet computer I have for a couple of years, trying to test a few JavaFX multitouch enabled samples from the <a href="http://jfxtras.org/">JFXtras</a> project, from Carl Dea (<a href="https://twitter.com/carldea">@carldea</a>) and Gerrit Grunwald (<a href="https://twitter.com/hansolo_">@hansolo_</a>).</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">First of all, let me introduce you to the WeTab tablet, for those of you who haven't heard of it yet. Instead of boring you with technical data, <a href="http://wetab.mobi/en/product/">here</a> you can find the specs, and <a href="http://my-maemo.com/reviews/review_wetab.php">here</a> you have a very good review of the WeTab. But in short, it includes a (big) 11.6" TN-panel touch screen (1366×768 resolution) powered with Intel GMA 3150 graphics and PCIe x1 Broadcom CrystalHD BCM70015, a 1.66 GHz Intel Atom N450 processor with fan, 1 GB RAM memory, 16 or 32 GB flash memory, WiFi and optional 3G, 2 USB ports, and a total weight of the device of 1.002 kg.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDW15mm_2BVJS0kyURQH4DYItGUz5Sh_kmeOJ87ALipq44vu2VHfBb9hFqUIbZtjSxNjvB5IDFW3p7Q3PxQrTyn_31C4mV0-AEQTdnb7wOOKIWZBsU26EJv0DtfZmyxx-8YPmSDfyVWEc/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="223" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDW15mm_2BVJS0kyURQH4DYItGUz5Sh_kmeOJ87ALipq44vu2VHfBb9hFqUIbZtjSxNjvB5IDFW3p7Q3PxQrTyn_31C4mV0-AEQTdnb7wOOKIWZBsU26EJv0DtfZmyxx-8YPmSDfyVWEc/s320/5.png" width="320" /></a></div>
<br />
<span style="font-family: Georgia, "Times New Roman", serif;"><span style="font-family: Georgia, "Times New Roman", serif;">I knew of it back in 2010, thanks to my partner Ricardo Vela (<a href="https://twitter.com/rikivela">@rikivela</a>),
when we were developing client and server Java based projects, and we
felt the necessity to go wireless with our client applications. So we
searched for a tablet where we could run java and he found this linux
based tablet with MeeGo technology. It was only sold in Germany by then,
so we have to buy it through Amazon.de store.</span></span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><span style="font-family: Georgia, "Times New Roman", serif;"> </span> </span><br />
<span style="font-family: Georgia, "Times New Roman", serif;">So at the beginning of 2011 I had in my hands this heavy tablet. It took me a while to refresh my rusty linux knowledge, but I was able to download and install the last JRE linux version, and test that my application ran on it really nice and smooth. I was so excited I took a (very bad) picture of it:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhdMlPcMntDx-5GpLTWBg7Ps9b31BV5mr7TR_hUwTt3qEa0HtvmbJJd4UZWgIuuQIlCbn5VyvlmcpofnmufqSV8MPcUe-CLGCXiMovZGYK5cxvG7U2bfoZZ-7T6NkmBI2hVALDsv7sDf0/s1600/DSCN9075.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhdMlPcMntDx-5GpLTWBg7Ps9b31BV5mr7TR_hUwTt3qEa0HtvmbJJd4UZWgIuuQIlCbn5VyvlmcpofnmufqSV8MPcUe-CLGCXiMovZGYK5cxvG7U2bfoZZ-7T6NkmBI2hVALDsv7sDf0/s320/DSCN9075.JPG" width="320" /></a></div>
<div style="text-align: center;">
<br /></div>
<span style="font-family: Georgia, "Times New Roman", serif;"></span><br />
<span style="font-family: Georgia, "Times New Roman", serif;">Even it had its plastic cover on it! I realized I have to fix the screen size of my app, but apart from that, you know what they say: <i>write once, run everywhere</i>.</span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">Then we had the chance to make an iOS version for iPhone and iPad tablet of the same project, and we found the pros and cons of it: The WeTab screen had nothing to do with the Retina Display of iOS devices... but we have to re-write all the client app to Objetive-C. Anyway, this is a story for another day.</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">For several reasons I had my WeTab parked somewhere, until recently, when Carl Dea tweeted about finding a linux tablet to run JavaFX. I offered myself, and then... the story begins...</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<b><span style="font-family: Georgia, "Times New Roman", serif;">Java, JavaFX and MeeGo</span></b><br />
<b><span style="font-family: Georgia, "Times New Roman", serif;"></span></b><br />
<span style="font-family: Georgia, "Times New Roman", serif;"></span>
<span style="font-family: Georgia, "Times New Roman", serif;">If you want to test Java and JavaFX based applications you have to install the last JRE in the tablet. There're plenty of blogs out there to show you how, but here it's what I do:</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">First of all, go to the WeTab Market (first icon you'll find on the desktop), Tools & Utilities section, and download </span><span style="font-family: Georgia, "Times New Roman", serif;"><span style="font-family: Georgia, "Times New Roman", serif;"> the Firefox Internet browser. It will install itself inmediately. Open it and go to <a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html">Oracle site</a> and download jre-7u7-linux-i586.rpm. It will be stored by default in<span style="font-family: "Courier New",Courier,monospace;"> /home/<user>/Downloads</span>.</span></span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;"><span style="font-family: Georgia, "Times New Roman", serif;">Download also </span>the RootShell app, so you can open a terminal window and enter the command line world.</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-cnhA7hGk2rHYzK1Mk95cRH-N62hyphenhyphengBvq7OgPPqdUir6wXomDQyo7Ueb3Bs1EJM8TrbczeMqtpoUS_4Y4b-srqzhH0LfkvKm0m8poVU6X90StxidWYzrePMTr_eL_SO9svFtiytEJdws/s1600/2012-09-22_18-04-28-840.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="179" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-cnhA7hGk2rHYzK1Mk95cRH-N62hyphenhyphengBvq7OgPPqdUir6wXomDQyo7Ueb3Bs1EJM8TrbczeMqtpoUS_4Y4b-srqzhH0LfkvKm0m8poVU6X90StxidWYzrePMTr_eL_SO9svFtiytEJdws/s320/2012-09-22_18-04-28-840.png" width="320" /></a></div>
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">Write and insert password when asked:</span><br />
<span style="font-family: "Courier New",Courier,monospace;">#cd Downloads</span><br />
<span style="font-family: "Courier New",Courier,monospace;">#sudo rpm -ivh jre-7u7-linux-i586.rpm</span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">Then the whole package will be installed in <span style="font-family: "Courier New",Courier,monospace;">/usr/java/jre1.7.0_07</span>. Also the links <span style="font-family: "Courier New",Courier,monospace;">/usr/java/default</span> and <span style="font-family: "Courier New",Courier,monospace;">/usr/java/latest</span> will be added. Ignore a few errors shown at the end of the process.</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">To add the JRE to the path, edit the file:</span><br />
<span style="font-family: "Courier New",Courier,monospace;">#sudo gedit java.sh </span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">add the following: </span><br />
<span style="font-family: "Courier New",Courier,monospace;">#!/bin/sh<br />export PATH=/usr/java/default/bin:$PATH<br />export JAVA_HOME=/usr/java/default </span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">save and close it, and install it:</span><br />
<span style="font-family: "Courier New",Courier,monospace;">#sudo install -m755 java.sh /etc/profile.d</span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">Close the console, and you're ready to try any Java or JavaFX application.</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<b><span style="font-family: Georgia, "Times New Roman", serif;">SlideLockSample. First try</span></b><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">In my desktop I create a sample class called SlideLockSample1, which I compile and build and then I copy the jar file to the WeTab, with an USB flash drive, for instance. This is part of the <span style="font-family: "Courier New",Courier,monospace;">start</span> method:</span><br />
<br />
<pre class="brush:java"> @Override
public void start(Stage primaryStage) {
final Label lbl=new Label("Locked!");
lbl.styleProperty().set("-fx-text-fill: red;-fx-font: 1.5em \"Arial Bold\";");
SlideLock slideLock = new SlideLock("slide to JavaFX");
slideLock.setBackgroundVisible(true);
slideLock.lockedProperty().addListener(new ChangeListener< boolean >() {
@Override
public void changed(ObservableValue observable, Boolean oldValue, Boolean newValue) {
if (!newValue) {
lbl.styleProperty().set("-fx-text-fill: blue");
lbl.textProperty().set("Unlocked!!!!");
} else {
lbl.styleProperty().set("-fx-text-fill: red");
lbl.textProperty().set("Locked!");
}
}
});
}</pre>
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<br />
<span style="font-family: Georgia, "Times New Roman", serif;"><a href="https://dl.dropbox.com/u/27006338/slidelocksample1.rar">Here</a> you have the code and the jar from <a href="https://github.com/JFXtras/jfxtras-labs">JFXtras project</a>, with the very last commits.</span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">And here you have a small video taken with my iPhone showing how I slide the control with my finger, after starting the project with <span style="font-family: "Courier New",Courier,monospace;">java -jar SlideLockSample1.jar</span> in the console.</span><br />
<div style="text-align: center;">
<span style="font-family: Verdana,sans-serif;"><video controls="controls" loop="loop" poster="https://dl.dropbox.com/u/27006338/IMG_1805.mp4video.mp4" tabindex="0" width="400">
<source src="https://dl.dropbox.com/u/27006338/IMG_1805.mp4video.mp4" type="video/mp4; codecs="avc1.42E01E, mp4a.40.2""></source>
<source src="https://dl.dropbox.com/u/27006338/IMG_1805.webmvp8.webm" type="video/webm; codecs="vp8, vorbis""></source>
Your browser does not support the video tag, view this video <a href="https://dl.dropbox.com/u/27006338/IMG_1805.mp4video.mp4">here</a>.<br />
</video></span></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">I was excited to see the touch event working... until I realized it was in fact a simple mouse event. To test that I commented first the touch events in <span style="font-family: "Courier New",Courier,monospace;">addHandlers()</span> method in <span style="font-family: "Courier New",Courier,monospace;">SlideLockSkin </span>class:</span><br />
<br />
<pre class="brush:java">private void addHandlers() {
// MouseEvents
setOnMousePressed(mouseHandler);
setOnMouseDragged(mouseHandler);
setOnMouseReleased(mouseHandler);
// TouchEvents
setOnTouchPressed(touchHandler);
setOnTouchMoved(touchHandler);
setOnTouchReleased(touchHandler);
}
</pre>
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">And ran the jar again, working as it did the first time. So then I uncommented touch events and commented mouse events. Guess what? It didn't work.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span><span style="font-family: Georgia,"Times New Roman",serif;">So my multitouch tablet was only handling single touch events as pure mouse events. I found googling that with the last touchscreen firmware (which I had) the multitouch was supported <just> in the browser, the maps and the Picture Viewer app. I tested this one, and it really had zoom, rotation, scrolling gestures working. You can find it in the WeTab Market, Entertainment and Multimedia section.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">What about other applications? Nothing at all.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Quite dissapointed I looked for other options. I found a really good WeTab blog <a href="http://wetabz.blogspot.com/">here</a>, and following his <a href="http://wetabz.blogspot.com/2011/02/more-about-touchsceen-and-multitouch.html">post</a> about multitouch I took the plunge and installed Ubuntu in my WeTab, now with </span><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">instructions from </span>this other <a href="http://wetabz.blogspot.com.es/2011/02/wetab-ubuntu-installation.html">post</a>.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<b><span style="font-family: Georgia,"Times New Roman",serif;">Partitioning the WeTab</span></b><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">As I wanted to keep the WeTab OS, I had to partition the 32 GB disk. For that I used GParted software, which I could download from <a href="http://sourceforge.net/projects/gparted/files/gparted-live-stable/0.13.1-2/gparted-live-0.13.1-2.iso/download">here</a>. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Shortly, from Windows OS, you have to unzip the content to the USB flash drive, open a command line window with administrator privileges. Then let's say </span><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">USB drive is H:, type <span style="font-family: "Courier New",Courier,monospace;">H:</span>, <span style="font-family: "Courier New",Courier,monospace;">cd </span></span><span style="font-family: "Courier New",Courier,monospace;">utils/win32</span>, and run <span style="font-family: "Courier New",Courier,monospace;">makeboot.bat</span> to install a hidden file and make the flash drive bootable.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Then comes the tricky part... Plug in the flash drive in the WeTab, turn it on and as soon as you see the blue led in the top left corner light up, press both the power-button and the quicktouch orange button (top left corner) together for approximately 1 second, and release them. GParted Live should boot up from the USB flash drive.<br /><br />Then use the quicktouch orange button on the upper left corner of the WeTab to browse through the Boot menu for Gparted. One short touch switches between options. Select "other modes of Gparted", </span><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">hold down a second on the quicktouch button, it selects the entry. Then choose</span> "Run from RAM or memory". It takes a while to load, but at the end it shows a graphical application with all the partitions. Select the <b>sda3 </b>partition (the biggest one) and then Resize. I resized mine to allow 8 GB for Ubuntu. Select the unasigned space and create a new partition with ext3 format (<b>sda4</b>).</span><br />
<br />
<b><span style="font-family: Georgia,"Times New Roman",serif;">Installing Ubuntu</span></b><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Download last Ubuntu version, 12.04, from <a href="http://www.ubuntu.com/netbook/get-ubuntu/download">here</a>. To create a bootable USB drive from Windows, also download and install <a href="http://www.pendrivelinux.com/downloads/Universal-USB-Installer/Universal-USB-Installer-1.9.1.1.exe">this</a>. Then open it, locate the ubuntu image, select the flash drive and click on Create.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Now you have to plug in the flash drive in the WeTab, turn it on, press both power and quicktouch buttons for a second, and when the installer boots up, follow the process to install Ubuntu. At the end of the process, you should modify the WeTab booting options to select WeTab OS or Ubuntu.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Finally, in order to allow multitouch events, following Etheros blog, I opened a console and typed:</span><br />
<span style="font-family: "Courier New",Courier,monospace;">sudo add-apt-repository ppa:utouch-team/utouch</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Guess what? The repository was gone... You can check it <a href="https://launchpad.net/~utouch-team/+archive/utouch">here</a>, the page is not found.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">That's one of the thing I fear from Linux... you're always depending on some team's work, which suddenly may disappear.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Belive me I tried other options from the repository, but nothing worked, although I must say probably I missed something as clearly I am not a Linux expert.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">Anyway, I installed Java, pretty much as described before, and it happened the same, the touch event was treated like a mouse event. No multitouch at all, as expected.</span></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">So I took another approach...</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<b><span style="font-family: Georgia,"Times New Roman",serif;">Installing Windows 8</span></b><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">A friend of mine told me about the native support of Windows 7 and Windows 8 for touchscreens, so I decided to give it a try, as the WeTab is x86 based.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">So I downloaded Windows 8 Release Preview from <a href="http://go.microsoft.com/fwlink/?LinkId=251533">here</a>, and unzipped it to the USB flash drive. <span style="font-family: Georgia,"Times New Roman",serif;">Plugged in the flash
drive in the WeTab, turned it on, pressed both power and quicktouch buttons
for a second, and followed the process to
install Windows 8 (in my case, in the sda4 partition, overwritting Ubuntu). At the end of the process the new interface of Windows was ready, with multitouch gestures support.</span></span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">I won't go any further with that... Just two things: I ran out space! The installation took the 8 GB and left 90 MB free!! And there were no way to boot from WeTab anymore...</span></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"><br /></span></span>
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">Well, I had to start all over again... Booting with GParted Live, I deleted sda4 partition, resized sda3 to have at least 16 GB of free space, created a new NTFS partition and installed again Windows 8.</span></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"><br /></span></span>
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">To allow booting from WeTab OS, I found the EasyBCD software <a href="http://neosmart.net/EasyBCD/">here</a>, which allows to create a bootable menu from Windows. Just add a new Entry, select Bios Extender and install PLOP.</span></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"><br /></span></span>
<b><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">Java and JavaFx, second try</span></span></b><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"><br /></span></span>
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">Downloading and installing JRE as usually in Windows, I was finally ready to test JavaFX multitouch gestures!</span></span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">To end this long post, let me just show you two samples I've tested from JFXtras project. Also you may have a look at <a href="http://docs.oracle.com/javafx/2/events/gestures.htm">this</a> tutorial from JavaFX documentation.</span></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"><br /></span></span>
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">This is the SlideLock Sample video I've taken with my iPhone, so excuse me for its bad quality:</span></span><br />
<div style="text-align: center;">
<span style="font-family: Verdana,sans-serif;"><video controls="controls" loop="loop" poster="https://dl.dropbox.com/u/27006338/IMG_1810.mp4video.mp4" tabindex="0" width="400">
<source src="https://dl.dropbox.com/u/27006338/IMG_1810.mp4video.mp4" type="video/mp4; codecs="avc1.42E01E, mp4a.40.2""></source>
<source src="https://dl.dropbox.com/u/27006338/IMG_1810.webmvp8.webm" type="video/webm; codecs="vp8, vorbis""></source>
Your browser does not support the video tag, view this video <a href="https://dl.dropbox.com/u/27006338/IMG_1810.mp4video.mp4">here</a>.<br />
</video></span></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">You can see real touch events in action. Really smooth. <span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"><a href="https://dl.dropbox.com/u/27006338/slidelocksample2.rar">Here</a> you may find the surce code for the SlideLockSample2. <span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">Great job Carl!! </span></span></span></span></span></span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">And this is the Gauge Radial Sample video:</span><br />
<br />
<div style="text-align: center;">
<span style="font-family: Verdana,sans-serif;"><video controls="controls" loop="loop" poster="https://dl.dropbox.com/u/27006338/IMG_1804.mp4video.mp4" tabindex="0" width="400">
<source src="https://dl.dropbox.com/u/27006338/IMG_1804.mp4video.mp4" type="video/mp4; codecs="avc1.42E01E, mp4a.40.2""></source>
<source src="https://dl.dropbox.com/u/27006338/IMG_1804.webmvp8.webm" type="video/webm; codecs="vp8, vorbis""></source>
Your browser does not support the video tag, view this video <a href="https://dl.dropbox.com/u/27006338/IMG_1804.mp4video.mp4">here</a>.<br />
</video></span></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">Now, you can see rotate gestures in action. For the code, just look at Gerrit's blog <a href="http://harmoniccode.blogspot.com.es/2012/09/touch-me.html">here</a>. <span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">As always, great work, Gerrit. </span></span></span></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"><br /></span></span>
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">Well, sure you've noticed in the video the slow performance of the radial... </span></span><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">Don't blame Gerrit... As he pointed out, the </span></span>issue here is <i>hardware vs software acceleration</i>.</span></span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">If you look at JavaFX 2.2 system requirements <a href="http://docs.oracle.com/javafx/2/system_requirements_2-2/jfxpub-system_requirements_2-2.htm">here</a></span></span><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"> you'll read </span></span>
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">that for JavaFX applications to take advantage of the new hardware acceleration pipeline provided by JavaFX, your system must feature one of a wide range of GPUs currently available in the market. For Intel graphics cards, as mine, you need at least GMA 4500MHD and GMA HD. </span></span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">But the WeTab card is GMA 3150, <b>so that's why if the system does not support hardware acceleration, then JavaFX uses the Java2D software pipeline</b>. Here the Atom N450 does its best...</span></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"></span></span><br />
<b><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">Conclusions</span></span></b><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">First of all, excuse me for this long post.</span></span>
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">I've tried to talk about many things... You can always skip most of them and go to a part of your interest.</span></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"></span></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">Second, you can see there's plenty of room to experiment here... Please, feel free to share any experience!</span></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"><br /></span></span>
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">Finally, about multitouch gestures, although they look astonishing, there's lot to do in many devices and OSes. In this early stages, you should find the proper combination of hardware and software. A bad choice can ruin your work. That's where tablets like the WeTab have their pros, easily you can make a light demo of your projects. </span></span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">And it's for another discussion <span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">what happens with Java Embedded and JavaFX for
more limited architectures like ARM platforms, with devices like Raspberry Pi.</span></span></span></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"> </span></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>Anonymoushttp://www.blogger.com/profile/15247383133952423416noreply@blogger.com4tag:blogger.com,1999:blog-7114525940495426773.post-88276135543549400002012-09-14T17:27:00.000+02:002012-09-14T19:32:27.428+02:00A JavaFX weekly scheduler<span style="font-family: Georgia, "Times New Roman", serif;">Hi there!</span><br />
<br />
<span style="font-family: Georgia, "Times New Roman", serif;">This post pretends to be a detailed tutorial of how you can use custom JavaFX controls to make a nice UI, taking advantage of the <a href="http://www.oracle.com/technetwork/java/javafx/tools/index.html">JavaFX Scene Builder</a>. </span><br />
<span style="font-family: Georgia, "Times New Roman", serif;"><br /></span>
<span style="font-family: Georgia, "Times New Roman", serif;">As custom control I'll use a slightly modified DoubleSlider control, which originally was developed by Altuğ Uzunali by modifying the Slider Control in <a href="http://openjdk.java.net/projects/openjfx/">OpenJFX project</a>. You can see his blog <a href="http://javafxwidgets.blogspot.com.es/">here</a>, and download his code <a href="http://code.google.com/p/javafx-widgets/downloads/list">here</a>. Nice work, Altuğ!</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">As UI I'll try to develop a weekly scheduler for working or bussines hours, using one double slider to set beginning and ending hours for each day of the week.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">Here you can see in advance the result. If you find it interesting and want to learn how can you do it yourself, please, keep reading!</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlb8Zryc1Tnlt6TDlTVAYhsQAKOX2mjyP7Y9-iW0d-vmxppuTryBhXQQYS7JaqWTkWTS6URz-jCEuflHR8WIJeOyvE9HBX601SlWnS1VwyrkhfQu9CxIlq7ymKybrLQ9m144bAHtcJhp8/s1600/Final+UI.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="316" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlb8Zryc1Tnlt6TDlTVAYhsQAKOX2mjyP7Y9-iW0d-vmxppuTryBhXQQYS7JaqWTkWTS6URz-jCEuflHR8WIJeOyvE9HBX601SlWnS1VwyrkhfQu9CxIlq7ymKybrLQ9m144bAHtcJhp8/s400/Final+UI.png" width="400" /> </a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;"><b>Step 1. Create the project</b></span></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<span style="font-family: Georgia,"Times New Roman",serif;">For starters, let's make a new project in <a href="http://netbeans.org/">NetBeans </a>7.2, selecting the <i>JavaFX FXML Application </i>type.</span> <span style="font-family: Georgia,"Times New Roman",serif;">Let's call the project <span style="font-family: "Courier New",Courier,monospace;">WeeklySchedulerFX</span>. Also, the FXML file will be called <span style="font-family: "Courier New",Courier,monospace;">scheduler</span>:</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzdANycDG-4mLoW0epkW5c12HvdmE1r3A2szUdFQiHSR7BqjiJ6SAAhlHolQ8spbDGimtPAyGTuBI-GOL71xuKKyuRjsOwTFLiK9h8YKPpwSqs5q7U9ryKFX84juSaCLE-CjCef0INfLo/s1600/Step+1-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="263" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzdANycDG-4mLoW0epkW5c12HvdmE1r3A2szUdFQiHSR7BqjiJ6SAAhlHolQ8spbDGimtPAyGTuBI-GOL71xuKKyuRjsOwTFLiK9h8YKPpwSqs5q7U9ryKFX84juSaCLE-CjCef0INfLo/s400/Step+1-2.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmm8WviBsmE5ZKl6CbHUdOocA3epCTtAa8fdLmrGioxtg4mYMOGeItbsE597RTfbSUm7qyJjAPxfqteuHyxEWP86eIliuU4rzo9DKY_hCfm_aUlmO1ndxRt0K6i_axbeIyU13BHa8J7vI/s1600/Step+1-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"></a></div>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">At end of the wizard three files will be created:</span> <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwM_VHstU3AfX7QC_WIyWOw6MDDKC3tlhMLfwrWMFTUr4xjuTjXblZpR3GAgSM9Yw0h4uRnop9FHi5-7LqMvV5XgFNgFuidIydwHsEPo79jrqOP2f50w3_wDoNfrPV7toIdFs7hw0ex7c/s1600/Step+1-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwM_VHstU3AfX7QC_WIyWOw6MDDKC3tlhMLfwrWMFTUr4xjuTjXblZpR3GAgSM9Yw0h4uRnop9FHi5-7LqMvV5XgFNgFuidIydwHsEPo79jrqOP2f50w3_wDoNfrPV7toIdFs7hw0ex7c/s1600/Step+1-3.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<b><span style="font-family: Georgia,"Times New Roman",serif;">Step 2. Create the UI</span></b></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;">By double clicking in scheduler.fxml the JavaFX Scene Builder will be open. Now we have to delete what it contains by default (a button and a label) and keep the AnchorPane, and start designing our own scene, by first resizing it to 600x450 dragging it or in the Layout panel, setting Pref width to 600 and Pref height to 450.</span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;">Now, let's add the label for the title. It has the following settings:</span><span style="font-family: Georgia,"Times New Roman",serif;"> </span></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;">Properties panel: </span></div>
<ul>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Text: Weekly Scheduler - Working Hours</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Alignment: CENTER</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Effect: DropShadow, spread 0.20, color #956e07, </span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Font: System 16px (Bold)</span></li>
</ul>
<span style="font-family: Georgia,"Times New Roman",serif;"> Layout panel:</span><br />
<ul>
<li><span style="font-family: Georgia,"Times New Roman",serif;">AnchorPane Constraints: Top 30, Right 20</span><span style="font-family: Georgia,"Times New Roman",serif;">, Left 20</span><span style="font-family: Georgia,"Times New Roman",serif;">.</span></li>
</ul>
<span style="font-family: Georgia,"Times New Roman",serif;"></span><br />
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;">Now, we need 7 rows for the doubleSlider controls, and a place to locate the labels (day and range of hours selected), so let's use a GridPane with 7 rows and 2 columns. In the Properties panel, first of all, we set the fx:id for this control, so we can use it in our code later.</span></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;">Properties panel: </span></div>
<ul>
<li><b><span style="font-family: Georgia,"Times New Roman",serif;">fx:id: grid</span></b></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Alignment: CENTER</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Vgap: 2 </span></li>
</ul>
<span style="font-family: Georgia,"Times New Roman",serif;"> Layout panel:</span><br />
<ul>
<li><span style="font-family: Georgia,"Times New Roman",serif;">AnchorPane Constraints: Top 70, Right 20</span><span style="font-family: Georgia,"Times New Roman",serif;">, Bottom 20, Left 20</span><span style="font-family: Georgia,"Times New Roman",serif;">.</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">add Row[3], Row [4], Row [5] and Row [6]</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Column [0], Pref Width=100, Hgrow NEVER</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Column [1], <span style="font-family: Georgia,"Times New Roman",serif;">Pref Width=460, Max Width: USE_COMPUTED_SIZE, Hgrow ALWAYS</span></span></li>
</ul>
<span style="font-family: Georgia,"Times New Roman",serif;"></span><br />
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;">This is what we have for now:</span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQni0sHSVDUMgYpVqYRArDAEIzASqhw5bA0RUrhF2J0GNzp0KHnuNm3w-G-0KJAWOpp-G1UVzXq5bYT8L-O83Pzguij7IyKpba1ZAnUk34Y4s2aI_UnWHCJaiQ3jPYvNGoCBaAbD-basU/s1600/Step+2-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="251" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQni0sHSVDUMgYpVqYRArDAEIzASqhw5bA0RUrhF2J0GNzp0KHnuNm3w-G-0KJAWOpp-G1UVzXq5bYT8L-O83Pzguij7IyKpba1ZAnUk34Y4s2aI_UnWHCJaiQ3jPYvNGoCBaAbD-basU/s320/Step+2-2.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;">Although the GridPane is a container and we could </span><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">just <span style="font-family: "Courier New",Courier,monospace;">add </span>the controls to the specified row and column</span>, we can add to each cell another container, so we can insert later on the custom control or other controls from code, just looping through the gridPane children. </span></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;">Let's add a VBox to each cell, and set for all of them</span><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"> Alignment: CENTER</span>. For the first column, also we set Spacing: 5 and Padding 5-5-5-5. For the second column, set Padding: 10-10-10-10.</span></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;">Now, in the first column, we can add a label with the day of the week. For the first one:</span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;">Properties panel: </span></div>
<ul>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Text: MONDAY</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Effect: DropShadow </span></li>
</ul>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;">and so on. </span></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;">That's all for the Java Scenic Builder. We can close it and move on to the code part. You should have something like this:</span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWkz_PxRA7PKEdsuR7U5cTg01z6GkxxaqeaGofoUc1TaciGIpqNiGwNtzy9NCyrhjEnSg96tNKEFcP2_vxr2pnruAiu73E1vx47pTtNdrtl7WpYZ0hLL_ClJv3KtZfqfIZAjaUFnvwy8A/s1600/Step+2-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="251" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWkz_PxRA7PKEdsuR7U5cTg01z6GkxxaqeaGofoUc1TaciGIpqNiGwNtzy9NCyrhjEnSg96tNKEFcP2_vxr2pnruAiu73E1vx47pTtNdrtl7WpYZ0hLL_ClJv3KtZfqfIZAjaUFnvwy8A/s320/Step+2-3.png" width="320" /></a></div>
<br />
<b><span style="font-family: Georgia,"Times New Roman",serif;">Step 3. DoubleSlider custom control</span></b><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<span style="font-family: Georgia,"Times New Roman",serif;">First of all, we download <span style="font-family: Georgia, "Times New Roman", serif;">Altuğ Uzunali's code, and in our project we create a package called doubleSlider and insert DoubleSlider.java, DoubleSliderBehavior.java, DoubleSliderSkin.java and double_slider.css files there. Make sure you change the package declaration to <span style="font-family: "Courier New",Courier,monospace;">weeklyschedulerfx.doubleSlider</span> in the java files, and set the -fx-skin property:</span></span><br />
<br />
<pre class="brush:css">.double-slider
{
-fx-skin: "weeklyschedulefx.doubleSlider.DoubleSliderSkin";
}
</pre>
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>i<span style="font-family: Georgia,"Times New Roman",serif;">n the css one. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Now we are going to make a few changes in his code. (<i>For shortness sake I won't comment here every change I make, but there are very few; please have a look at the code you can download below</i>)</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">In order to display time formats instead of numbers in the ticks of the slider, i</span>n <span style="font-family: "Courier New",Courier,monospace;">DoubleSliderSkin </span>class, in the <span style="font-family: "Courier New",Courier,monospace;">setShowTickMarks </span>method, insert this code just after the <span style="font-family: "Courier New",Courier,monospace;">tickLine.setMinorTickCount</span> line:</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<pre class="brush:java">if (doubleSlider.getLabelFormatter() != null){
tickLine.setTickLabelFormatter(new StringConverter<number>(){
@Override
public String toString(Number object) {
return getSkinnable().getLabelFormatter().toString((Double)object);
}
@Override
public Number fromString(String string) {
return getSkinnable().getLabelFormatter().fromString(string);
}
});
}
</pre>
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Let's add now a node to the control in order to highlight the range of hours between the two thumbs. In <span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: "Courier New",Courier,monospace;">DoubleSliderSkin </span>class, declare it:</span></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<pre class="brush:java">private StackPane range;</pre>
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Initialize it (</span><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">in <span style="font-family: "Courier New",Courier,monospace;">initialize </span><span style="font-family: "Courier New",Courier,monospace;"></span>method) </span>and add it with the other stackpanes (both in <span style="font-family: "Courier New",Courier,monospace;">initialize </span>and <span style="font-family: "Courier New",Courier,monospace;">setShowTickMarks </span>methods) to the scene: </span><br />
<br />
<pre class="brush:java">range = new StackPane();
range.getStyleClass().setAll("range");
getChildren().clear();
getChildren().addAll(track, thumb1, thumb2, range);
</pre>
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Now we have to set the <span style="font-family: "Courier New",Courier,monospace;">range </span>layout. Every time the thumb's layout change the method <span style="font-family: "Courier New",Courier,monospace;">positionThumb </span>is called. So there insert the following code:</span><br />
<br />
<pre class="brush:java">if(horizontal){
range.setLayoutX(lx1+thumbWidth/2);
range.setLayoutY(track.getLayoutY());
range.setPrefWidth(lx2-lx1);
range.resize(lx2-lx1, track.getHeight());
} else{
range.setLayoutX(track.getLayoutX());
range.setLayoutY(ly2+3*thumbHeight/4);
range.setPrefHeight(Math.abs(ly2-ly1+thumbHeight/2));
range.resize(track.getWidth(),Math.abs(ly2-ly1+thumbHeight/2));
}
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">In the <span style="font-family: "Courier New",Courier,monospace;">layoutChildren </span>method call positionThumb <i>after </i>the track is resized and relocated, not before:</span><br />
<br />
<pre class="brush:java">// layout track
track.resizeRelocate(trackStart - trackRadius, trackTop , trackLength + trackRadius + trackRadius, trackHeight);
// layout thumbs and range
positionThumb();
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Finally we add the styling properties to the css file (a few more are in the file below):</span><br />
<br />
<pre class="brush:css">.double-slider Text {
-fx-text-fill: #303030;
}
.double-slider .range {
-fx-base: #956e07;
-fx-background-color: -fx-base,
derive(-fx-base,-22%),
linear-gradient(to bottom, derive(-fx-base,-15.5%), derive(-fx-base,34%) 30%, derive(-fx-base,68%));
-fx-background-insets: 1 0 -1 0, 0, 1;
-fx-padding: 0.208333em; /* 2.5 */
}
.double-slider .axis {
-fx-tick-label-fill: #303030;
}
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">And that's it!</span><br />
<br />
<b><span style="font-family: Georgia,"Times New Roman",serif;">Step 4. Application setup</span></b><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Back to our application, we need to add to the Stylesheets the custom control one, and set the stage title:</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span>
<br />
<pre class="brush:java">Scene scene = new Scene(root);
scene.getStylesheets().addAll(DoubleSlider.class.getResource("double_slider.css").toExternalForm());
stage.setScene(scene);
stage.setTitle("JavaFX Tutorial");
</pre>
<span style="font-family: Georgia,"Times New Roman",serif;"><br />
</span><b><span style="font-family: Georgia,"Times New Roman",serif;">Step 5. The Controller</span></b><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">First of all, we add the GridPane container defined in the fxml file:</span><br />
<br />
<pre class="brush:java">@FXML
private GridPane grid;
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">In the initialize method now we loop through the grid children. In the first seven nodes, related to the first column, we add to each VBox a new label which will show the selected range in the format [00:00 - 24:00]. </span><br />
<br />
<pre class="brush:java">final Label[] rangeLabel=new Label[7];
int cont=0;
for(Node n:grid.getChildren()){
if(n instanceof VBox){
VBox cell=(VBox)n;
if(cont<7){
rangeLabel[cont]=new Label();
rangeLabel[cont].textProperty().set("[00:00 - 00:00]");
cell.getChildren().add(rangeLabel[cont]);
}
...
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">In the last seven nodes, we add the doubleSlider controls. We want to show a range of hours and half hours, so we have 2*24+ 1 tick marks, and that's why we set the min value to 0 and the max value to 48. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Also, the unit distance between major tick marks is set to 2 (hours), and the number
of minor ticks between any two major ticks is specified as 1 (half hour). The <code>setSnapToTicks</code> method is set to <code>true</code> to keep the slider's value aligned with the tick marks.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"> </span>
<br />
<pre class="brush:java">...
else {
final int day=cont-7;
final DoubleSlider doubleSlider1 = new DoubleSlider();
doubleSlider1.setPrefWidth(300);
doubleSlider1.setShowTickMarks(true);
doubleSlider1.setShowTickLabels(true);
doubleSlider1.setMajorTickUnit(2);
doubleSlider1.setMinorTickCount(1);
doubleSlider1.setSnapToTicks(true);
doubleSlider1.setMin(0);
doubleSlider1.setMax(48);
doubleSlider1.setLabelFormatter(new StringConverter<double>(){
@Override
public String toString(Double object) {
if(object==null){
return null;
}
return toTime(object.doubleValue());
}
@Override
public Double fromString(String string) {
return (string!=null?new Double(string):new Double(0));
}
});
doubleSlider1.value1Property().addListener(new ChangeListener<number>() {
@Override
public void changed(ObservableValue arg0,
Number arg1, Number arg2) {
rangeLabel[day].textProperty().set("["+toTime(arg2.intValue())+" - "+toTime(doubleSlider1.getValue2())+"]");
}
});
doubleSlider1.value2Property().addListener(new ChangeListener<number>() {
@Override
public void changed(ObservableValue arg0,
Number arg1, Number arg2) {
rangeLabel[day].textProperty().set("["+toTime(doubleSlider1.getValue1())+" - "+toTime(arg2.intValue())+"]");
}
});
}
</number></number></double></pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">You can see that we set the labelFormatter to map the double value of the thumb position to a time string. For this, a simple string formatter is used:</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"> </span>
<span style="font-family: Georgia,"Times New Roman",serif;">
</span>
<br />
<pre class="brush:java">private String toTime(double value){
return String.format("%02d:%02d", (int)(value/2), (int)(30*(value%2)));
}
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Also, we add a valueProperty() listener for each thumb so we can reflect their position changes in the proper rangeLabel.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Finally, we decorate the cells, taking here the advices of Jasper Potts in his </span><span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"> the excellent post </span><a href="http://fxexperience.com/2011/12/styling-fx-buttons-with-css/">Styling FX Buttons with CSS</a>:</span>
<br />
<pre class="brush:java"> </pre>
<pre class="brush:java">cell.setStyle("-fx-background-color: #ecebe9,rgba(0,0,0,0.05),linear-gradient(#dcca8a, #c7a740),"
+ "linear-gradient(#f9f2d6 0%, #f4e5bc 20%, #e6c75d 80%, #e2c045 100%),"
+ "linear-gradient(#f6ebbe, #e6c34d);"
+ "-fx-background-radius: 4;-fx-background-insets: 0,2 2 1 2,2,3,4;");
</pre>
<br />
<b><span style="font-family: Georgia,"Times New Roman",serif;">End</span></b><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">And this is pretty much everything you need to do to have a cool control and a really nice UI up and running in a few hours! Even minutes if you grab the code <a href="https://dl.dropbox.com/u/27006338/WeeklySchedulerFX.rar">here</a>!</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Now it's up to you to make any changes you may have missed here.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Please let me know if you have any comment or suggestion. They would be much appreciated!</span>Anonymoushttp://www.blogger.com/profile/15247383133952423416noreply@blogger.com0tag:blogger.com,1999:blog-7114525940495426773.post-3583788578575520802012-08-02T21:20:00.000+02:002012-08-02T21:20:34.551+02:00Traffic Signs. MatrixPanel samples for the summer<div style="font-family: Georgia,"Times New Roman",serif;">
Hi everyone! </div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
If you're reading this, maybe you, like me, are not on vacation right now... but the most of you should be spending these days abroad, so perhaps you're checking this blog with your hotel's WiFi...</div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
For those of you who have taken the car to travel to different countries, this short post is about a traffic sign display we have in Spain's highways, warning the drivers not to exceed the speed limit: 120 km/h top, or even 100 km/h in some places. Sanctions apply otherwise... </div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
So let's use our MatrixPanel JavaFX UI control (read <a href="http://jperedadnr.blogspot.com.es/2012/07/hi-this-is-my-first-javafx-post-so-dont.html">Part 1</a> or <a href="http://jperedadnr.blogspot.com.es/2012/07/matrixpanel-new-javafx-ui-control.html">Part 2</a> of my previous posts if you don't know about it yet) to warn scatterbrained drivers...</div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
For starters, here is a vertical sign display:</div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiA_j8egoY2JoEziNt0SZh-jl7RB1uP-WMo5U2ukw7vhr___DGku2zZBHqmOjRvonXxPLQHZILTSE_tThpzpmE4jPHGt6sO3KCr2xH1sHldsCtivugcwenHZrMhKKRiPW0_uTkuQkt_lBs/s1600/120.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiA_j8egoY2JoEziNt0SZh-jl7RB1uP-WMo5U2ukw7vhr___DGku2zZBHqmOjRvonXxPLQHZILTSE_tThpzpmE4jPHGt6sO3KCr2xH1sHldsCtivugcwenHZrMhKKRiPW0_uTkuQkt_lBs/s1600/120.png" /></a></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
And now, the traffic sign LEDs display you'll find in highways. It's usually only in Spanish, so the translation is an improvment of this digital version.</div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="text-align: center;">
<span style="font-family: Verdana,sans-serif;"><video autoplay="autoplay" controls="controls" height="150" loop="loop" poster="https://dl.dropbox.com/u/27006338/traffic.mp4" preload="" tabindex="0" width="600">
<source src="https://dl.dropbox.com/u/27006338/traffic.mp4" type="video/mp4; codecs="avc1.42E01E, mp4a.40.2"">
<source src="https://dl.dropbox.com/u/27006338/traffic.webm" type="video/webm; codecs="vp8, vorbis"">
Your browser does not support the video tag, view this video <a href="https://dl.dropbox.com/u/27006338/traffic.mp4">here</a>.<br />
</source></source></video></span></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br />
If you're interested in the code behind this digital sign versions, <a href="https://dl.dropbox.com/u/27006338/signCode.rar">here</a> you'll find the images and the sample class. You need a jar you'll find it in the <a href="https://github.com/JFXtras">JFXtras</a> jfxtras-ensemble project. Please, change the path to the images accordingly.<br />
<br />
That's all for now! Enjoy your vacation if you can, and remember: don't drive fast if you see these signs!!<br />
<br />
<br /></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>Anonymoushttp://www.blogger.com/profile/15247383133952423416noreply@blogger.com0tag:blogger.com,1999:blog-7114525940495426773.post-77418902702610716872012-07-26T23:57:00.001+02:002012-07-28T21:46:25.019+02:00MatrixPanel, a new JavaFX UI control. Second part<div style="font-family: Georgia,"Times New Roman",serif; text-align: left;">
Hi again! </div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
In this second post of this two part series I'll show you how easy is to bring to 'live' the MatrixPanel control, once we learnt in the first post how to create one and insert text or image content. Those of you who still haven't read it, please do it before, <a href="http://jperedadnr.blogspot.com.es/2012/07/hi-this-is-my-first-javafx-post-so-dont.html">here</a>.</div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
So let's talk about applying animation effects to the contents in the MatrixPanel control, starting with our first sample (in low quality to minimize size, sorry):</div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="text-align: center;">
<span style="font-family: Verdana,sans-serif;"><video autoplay="autoplay" controls="controls" height="90" loop="loop" poster="https://dl.dropbox.com/u/27006338/matrixPanel1.mp4" preload="" tabindex="0" width="400">
<source src="https://dl.dropbox.com/u/27006338/matrixPanel1.mp4" type="video/mp4; codecs="avc1.42E01E, mp4a.40.2""></source>
<source src="https://dl.dropbox.com/u/27006338/matrixPanel1.webm" type="video/webm; codecs="vp8, vorbis""></source>
Your browser does not support the video tag, view this video <a href="https://dl.dropbox.com/u/27006338/matrixPanel1.mp4">here</a>.<br />
</video></span></div>
<div style="font-family: Georgia,"Times New Roman",serif; text-align: left;">
To make the animation effect we use the <span style="font-family: "Courier New",Courier,monospace;">MatrixPanelBuilder </span>builder class like this:<br />
<br />
<pre class="brush:java">private final MatrixPanel animatedPanelDemo =
MatrixPanelBuilder.create()
.ledWidth(140).ledHeight(26)
.prefWidth(580.0).prefHeight(580.0)
.frameDesign(FrameDesign.DARK_GLOSSY)
.contents(new Content[] {
new ContentBuilder().create()
.color(MatrixColor.RED)
.type(Type.TEXT)
.origin(0, 5)
.area(0, 0, 140, 26)
.txtContent("MatrixPanel ROCKS!! ")
.font(MatrixFont.FF_10x16)
.fontGap(Gap.DOUBLE)
.align(Align.RIGHT)
.effect(Effect.SCROLL_LEFT)
.lapse(120)
.postEffect(PostEffect.PAUSE)
.pause(2000)
.order(RotationOrder.SINGLE)
.clear(true)
.build()
})
.build();
</pre>
<br />
The main option to add visual effects is <span style="font-family: "Courier New",Courier,monospace;">effect</span>, which take the type of animation for the content. For now these are available:<br />
<ul>
<li style="font-family: "Courier New",Courier,monospace;">NONE</li>
<li><span style="font-family: "Courier New",Courier,monospace;">SCROLL_LEFT</span>, <span style="font-family: "Courier New",Courier,monospace;">SCROLL_RIGHT</span>, <span style="font-family: "Courier New",Courier,monospace;">SCROLL_DOWN</span>, <span style="font-family: "Courier New",Courier,monospace;">SCROLL_UP</span></li>
<li><span style="font-family: "Courier New",Courier,monospace;">MIRROR </span></li>
<li><span style="font-family: "Courier New",Courier,monospace;">BLINK</span>, <span style="font-family: "Courier New",Courier,monospace;">BLINK_10</span>, <span style="font-family: "Courier New",Courier,monospace;">BLINK_4</span></li>
<li><span style="font-family: "Courier New",Courier,monospace;">SPRAY</span></li>
</ul>
The scrolling effects are intended for wider texts than its visible area. When scrolling to the left the text should be aligned to the right, and viceversa.<br />
<br />
The other effects, on the contrary, should be used when all the content is visible. The mirror effect starts scrolling from the center of the content one half to the left, the other to the right. The blinking effects make the content blink indefinetely, ten or four times, respectively. And the spray effect makes the content appear switching LEDs on randomly.<br />
<br />
With the <span style="font-family: "Courier New",Courier,monospace;">lapse </span>option, you set the time lapse, in milliseconds, for each frame of the animation effect. Here it's a little code snippet I use to animate each content with its own time-lapse. Note that parameter in <span style="font-family: "Courier New",Courier,monospace;">handle </span>method <span style="font-family: "Courier New",Courier,monospace;">now </span>is in nanoseconds.<br />
<br />
<pre class="brush:java"> for (final Content content : contents) {
...
new AnimationTimer() {
private long lastUpdate=0l;
@Override public void handle(long now) {
if (now > lastUpdate + content.getLapse()*1000000){
/*
* display the visible area in its current step position
*/
...
lastUpdate = now;
}
}
}.start();
}
</pre>
There is a lower limit to time lapse, due to several factors such as CPU load and speed, RAM, size of content to display,... To get lapses as 20 ms, as I <a href="http://jperedadnr.blogspot.com.es/2012/07/hi-this-is-my-first-javafx-post-so-dont.html#css">wrote</a> in the first part of this post, I had to remove the css styling and the shadow effects.<br />
<br />
You may have noticed the use of the JavaFX <span style="font-family: "Courier New",Courier,monospace;">AnimationTimer </span>class. There`s a very good post by Michael Heinrichs <a href="http://blog.netopyr.com/2012/06/14/using-the-javafx-animationtimer/">here</a> explaining its use. Although it's not the most used animation class of JavaFX, its suits pretty well here to perform just one single step between the time-lapse you set and to control the speed of the effect.<br />
<br />
After all the steps of the animation effect have been done, the effect ends, and you should decide what to do next. For that, the option <span style="font-family: "Courier New",Courier,monospace;">postEffect </span>allows you to choose between:<br />
<ul>
<li style="font-family: "Courier New",Courier,monospace;">STOP</li>
<li style="font-family: "Courier New",Courier,monospace;">PAUSE</li>
<li><span style="font-family: "Courier New",Courier,monospace;">REPEAT</span></li>
</ul>
With the <span style="font-family: "Courier New",Courier,monospace;">PostEffect.STOP</span> option the content remains in the last position with no further animation effect, while with PAUSE and REPEAT the effect starts again. In the first case, you must set a <span style="font-family: "Courier New",Courier,monospace;">pause </span>in milliseconds, while in the second, there is no pause.<br />
<br />
Here comes in handy the JavaFX <span style="font-family: "Courier New",Courier,monospace;">PauseTransition </span>class. You set the Duration of the pause, and while the transition hasn't finished a boolean avoids any step to happen (see the snippet below, in the outer <span style="font-family: "Courier New",Courier,monospace;">if</span>). When the pause time is reached the <span style="font-family: "Courier New",Courier,monospace;">handle </span>method resets the boolean and the effect starts again:<br />
<br />
<pre class="brush:java">
for (final Content content : contents) {
...
new AnimationTimer() {
private long lastUpdate=0l;
private boolean bPauseEffect=false;
@Override public void handle(long now) {
if (now > lastUpdate + content.getLapse()*1000000
&& !bPauseEffect){
...
/*
* at the end of the effect
*/
if(content.getPostEffect().equals(PostEffect.REPEAT) ||
content.getPostEffect().equals(PostEffect.PAUSE)){
PauseTransition t=new PauseTransition();
bPauseEffect=true;
t.setDuration(Duration.millis(content.getPause()));
t.setOnFinished(new EventHandler<actionevent>() {
@Override public void handle(ActionEvent event) {
bPauseEffect=false;
}
});
t.playFromStart();
}
lastUpdate = now;
}
}
}.start();
}
</pre>
<br />
Let's add some video here to show you all this stuff. Let's use a <span style="font-family: "Courier New",Courier,monospace;">MIRROR</span> effect combined with a pause of 3 seconds to start again:<br />
<br />
<div style="text-align: center;">
<span style="font-family: Verdana,sans-serif;"><video controls="controls" height="120" loop="loop" poster="https://dl.dropbox.com/u/27006338/matrixPanel3.mp4" tabindex="0" width="400">
<source src="https://dl.dropbox.com/u/27006338/matrixPanel3.mp4" type="video/mp4; codecs="avc1.42E01E, mp4a.40.2""></source>
<source src="https://dl.dropbox.com/u/27006338/matrixPanel3.webm" type="video/webm; codecs="vp8, vorbis""></source>
Your browser does not support the video tag, view this video <a href="https://dl.dropbox.com/u/27006338/matrixPanel3.mp4">here</a>.<br />
</video></span></div>
<br />
Finally, the last option to build the matrixPanel is <span style="font-family: "Courier New",Courier,monospace;">order</span>. The default option is set to <span style="font-family: "Courier New",Courier,monospace;">RotationOrder.SINGLE</span>, for just one single content displayed in its area. But if you want to use the same area to show alternatively two different contents or apply two different effects two the same content, use <span style="font-family: "Courier New",Courier,monospace;">RotationOrder.FIRST</span> as parameter for <span style="font-family: "Courier New",Courier,monospace;">order </span>option in the first content, and <span style="font-family: "Courier New",Courier,monospace;">RotationOrder.SECOND</span> for the second. You may consider setting the option <span style="font-family: "Courier New",Courier,monospace;">clear </span>to <span style="font-family: "Courier New",Courier,monospace;">true </span>in order to clean the screen before the next content is displayed. <br />
<br />
Here we have another video, with the same image, but two different effects, <span style="font-family: "Courier New",Courier,monospace;">SPRAY </span>and <span style="font-family: "Courier New",Courier,monospace;">BLINK_4</span>.<br />
<br />
<div style="text-align: center;">
<span style="font-family: Verdana,sans-serif;"><video controls="controls" height="400" loop="loop" poster="https://dl.dropbox.com/u/27006338/matrixPanel4.mp4" tabindex="0" width="400">
<source src="https://dl.dropbox.com/u/27006338/matrixPanel4.mp4" type="video/mp4; codecs="avc1.42E01E, mp4a.40.2""></source>
<source src="https://dl.dropbox.com/u/27006338/matrixPanel4.webm" type="video/webm; codecs="vp8, vorbis""></source>
Your browser does not support the video tag, view this video <a href="https://dl.dropbox.com/u/27006338/matrixPanel4.mp4">here</a>.<br />
</video></span></div>
<br />
<br />
Well, this is pretty much what you need to know to create a MatrixPanel control and use it in a JavaFX scene.<br />
<br />
For those of you willing to use it or, even more, use the code to add more effects, more fonts, more possibilities, ... <br />
<br />
...I'm really glad to tell you that now this control has been included in the <a href="http://jfxtras.org/">JFXtras Project</a>, so you can find the source code bundled with all their cool stuff, sure you already know by now.<br />
<br />
I want to express my sincere gratitude to the JFXtras TEAM for letting me in.This final video is a small tribute to you all, specially to Gerrit. By the way, I'm taking as image his logo for the JFXtras Ensemble application installer:<br />
<br />
<div style="text-align: center;">
<span style="font-family: Verdana,sans-serif;"><video controls="controls" height="131" loop="loop" poster="https://dl.dropbox.com/u/27006338/matrixPanel6.mp4" tabindex="0" width="400">
<source src="https://dl.dropbox.com/u/27006338/matrixPanel6.mp4" type="video/mp4; codecs="avc1.42E01E, mp4a.40.2""></source>
<source src="https://dl.dropbox.com/u/27006338/matrixPanel6.webm" type="video/webm; codecs="vp8, vorbis""></source>
Your browser does not support the video tag, view this video <a href="https://dl.dropbox.com/u/27006338/matrixPanel6.mp4">here</a>.<br />
</video></span></div>
<br />
Well, and that's all for now! Hope you like the control. Any comment or suggestion is always welcome! Please, remind it's work in progress...</div>Anonymoushttp://www.blogger.com/profile/15247383133952423416noreply@blogger.com0tag:blogger.com,1999:blog-7114525940495426773.post-81642910206555373512012-07-19T18:49:00.000+02:002012-07-25T16:17:13.660+02:00MatrixPanel, a new JavaFX UI control. First part<div style="font-family: Georgia,"Times New Roman",serif;">
Hi, this is my first JavaFX post, so don't be too cruel on me, please!<br />
<br />
In this two part series post, I'd like to share with you the work I've been doing on a new JavaFX 2.x control, in close collaboration with Gerrit Grunwald (<a href="https://twitter.com/hansolo_">@hansolo_</a>, <a href="http://www.harmonic-code.org/">http://www.harmonic-code.org/</a>). </div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
In fact, the control is based in his awesome well-known Gauge's collection, which recently he has upgraded from Swing to JavaFX 2.x. </div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
It is a mix of a Linear gauge and an extension on his DotMatrixSegment control which, as you may know, are available at the <a href="http://jfxtras.org/">JFXtras project</a>.</div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
So let me introduce you the brand new <b>MatrixPanel</b> control.<br />
<br />
For those anxious to find out what is this about, if the name isn't quite self-explanatory, here you have a pic:</div>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiroOOy-CsxICuXeyU8b0JsuyTP8UsslG5xCL46rq1hkL_-sIFRNqD8OViR5aSp8Sf_XQ0wAhWoWdFkkhPX23lGK6_Omd0V5UXF3Cn8nBSXuaEdlfPBbvMCWf9QL4kd3VGOfyOE4zzizO8/s1600/matrixPanel1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="248" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiroOOy-CsxICuXeyU8b0JsuyTP8UsslG5xCL46rq1hkL_-sIFRNqD8OViR5aSp8Sf_XQ0wAhWoWdFkkhPX23lGK6_Omd0V5UXF3Cn8nBSXuaEdlfPBbvMCWf9QL4kd3VGOfyOE4zzizO8/s640/matrixPanel1.png" width="640" /></a></td></tr>
</tbody></table>
<div style="font-family: Georgia,"Times New Roman",serif;">
As you can see, the control is based on a rectangular matrix of Circles (JavaFX nodes), each one acting as a LED, so the control is a digital version of those LED displays you can find everywhere nowadays. </div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
So its main features are:</div>
<ul>
<li style="font-family: Georgia,"Times New Roman",serif;">Create any size of matrix (width and height)</li>
<li style="font-family: Georgia,"Times New Roman",serif;">Monochromatic or color (3 tones of R, G & B) panel </li>
<li style="font-family: Georgia,"Times New Roman",serif;">Display both text (several dotted fonts) or images (in BMP format)</li>
<li style="font-family: Georgia,"Times New Roman",serif;">Creation of several areas within the panel to display independent content in each one of them</li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Animation effects of the content of each area. Adjustable speed, and pause time between next effect is displayed.</span></li>
<li><span style="font-family: Georgia,"Times New Roman",serif;">Rotation of contents in the same area.</span></li>
</ul>
<span style="font-family: Georgia,"Times New Roman",serif;">To go in full detail with all the properties involved in the creation of a MatrixPanel control, let's have a look at the code to build it, using the proper builder class, <span style="font-size: small;"><span style="font-family: "Courier New",Courier,monospace;">MatrixPanelBuilder</span></span>:</span><br />
<br />
<pre class="brush:java"> private final MatrixPanel panelDemo =
MatrixPanelBuilder.create()
.ledWidth(140)
.ledHeight(40)
.prefWidth(760.0)
.prefHeight(760.0)
.frameDesign(Gauge.FrameDesign.DARK_GLOSSY)
.frameVisible(true)
.contents(new Content[] {
new ContentBuilder().create()
.color(Content.MatrixColor.RED)
.type(Content.Type.TEXT)
.origin(0, 10)
.area(0, 0, 140, 40)
.txtContent("MATRIXPANEL")
.font(Content.MatrixFont.FF_10x16)
.fontGap(Content.Gap.DOUBLE)
.align(Content.Align.CENTER)
.effect(Content.Effect.NONE)
.postEffect(Content.PostEffect.STOP)
.order(Content.RotationOrder.SINGLE)
.build()
})
.build();
</pre>
<div style="font-family: Georgia,"Times New Roman",serif;">
And you can load it in a JavaFX scene simply by:</div>
<br />
<pre class="brush:java">public void start(Stage primaryStage) {
primaryStage.setTitle("MatrixPanel Demo");
StackPane root = new StackPane();
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
root.getChildren().add(panelDemo);
primaryStage.setScene(new Scene(root, 800, 600));
primaryStage.centerOnScreen();
primaryStage.show();
}
</pre>
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Basically, first you have to set the number of LED columns </span>(<span style="font-family: "Courier New",Courier,monospace;">ledWidth</span>)<span style="font-family: Georgia,"Times New Roman",serif;"> and rows (<span style="font-family: "Courier New",Courier,monospace;">ledHeigth</span>)</span> <span style="font-family: Georgia,"Times New Roman",serif;">and the size of the control in the scene. In order to keep the LEDs shape as circles, one dimension</span> (<span style="font-family: "Courier New",Courier,monospace;">prefWidth</span> <span style="font-family: Georgia,"Times New Roman",serif;">or </span><span style="font-family: "Courier New",Courier,monospace;">prefHeight</span>)<span style="font-family: Georgia,"Times New Roman",serif;"> will be recalculated, so you don't need to worry, and just set the maximum space available within the scene for the control. Then you can select the frame design (<span style="font-family: "Courier New",Courier,monospace;">DARK_GLOSSY </span>or <span style="font-family: "Courier New",Courier,monospace;">GLOSSY_METAL</span>, for now), or simply hide it.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;">And now we deal with the <span style="font-family: "Courier New",Courier,monospace;">contents </span>to display. You can add as many contents as you want, creating each one by its builder class <span style="font-family: "Courier New",Courier,monospace;">ContentBuilder</span>.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;">First you choose the color: <span style="font-family: "Courier New",Courier,monospace;">RED</span>, <span style="font-family: "Courier New",Courier,monospace;">GREEN</span>, <span style="font-family: "Courier New",Courier,monospace;">BLUE </span>or <span style="font-family: "Courier New",Courier,monospace;">RGB</span>, and then you set the content type: <span style="font-family: "Courier New",Courier,monospace;">IMAGE </span>or <span style="font-family: "Courier New",Courier,monospace;">TEXT</span>. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">For text content, these are the four colors available:</span><br />
<div style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgY3A0-ghnIKeT0f6Y8ZxoJ930s27R1jF3-j8ahdtInzmcwfzVIcaXkBPpso725X_7or0d-GjXmZkaDIFUWPdK4iwORA05mDI_h2mtJFzuXh9GejtatMOP_jHc5PHZhyrGDFYo2XKAp_iA/s1600/matrixPanel2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="161" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgY3A0-ghnIKeT0f6Y8ZxoJ930s27R1jF3-j8ahdtInzmcwfzVIcaXkBPpso725X_7or0d-GjXmZkaDIFUWPdK4iwORA05mDI_h2mtJFzuXh9GejtatMOP_jHc5PHZhyrGDFYo2XKAp_iA/s640/matrixPanel2.png" width="640" /></a><span style="font-family: Georgia,"Times New Roman",serif;"> </span></div>
<span style="font-family: Georgia,"Times New Roman",serif;">For bmp images, three tones of each color are considered, so the bmp rawdata is filtered accordingly. Let's load this 32x32 bmp:</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBoZ71mqixxZl3F5QtDd7qImJDpL3iphqJpimzTrIY0ieVeYLhZyijX722wlSPii4Rcbi2Glj3gz1cDK7PJBMIA6mfOAlRCRLyWSNb5s2BttmIKOhrnh_xOFM43xPruTMxGyQzuREj0vk/s1600/gradient.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBoZ71mqixxZl3F5QtDd7qImJDpL3iphqJpimzTrIY0ieVeYLhZyijX722wlSPii4Rcbi2Glj3gz1cDK7PJBMIA6mfOAlRCRLyWSNb5s2BttmIKOhrnh_xOFM43xPruTMxGyQzuREj0vk/s1600/gradient.png" /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;">in four areas to show you the four possibilities of coloring:</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgP3ByPSMhSrGJeU93Yx_IQAS636NXl1S1xMJqdyekqtjWsm0TwZZHQplVUpRoDqzmUOwo9Y7lF41B9DFGHfC-bGiN5ENdXZfl2YXFuGDdt-umg8EXymr_NjKCG0fYMvzupU6Mr6ubOhQw/s1600/matrixPanel3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgP3ByPSMhSrGJeU93Yx_IQAS636NXl1S1xMJqdyekqtjWsm0TwZZHQplVUpRoDqzmUOwo9Y7lF41B9DFGHfC-bGiN5ENdXZfl2YXFuGDdt-umg8EXymr_NjKCG0fYMvzupU6Mr6ubOhQw/s640/matrixPanel3.png" width="640" /></a></div>
<span style="font-family: Georgia,"Times New Roman",serif;">The image is loaded with the option <span style="font-family: "Courier New",Courier,monospace;">bmpName</span>, which requieres the bmp name of the image if it is locally stored in resources, or a full external path and its name, otherwise.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">For those of you disappointed with the poor performance of the LEDs matrix style, let me tell you that first I used css styling for each circle, with gradients and inner shadows. But there were serious CPU issues loading the panel and applying animation effects. So following Gerrit's <a href="http://www.canoo.com/blog/2012/07/10/javafx-recipes-css-vs-code/">advice</a> <a name="css"></a> I removed css styling and provided the same effects by code. Although there were some improvements, the best solution was to remove any effect, and just leave a simple filling of each circle with the proper color. Anyway, as the size of the LEDs decrease, the difficult to appreciate any lack of shadowing effect increase.</span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">You may have noticed that the former matrixpanels contained four areas, so let's talk about <span style="font-family: "Courier New",Courier,monospace;">origin</span> and <span style="font-family: "Courier New",Courier,monospace;">area </span>options. An <span style="font-family: "Courier New",Courier,monospace;">area </span>is defined by its left-top and right-bottom LED coordinates. You shouldn't overlap differents areas. The <span style="font-family: "Courier New",Courier,monospace;">origin </span>is the left-top LED coordinate where the content will start being displayed. By <i>LED coordenate</i> I mean number of LEDs from the left and the top, instead of pixels.</span><br />
<span style="font-family: Georgia,"Times New Roman",serif;"><br /></span><br />
<span style="font-family: Georgia,"Times New Roman",serif;">For text content, by using the option <span style="font-family: "Courier New",Courier,monospace;">txtContent </span>you set the string of text. It will be displayed in one line. If it's wider than the visible area, you should use a scrolling effect to visualice it completely, as we'll see in the second part of this post. </span><br />
<br />
<span style="font-family: Georgia,"Times New Roman",serif;">Then you have to choose a font. For now there is a bunch of proportional dotted fonts, with the usual ASCII range of printable characteres (<i>A</i> to <i>Z</i>, <i>a</i> to <i>z</i>, <i>0</i> to <i>9</i>, <i>+</i>, <i>-</i>, <i>*</i>,<i> /</i>, <i>\</i>,...) available. The next matrixpanel shows their name, which stands for width and height of each character:</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgr8U_cYPCnXdu-1W948abW5Bj-S1DW2nLPGrBSDt4c0pb1fC-RGoHsku937TSSsRmc1qV91u0sRVnao4r6WNRKor_hk9GbTzdiYpCEZa9Wi8qlFGyRHL9ThUBFVm0IWc0HkTMf9TNTRw/s1600/matrixPanel4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="352" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgr8U_cYPCnXdu-1W948abW5Bj-S1DW2nLPGrBSDt4c0pb1fC-RGoHsku937TSSsRmc1qV91u0sRVnao4r6WNRKor_hk9GbTzdiYpCEZa9Wi8qlFGyRHL9ThUBFVm0IWc0HkTMf9TNTRw/s640/matrixPanel4.png" width="640" /></a></div>
<div style="text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">The <span style="font-family: "Courier New",Courier,monospace;">fontGap </span>option allows to insert a column of 0 (<span style="font-family: "Courier New",Courier,monospace;">NONE</span>), 1 (<span style="font-family: "Courier New",Courier,monospace;">SIMPLE</span>) or 2 (<span style="font-family: "Courier New",Courier,monospace;">DOUBLE</span>) leds between each character. And the <span style="font-family: "Courier New",Courier,monospace;">align </span>option allows <span style="font-family: "Courier New",Courier,monospace;">LEFT</span>, <span style="font-family: "Courier New",Courier,monospace;">CENTER </span>or <span style="font-family: "Courier New",Courier,monospace;">RIGHT</span> justification for the text in its area.</span></span></div>
<div style="text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"><br /></span></span></div>
<div style="text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">To end this first part of the post, let me just finish with <span style="font-family: "Courier New",Courier,monospace;">effect </span>option set to <span style="font-family: "Courier New",Courier,monospace;">NONE</span>, <span style="font-family: "Courier New",Courier,monospace;">postEffect </span>option set to <span style="font-family: "Courier New",Courier,monospace;">STOP </span>and order option set to <span style="font-family: "Courier New",Courier,monospace;">SINGLE</span>. This means the content will be static, no further action will be done after been displayed and no other content will supply it.</span></span></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">The next post will be dedicated to animation, so stay tuned!</span></span></div>
<div style="text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"><br /></span></span></div>
<div style="text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">Meanwhile, any comment or suggestion will be really appreciated!!</span></span></div>
<div style="text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;"><br /></span></span></div>
<div style="text-align: left;">
<span style="font-family: Georgia,"Times New Roman",serif;"><span style="font-family: Georgia,"Times New Roman",serif;">Finally, thank you Gerrit, for your inspiring work.</span></span></div>
<br />Anonymoushttp://www.blogger.com/profile/15247383133952423416noreply@blogger.com1tag:blogger.com,1999:blog-7114525940495426773.post-78963259967426917082012-07-18T16:02:00.001+02:002012-07-25T10:17:31.473+02:00My brand new blog. Short presentation<div style="font-family: Georgia,"Times New Roman",serif;">
Hi everyone!</div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<span style="font-family: Georgia,"Times New Roman",serif;">My name is José Pereda, and I'm an Structural Engineer, working as a professor in the School of Industrial Engineering in the University of Valladolid (Spain), since 1997</span>.<br />
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<span style="font-family: Georgia,"Times New Roman",serif;">This blog is not about structures nor teaching, as my real passion is coding, and make useful applications to solve engineering problems.</span><br />
<br />
<div style="font-family: Georgia,"Times New Roman",serif;">
So the incoming posts will be, mostly, related to coding stuff of applications I'm working on or have been, not neccessary in historic order.</div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
I must warn you I'm not a professional software developer, so this blog it's not intended to provide with academic solutions for advanced problems.</div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
On the contrary, I'd like to share with you snnipets of my not-allways-by-the-book practical solutions. I'll be glad if they're usefull for anyone. Your opinion will be allways welcome!</div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
The next post will be related to <a href="http://www.javafx.com/">JavaFX</a>, <span style="font-size: small;"><span style="font-family: Georgia,"Times New Roman",serif;"><i>the next step in the evolution of Java as a rich client platform</i>.</span><span style="font-family: Georgia,"Times New Roman",serif;"> </span></span></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>
<div style="font-family: Georgia,"Times New Roman",serif;">
<br /></div>Anonymoushttp://www.blogger.com/profile/15247383133952423416noreply@blogger.com0