Showing posts with label fridayfun. Show all posts
Showing posts with label fridayfun. Show all posts

Friday, April 9, 2021

Friday Fun LXIII - JDKMon

 Aloha,

I've took some days off this week and continued working on a little tool I wrote, JDKMon. Because I have a couple of JDK's on my machine and I usually don't use tools like sdkman or other installers but instead install the JDK's by hand I always wanted to have a tool that keeps track on the latest available versions and inform me about updates.

Well because I've created the Disco API for foojay I'm now able to use this api to create this tool. In principle the tool will try to find all JDK's installed in a folder (that you can define) and checks the Disco API for updates for each of the JDK's found. If an update was found it will tell you the latest available version for the distribution and give you the ability to download the different available versions.

Just to be clear, the tool won't scan your whole harddrive for installed JDK's but only the given folder with all it's subfolders. For me that works fine because I have all JDK's installed in the same folder. 

On MacOS this folder usually can be found at /Library/Java/JavaVirtualMachines where on Windows it can be C:\Program Files\Java and on Linux it might be /usr/lib/jvm. But like mentioned you can choose the folder JDKMon should look for JDK's.

JDKMon will scan for new updates every 3 hours and will show you a notification if there are updates available. The app makes use of FXTrayIcon which makes it possible to have the app running in the SystemTray (if available). On MacOS and Windows that works fine. On Linux it won't work so in this case it is simply an app that comes with a menu.

After the app started it will scan for JDK's and will then show a window with all JDK's found. On MacOS that window will like look like follows:


On the screenshot above you see the alphabetical list of JDK's found in the folder that is shown on top. The distributions where the tool found updates will have additional info like the arrow followed by the latest available version and some colored buttons. The buttons on the right will show the available archive types for each distribution. Once you click on one button it will ask you for a folder to download the package to and after you have selected one it will download the selected package to that folder.

So the tool won't install automatically the downloaded JDK...this is up to you. The tool will also adopt to the selected screen mode (dark/bright) on MacOS and Windows. I've also tried to make the main window and notifications look like the native windows. Here is a screenshot of the Windows version on a bright themed Windows 10 installation:


The supported distributions at the moment are:

- Adoptium (but there are no packages yet)

- AdoptOpenJDK HotSpot

- AdoptOpenJDK OpenJ9

- Corretto

- Dragonwell

- GraalVM CE

- Liberica

- Liberica Native

- Mandrel

- Microsoft Build of OpenJDK*

- OJDK Build

- Oracle JDK (no direct download)

- Oracle OpenJDK

- RedHat (no direct download)

- SAP Machine

- Trava

- Zulu

* The Microsoft Build of OpenJDK is currently not available on the public Disco API but only on my own server which is the reason why you can see it on the Windows screenshot. But it will come with the next deployment.


Like already mentioned this is just a tool that I needed for my own machine and I also used it to test the detection of dark/light mode etc.

Because the Disco API still is under development it might come to situation where you don't find the latest available JDK of a distribution directly but I'm working on that so that it will hopefully be up to date most of the times.

As always the source code is available over at github where you can also find the installers/jars in the releases section.

I'm using github actions to build and upload the artifacts with each build so that you can also find the latest available artifacts for MacOS and Windows in the actions section.

The tool is not finished yet because it needs a bit more love for Linux which will be the next task to do. At least it should look not like a MacOS app on Linux forever. So I will try to adjust it to maybe the Ubuntu UI.

That's it for today, so enjoy the upcoming weekend and...keep coding... 


Friday, March 6, 2020

Friday Fun LXII - Expandable TextArea

Aloha,

Today I have another little JavaFX control for you that is an expandable TextArea.
This is a TextArea as you know it but with the ability to grow/shrink it's size dependent on it's content. Meaning to say it doesn't have scroll bars.
You can define a fixed number of lines that will be used to calculate the height when the control is not expandable and when you set expandable = true it will automatically resize to the size it's defined by it's content.
In addition it comes with the ability to limit the amount of characters you can type into the text area.
To give the user a hint that the limit is near you can also define the character threshold that will be used to show a little label below the text area that shows the characters left before you reach the limit.
For example you limit the amount of characters to 300 and set the threshold to 50. In this case the label will appear as soon as you reach 250 characters.
Here is a little screenshot that shows how the ExpandableTextArea looks like:



In addition I've also created a little video over at youtube that shows the control in action.



And as always the source code is available at github.

That's it for today...enjoy the upcoming weekend and...keep coding...

Friday, January 17, 2020

Friday Fun LXII - Formatted text field

Aloha,

In the current project we had the need for a custom JavaFX text field that has the ability of formatting numbers in a specific way e.g. "0.05 EUR" or "6 years" etc.
I remembered that I did such a field long time ago and found it in on my disk. At the same time I figured out that I had never put it on github...so here you go...

In addition there have been some special requirements like when in edit mode the units should not be visible but as soon as you leave the field they should be shown etc.

There is a gradle task to start the demo and you can execute it on the command line by typing in: 

./gradlew demo

Just make sure that you are at least on JDK 11 with OpenJFX11.

As always the code can be found on github..

That's it for today...so 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, February 1, 2019

Friday Fun LXI Part 2

Aloha,
Found some additional time today to close some bugs in my Medusa JavaFX library and in addition I've quickly created another skin called PlainAmpSkin.
There was the request for the ability to switch off the black part of the AmpSkin and just show the scale to save place.
Instead of adding another property to the Gauge class I've simply created another skin based on the AmpSkin but without the black frame.
Here are images of both skins to give you a better understanding of what I'm talking about...



Both controls have the same size but it is obvious that the lower control makes better use of the available space.
So I hope this new skin will fit the needs of the people that requested the feature.
This additional skin and some bugfixes can be found in the latest Medusa release which is now 8.2.

As always you can find the 

source at github

binary at bintray

and on maven central.

So that's it for today...enjoy your weekend and keep coding...

Friday Fun LXI

Aloha,

In the current project I needed a matrix like data structure that is observable and because I was not aware of one (even I'm pretty sure that there is something out there) I created my own observable matrix.
This little class is more or less a wrapper around an 2-dimensional array of objects which offers some convenient methods to add columns and rows.
Meaning to say it can hold for example a matrix of your own custom objects and it will fire different kind of events in case you added/removed/changed an item in the matrix. There are also events if columns/rows have been added or removed from the matrix.
If you remove a column item by item until all items of that column are gone the matrix will fire an event as soon as the column only contains null objects.
Because this is only a data structure and nothing fancy, I do not have any nice and shiny images to show this time.
The source code is available on github as always and you will find a little demo class in the source that demonstrates the functionality.

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

Wednesday, May 30, 2018

Time for something new...iOS FX

Aloha,

Well after I did my last blog post about the iOS style switch control some people mentioned it might be nice to have more than one iOS style control available.
First I thought...bah...no time...but I could not resist and started to create a little new side project which I named iosFX.
In principle this is a little library of JavaFX controls that more or less look and behave like the current iOS controls.
My first idea was to tweak existing controls using css and code to achieve this but after I took a closer look to the iOS controls I figured out that these controls are very special and a simple css tweak won't work.
For that reason I've decided to create completely independent controls that are based on JavaFX Regions at the moment.
So at the moment I have the following controls available...

  • IosSwitch
  • IosEntry
  • IosListView
  • IosMultiButton
  • IosSlider
  • IosSegmentedButtonBar
  • IosPlusMinusButton

The IosSwitch you already saw in my last blog post, I've just ported it to the lib. The IosListView is a simple extension of the existing JavaFX ListView.
Because a big part of the current iOS controls is used in list views I've created an IosEntry which is a control that takes a left node, a right node and comes with a title and a subtitle.
And when you take a closer look to the iOS ui controls you will figure out that a lot of controls/indicators are very similar.
For this reason I've created the IosMultiButton which is a JavaFX control that has a specific type which defines the ui.
At the moment the available types are...

  • CHECKBOX
  • ADD
  • DELETE
  • CHECK_MARK
  • DOT
  • SMALL_DOT
  • INFO
  • PLUS
  • FORWARD

Dependent on the type also the behaviour is different, e.g. if the type is CHECKBOX one can toggle the control by clicking on it.
Instead of using the standard JavaFX events I've implemented my own events to be able to make use of the ShadowField properties. So the controls fire IosEvents which have different EventTypes that are...

  • SELECTED
  • DESELCTED
  • DELETE_ENTRY
  • ADD_ENTRY
  • PRESSED
  • RELEASED

With this event types (and there will come more) one can use these controls as normal JavaFX controls.

You will find a Demo class that shows more or less everything that is available right now and at this moment it looks like follows...




If you drag the 1st or the 4th entry to the left you will see that it also shows the Delete button that is available in iOS. By dragging more than half of the width the Delete button will fill the complete row and by releasing the mouse the entry will be deleted.

That's all I was able to implement right now but there will be more...so stay tuned... :)

Here is a little video of an older version...



As always the source code is available on github and you are more than welcome to help out creating more iOS controls. The final goal will be to create a Gluon app that looks more or less like a native iOS app...hopefully ;)

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

Monday, May 14, 2018

Monday fun component... ;)

Aloha,

I know it was a long time without a new blog post but I was busy with a project that took all my time. But last week I've decided to always reserve some time to focus on my favourite part...JavaFX :)
When I was playing around with my iPhone I saw that the check box in iOS still is something that I wanted to do again. This is not the first time I've created a check box (as you can see here and here) so I thought...why not... :)
For those of you that are not familiar with the latest iOS check box, here is a little screenshot from my iPhone to give you an idea...



The nice thing about that control is that you don't need an additional text as on the original JavaFX check box. So I've decided to simply create a Region based control that only contains the switch itself.
So the control is not really complicated to do and it just took me around 45 min to create it. So here is a little screen shot of my version:


I've tried to make it look like the iOS original as good as I could. It comes with only 3 properties which are:

  • checked
  • checkedColor
  • showOnOffText

Like I said nothing really special but it was fun to create it and maybe it will be useful for one or the other.
As always you can find the source code on github.

There is also a little video which shows the control in action...





UPDATE:
In the comment from Wim Deblauwe he mentioned that there is a specific press and hold behaviour in iOS for the switch. So the latest version on github also contains this behaviour.
In addition the control was renamed to IosSwitch which makes more sense. 


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

Friday, February 2, 2018

Friday Fun LIX - Parallel Coordinates Chart

Aloha from Singapore one more time,

Today I have a chart for you which was a bit tricky to implement. It's a so called parallel coordinates chart and to be honest it doesn't look really fancy at a glance but it is really neat.
If you have no idea what it looks like here is a little image that show one...



First it looks like some kind of simple line chart but it's not. With this chart one can compare objects in a nice way. So let's assume you would like to compare cars and you would not only to compare one property of the cars like horsepower but a set of properties (e.g. cylinder, horsepower, acceleration, weight, consumption, etc.) and the whole comparison should be in one chart.
In this case the parallel coordinates chart ist perfect. Here is a little screenshot of my implementation that uses this example...



In this chart each line represents one car (e.g. the blue line represents my Smart Roadster Coupé and the purple line represents the Bugatti Veyron...a fair comparison right ;) ).
Now the interesting part about that chart is that it makes it easy to find similarities between cars or that you can figure out trends easily.
There are tons of possibilities on how to extend this chart but because I do not have the time for that I've just added two things that I've found very nice:

  • Dragging axes
  • Filter specific lines

So you can imagine that the order of the categories has a big impact on the expressiveness of the chart and therefor it is possible to drag a category by dragging the header of a category to another position...



Here I've simply moved some categories around to show the effect of the different category order.
The filtering of specific lines (cars in this example) works similar. Here you simply click close to one axis and drag down and you will see that a little rectangle will be drawn that selects the lines in the range of the rectangle.
If you release the mouse button it will show you something similar like follows...



As soon as you select some lines all other "unselected" lines will be drawn in light gray (configurable) and the selected lines will be drawn in blue (configurable). In addition the names of the selected lines will be shown on the left side. 
Once you click again somewhere the selection will be removed again.
In principle that's it and I hope it might be useful for someone. As always I have no usage for it at all :)
The chart is part of my JavaFX charts library where the current version is 1.0.5 and that you can found here:

Source at github

Binaries at bintray or Maven Central

And if you would like to see it in action, here is a short screen video that I've recorded...



That's it for today, so enjoy Carnival and don't forget...keep coding... ;)

Friday, January 26, 2018

Friday Fun LVIII - Some more charts...

Aloha from Singapore again,

Today I finally managed to create some little Friday Fun components for you, so today I have two new charts for you that are part of my JavaFX charts library.
The first chart is a ConcentricRingChart that looks as follows:


Nothing really special but fun. You have a set of ChartItems that you pass to the ConcentricRingChart. The values are always normalized so that the current maximum value fills up to 100%. You can choose between different NumberFormats for the value in the bar and if you click with the mouse on a bar a little popup will show you some information on the clicked item.
In addition you can also sort the bars ascending and descending if you like and of course animation is also available. If you would like to see it in action...here you go...



The other chart that I've created is similar which is the reason why I have two components today. This one is a ComparisonRingChart where you can compare two series of ChartItems. The first series will be shown on the upper part and the second series on the lower part. It looks like this...


The upper series goes from left to right where the lower series will be visualized from right to left. Everything else is exactly the same as in the ConcentricRingChart, meaning to say you also have the mouse interaction and the popup.
And again if you are interested in how it looks in action...here you are...



As already mentioned both components are part of my little JavaFX charts library that you can found here...

Source Code: github

Binaries: bintray and Maven Central 

And that's it for today...cheers from Singapore and keep coding... ;)

Friday, January 12, 2018

Friday Fun LVII - Stream Charts

Aloha,

Here we go again...last week I stumbled upon a really nice charts that are inspired by Edward Tufte's Stream Charts. This is how they look like...



And here you can find more information about them.
The chart can be used to visualize a series of data with the variation of the data it contains over time. On each date different items of the series are stacked like in a stacked bar chart. The items are sorted by their value from bottom to top, meaning to say the items with the lowest value are always at the bottom and the one with the highest values are always on top.
To be honest I have again no use case for it but I really like the visualization. By choosing the colors wisely one could visualize dominating series over time very well.
Long story short...here is my implementation of such a chart...



As you can see I don't have such good data as in the example above but you get the idea :)

To create such a chart I make use of my ChartItem class which has a timestamp property that is needed here to group the chart items for the visualization. The idea is to have a list of ChartItems that may also contain multiple ChartItems from the same day. The StreamChart component has a category property which can be one of the following categories:

  • DAY
  • WEEK
  • MONTH
  • YEAR

So let's assume you have a list of ChartItems that contains data from different days and you would like to visualize the chart grouped by days you set the category to DAY.
With this feature you can define if you would like to group the given list of ChartItems by one of the above categories...hmm...hope that explanation was more or less clear :)

It might be easier to simply take a look at the source code (see links below).

In principle this chart is similar to the Sankey Plot but not exactly the same so I've started from there and created a modified version which you can see above. This chart is now part of my own JavaFX charts library which you can find here:

sources at github

binaries at bintray

Please find the demo classes for all the charts in the test package.

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

Friday, January 5, 2018

Friday Fun LVI - Nested Bar Charts

Aloha and a happy new year,

Today I just have a short post about some nice little bar chart which I've found last week when looking for new ideas on the web. Here is the image I've stumbled upon...



I saw it and directly understood the idea behind it because it's like a compact view of two bar charts. The big bars in the background show the sum of all inner bars (like in a stacked bar chart) and the inner bars visualize the distribution of that sum into portions. If you want to know how to realize this kind of chart in Excel you should take a look at Jon Peltier's blog where this image was taken from.

To be honest the thing that got my attention are the colors he used for this chart because they directly reminded me on the Material Design colors
And because I once implemented these colors in JavaFX I gave it a try.
Well I was surprised how easy it was to implement and finished the whole thing within 1.5h and here is a little screenshot of the result...



As you can see this kind of chart works really well with the Material Design colors. So it does not make sense to use it for everything but there might be some use cases where saves a lot of space and still transfers the information.

Because I will add this chart to my JavaFX charts library anyway I did not spend more time on implementing axis and labels but I only implemented a mouse event handler. Meaning to say if you click on an element the component will fire an event that contains the data of the selected series (the big bar) and if you've clicked on one of the inner bars it will also contain the data of the selected item.
Using this event I've implemented a simple popup that will show the selected data.

This kind of chart can for example be used to compare sales data of products per quarter or similar things, you will find something ;)

As always the source is available over at github so enjoy playing around with it.

That's it for today...I wish you a codeful year 2018...keep coding...

Friday, December 29, 2017

Friday Fun LV - Florence Nightingale inspired Coxcomb Chart

Aloha,

For the last Friday in 2017 I have another friday fun component for you. It's a variation of a donut chart where the segment width varies with the value. To give you an idea what this chart looks like here is a little picture that I've found on the web...



In principle this chart is not really fancy but the one thing that caught my attention are the shadows that are only visible on the lower elements, meaning to say they are not besides the segment. In JavaFX we have effects like drop shadows directly build in but they won't work here because if you would use a simple drop shadow on each segment you would get something like follows...


So we now have the drop shadows but not only on the lower segments but all around each segment which is not the same. One trick could be to use clipping for which you have to clip each segment with a path.
Well to be honest I was to lazy and decided to go another way. In principle you could imitate a drop shadow by using a linear gradient. This works but is not easy to achieve. So what I did was to add another segment at the end of each segment which I stroke with a linear gradient that goes from gray to transparent. To give it a little bit more realistic look I've also added a gaussian blur. So my result looks as follows...


To be honest I'm not really satisfied with the result because the shadow still looks not realistic enough but hey...for an early morning hack it's good enough ;)

UPDATE:
Well what I should I say...this shadow problem nagged me last night so I reworked my implementation and now I make use of the inbuild DropShadow effect in combination with clipping. On the following picture you can see the LinearGradient based approach (left) compared to the DropShadow approach (right)...




To make the effect more visible I've set the drop shadow color to opaque black on the right side and now the shadows look more natural...at least to me ;)
For the new approach I've added an additional path at the end of each segment and adjusted the shadow offset in x- and y-direction for each segment.
This worked nicely except the fact that the shadow also showed up outside of the segment.
Well a picture is better than thousand words, so on the following picture I've used plain red for the drop shadow color and here you can see what I'm talking about...



To avoid this problem I simply clip each segment with a circle that has the same radius and by using this little clipping trick the result looks like follows (with black as shadow color again)...



And now it looks exactly as I would like to see it :)
To use the clipping in JavaFX Canvas (or HTML5 Canvas) you have to define a path that will be used for the clipping and call clip() before you draw the elements that you would like to clip. The next thing you need to keep in mind is that once the clipping path is set it will affect all operations defined here.
In my case the problem is that the radius of the segments will increase so the clipping path also has to "grow". The canvas has a nice feature which makes it possible to save and restore it's state. Meaning to say before I set the clipping path I save the canvas by calling the save() method on the GraphicsContext and after I've finished the segment I reset the canvas to the saved state by calling the restore() method on the GraphicsContext. The nice thing is that you can save/restore the canvas as often as you like...but you have to keep track of it.

As a little advice let me tell you that if you use save/restore more than once it makes sense to either add some comments to each save/restore statement so that you know for which operation you save/restore or you also simply intend your code after each save statement to visualize the saved code blocks.

But now back to the chart...
For my implementation of the coxcomb chart the values of each segment are not related to the segment radius and therefor it only makes sense if you sort the items in the list.
For this reason I've added two convenience methods to sort the chart either ascending or descending where ascending would look like this...


Well I like the descending version more but this is personal taste.
I did not add a legend to the chart because I think this could be a separate control to be more flexible. I've created a simple item class that has properties for value, name and color.
To create the chart above you simply need the following code...


Item[] items = {
    new Item(27, Color.web("#96AA3B")),
    new Item(24, Color.web("#29A783")),
    new Item(16, Color.web("#098AA9")),
    new Item(15, Color.web("#62386F")),
    new Item(13, Color.web("#89447B")),
    new Item(5, Color.web("#EF5780"))
};
CoxcombChart chart = new CoxcombChart(items);

I'm always impressed how easy it is to create stuff like this in JavaFX, creating the control and writing this blog post tooks me only 2.5h which is one of the reasons why I love coding in JavaFX, the time from the first idea to some running code is really short and with this the productivity is really high and the code for the control is just around 280 330 (I've added 2 properties with their getters and setters) lines incl. import statements, license etc. Long story short...I ❤️ it :)

Oh and if you are interested in the code you can find it as always on github.

This chart is very specific and does not work for everything but it could be quite useful for some special use cases and to be honest it simply looks nice :)

So I guess that's it for 2017, now I wish all of you a happy new year and...keep coding...

Friday, December 1, 2017

Friday Fun LIII - Sankey Plots

Aloha everyone,

Creating charts is really fun...again Thomas Nield(@thomasnield9272) pointed me to a nice chart which is called Sankey chart and again I could not withstand to try my best to implement it in JavaFX.
To give you an idea on what I'm talking about here is an example of such a Sankey plot...


Compared to last weeks Circular plots these plots can be multilevel and after searching the web for some hours I figured out that you can find all sorts of Sankey plots which might look completely different...here another example...


That doesn't make it easier to implement such a chart and so I've started reading about the history of that chart. In the end it turned out that the main purpose of this kind of chart is the visualization of flows where the width of the arrows/lines is shown proportionally to the flow quantity.
So I had to make a decision which style I should follow and I've decided to go with the first visualization of the above pictures.
Lucky me there is also a version of the Sankey plots in the Google charts which I took as a template.
Here is a screenshot of what I've come with...


This is the more colorful version of the chart but it is also possible to create other versions as you can see here...


In this version I've used different parameters for the width of the items and here also the direction of the flow is indicated by arrows (but I only support one direction anyway).

To get nice results you have to keep in mind that in my implementation you have to think about how to order the items in the chart when adding it to the control.
Meaning to say I do not have some hyper smart algorithm that do some fancy automatic sorting of items but you have to use your own brain and think about the chart before you create it. For the example above I've added the items exactly in the order as they appear on the chart which would look like follows...

// Setup chart itemsPlotItem brazil      = new PlotItem("Brazil", Colors.LIGHT_BLUE.get());
PlotItem mexico      = new PlotItem("Mexico", Colors.ORANGE.get());
PlotItem usa         = new PlotItem("USA", Colors.ORANGE.get());
PlotItem canada      = new PlotItem("Canada", Colors.LIGHT_RED.get());

PlotItem germany     = new PlotItem("Germany", Color.web("#FF48C6"));

PlotItem portugal    = new PlotItem("Portugal", Colors.LIGHT_BLUE.get());
PlotItem spain       = new PlotItem("Spain", Colors.LIGHT_GREEN.get());
PlotItem england     = new PlotItem("England", Colors.LIGHT_RED.get());
PlotItem france      = new PlotItem("France", Colors.LIGHT_GREEN.get());

PlotItem southAfrica = new PlotItem("South Africa", Colors.YELLOW.get());
PlotItem angola      = new PlotItem("Angola", Colors.PURPLE.get());
PlotItem morocco     = new PlotItem("Morocco", Colors.YELLOW.get());
PlotItem senegal     = new PlotItem("Senegal", Colors.PURPLE.get());
PlotItem mali        = new PlotItem("Mali", Colors.BLUE.get());

PlotItem china       = new PlotItem("China", Colors.BLUE.get());
PlotItem japan       = new PlotItem("Japan", Colors.GREEN.get());
PlotItem india       = new PlotItem("India", Colors.GREEN.get());

After that is done you have to define the connections between the items by defining only the outgoing streams for each item. Because that's a lot for the chart above I will only show you the ones for the first column which will look as follows...

// Setup flowsbrazil.addToOutgoing(portugal, 5);
brazil.addToOutgoing(france, 1);
brazil.addToOutgoing(spain, 1);
brazil.addToOutgoing(england, 1);
canada.addToOutgoing(portugal, 1);
canada.addToOutgoing(france, 5);
canada.addToOutgoing(england, 1);
mexico.addToOutgoing(portugal, 1);
mexico.addToOutgoing(france, 1);
mexico.addToOutgoing(spain, 5);
mexico.addToOutgoing(england, 1);
usa.addToOutgoing(portugal, 1);
usa.addToOutgoing(france, 1);
usa.addToOutgoing(spain, 1);
usa.addToOutgoing(england, 5);

As mentioned you define the streams that goes from each item to other items with their values.
After that is done you can setup the chart using the SankeyPlotBuilder as follows...

SankeyPlot sankeyPlot = SankeyPlotBuilder.create()
                                         .prefSize(600, 400)
                                         .items(brazil, mexico, usa, canada,germany,
                                                portugal, spain, england, france,
                                                southAfrica, angola, morocco, 
                                                senegal, mali, china, japan, india)
                                         .build();

And that's all it takes to create such a chart. Because the chart is again based on the JavaFX Canvas node there is no interactivity at the moment but I'm already working on a little project that will make it possible to have interactivity in the future...so stay tuned :)

Of course the code is available on github as always.

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