Tuesday, December 3, 2019

Some fun with JavaFX Canvas - SpaceFX

Aloha,

Last weekend I decided to write a little game because I never did that before. Saturday and Sunday morning I've spend some time to create a little space game where you have to dodge asteroids or shoot them. In addition there might be enemy ships that will fire back if you are in line of sight.
To realize that game I made use of the JavaFX Canvas node which seem to be a perfect fit for games like stuff.
In the Canvas node the redraw is in your hands (more or less) but at least you can decide when to redraw and how often. So what I did was to create an AnimationTimer and simply call a draw() method as often as possible (which usually should be around 60 times per second).
In this draw method I set the coordinates of all my objects and draw them, do the collision detection and play the sounds.
For the sprites I use simple images which I've found on the internet.
Long story short, here is a little video of the game...



If you hit an asteroid or an enemy ship you will be destroyed. Also when an enemy torpedo hits you you will be destroyed. The score will always be reset to 0 when you have been destroyed and the game will start over and over again...like I said only for the fun of it.
You can use the cursor keys to maneuver the ship and the space key to fire torpedos and that's it...as you can see...really simple :)
If you are interested in the game you can find the source code over at github.

For those of you that are also interested in jpro (running JavaFX apps in the browser), the latest version of jpro makes it possible to also run JavaFX apps that use the Canvas node!!! Oh yeah!!!

UPDATE:
Well after the last weekend the game now looks a bit more advanced and therefor I've created a new little video that shows the current version...here you go...



Well that's it for today...so keep coding...

Saturday, November 9, 2019

ImageCounterTileSkin...

Aloha,

This morning I could not sleep and was skimming the web for new ideas where I stumbled upon a social media dashboard.
And on that dashboard I've found widgets that simply shows a social media icon with a counting number.
Well...that's an easy one I thought and quickly created a new skin for TilesFX which is the ImageCounterTileSkin.
So here is a screenshot of a little demo I quickly put together to give you an idea on what it will look like...


As you can see this tile does not show a lot but simply an image with a number and some text. Of course you can enable the title and the bottom text in the skin too but without it also looks ok.
It doesn't take a lot of code to create such a tile, here is what you need:

Tile tile = TileBuilder.create()
                       .skinType(SkinType.IMAGE_COUNTER)
               .prefSize(WIDTH, HEIGHT)
               .value(0) // START VALUE
               .description("TEXT")
               .image(new Image("file:PATH_TO_IMAGE"))
               .imageMask(ImageMask.ROUND)

               .build();

And to make it even easier to handle it, I've added two new convenience methods to TilesFX to increase or decrease the value by a given amount.

So to increase the tiles value you simply can call

tile.increaseValue(1) 

and that's it.

And because I had some time this morning I directly created a new version of TilesFX which is now 11.27 and is available here...

github

bintray

maven central

And with this I wish all of you a nice weekend and...keep coding...

Saturday, September 28, 2019

ClusterMonitorTileSkin...

Aloha,

Back from CodeOne in San Francisco I think it's time to blog about a new skin that I've added to the library.
In my last post you saw that I was preparing a custom skin for Gluon to monitor the 1060 Raspberry Pi cluster from Oracle.
Once I saw the use case I thought it might make sense to add this skin as a regular skin to the library.
Because visualizing 1060 tiles in a dashboard was a challenge I came to the conclusion that this skin should also get the ability to reduce the details it shows when it's size goes below a given threshold (in this case 100 x 100px).
In addition I had to add several features to be able to show for example different units for the cpu/mem and temperature data.
So here are the screen shots of both modes:



As you can see the reduced version shows the text within the bar which saves some vertical space.
In addition one can add an SVG string that will be used to visualize a button (in the lower right corner). This button could for example be used to reset a single node in case it got stuck or hangs.
The new skin can now be found under the name: ClusterMonitorTileSkin and is available since version 11.13.

The latest version can be found here:

Source github

Binary bintray


That's it for today...so keep coding...

Sunday, September 15, 2019

Custom skins for TilesFX

Aloha,

Today I will tell you something about customs skins for TilesFX. This year at CodeOne in San Francisco the Oracle team created a really nice Raspberry Pi SuperComputer. And for this Pi cluster the guys from Gluon created a dashboard that monitors the cpu and memory usage and also the temperature of each Pi.
Lucky me they decided to go with TilesFX to create the dashboard but they had the problem that there was no skin that was good enough to visualize the CPU and memory usage in one Tile.
So they asked me if I could help them out with a skin and so I've created the CpuMemTileSkin which looks as follows:



If you would like to make use of the skin feel free to take the code from this gist.

To use the skin you first need to set it up as follows:

Random         rnd      = new Random();
ChartData      cpuItem  = new ChartData("CPU", Bright.RED);
ChartData      memItem  = new ChartData("MEM", Bright.BLUE);
GradientLookup gradient = new GradientLookup(Arrays.asList(
    new Stop(0.0, Bright.GREEN),
    new Stop(0.4, Bright.YELLOW),
    new Stop(0.8, Bright.RED)));


Tile cpuMemTile = TileBuilder.create()
                             .skinType(SkinType.CUSTOM)
                             .prefSize(200, 200)
                             .unit("\u0025")
                             .title("Node XY")
                             .chartData(memItem, cpuItem)
                             .build();
cpuMemTile.setSkin(new CpuMemTileSkin(cpuMemTile);

// Creating random values for cpu and mem
double cpu = rnd.nextDouble() * 100.0;
double mem = rnd.nextDouble() * 100.0;

// Set the cpu color related to it's value and set the value
cpuItem.setFillColor(gradient.getColorAt(cpu / 100));
cpuItem.setValue(cpu);

// Set the mem color related to it's value and set the value
memItem.setFillColor(gradient.getColorAt(mem / 100));
memItem.setValue(mem);


So you set up two ChartData objects, one for cpu and one for mem and add them to the tiles chartData property in the TileBuilder.
Then you create a GradientLookup to change the color of each bar dependent on the current value (from green over yellow to red).
When this is done you can set the color and value of each bar by changing the value of the ChartData objects and that's all you need to do to make use of the custom TilesFX skin.

I hope this will help one or the other of you and if you have created a nice TilesFX skin please let me know...I'm always keen on seeing new stuff... :)

That's it for today...so keep coding... :)

Monday, September 9, 2019

A new skin for TilesFX

Aloha,

Finally found some time to add some more features to TilesFX. This time I've added a new skin to visualize timeline data. Because our son has diabetes I would like to monitor his blood glucose values more closely using my TilesFX based dashboard at home.
So I figured out that there was no really useful skin available for TilesFX that I can use. But now there is...the TimelineTileSkin.
Here is a little screenshot of the new skin:


In principle the skin looks very similar to the SparklineTileSkin but here I simply can use time based to data. There is a ChartData class in TilesFX that has a timestamp property and so instead of using the value property of the Tile class one now simply add ChartData to the Tile.
To be able to visualize time based data I've added properties for a time period and for a max time period.
On the screenshot above the timePeriod is 3 hours which means the width of the tile represents 3 hours counting from now to 3 hours back in time. Every chartData object that you add which has a timestamp that is within the last 3 hours will be drawn. The tile will keep a max no of data defined by the maxTimePeriod property. Data that is older than 3 hours will move out of the graph on the left side and new data will be added on the right side. It is also possible to define a resolution in TimeUnits.
So if you would like to visualize the last 3 hours with a resolution of minutes you can define it by setting the timePeriodResolution with TimeUnit.MINUTES.
The smallest possible resolution is seconds, even if you set it to milliseconds or smaller it will be set back to seconds.
It is possible to use seconds, minutes, days and months. 
In addition to this I've also added a lowerThreshold with a lowerThresholdColor. The threshold and the lowerThreshold will be visualized with a dashed line that will be stroked with their given colors.
One could also define Sections with colors (the two red and one green areas on the screenshot above).
On the left side of the graph you see the minimum and maximum measured value (taken from the visible values!) In this case the highest measured value within the last 3 hours has been 349.
Like with the other charts one can define a number of stops that will be used to stroke the line when strokeWithGradient == true. Otherwise it will use the barColor to stroke the line. If you enable smoothing the line between the points will be smoothed, otherwise it will simply connect the data points.
On the right side of the chart you see the minimum and maximum value of the chart (0 and 350).
The small percentage values on the left side will show how many data points that are within the timePeriod are in each section. In the screenshot above this would mean for example that 18% of all visible data points are in the green section.

I've created the TimelineTimeSkin because I have a need for it to visualize the blood glucose values of our son, meaning to say it might not fit for your needs. So please let me know if you find some bugs or if you need something else and maybe I can help you with that.

The new skin is part of the latest version 11.6 which can be found here:


I hope this additional skin might help one or the other, so that's it for today...keep coding...

Friday, July 12, 2019

Some fun again...

Aloha everyone,

I think I'm too busy these days...just saw that my last post was from March...ZOMG
So last week I was in Basel at the Karakun office and met with my old friend Andres Almiray (which always is a pleasure) and we talked about some JavaFX and SVG stuff. After he left the office I was thinking about how to visualize multi-color SVG paths in JavaFX and started to create my own SvgNode.
But first why do I not use the already SVGNode in JavaFX to solve that problem? Well the SVGNode is nice but comes with the drawback that it only supports one single path with it's fill and stroke and that's it.
This works for almost all things I need but sometimes you simply need to support more than one path with separate fill and stroke for each path.
Here is a little example SVG that I've found on the web (over at flaticon.com) which shows the problem...

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 513 513" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
    <path d="M235.135,235.135C159.925,310.345 135.231,275.075 26.004,422.225C17.921,433.114 20.496,448.544 31.694,456.194L33.637,457.521C38,460.501 43.224,461.88 48.503,461.642C58.83,461.176 80.859,464.825 116.106,492.951C167.374,533.862 226.035,500.058 252.374,473.719C281.232,450.167 320.774,395.21 285.236,340.081C273.056,321.188 266.597,306.174 263.368,294.601C258.079,275.643 275.644,258.078 294.602,263.367C306.175,266.596 321.188,273.055 340.082,285.235C395.21,320.773 450.168,281.233 473.72,252.373C500.06,226.033 533.863,167.373 492.952,116.105C464.826,80.858 461.176,58.829 461.643,48.502C461.881,43.223 460.503,37.999 457.522,33.636L456.195,31.693C448.545,20.495 433.115,17.921 422.226,26.003C275.075,135.231 310.344,159.925 235.135,235.135Z" style="fill:rgb(253,111,113);fill-rule:nonzero;"/>
    <path d="M85.968,403.053C106.035,377.514 146.121,331.223 206.362,303.757C214.498,300.047 224.098,301.902 230.29,308.353C252.48,331.471 295.699,389.535 234.658,451.558C177.284,509.854 117.679,460.332 87.834,431.163C80.132,423.636 79.314,411.522 85.968,403.053Z" style="fill:rgb(133,231,255);fill-rule:nonzero;"/>
    <path d="M109.686,409.31C101.985,401.784 101.167,389.668 107.82,381.2C113.481,373.995 120.737,365.137 129.588,355.518C110.289,373.237 95.743,390.609 85.966,403.054C79.313,411.521 80.131,423.637 87.832,431.164C117.677,460.334 177.283,509.855 234.656,451.559C238.431,447.724 241.803,443.904 244.81,440.106C191.186,481.359 137.507,436.5 109.686,409.31Z" style="fill:rgb(87,208,230);fill-rule:nonzero;"/>
    <path d="M61.163,438.023C64.067,435.193 62.465,430.262 58.452,429.679L45.283,427.766C43.689,427.534 42.312,426.534 41.599,425.09L35.71,413.157C33.915,409.52 28.729,409.52 26.935,413.157L21.046,425.09C20.333,426.534 18.956,427.535 17.362,427.766L4.193,429.679C0.18,430.262 -1.421,435.194 1.482,438.023L11.01,447.311C12.164,448.435 12.689,450.054 12.417,451.641L10.169,464.757C9.484,468.754 13.679,471.801 17.268,469.914L29.046,463.722C30.471,462.973 32.174,462.973 33.599,463.722L45.377,469.914C48.966,471.801 53.161,468.753 52.476,464.757L50.227,451.642C49.955,450.055 50.48,448.436 51.634,447.312L61.163,438.023Z" style="fill:rgb(250,220,96);fill-rule:nonzero;"/>
    <path d="M403.053,85.968C377.514,106.035 331.223,146.121 303.757,206.362C300.047,214.498 301.902,224.098 308.353,230.29C331.471,252.48 389.535,295.699 451.558,234.658C509.854,177.284 460.332,117.679 431.163,87.834C423.636,80.132 411.52,79.315 403.053,85.968Z" style="fill:rgb(133,231,255);fill-rule:nonzero;"/>
    <path d="M330.206,208.437C323.755,202.245 321.902,192.645 325.61,184.509C339.693,153.62 358.724,128.045 377.37,107.736C352.891,130.259 323.473,163.116 303.756,206.362C300.046,214.498 301.9,224.098 308.352,230.29C331.47,252.48 389.534,295.699 451.556,234.658C455.511,230.765 458.954,226.863 461.957,222.959C404.341,268.579 351.895,229.255 330.206,208.437Z" style="fill:rgb(87,208,230);fill-rule:nonzero;"/>
    <path d="M463.722,33.601C462.972,32.175 462.972,30.473 463.722,29.048L469.914,17.27C471.8,13.681 468.753,9.486 464.757,10.171L451.642,12.421C450.055,12.693 448.436,12.167 447.311,11.014L438.023,1.485C435.192,-1.419 430.262,0.184 429.677,4.196L427.763,17.365C427.531,18.959 426.531,20.336 425.087,21.049L413.154,26.939C409.517,28.734 409.517,33.918 413.154,35.713L425.087,41.603C426.531,42.316 427.532,43.693 427.763,45.287L429.677,58.456C430.26,62.469 435.192,64.072 438.023,61.167L447.311,51.638C448.435,50.485 450.054,49.959 451.642,50.231L464.757,52.481C468.753,53.166 471.801,48.971 469.914,45.382L463.722,33.601Z" style="fill:rgb(250,220,96);fill-rule:nonzero;"/>

</svg>

If you open that file in a browser or graphics program you will see something like this...



As you can see this SVG contains multiple paths with different colors and it would simply be great to be able to visualize this in JavaFX.

So my SvgNode can handle multiple SvgPath objects which can be created using the SvgPathBuilder as follows:


SvgPath svgPath = SvgPathBuilder.create()
                                .path("M0,0L100,0L100,100,L0,100,L0,0Z")
                                .fill(Color.web("#85E7FF"))
                                .stroke(Color.TRANSPARENT)
                                .strokeWidth(0)
                                .lineCap(StrokeLineCap.ROUND)
                                .lineJoin(StrokeLineJoin.BEVEL)
                                .effect(new DropShadow())
                                .build();

Then you can create a SvgNode as follows:


SvgNode svgNode = new SvgNode(svgPath);
svgNode.setPrefSize(100, 100);

The initial size of the SvgNode should be the original size of your SVG file to get the best results. 
The SvgNode will parse the given path string and will draw the path on a JavaFX canvas node. This works great even with multiple paths but comes with one problem...scaling.
If you scale the SvgNode control it will scale the embedded Canvas node which can lead to blurred shapes if your original size was small and you size the control to a large size.
So for the best result you should create the SVG in the size you later on need the SVG in your application.
To come back to the example in the beginning...here is the representation of the sunglasses using my JavaFX SvgNode...


Not too bad for a quick hack... :)

As always you can find the source code over at github

I hope this will help one or the other...enjoy the upcoming weekend and keep coding...

Friday, March 1, 2019

TilesFX 11.1

Aloha,

Finally I've found some time to port TilesFX to Java 11. There are no new features yet but only the ability to use TilesFX with JDK11 and above.

Binaries for the different platforms can be found here:

JDK 8
Version 1.6.5 bintray 

Version 1.6.5 maven central.


JDK 11
Version 11.1 bintray

Version 11.1 maven central.

And that's it already, so keep coding...