tag:blogger.com,1999:blog-68850674606877138932024-02-19T11:22:19.812+01:00Harmonic CodeThe life of a geek that loves Java(FX) and IoTHan Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.comBlogger254125tag:blogger.com,1999:blog-6885067460687713893.post-47027158076146916852023-10-27T10:51:00.002+02:002023-10-27T10:51:41.885+02:00Stay up to date...<p> <span style="font-family: verdana;">Aloha,</span></p><p><span style="font-family: verdana;">after quite some time I thought it's time again for a little blog post. During one of my travels I've created a little tool in Swift that works on the latest version of Apple's MacOS operating system called Sonoma. </span></p><p><span style="font-family: verdana;">The main thing that was interesting to me is the fact that you now also can have widgets on MacOS. So I've created a little tool called JDKMonitor which is also available in the <a href=" https://apps.apple.com/us/app/jdkmonitor/id6468484792" target="_blank">MacOS App Store</a>.</span></p><p><span style="font-family: verdana;">Because I always forget when the next update and release of the OpenJDK project is scheduled I thought why not creating a widget that simply tells me the date and the number of days I have to wait.</span></p><p><span style="font-family: verdana;">Because you cannot simply create a standalone widget but it has to come with an application, JDKMonitor also offers some addional functionality. </span></p><p><span style="font-family: verdana;">It will show you the dates of the next OpenJDK update and release, lets you download one of the last 4 Long Term Support versions (LTS), either as Java Runtime Environment (JRE) or as Java Development Kit (JDK) and if available, it will also give you the opportunity to download a version bundled with JavaFX.</span></p><p><span style="font-family: verdana;">The downloads are based on Azul's build of OpenJDK named Zulu which you can download and use for free also in production. You can also click a link that will open the <a href=" https://apps.apple.com/us/app/jdkmonitor/id6468484792" target="_blank">Azul Zulu download page</a> in your default browser and if you want to read the release notes of the latest LTS builds of OpenJDK it will also offer you a link to that.</span></p><p><span style="font-family: verdana;">The following images show the application and the available widgets that you can place on your desktop. When you click on the widget, it will open JDKMonitor.</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7AwlcSEitTa7I5BVh1Oij7So7mGJIPHkhfuB1paQPz0pRkOtHn5YY00kpujIdm-7J0oa6IY5MO9mKrUNy8-kfassKfHeq6gadqDW4iaVM1y4xFtJEJFFDhLPiPcuwdhlU4-FpNnffXKl79r-t_mKpOZBiBSK4EC87e8GChyphenhyphenAD0e3E3imtwNz6CyE8wCA/s1280/JDKMonitor.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="800" data-original-width="1280" height="250" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7AwlcSEitTa7I5BVh1Oij7So7mGJIPHkhfuB1paQPz0pRkOtHn5YY00kpujIdm-7J0oa6IY5MO9mKrUNy8-kfassKfHeq6gadqDW4iaVM1y4xFtJEJFFDhLPiPcuwdhlU4-FpNnffXKl79r-t_mKpOZBiBSK4EC87e8GChyphenhyphenAD0e3E3imtwNz6CyE8wCA/w400-h250/JDKMonitor.png" width="400" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiF7LvjGsGWk9WkfkKJYQFjNUCOi4ZVvUGkkGLGpQfBiBygEXJLyISN4CHnNSIMXrv9PQRRQgs0P4krOF6hNrFSfAJDhzwlMhqDmNYHe8JjUFN1J1iNXPMzJdrbjamxSjXz5lwEES6mvcD2B8TY8v9fcLYD6OfKEiZxEuru5sZw4ljmfubYYxw7CQvUo4c/s1280/JDKMonitor_Widget.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="800" data-original-width="1280" height="250" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiF7LvjGsGWk9WkfkKJYQFjNUCOi4ZVvUGkkGLGpQfBiBygEXJLyISN4CHnNSIMXrv9PQRRQgs0P4krOF6hNrFSfAJDhzwlMhqDmNYHe8JjUFN1J1iNXPMzJdrbjamxSjXz5lwEES6mvcD2B8TY8v9fcLYD6OfKEiZxEuru5sZw4ljmfubYYxw7CQvUo4c/w400-h250/JDKMonitor_Widget.png" width="400" /></a></div><span style="font-family: verdana;"><br /></span><p></p><p><span style="font-family: verdana;">As you can see on the image above, it contains an image of Jay, the foojay mascott and if you click on it, it will open the <a href="http://foojay.io">foojay.io</a> page in your default browser to get the latest news from the Java community :)</span></p><p><span style="font-family: verdana;">All in all it's nothing very special but fun...so keep coding...</span></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-5832858228297943192023-05-17T13:51:00.000+02:002023-05-17T13:51:30.569+02:00Keep track of your Java inventory...<p><b><span style="font-family: helvetica;">JavaFinder</span></b></p><p><span style="font-family: helvetica;">Do you ever wondered how many Java distributions you have installed on your machine? I not only mean versions that you use for development but also versions that come bundled with an application. Last weekend I've decided to write a little tool that can help me figuring that out...JavaFinder.</span></p><p><span style="font-family: helvetica;">This is a simple command line tool that will search a given path including it's subfolders for any OpenJDK distribution or GraalVM derivate. So it's nothing really fancy but could be useful sometimes.</span></p><p><span style="font-family: helvetica;"><br /></span></p><p><b><span style="font-family: helvetica;">Usage</span></b></p><p><span style="font-family: helvetica;">Per default it will print all distributions found in json format on the console, when I run it on my Macbook Pro without any parameters like this </span><span style="font-family: Source Code Pro;">javafinder</span><span style="font-family: helvetica;">, the output will look as follows</span></p><div style="text-align: left;"><span style="font-family: Source Code Pro; font-size: x-small;">{<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "search_path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "sysinfo":{<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "operating_system":"macos",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "architecture":"arm64",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "bit":"64"<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> },<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "distributions":[<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> {<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "vendor":"Azul",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "name":"Zulu",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "version":"17.0.7",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-17.jdk/zulu-17.jdk/Contents/Home/",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "build_scope":"OpenJDK"<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> },<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> {<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "vendor":"Azul",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "name":"Zulu",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "version":"8.0.372+7",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-8.jdk/zulu-8.jdk/Contents/Home/jre/",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "build_scope":"OpenJDK"<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> },<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> {<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "vendor":"Azul",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "name":"Zulu",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "version":"8.0.372+7",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-8.jdk/zulu-8.jdk/Contents/Home/",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "build_scope":"OpenJDK"<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> },<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> {<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "vendor":"Azul",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "name":"Zulu",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "version":"11.0.19",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-11.jdk/zulu-11.jdk/Contents/Home/",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "build_scope":"OpenJDK"<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> },<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> {<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "vendor":"Azul",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "name":"Zulu",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "version":"20.0.1",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-20.jdk/zulu-20.jdk/Contents/Home/",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "build_scope":"OpenJDK"<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> },<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> {<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "vendor":"Oracle",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "name":"Graal VM CE",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "version":"22.3.1",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines/graalvm-ce-java17-22.3.1/Contents/Home/",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "build_scope":"GraalVM"<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> },<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> {<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "vendor":"Azul",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "name":"Zulu",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "version":"21-ea+21",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-21.jdk/zulu-21.jdk/Contents/Home/",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "build_scope":"OpenJDK"<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> },<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> {<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "vendor":"Gluon",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "name":"Gluon GraalVM",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "version":"22.1.0.1",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "path":"/System/Volumes/Data/Library/Java/JavaVirtualMachines/gluon-graalvm/Contents/Home/",<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> "build_scope":"GraalVM"<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> }<br /></span><span style="font-family: Source Code Pro; font-size: x-small;"> ]<br /></span><span style="font-family: Source Code Pro; font-size: x-small;">}</span></div><p><span style="font-family: helvetica;"><br /></span></p><p><span style="font-family: helvetica;">Well that's not completely true because the output is not formatted like this but in a more compact format without linebreaks and intendation. If you prefer something more simple there is also the possibility to output the results in csv format. In this case you need to run the program as follows ```javafinder csv```and the output will look like follows:</span></p><p><span style="font-family: helvetica;"><br /></span></p><div style="text-align: left;"><span style="font-family: Source Code Pro;"><span style="font-size: x-small;">Vendor,Distribution,Version,Path,Type<br /></span><span style="font-size: x-small;">Azul,Zulu,21-ea+21,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-21.jdk/zulu-21.jdk/Contents/Home/,OpenJDK<br /></span><span style="font-size: x-small;">Oracle,Graal VM CE,22.3.1,/System/Volumes/Data/Library/Java/JavaVirtualMachines/graalvm-ce-java17-22.3.1/Contents/Home/,GraalVM<br /></span><span style="font-size: x-small;">Azul,Zulu,20.0.1,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-20.jdk/zulu-20.jdk/Contents/Home/,OpenJDK<br /></span><span style="font-size: x-small;">Azul,Zulu,11.0.19,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-11.jdk/zulu-11.jdk/Contents/Home/,OpenJDK<br /></span><span style="font-size: x-small;">Azul,Zulu,8.0.372+7,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-8.jdk/jre/,OpenJDK<br /></span><span style="font-size: x-small;">Azul,Zulu,8.0.372+7,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-8.jdk/,OpenJDK<br /></span><span style="font-size: x-small;">Azul,Zulu,20.0.1,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-20.jdk/,OpenJDK<br /></span><span style="font-size: x-small;">Azul,Zulu,21-ea+21,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-21.jdk/,OpenJDK<br /></span><span style="font-size: x-small;">Gluon,Gluon GraalVM,22.1.0.1,/System/Volumes/Data/Library/Java/JavaVirtualMachines/gluon-graalvm/Contents/Home/,GraalVM<br /></span><span style="font-size: x-small;">Azul,Zulu,8.0.372+7,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-8.jdk/zulu-8.jdk/Contents/Home/,OpenJDK<br /></span><span style="font-size: x-small;">Azul,Zulu,17.0.7,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-17.jdk/,OpenJDK<br /></span><span style="font-size: x-small;">Azul,Zulu,11.0.19,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-11.jdk/,OpenJDK<br /></span><span style="font-size: x-small;">Azul,Zulu,17.0.7,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-17.jdk/zulu-17.jdk/Contents/Home/,OpenJDK<br /></span><span style="font-size: x-small;">Azul,Zulu,8.0.372+7,/System/Volumes/Data/Library/Java/JavaVirtualMachines/zulu-8.jdk/zulu-8.jdk/Contents/Home/jre/,OpenJDK</span></span></div><p><span style="font-family: helvetica;">```</span></p><p><span style="font-family: helvetica;">On Linux and Mac you simply can save that into a file using </span><span style="font-family: Source Code Pro;">javafinder csv > jdks.csv</span></p><p><span style="font-family: helvetica;"><br /></span></p><p><span style="font-family: helvetica;">In the examples above I did not specify any paths to search for distributions which will make JavaFinder search in</span></p><p><span style="font-family: helvetica;">pre-defined folders for each operating system</span></p><p><span style="font-family: helvetica;">- Windows: </span><span style="font-family: Source Code Pro;">C:\Program Files\Java\</span></p><p><span style="font-family: helvetica;">- Linux: </span><span style="font-family: Source Code Pro;">/usr/lib/jvm</span></p><p><span style="font-family: helvetica;">- MacOS: </span><span style="font-family: Source Code Pro;">/System/Volumes/Data/Library/Java/JavaVirtualMachines/</span></p><p><span style="font-family: helvetica;"><br /></span></p><p><span style="font-family: helvetica;">In case you would like to search in a specific path you can simply add it to the command as follows:</span></p><div style="text-align: left;"><span style="font-family: helvetica;">- MACOS<br /></span><span style="font-family: helvetica;"> Search all paths in </span><span style="font-family: Source Code Pro;">/Users/hansolo</span><span style="font-family: helvetica;"> by executing<br /></span><span style="font-family: helvetica;"> </span><span style="font-family: Source Code Pro;">>: javafinder /Users/YOUR_USER_NAME</span><span style="font-family: helvetica;"> -> output will be in json format<br /></span><span style="font-family: helvetica;"> </span><span style="font-family: Source Code Pro;">>: javafinder csv /Users/YOUR_USER_NAME</span><span style="font-family: helvetica;"> -> output will be in csv format</span><span style="font-family: helvetica;"><br /></span><span style="font-family: helvetica;"><br /></span></div><div style="text-align: left;"><span style="font-family: helvetica;">- LINUX<br /></span><span style="font-family: helvetica;"> Search all paths in your home folder<br /></span><span style="font-family: helvetica;"> </span><span style="font-family: Source Code Pro;">>: javafinder /home/YOUR_USER_NAME</span><span style="font-family: helvetica;"> -> output will be in json format<br /></span><span style="font-family: helvetica;"> </span><span style="font-family: Source Code Pro;">>: javafinder csv /home/YOUR_USER_NAME</span><span style="font-family: helvetica;"> -> output will be in csv format</span><span style="font-family: helvetica;"><br /></span><span style="font-family: helvetica;"><br /></span></div><div style="text-align: left;"><span style="font-family: helvetica;">- WINDOWS<br /></span><span style="font-family: helvetica;"> Search all paths in your home folder<br /></span><span style="font-family: helvetica;"> </span><span style="font-family: Source Code Pro;">>: javafinder.exe c:\Users\YOUR_USER_NAME</span><span style="font-family: helvetica;"> -> output will be in json format<br /></span><span style="font-family: helvetica;"> </span><span style="font-family: Source Code Pro;">>: javafinder.exe csv c:\Users\YOUR_USER_NAME</span><span style="font-family: helvetica;"> -> output will be in csv format</span></div><p style="text-align: left;"><span style="font-family: helvetica;"><br /></span></p><p style="text-align: left;"><b><span style="font-family: helvetica;">ATTENTION:</span></b></p><p><span style="font-family: helvetica;">Please be aware that scanning for distributions can take some time, esp. when you point to a path like your </span><span style="font-family: helvetica;">user home folder etc. The less subfolders are in the given path, the faster the scanning will be.</span></p><p><span style="font-family: helvetica;"><br /></span></p><p><b><span style="font-family: helvetica;">Download</span></b></p><p><span style="font-family: helvetica;">JavaFinder is available for Windows (x64), Linux (x64 and aarch64) and MacOS (x64 and aarch64) and you can find it </span><span style="font-family: helvetica;">over at <a href="https://github.com/HanSolo/javafinder/releases" rel="nofollow" target="_blank">github</a></span></p><div><span style="font-family: helvetica;">That's it for today...keep coding...</span></div>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-59018102191072726932022-03-03T17:24:00.006+01:002022-04-05T11:29:09.087+02:00DiscoCLI<p> <span style="font-family: Lato;">Aloha,</span></p><p><span style="font-family: Lato;">As some of you might know I've created the so called DiscoAPI for <a href="http://foojay.io" target="_blank">foojay.io</a>. This api should help you to get an OpenJDK distribution of your choice. In addition to the api itself I've also created different plugins for IDE's and browsers and also some tools like <a href="https://github.com/HanSolo/JDKMon" target="_blank">JDKMon</a>.</span></p><p><span style="font-family: Lato;">That's all good stuff but I was always missing a command line tool which enables me to simply download an OpenJDK package of my choice in the terminal. Thank god there is <a href="https://picocli.info/" target="_blank">Picocli</a> which makes it possible to create a command line interface using Java.</span></p><p><span style="font-family: Lato;">If you use Picocli in combination with <a href="https://www.graalvm.org/22.0/reference-manual/native-image/">GraalVM's native image</a> feature you can create a single binary which was exactly what I was looking for.</span></p><p><span style="font-family: Lato;">And even better...you can build the binaries for x64 based platforms easily using github actions.</span></p><p><span style="font-family: Lato;">Long story short, I've created the Disco Command Line Client or short DiscoCLI which makes it easy to download an OpenJDK distribution of your choice.</span></p><p><span style="font-family: Lato;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: Lato;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgW-z0goEa-L-0XEQ8zqBTiQ55PmTOoln3gRioZ1tP8fHg1GaxtFzAiwkBGWxqCz9C2D6cKuX6n7qxBYvbaCnShfGw1f2idgmsA6HuBG23eHyKc0OV3Yl7dOxivlUhXKiSXRK4KLOssRkOVJNDjwoGBxKlj8Kth43ITPEHbziC0P0dj3D3alqNoM73N" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="926" data-original-width="2116" height="280" src="https://blogger.googleusercontent.com/img/a/AVvXsEgW-z0goEa-L-0XEQ8zqBTiQ55PmTOoln3gRioZ1tP8fHg1GaxtFzAiwkBGWxqCz9C2D6cKuX6n7qxBYvbaCnShfGw1f2idgmsA6HuBG23eHyKc0OV3Yl7dOxivlUhXKiSXRK4KLOssRkOVJNDjwoGBxKlj8Kth43ITPEHbziC0P0dj3D3alqNoM73N=w640-h280" width="640" /></a></span></div><span style="font-family: Lato;"><br />Here are some examples on how to use it:</span><p></p><pre style="background-color: white;"><span style="font-family: Lato;">Get Zulu with version 17.0.2 for the current operating system including JavaFX:</span><span style="font-family: JetBrains Mono, monospace;"><br /></span><span style="color: green; font-family: "JetBrains Mono", monospace; font-style: italic;"><br /></span><span style="color: #0073bf; font-style: italic;"><span style="font-family: Source Code Pro;">discocli</span></span><span style="font-style: italic;"><span style="color: #0073bf; font-family: Source Code Pro;"> -d zulu -v 17.0.2 -fx</span><span style="color: green; font-family: JetBrains Mono, monospace;"><br /></span></span><span style="color: green; font-family: "JetBrains Mono", monospace; font-style: italic;"><br /></span><span style="color: green; font-family: "JetBrains Mono", monospace; font-style: italic;"><br /></span><span style="font-family: Lato;">Get the latest version of JDK 16 for Liberica on Windows:</span><span style="font-family: JetBrains Mono, monospace;"><br /></span><span style="color: green; font-family: "JetBrains Mono", monospace; font-style: italic;"><br /></span><span style="color: #0073bf;"><span style="font-style: italic;"><span style="font-family: Source Code Pro;">discocli</span></span><span style="font-style: italic;"><span style="font-family: Source Code Pro;"> -d liberica -v 16 -os windows -latest</span><span style="font-family: JetBrains Mono, monospace;"><br /></span></span></span><span style="color: green; font-family: "JetBrains Mono", monospace; font-style: italic;"><br /></span><span style="font-family: Lato;"><br /></span></pre><pre style="background-color: white;"><span style="font-family: Lato;">Get the JDK 17.0.2 of temurin for macos with aarch64 as a tar.gz and store it to a folder</span><span style="font-family: JetBrains Mono, monospace;"><br /></span><span style="color: green; font-family: "JetBrains Mono", monospace; font-style: italic;"><br /></span><span style="color: #0073bf;"><span style="font-style: italic;"><span style="font-family: Source Code Pro;">discocli</span></span><span style="font-style: italic;"><span style="font-family: Source Code Pro;"> -d temurin -v 17.0.2 -os macos -arc aarch64 -at tar.gz -p /Users/hansolo</span><span style="font-family: JetBrains Mono, monospace;"><br /></span></span></span><span style="color: green; font-family: "JetBrains Mono", monospace; font-style: italic;"><br /></span><span style="color: green; font-family: "JetBrains Mono", monospace; font-style: italic;"><br /></span><span style="font-family: Lato;">In case a JDK pkg cannot be found discocli will try to give you the available pkgs.</span><span style="font-family: JetBrains Mono, monospace;"><br /></span><span style="color: green; font-family: "JetBrains Mono", monospace; font-style: italic;"><br /></span><span style="color: #0073bf; font-family: Source Code Pro;"><span style="font-style: italic;">discocli</span><span style="font-style: italic;"> -d liberica -v 12 -os linux -arc x64 -fx -latest<br /></span><span style="font-style: italic;"><br /></span><span style="font-style: italic;">Sorry,</span><span style="font-style: italic;"> defined pkg not found in Disco API<br /></span><span style="font-style: italic;"><br /></span><span style="font-style: italic;">Packages</span><span style="font-style: italic;"> available for Liberica for version 12:<br /></span><span style="font-style: italic;">discocli</span><span style="font-style: italic;"> -d liberica -v 12.0.2 -os linux -lc glibc -arc amd64 -at tar.gz -pt jdk<br /></span><span style="font-style: italic;">discocli</span><span style="font-style: italic;"> -d liberica -v 12.0.1 -os linux -lc glibc -arc amd64 -at tar.gz -pt jdk<br /></span><span style="font-style: italic;">discocli</span><span style="font-style: italic;"> -d liberica -v 12 -os linux -lc glibc -arc amd64 -at tar.gz -pt jdk<br /></span></span><span style="font-family: JetBrains Mono, monospace;"><br /></span></pre><pre style="background-color: white;"><span style="font-family: Lato;">It's also possible to simply check what packages are available for given set of parameters. If we would like</span></pre><pre style="background-color: white;"><span style="font-family: Lato;">to know what packages are available of Zulu for Macos on aarch64 that come as tar.gz and offer a jdk for </span></pre><pre style="background-color: white;"><span style="font-family: Lato;">version 17.0.2, we could type the following:</span></pre><pre style="background-color: white;"><span style="font-family: Lato;"><br /></span></pre><pre><pre><span style="background-color: white; color: #0073bf; font-family: Source Code Pro;"><span style="font-style: italic;">discocli</span><span style="font-style: italic;"> -f -d zulu -v 17.0.2 -os macos -arc aarch64 -pt jdk -at tar.gz<br /></span></span></pre><pre><span style="font-family: Source Code Pro;"><span style="background-color: white; color: #0073bf; font-style: italic;"><br /></span></span></pre><pre><pre style="font-family: "JetBrains Mono", monospace;"><span style="background-color: white;"><span style="color: #0073bf;"><span style="font-style: italic;">Packages found for Zulu for version 17:<br /></span><span style="font-style: italic;">discocli -d zulu -v 17.0.2 -os macos -lc libc -arc x64 -at tar.gz -pt jdk -fx<br /></span><span style="font-style: italic;">discocli -d zulu -v 17.0.2 -os macos -lc libc -arc x64 -at tar.gz -pt jdk</span></span></span></pre></pre></pre><pre style="background-color: white;"><span style="font-family: Lato;"><br /></span></pre><pre style="background-color: white;"><span style="font-family: Lato;">As always the code is available on <a href="https://github.com/HanSolo/discocli" target="_blank">github</a> if you would like to build it yourself but if you simply would</span></pre><pre style="background-color: white;"><span style="font-family: Lato;">like to download the binary, feel free to get it from the <a href="https://github.com/HanSolo/discocli/releases" target="_blank">github releases</a>.</span></pre><pre style="background-color: white;"><span style="font-family: Lato;">I provide binaries for the following platforms:</span></pre><pre style="background-color: white;"><span style="font-family: Lato;">- Macos x64 (but this also works on M1 chips because of Rosetta 2)</span></pre><pre style="background-color: white;"><span style="font-family: Lato;">- Linux x64</span></pre><pre style="background-color: white;"><span style="font-family: Lato;">- Linux aarch64</span></pre><pre style="background-color: white;"><span style="font-family: Lato;">- Windows x64</span></pre><pre style="background-color: white;"><span style="font-family: Lato;"><br /></span></pre><pre style="background-color: white;"><span style="font-family: Lato;">For those of you that are asking themselves if this is not the same as <a href="https://sdkman.io/" target="_blank">SDKMan</a>? No, it's not, SDKMan is much more</span></pre><pre style="background-color: white;"><span style="font-family: Lato;">than this. DiscoCLI really only helps you downloading an OpenJDK distribution and that's it, no installation </span></pre><pre style="background-color: white;"><span style="font-family: Lato;">and nothing else than OpenJDK distributions. </span></pre><pre style="background-color: white;"><span style="font-family: Lato;">And in the near future DiscoCLI might also be available via SDKMan using <a href="https://jreleaser.org/" target="_blank">JReleaser</a>... :)</span></pre><pre style="background-color: white;"><br /></pre><pre style="background-color: white;"><span style="font-family: Lato;">That's it for today...so keep coding... :)</span></pre><pre style="background-color: white;"><span style="font-family: Lato;"><br /></span></pre>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-67837579461292765282022-01-15T15:56:00.002+01:002022-01-15T15:56:27.899+01:00GlucoStatusFX<p><span style="font-family: Lato;">Aloha,</span></p><p><span style="font-family: Lato;">Two years ago I wrote an iOS app to monitor the diabetes of our son. This app (GlucoTracker) is written in Swift using SwiftUI and I still use it today on my iPhone and my AppleWatch.</span></p><p><span style="font-family: Lato;">After I've created that app I decided it would be nice to also have such an app on my Mac and so I wrote a Macos app using Swift and SwiftUI (GlucoStatus) that I run on all of my Macs.</span></p><p><span style="font-family: Lato;">Last week I thought by myself it might be a nice exercise to port this native Swift Macos app to JavaFX. Well I was really surprised how easy it was to rewrite this app in Java (I'm just more used to Java than to Swift which might be the main reason for this).</span></p><p><span style="font-family: Lato;">So the app gets it's data from a <a href="http://www.nightscout.info/" target="_blank">Nightscout</a> server that you have to setup to monitor the blood glucose values. And in addition you somehow need to feed the blood glucose data into the Nightscout server which usually is done by using a specific sensor like the Dexcom G6, the Freestyle Libre, Enlight or others.</span></p><p><span style="font-family: Lato;">Meaning to say without a Nightscout server my app is useless.</span></p><p><span style="font-family: Lato;">But if you have such a server in place you should be able to use the app.</span></p><p><span style="font-family: Lato;">So the main screen looks as follows:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://i.ibb.co/gP5w82q/Monosnap-2022-01-14-16-24-52.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="545" data-original-width="640" height="545" src="https://i.ibb.co/gP5w82q/Monosnap-2022-01-14-16-24-52.png" width="640" /></a></div><br /><span style="font-family: Lato;">In the upper (colored) part you will see the current value in large letters. Below it you will find the 5 last delta values which can help you to figure out the current trend (this assumes that the values from you sensor will be updated in intervals of 5 minutes.</span><p></p><p><span style="font-family: Lato;">Then there is the date and time of the last update and the average of the selected range.</span></p><p><span style="font-family: Lato;">On top of the window you can select the range that should be visualized and used for the statistics.</span></p><p><span style="font-family: Lato;">To setup the application you can click on the little button with the gear on the upper right corner and it will show you the following screen:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://i.ibb.co/BT53NY4/Monosnap-2022-01-14-16-25-06.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="545" data-original-width="640" height="545" src="https://i.ibb.co/BT53NY4/Monosnap-2022-01-14-16-25-06.png" width="640" /></a></div><br /><span style="font-family: Lato;">In the preferences screen you first of all have to set the url of your nightscout server (e.g. https://YOUR-DOMAIN.herokuapp.com).</span><p></p><p><span style="font-family: Lato;">In addition you can define if you would like to get notifications for different situations (e.g. the value is low, or acceptable low etc.). Except for the "too low" and "too high" situations you can define whether you would like to get a notification. For all situations you can enable/disable an additional sound that will be played with the notification.</span></p><p><span style="font-family: Lato;">Then you can also define intervals for situations like "too low" or "too high". This means that for example if your blood glucose value is too high the app will show you a notification with the given interval (e.g. every 5 minutes or every 20 minutes etc.)</span></p><p><span style="font-family: Lato;">In the lower part of the preferences screen you can then also define the different ranges that you would like to use (e.g. normal values should be within the range of 70-110 mg/dl etc.)</span></p><p><span style="font-family: Lato;">You could also switch to another unit that is more often used in the US which is mmol/l instead of mg/dl.</span></p><p><span style="font-family: Lato;">On the main screen you will also find 3 icons in the colored area. The rounded arrow on the upper right corner will reload the values (this is usually not needed, only in case you would manually update the values). </span></p><p><span style="font-family: Lato;">The app pings the Nightscout server every minute to check for the latest value.</span></p><p><span style="font-family: Lato;">The icon on the lower right will show you the "time in range" chart which looks as follows:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://i.ibb.co/1Gmh6tx/Monosnap-2022-01-14-16-25-32.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="606" data-original-width="626" height="606" src="https://i.ibb.co/1Gmh6tx/Monosnap-2022-01-14-16-25-32.png" width="626" /></a></div><br /><span style="font-family: Lato;">This chart will simply show you how often your blood glucose values have been in the defined ranges in the given time range (defined by the buttons on top of the main screen e.g. 7 days, 24 hours etc.)</span><p></p><p><span style="font-family: Lato;">The last icon that you will find on the lower left corner will show you a pattern view of the values from the last week and it looks as follows:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://i.ibb.co/6Rz4kZ6/Monosnap-2022-01-14-16-25-19.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="653" data-original-width="640" height="653" src="https://i.ibb.co/6Rz4kZ6/Monosnap-2022-01-14-16-25-19.png" width="640" /></a></div><br /><span style="font-family: Lato;">On top you will find the HbAc1 value that is calculated from the last 30 days. Below that you will find a list of patterns that have been identified by the app.</span><p></p><p><span style="font-family: Lato;">And at the bottom you will see a chart based on the values from the last week that shows the median of all values of a given hour of the day. This makes it possible to identify times where your values are too high or too low. The gray shaded area covers the percentiles between 10 and 90%.</span></p><p><span style="font-family: Lato;">Like I said in the beginning the main reason for doing this was to see how easy it is to port an existing Swift app to JavaFX and give it more or less the same look and feel.</span></p><p><span style="font-family: Lato;">Because I originally created the app for Macos, I did not create a version that looks like a native Windows version yet but it will always look like on the images above.</span></p><p><span style="font-family: Lato;">Because Macos has some special controls (e.g. the Switch), I created a little helper library called AppleFX that you can also find on github and maven central. It does not cover all available controls but only the ones I needed for this app. But if I will find more time I will probably add more Macos controls to the libray.</span></p><p><span style="font-family: Lato;">Be aware that you also need my <a href="https://github.com/HanSolo/toolbox" target="_blank">Toolbox</a> and <a href="https://github.com/HanSolo/toolboxfx" target="_blank">ToolboxFX</a> libraries to use the AppleFX lib.</span></p><p><span style="font-family: Lato;">As always you can find the <a href="https://github.com/HanSolo/glucostatusfx" target="_blank">source code</a> and also the <a href="https://github.com/HanSolo/glucostatusfx/releases" target="_blank">binaries</a> over at github.</span></p><p><span style="font-family: Lato;">There are no versions for Linux yet because this is again an app that runs in the background and sits in the system tray. Unfortunately this is not really supported on Ubuntu at the moment which is the reason why I did not created installers yet. But I will work on that and will probably add them in the future.</span></p><p><span style="font-family: Lato;">This does not mean that it does not work on Ubuntu, you can run it, create the installers and so on but it does not behave like it should because it should sit in the menu bar and that does not really work yet.</span></p><p><span style="font-family: Lato;">Here are also the links to the latest release:</span></p><p></p><ul style="text-align: left;"><li><span style="font-family: Lato;"><a href="https://github.com/HanSolo/glucostatusfx/releases/download/17.0.0/GlucoStatusFX-17.0.0-aarch64.pkg" target="_blank">Macos AARCH64</a></span></li><li><span style="font-family: Lato;"><a href="https://github.com/HanSolo/glucostatusfx/releases/download/17.0.0/GlucoStatusFX-17.0.0.pkg" target="_blank">Macos X64</a></span></li><li><span style="font-family: Lato;"><a href="https://github.com/HanSolo/glucostatusfx/releases/download/17.0.0/GlucoStatusFX-17.0.0.msi" target="_blank">Windows X64</a></span></li><li><span style="font-family: Lato;"><a href="https://github.com/HanSolo/glucostatusfx/releases/download/17.0.0/glucostatusfx_17.0.0-1_arm64.deb" target="_blank">Linux AARCH64</a></span></li></ul><div><span style="font-family: Lato;"><br /></span></div><div><span style="font-family: Lato;">One last thing...the app is localized and currently I support Germany and English but it would be awesome I could provide more localizations for other languages...so if you are willing to help...you are very welcome...just create a pull request on github or ping me.</span></div><div><br /></div><div><span style="font-family: Lato;">And that's it for today...so enjoy your weekend and keep coding...</span></div><p></p><p><br /></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-37443774589573594642022-01-05T04:40:00.000+01:002022-01-05T04:40:10.452+01:00Holiday fun...<p> <span style="font-family: Lato;">Aloha and a happy new year...</span></p><p><span style="font-family: Lato;">I took the first week of 2022 off and because I love coding I was looking for something that I might add to one of my libraries.</span></p><p><span style="font-family: Lato;">It was not too hard to find something interesting and I decided to give it a try...the Radial Tidy Tree...</span></p><p><span style="font-family: Lato;">For those of you that have no idea what I am talking about...here is a little example from the web...</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://bioviz.tokyo/functree2/static/images/metabo-root.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="800" data-original-width="789" height="640" src="https://bioviz.tokyo/functree2/static/images/metabo-root.png" width="631" /></a></div><br /><span style="font-family: Lato;">It is a tree structure that is visualized using a radial layout.</span><p></p><p><span style="font-family: Lato;">Looks like a fun thing to do but it really gave me some time to get it right. First of all (as nearly always) I had no real use case for it but just wanted to be able to create a chart like that.</span></p><p><span style="font-family: Lato;">So I decided to simply visualize a year. The root node has 4 child nodes, the 4 quarters and each quarter has 3 child nodes, the months of each quarter. Finally each month has it's specific number of days.</span></p><p><span style="font-family: Lato;">That's not real useful data but at least you can use it to create a Radial Tidy Tree. In principle it looks like an easy task but there are some things that are not that easy to solve. </span></p><p><span style="font-family: Lato;">First of all you have to create the tree structure which is easy using my TreeNode class which I already used for the Sunburst chart. For this one I had to add more properties to it like x, y and angle. Thanks to the java.time package the creation of the tree was easy.</span></p><p><span style="font-family: Lato;">I won't show all the code here but if you like you can simply head over to <a href="https://github.com/HanSolo/charts/blob/jdk17/src/test/java/eu/hansolo/fx/charts/RadialTidyTreeTest.java" target="_blank">github</a> and check it out there...</span></p><p><span style="font-family: Lato;">The really tricky part was figuring out the angle step between the items on each level and I tried different approaches before I finally found a way that worked for me. </span></p><p><span style="font-family: Lato;">Once I was able to place the items in the right place the next thing was to create all the bezier curves between the items to make it look good. And the last step was to put the text in the right position and rotate it correctly.</span></p><p><span style="font-family: Lato;">Well...long story short...here is the result...</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiXfvHwChgdvUUKjz6YZf-aIeYXLEwsSgZilnQBCMPP6kEtr4sAy2Iag-wYuLZUw_LcdUQxMHHCUPC70dOdA0llU76azCb8KO1Q0Xu7iSRqH7AsJH7gTJlfWmSDOo_JKqEyyIlNMp2ttIvzGIe-4Ffv2-hv5rgL3tHCA5ODDYMOb8VvxxnYC4BpftDR=s2150" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2122" data-original-width="2150" height="632" src="https://blogger.googleusercontent.com/img/a/AVvXsEiXfvHwChgdvUUKjz6YZf-aIeYXLEwsSgZilnQBCMPP6kEtr4sAy2Iag-wYuLZUw_LcdUQxMHHCUPC70dOdA0llU76azCb8KO1Q0Xu7iSRqH7AsJH7gTJlfWmSDOo_JKqEyyIlNMp2ttIvzGIe-4Ffv2-hv5rgL3tHCA5ODDYMOb8VvxxnYC4BpftDR=w640-h632" width="640" /></a></div><br /><span style="font-family: Lato;">And I really like the way the result looks :)</span><p></p><p><span style="font-family: Lato;">As I already mentioned, I do not have a real use case for it and therefor I cannot guarantee that the tree will work for all use cases. But I did a few other tests and it seems to be ok.</span></p><p><span style="font-family: Lato;">The RadialTidyTree can be found in the latest release of my <a href="https://github.com/HanSolo/charts/tree/jdk17" target="_blank">charts library (17.1.2)</a> which you can either get on <a href="https://github.com/HanSolo/charts/releases" target="_blank">github</a> or on <a href="https://repo1.maven.org/maven2/eu/hansolo/fx/charts/17.1.2/" target="_blank">maven central.</a></span></p><p><span style="font-family: Lato;">As with all the charts in my charts library you can find a class that shows how to use it in the test package, for this one just look for <a href="https://github.com/HanSolo/charts/blob/jdk17/src/test/java/eu/hansolo/fx/charts/RadialTidyTreeTest.java" target="_blank">RadialTidyTreeTest.java</a>.</span></p><p><span style="font-family: Lato;">And that's it...so keep coding... :)</span></p><p><br /></p><p><span style="font-family: Lato;"><br /></span></p><p><span style="font-family: Lato;"><br /></span></p><p><span style="font-family: Lato;"><br /></span></p><p><span style="font-family: Lato;"><br /></span></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-86766341039770222452021-12-31T07:35:00.002+01:002021-12-31T07:36:01.071+01:00Harmony...finally<p> <span style="font-family: Lato;">Aloha,</span></p><p><span style="font-family: Lato;">When you create different libraries and components you find yourself writing the same code in different places over time. In principle that's ok, except you combine those libraries and components. In this case you suddenly have the same classes twice or even more often in your code base. When I started creating Medusa, TilesFX and Charts I did not really think about the possibility to combine those libraries in one project at some point in the future. The main reason for this is that I never plan to create those libraries but they simply grow from components to libraries over time. </span></p><p><span style="font-family: Lato;">The thing that started me thinking about to re-use more code between those libraries was a project where I needed TilesFX and Charts in the same project. Both of these libraries came with a Country class with different properties and methods. Now in that project I needed both of them and I needed to write some ugly code to convert between them</span></p><p><span style="font-family: Lato;">That was the starting point of the <a href="https://github.com/HanSolo/countries" target="_blank">Countries</a> library which you can now find either on github and on maven central.</span></p><p><span style="font-family: Lato;">But then I saw that there are other classes that I more or less use in both libraries and I decided to put those shared classes in a separate project. Because there are projects that use JavaFX and others which don't, I decided to create two projects:</span></p><p></p><ul style="text-align: left;"><li><span style="font-family: Lato;">eu.hansolo.toolbox</span></li><li><span style="font-family: Lato;">eu.hansolo.toolboxfx</span></li></ul><p></p><h4 style="text-align: left;"><span style="font-family: Lato;">Toolbox:</span></h4><p><span style="font-family: Lato;">This library contains the code from my <a href="https://github.com/HanSolo/evt" target="_blank">Evt</a> project, meaning to say an event system which is similar to the JavaFX events.</span></p><p><span style="font-family: Lato;">Then I also added the code from my <a href="https://github.com/HanSolo/properties" target="_blank">Properties</a> project to the <a href="https://github.com/HanSolo/toolbox">Toolbox.</a> The properties are very similar to the JavaFX properties incl. binding. And in the Toolbox they will use the Evt events for property changes.</span></p><p><span style="font-family: Lato;">There are now also tuples in the <a href="https://github.com/HanSolo/toolbox" target="_blank">Toolbox</a> which sometimes can come in handy. They are not that fancy and their getters and setters do look like getA(), get(B) and setA(), setB(). Not so nice but useful.</span></p><p><span style="font-family: Lato;">The last thing I've added is the code from my UnitConverter which contains all kinds of different units and a converter that can convert between them (in the same category e.g. Temperature).</span></p><p><span style="font-family: Lato;">Then there is a Helper method that contains all sorts of methods that I use here and there in my code e.g. clamp() etc.</span></p><h4 style="text-align: left;"><span style="font-family: Lato;">ToolboxFX:</span></h4><p><span style="font-family: Lato;">Then there is the <a href="https://github.com/HanSolo/toolboxfx" target="_blank">ToolboxFX</a> library which depends on JavaFX but that does not only contain JavaFX related stuff. Here you will find things like my ConicalGradient, FontMetrix, GradientLookup, the Fonts that I do use often and other stuff like Point, Bounds, CornerRadii, Dimension, Location Po, CatmullRom etc.</span></p><p><span style="font-family: Lato;">ToolboxFX depends on Toolbox so you need to add Toolbox too if you use ToolboxFX.</span></p><p><span style="font-family: Lato;">This is stuff that I use a lot in the Charts library but also in TilesFX and Medusa.</span></p><p><span style="font-family: Lato;">But that's not enough, I've also separated the <a href="https://github.com/HanSolo/heatmap" target="_blank">HeatMap</a> from Charts and <a href="https://github.com/HanSolo/countries" target="_blank">Countries</a> and put it in a separate project.</span></p><p><span style="font-family: Lato;">So what does that mean for you as a user of one of my libraries?</span></p><p></p><ul style="text-align: left;"><li><span style="font-family: Lato;">Update your dependencies</span></li><ul><li><span style="font-family: Lato;">TilesFX depends on:</span></li><ul><li><span style="font-family: Lato;">eu.hansolo:toolbox:17.0.6</span></li><li><span style="font-family: Lato;">eu.hansolo:toolboxfx:17.0.15</span></li><li><span style="font-family: Lato;">eu.hansolo.fx:heatmap:17.0.3</span></li><li><span style="font-family: Lato;">eu.hansolo.fx:countries:17.0.16</span></li></ul><li><span style="font-family: Lato;">Medusa depends on:</span></li><ul><li><span style="font-family: Lato;">eu.hansolo:toolbox:17.0.6</span></li><li><span style="font-family: Lato;">eu.hansolo:toolboxfx:17.0.15</span></li></ul><li><span style="font-family: Lato;">Charts depends on:</span></li><ul><li><span style="font-family: Lato;">eu.hansolo:toolbox:17.0.6</span></li><li><span style="font-family: Lato;">eu.hansolo:toolboxfx:17.0.15</span></li><li><span style="font-family: Lato;">eu.hansolo.fx:heatmap:17.0.3</span></li><li><span style="font-family: Lato;">eu.hansolo.fx:countries:17.0.16</span></li></ul></ul><li><span style="font-family: Lato;">Use the new event system</span></li><ul><li><span style="font-family: Lato;">If you make use of things like TileEvent, you should change to TileEvt etc. The best way to see how it works is to take a look at the Demo classes within the library source code.</span></li></ul></ul><div><span style="font-family: Lato;"><br /></span></div><h4 style="text-align: left;"><span style="color: #cc0000; font-family: Lato;">ATTENTION: The libraries are not backwards compatible due to the new event system !!!</span></h4><div><span style="font-family: Lato;"><br /></span></div><div><span style="font-family: Lato;">The new versions of TilesFX, Charts and Medusa that will make use of the shared libraries will all start with version 17.1.0. There is still a lot of stuff to streamline (e.g. removing methods from the libraries Helper classes because they are already covered by Helper in Toolbox and HelperFX in ToolboxFX but for that I need more time.</span></div><div><span style="font-family: Lato;"><br /></span></div><div><span style="font-family: Lato;">So here are all libraries that are new or have changed:</span></div><div><ul style="text-align: left;"><li><span style="font-family: Lato;"><a href="https://github.com/HanSolo/toolbox" target="_blank">Toolbox</a></span></li><li><span style="font-family: Lato;"><a href="https://github.com/HanSolo/toolboxfx" target="_blank">ToolboxFX</a></span></li><li><span style="font-family: Lato;"><a href="https://github.com/HanSolo/heatmap" target="_blank">HeatMap</a></span></li><li><span style="font-family: Lato;"><a href="https://github.com/HanSolo/countries" target="_blank">Countries</a></span></li><li><span style="font-family: Lato;"><a href="https://github.com/HanSolo/medusa" target="_blank">Medusa</a> (jdk17 branch)</span></li><li><span style="font-family: Lato;"><a href="https://github.com/HanSolo/charts" target="_blank">Charts</a> (jdk 17 branch)</span></li><li><span style="font-family: Lato;"><a href="https://github.com/HanSolo/tilesfx" target="_blank">TilesFX</a> (jdk 17 branch)</span></li></ul></div><div><span style="font-family: Lato;"><br /></span></div><div><span style="font-family: Lato;">I will probably also use the Toolbox and ToolboxFX in future components and libraries.</span></div><div><span style="font-family: Lato;">So that was my holiday project and I'm really happy with it because now I could more easily use combinations of my libraries in projects.</span></div><div><span style="font-family: Lato;"><br /></span></div><div><span style="font-family: Lato;">I'm pretty sure there are still some things that do not work correctly, so please, if you stumble upon a problem do not hesitate to file an issue with some example code in the github repo.</span></div><div><span style="font-family: Lato;"><br /></span></div><div><span style="font-family: Lato;">I wish all of you a Happy New Year...and hopefully we will get rid of that Covid thing pretty soon...so stay healthy...and keep coding...</span></div><p></p><p><br /></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-19386391206264607272021-12-23T08:00:00.002+01:002021-12-23T09:14:04.678+01:00DateRanger...<p> <span style="font-family: Lato;">Aloha,</span></p><div style="text-align: left;"><span style="font-family: Lato;">Last week I needed some kind of a date picker which I can use to select ranges of dates. So I knew that I once created such a control when I was working for Canoo back in the days. But when I found it I saw that it was realized in JavaFX 2.0 that was based on JDK7 and made use of Skin and Behavior classes which changed in JDK8. </span></div><div style="text-align: left;"><span style="font-family: Lato;">Because I was not keen on rewriting that stuff I decided to simply create a new control...just for the fun of it :)</span></div><div style="text-align: left;"><span style="font-family: Lato;">And because I used a lot of Canvas recently I made the decision to make use of CSS for this control and not use the Canvas node for it. Using CSS makes the whole thing more usable for standard applications because you can easily style the control to your needs where when using the Canvas node it needs more programming effort to get the same styleability.</span></div><div style="text-align: left;"><span style="font-family: Lato;">So the first step was to figure out a control that I like to have some kind of template.</span></div><div style="text-align: left;"><span style="font-family: Lato;">And I found this one...</span></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEik85KmS5aLHaClkgsJj1cjZxSasa4lmdZ4pFERXYdULkM5c0tsEbX8b1zbM2WGXLr-BhwBbwbNooKOvaDiRJLCVQB3mNP_QVR-_8DpWL60QM8RHPwpqwbc9Adqid7mSIAjFAfw8dcragdx3MW9rysarJfqlfTM_6KJM1SpKrRC6EVDkK5PJKNcZ3yj=s175" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="159" data-original-width="175" src="https://blogger.googleusercontent.com/img/a/AVvXsEik85KmS5aLHaClkgsJj1cjZxSasa4lmdZ4pFERXYdULkM5c0tsEbX8b1zbM2WGXLr-BhwBbwbNooKOvaDiRJLCVQB3mNP_QVR-_8DpWL60QM8RHPwpqwbc9Adqid7mSIAjFAfw8dcragdx3MW9rysarJfqlfTM_6KJM1SpKrRC6EVDkK5PJKNcZ3yj=s16000" /></a></div><br /><span style="font-family: Lato;">It's not really fancy but I really like it's compact look which has all the info that I need. So I've created my version of it which looks as follows:</span></div><div style="text-align: left;"><span style="font-family: Lato;"><br /></span></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhO1dgLGkbSoyRfhcHhRtL-4UCfYauthDsZdCxguvHMWyoyDBgPdnO7a9SsFCprdME4NcT46L_AvLD7FsG1tZJxf_5EDQAVw4O_Csuw2MzWMri2V_0Y1JQ1mWm5w3amoB2Z6P5D2mI3KOCYnkncJUz8CA0KC4TNQ8dpVCIxlXK9-MxDR_GeGujXLhwH=s506" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="392" data-original-width="506" height="248" src="https://blogger.googleusercontent.com/img/a/AVvXsEhO1dgLGkbSoyRfhcHhRtL-4UCfYauthDsZdCxguvHMWyoyDBgPdnO7a9SsFCprdME4NcT46L_AvLD7FsG1tZJxf_5EDQAVw4O_Csuw2MzWMri2V_0Y1JQ1mWm5w3amoB2Z6P5D2mI3KOCYnkncJUz8CA0KC4TNQ8dpVCIxlXK9-MxDR_GeGujXLhwH=w320-h248" width="320" /></a></div><br /><span style="font-family: Lato;">As you can see I more or less created a copy of the control. So the next step was to add the functionality to select a range of dates.</span></div><div style="text-align: left;"><span style="font-family: Lato;">I've simply added a key listener and if you select a date by clicking somewhere with the mouse you can press the `SHIFT` key with the next click and it will create a range of dates for you.</span></div><div style="text-align: left;"><span style="font-family: Lato;">The range then looks as follows:</span></div><div style="text-align: left;"><span style="font-family: Lato;"><br /></span></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh_CmiQHXZEAfYZguceehNkycIsrenRWse8Pv3eCB1C3dUrZa2ssF5uU4He5pk491xPrJ3_QwbNQgIQKIcE-QU9Vw_5ABVr4wRTXgnYQeEnys_F2HdS1lztxV82zGlXlb_evFOypdo-RzMKfjGt8AW6uVZxiDKutByuabIDrytECb1Q42_JLtxRUHwm=s506" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="392" data-original-width="506" height="248" src="https://blogger.googleusercontent.com/img/a/AVvXsEh_CmiQHXZEAfYZguceehNkycIsrenRWse8Pv3eCB1C3dUrZa2ssF5uU4He5pk491xPrJ3_QwbNQgIQKIcE-QU9Vw_5ABVr4wRTXgnYQeEnys_F2HdS1lztxV82zGlXlb_evFOypdo-RzMKfjGt8AW6uVZxiDKutByuabIDrytECb1Q42_JLtxRUHwm=s320" width="320" /></a></div><br /><span style="font-family: Lato;">Most of the nodes can be styled using CSS and you will find all the available styles in the `date-ranger.css` file.</span></div><div style="text-align: left;"><span style="font-family: Lato;">The plain DateRanger comes without the month and year label and the buttons, so you can also use it for only showing the month. If you would like to use the version above, you can use the DateRangerControl which is also part of the code. This is in principle just a BorderPane that comes with the label and buttons on the top.</span></div><div style="text-align: left;"><span style="font-family: Lato;"><br /></span></div><div style="text-align: left;"><span style="font-family: Lato;">It's nothing really fancy but maybe it will be useful for one or the other.</span></div><div style="text-align: left;"><span style="font-family: Lato;">The code is available on <a href="https://github.com/HanSolo/dateranger" target="_blank">github</a> and also on maven central.</span></div><div style="text-align: left;"><span style="font-family: Lato;"><br /></span></div><div style="text-align: left;"><span style="font-family: Lato;">Well I guess that's it for 2021...I wish all of you a merry christmas and a happy new year...oh and keep coding... :)</span></div><div style="text-align: left;"><br /></div>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-49558502928319464872021-12-12T09:46:00.004+01:002021-12-12T09:46:43.855+01:00A versus B<p> <span style="font-family: verdana;">Aloha,</span></p><p><span style="font-family: verdana;">last week I was searching the web for some comparison between an older iMac and an older Macbook Pro. And when I was skimming the web for such comparison pages I saw those comparison charts on some of the pages I've found.</span></p><p><span style="font-family: verdana;">Well I just checked "comparison chart" on Google and took a look at the images and found something like this:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://cdn5.vectorstock.com/i/1000x1000/32/74/comparison-table-chart-compare-template-vector-28503274.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="800" data-original-width="710" height="800" src="https://cdn5.vectorstock.com/i/1000x1000/32/74/comparison-table-chart-compare-template-vector-28503274.jpg" width="710" /></a></div><br /><span style="font-family: verdana;">And you will find a lot of similar charts on the web. This could be a really useful chart for some use cases and the best of all...it's really easy to implement :)</span><p></p><p><span style="font-family: verdana;">Long story short...here is my version of a ComparisonBarChart...</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1FbWOrmM0HpPCoqkc3KsI2GhA-J6BqVzppN-XgzLJDK0VtjT8R1J2ZblrEijr-auQOLAGH6mgIJxKVb8UWsleRq_Kzs3-ZlE3P5rvs-vDE_UJysLL4ZsWL7WFiSu22z9iw6x1sjnjqdE/s2048/ComparisonBarChart+2021-12-12+09-15-18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1231" data-original-width="2048" height="384" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1FbWOrmM0HpPCoqkc3KsI2GhA-J6BqVzppN-XgzLJDK0VtjT8R1J2ZblrEijr-auQOLAGH6mgIJxKVb8UWsleRq_Kzs3-ZlE3P5rvs-vDE_UJysLL4ZsWL7WFiSu22z9iw6x1sjnjqdE/w640-h384/ComparisonBarChart+2021-12-12+09-15-18.png" width="640" /></a></div><br /><span style="font-family: verdana;">Just up front...the header with the "Product A" label and the "A" in the circle is NOT part of the chart. Because you not always need this labels I've decided to not add them to the chart itself but leave it to the user to add such things manually. So the chart only contains the bars and the categories.</span><p></p><p><span style="font-family: verdana;">More or less all of the chart is configurable, so here are just a few points that can be adjusted</span></p><p><span style="font-family: verdana;">- The colors of the background, the bars, the bar background, the text etc.</span></p><p><span style="font-family: verdana;">- The number format incl. no of decimals, percentage, shortened etc.</span></p><p><br /></p><p><span style="font-family: verdana;">There are some things you have to keep in mind to make the chart and the comparison work. Because you can only compare options that are available on both things you try to compare, the charts takes 2 ChartItemSeries with identical number of items. Each item needs to have the category property set to a category. You need to use the same categories (at least they should have the same name) for both series and their items.</span></p><p><span style="font-family: verdana;">To create the sample above I first have created a list of categories. Then I've created 2 maps with the categories as key and the chart items as value. This can be done in one loop like this:</span></p><pre style="background-color: white; font-family: "JetBrains Mono", monospace;"><span style="font-size: x-small;"><span style="color: #0004ff;">for </span>(<span style="color: #0004ff;">int </span>i = 0 ; i < 5 ; i++) {<br /> Category category = <span style="color: #0004ff;">new </span>Category(<span style="color: magenta;">"Option " </span>+ i);<br /> categories.<span style="color: #ff7500;">add</span>(category);<br /> optionsProductA.<span style="color: #ff7500;">put</span>(category, ChartItemBuilder.create().<span style="color: #ff7500;">name</span>(<span style="color: magenta;">"Product A (Option " </span>+ i + <span style="color: magenta;">")"</span>).<span style="color: #ff7500;">category</span>(category).<span style="color: #ff7500;">value</span>(0).<span style="color: #ff7500;">build</span>());<br /> optionsProductB.<span style="color: #ff7500;">put</span>(category, ChartItemBuilder.create().<span style="color: #ff7500;">name</span>(<span style="color: magenta;">"Product B (Option " </span>+ i + <span style="color: magenta;">")"</span>).<span style="color: #ff7500;">category</span>(category).<span style="color: #ff7500;">value</span>(0).<span style="color: #ff7500;">build</span>());<br />}</span></pre><p><span style="font-family: verdana;">Now you can create the ChartItemSeries by using it's builder and set the items to the e.g. new ArrayList<>(optionsProductA.values()).</span></p><p><span style="font-family: verdana;">To set the bar color for each series you can set the fill for the series for example to a LinearGradient or to a plain color.</span></p><p><span style="font-family: verdana;">Now you can create the ComparisonBarChart using it's builder as follows</span></p><pre style="background-color: white; font-family: "JetBrains Mono", monospace;"><span style="font-size: x-small;">chart = ComparisonBarChartBuilder.create(series1, series2)<br /> .<span style="color: #ff7500;">prefSize</span>(600, 300)<br /> .<span style="color: #ff7500;">backgroundFill</span>(Color.rgb(244, 250, 255))<br /> .<span style="color: #ff7500;">categoryBackgroundFill</span>(<span style="color: #0004ff;">new </span>LinearGradient(0, 0, 0, 1, <span style="color: #0004ff;">true</span>, CycleMethod.<span style="font-style: italic;">NO_CYCLE</span>, <br /> <span style="color: #0004ff;">new </span>Stop(0.0, Color.rgb(244, 250, 255)), <br /> <span style="color: #0004ff;">new </span>Stop(0.05, Color.<span style="font-style: italic;">WHITE</span>), <br /> <span style="color: #0004ff;">new </span>Stop(0.95, Color.<span style="font-style: italic;">WHITE</span>), <br /> <span style="color: #0004ff;">new </span>Stop(1.0, Color.rgb(244, 250, 255))))<br /> .<span style="color: #ff7500;">barBackgroundFill</span>(Color.rgb(232, 240, 252))<br /> .<span style="color: #ff7500;">barBackgroundVisible</span>(<span style="color: #0004ff;">true</span>)<br /> .<span style="color: #ff7500;">shadowsVisible</span>(<span style="color: #0004ff;">true</span>)<br /> .<span style="color: #ff7500;">textFill</span>(Color.<span style="font-style: italic;">WHITE</span>)<br /> .<span style="color: #ff7500;">categoryTextFill</span>(Color.rgb(64, 66, 100))<br /> .<span style="color: #ff7500;">shortenNumbers</span>(<span style="color: #0004ff;">false</span>)<br /> .<span style="color: #ff7500;">sorted</span>(<span style="color: #0004ff;">false</span>)<br /> .<span style="color: #ff7500;">order</span>(Order.<span style="font-style: italic;">DESCENDING</span>)<br /> .<span style="color: #ff7500;">numberFormat</span>(NumberFormat.<span style="font-style: italic;">PERCENTAGE</span>)<br /> .<span style="color: #ff7500;">doCompare</span>(<span style="color: #0004ff;">false</span>)<br /> .<span style="color: #ff7500;">categorySumVisible</span>(<span style="color: #0004ff;">false</span>)<br /> .<span style="color: #ff7500;">betterColor</span>(Color.<span style="font-style: italic;">BLUE</span>)<br /> .<span style="color: #ff7500;">poorerColor</span>(Color.<span style="font-style: italic;">RED</span>)<br /> .<span style="color: #ff7500;">build</span>();</span></pre><p><span style="font-family: verdana;">To create the chart above you don't need to set all the options that you see in the code above. I've simply added it to show that they are available. For example .doCompare(false), .sorted(false), .categorySumVisible(false), .order(Order.DESCENDING) etc. are all not needed in this case.</span></p><p><span style="font-family: verdana;">If you only would like to compare two feature sets than this is all you need but if you would like to do more...well you can :)</span></p><p><span style="font-family: verdana;">You can for example sort the bars either ascending or descending by the sum of each category items. Meaning to say order it by the sum of the left and the right bar value of each category. This sum can also be shown by setting categorySumVisible to true.</span></p><p><span style="font-family: verdana;">If you set doCompare to true the color of the bars will change so that the one with the higher value (which is always be the "better" value in this chart) get's a different color than the bar with the smaller value (which always is the "poorer" value in this chart). There are some default colors like green for better and orange for poorer but these colors can also be set.</span></p><p><span style="font-family: verdana;">In addition you can also switch some visual effects on by setting shadowsVisible = true. This will add a shadow to each bar and also to the category area (the rectangle in the center of the chart).</span></p><p><span style="font-family: verdana;">If you don't really want to set all colors manually you will find 2 convenience methods for setting the bar colors which are setBetterColor() and setPoorerColor().</span></p><p><span style="font-family: verdana;">When calling these methods the poorerDarkerColor, poorerBrighterColor, betterDarkerColor and betterBrighterColor will automatically be set.</span></p><p><span style="font-family: verdana;">If you don't want to see a gradient for the bar fill you simply have to set the brighter and darker colors to the same values.</span></p><p><span style="font-family: verdana;">The easiest way to figure out how it works is to take a look at the <a href="https://github.com/HanSolo/charts/blob/jdk17/src/test/java/eu/hansolo/fx/charts/ComparisonBarChartTest.java" target="_blank">ComparisonBarChartTest</a> class.</span></p><p><span style="font-family: verdana;">This class contains the code for the screenshot above and should be starting point for your own version of the chart.</span></p><p><span style="font-family: verdana;">You will find this new ComparisonBarChart in the <a href="https://github.com/HanSolo/charts/tree/jdk17" target="_blank">jdk17</a> branch of my JavaFX charts libray on <a href="https://github.com/HanSolo" target="_blank">github</a>.</span></p><p><span style="font-family: verdana;">And of course it is also available on <a href="https://repo1.maven.org/maven2/eu/hansolo/fx/charts/17.0.21/" target="_blank">Maven Central</a>...but keep in mind...you need to use JDK17 to run it...</span></p><p><span style="font-family: verdana;">That's it for today...so keep coding...</span></p><p><span style="font-family: verdana;"><br /></span></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-40756777302334441062021-12-03T10:05:00.000+01:002021-12-03T10:05:10.427+01:00All things countries...<p><span style="font-family: verdana;">Aloha,</span></p><p><span style="font-family: verdana;">As some of you might know I've created a JavaFX world map component a while back which I also integrated in my TilesFX and charts library.</span></p><p><span style="font-family: verdana;">Well the component itself works so far but it was always only a world map. I mean it is possible to strip out single countries and draw them but then you have the problem that this country map won't come with the same features as the world map.</span></p><p><span style="font-family: verdana;">So in principle I was always looking for something similar to <a href="https://developers.google.com/chart/interactive/docs/gallery/geochart" target="_blank">Googles GeoChart</a>. Well...long story short...I've created something similar :)</span></p><p><span style="font-family: verdana;">And I simply named it countries...because it is all about countries. It is another JavaFX library and it comes with 3 JavaFX controls:</span></p><p><span style="font-family: verdana;">- CountryPane</span></p><p><span style="font-family: verdana;">- RegionPane</span></p><p><span style="font-family: verdana;">- WorldPane</span></p><p><span style="font-family: verdana;">Each of those panes has more or less the same functionality. They show at least 1 country (CountryPane), a set of countries (RegionPane) or the whole world (WorldPane).</span></p><p><span style="font-family: verdana;">The WorldPane and the RegionPane in addition have the ability to select countries by clicking on them.</span></p><p><span style="font-family: verdana;">All 3 panes can overlay a heatmap and also so called connections (e.g. flight connections between airports).</span></p><p><span style="font-family: verdana;">In addition to the countries with their shapes the library also contains around 41000 cities with information about their population (if available), location, country and if it is a capital (if available). In addition I've also added around 8500 airports that you can use.</span></p><p><span style="font-family: verdana;">Countries can have value objects (metadata if you like) and can also have simple values, you can color them as you like. I hope I have build in enough capabilities to use for data visualization (if you miss something...just let me know).</span></p><p><span style="font-family: verdana;">Yep this is great...but it comes with lots of data...so the library is 13 MB in size!!!</span></p><p><span style="font-family: verdana;">But to be honest I don't care, it is just handy if you can directly make use of this data without loading other external data. Of course you can use your own data if you like, as long as you have latitude/longitude information it will work.</span></p><p><span style="font-family: verdana;">The library is build on JDK17, meaning to say if you would like to use it...move your stuff to JDK17 ;)</span></p><p><span style="font-family: verdana;">To give you an idea how it could be used, here are some screenshots...</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlox6mla2tAcCO5FR0moFlM8tTxmNIPXOCXWmuXMXg6-1LhiA-3JOjNwdFRyfulHtu72qgrI9BdSKtj0bjCQWr0rprQHH-uxea2ffAvEjzumSuzb_-bqrmJOICrbNPOu5gv0YbMXFS37E/s2048/DemoCountryPane.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1311" data-original-width="2048" height="410" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlox6mla2tAcCO5FR0moFlM8tTxmNIPXOCXWmuXMXg6-1LhiA-3JOjNwdFRyfulHtu72qgrI9BdSKtj0bjCQWr0rprQHH-uxea2ffAvEjzumSuzb_-bqrmJOICrbNPOu5gv0YbMXFS37E/w640-h410/DemoCountryPane.png" width="640" /></a></div><span style="font-family: verdana;">This is the CountryPane showing Germany with some cities (population > 300000). To make it easier I simply added the same cities as heatmapSpots. In addition you see a connection between the FMO airport and the MUC airport.</span><p></p><p><span style="font-family: verdana;"><br /></span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXsBXAE13JHkAllYwKDv66MMrnZYxObGgkv4pOwbmapEOvDFgIKv9IUCUGO9c-udsBTyFOz9zIZaO92PQa0jPCRmnw1lbJ0oiBzUJbTExwi7MfG4sahlpJRt-p67BpPcOxCMaFuqn6Xes/s2048/DemoRegionPane.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1311" data-original-width="2048" height="410" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXsBXAE13JHkAllYwKDv66MMrnZYxObGgkv4pOwbmapEOvDFgIKv9IUCUGO9c-udsBTyFOz9zIZaO92PQa0jPCRmnw1lbJ0oiBzUJbTExwi7MfG4sahlpJRt-p67BpPcOxCMaFuqn6Xes/w640-h410/DemoRegionPane.png" width="640" /></a></div><span style="font-family: verdana;">Here you see the RegionPane showing the European Union. In this map I show the available capitals (cyan dots) and used cities with a population > 200000 for the heatmap. In addition I here show 3 connections between some airports (LIS -> ARN, MAD -> ARN, FMO -> ARN). I already added some predefined country regions in the BusinessRegion class. There you will find regions like DACH, EMEA, APAC, AMERICAS, CENTRAL_ASIA, NORTHERN_AMERICA etc. You can define your own regions by using the CountryRegion class.</span><p></p><p><span style="font-family: verdana;"><br /></span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzatuNEhseXGRSj0QGUddTLa7FYfRM2bJPkOvMItkg3bLxDJT2AWi92iV-y7NsLRL9iZcEYYdp_sY54RSat2eNbI3qf9ytBnL7gHWPskjnkwdYvL_CkOQOaBXnSpZvv_gGHuZurxM43Gg/s2048/DemoWorldPane.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1311" data-original-width="2048" height="410" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzatuNEhseXGRSj0QGUddTLa7FYfRM2bJPkOvMItkg3bLxDJT2AWi92iV-y7NsLRL9iZcEYYdp_sY54RSat2eNbI3qf9ytBnL7gHWPskjnkwdYvL_CkOQOaBXnSpZvv_gGHuZurxM43Gg/w640-h410/DemoWorldPane.png" width="640" /></a></div><br /><p><span style="font-family: verdana;">And finally the WorldPane which again shows the available capitals (cyan spots), cities with a population > 1000000 for the heatmap and again some connections between international airports.</span></p><p><span style="font-family: verdana;">Not sure if this might be interesting for you but I needed it :)</span></p><p><span style="font-family: verdana;">As always the source is available over at <a href="https://github.com/HanSolo/countries" target="_blank">github</a> where you can also find the latest <a href="https://github.com/HanSolo/countries/releases" target="_blank">release</a>. And of course it is also availble on <a href="https://search.maven.org/artifact/eu.hansolo.fx/countries/17.0.5/jar" target="_blank">maven central</a>.</span></p><p><span style="font-family: verdana;">That's it for today...so keep coding... :)</span></p><p><br /></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-72467506223625514422021-11-23T16:31:00.004+01:002021-11-23T16:31:32.187+01:00TilesFX 17.0.11<p><span style="font-family: verdana;">Aloha,</span></p><p><span style="font-family: verdana;">when working with dashboard using my TilesFX library I came across some missing things...first of all I needed a Tile that simply has a text in the center of it. Well nothing easier than that...I simply added a CenterTextTileSkin class that offers exactly this functionality. Here is a little screenshot that shows it in action...</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5ixoefTMnE3Xwyecxg9OPQz1iclYUSsG8mTO-3ZUtUWWfyKQOvOd3BLCKq9BF3o9XuNQvwcsqpmEgzCJ_1_FBB_Ffu2NBMTcHCBGs355f6HwSyReiLwbSnp1qbYqGdQ6BfKEtj3ZSo-4/s1364/Screenshot+of+ScreenFloat+%252823-11-21%252C+16-16-08%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1364" height="422" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5ixoefTMnE3Xwyecxg9OPQz1iclYUSsG8mTO-3ZUtUWWfyKQOvOd3BLCKq9BF3o9XuNQvwcsqpmEgzCJ_1_FBB_Ffu2NBMTcHCBGs355f6HwSyReiLwbSnp1qbYqGdQ6BfKEtj3ZSo-4/w640-h422/Screenshot+of+ScreenFloat+%252823-11-21%252C+16-16-08%2529.png" width="640" /></a></div><br /><span style="font-family: verdana;">As you can see I needed it to visualize the state of a server. If the server goes down the text will change from "ONLINE" to "OFFLINE" and the background color of the tile will change from green to red...very simple but very effective :)</span><p></p><p><span style="font-family: verdana;">To change the text you have to set the description of the tile.</span></p><p><span style="font-family: verdana;">Here is the code to create the tile above:</span></p><pre style="background-color: white; font-family: "JetBrains Mono", monospace;">Tile serverTile = TileBuilder.create()<br /> .<span style="color: #ff7500;">skinType</span>(SkinType.<span style="font-style: italic;">CENTER_TEXT</span>)<br /> .<span style="color: #ff7500;">title</span>(<span style="color: magenta;">"Server"</span>)<br /> .<span style="color: #ff7500;">text</span>(<span style="color: magenta;">"Last check"</span>)<br /> .<span style="color: #ff7500;">backgroundColor</span>(Dark.<span style="font-style: italic;">GREEN</span>)<br /> .<span style="color: #ff7500;">description</span>(<span style="color: magenta;">"ONLINE"</span>)<br /> .<span style="color: #ff7500;">build</span>();</pre><p><span style="font-family: verdana;">The other thing I stumbled upon was the fact that handling big numbers in a dashboard can really suck. When you have a dashboard with lots of tiles, there is sometimes simply not enough space to show those big numbers.</span></p><p><span style="font-family: verdana;">So the idea again is simple...just shorten the big numbers to a more readable format. For example 2350 can become 2.3k and 1230401 can become 1.2M. With this you can also show big numbers in a small tile.</span></p><p><span style="font-family: verdana;">The feature to use here is the property shortenNumbers in Tile. I've added it for some tiles where I thought it might come in handy but there might still be places where it is missing...so if you find a place where it could make sense, please file an issue over at <a href="https://github.com/HanSolo/tilesfx/issues" target="_blank">github</a>. </span></p><p><span style="font-family: verdana;">You could also use the method </span><span style="background-color: white; font-family: "JetBrains Mono", monospace;">Helper.</span><span style="background-color: white; color: #ff7500; font-family: "JetBrains Mono", monospace;">shortenNumber</span><span style="background-color: white; font-family: "JetBrains Mono", monospace;">(<span style="color: #2b00fe;">final long</span> value)</span><span style="font-family: verdana;"> to shorten the numbers on your own before you set it somewhere. The method will return the formatted number as a String.</span></p><p><span style="font-family: verdana;">The result of using the shortenNumber property in Tile can be seen in the left Tile on image above.</span></p><p><span style="font-family: verdana;">Those new features can be found in the latest version of TilesFX which is 17.0.11 and which is available on <a href="https://github.com/HanSolo/tilesfx" target="_blank">github</a> and also on <a href="https://search.maven.org/artifact/eu.hansolo/tilesfx/17.0.11/jar" target="_blank">Maven central</a>.</span></p><p><span style="font-family: verdana;">That's it for today...so keep coding... :)</span></p><p><span style="font-family: verdana;"><br /></span></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-9193025049601374322021-11-16T13:23:00.003+01:002021-11-16T13:23:53.365+01:00Panel Barchart<p> <span style="font-family: verdana;">Aloha,</span></p><p><span style="font-family: verdana;">I'm currently working on a dashboard where I needed a way to visualize data in a specific way. Let's say we would like to compare the load of 3 servers for each day of a week. And in addition we would like to be able to compare the current week with the last week.</span></p><p><span style="font-family: verdana;">There are probably different ways how you can visualize this and I decided to go with a so called Panel Barchart.</span></p><p><span style="font-family: verdana;">Here is an example of such a chart:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://peltiertech.com/images/2010-03/tabgart4_barpanel_legend.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="466" data-original-width="512" height="466" src="https://peltiertech.com/images/2010-03/tabgart4_barpanel_legend.png" width="512" /></a></div><span style="font-family: verdana;"><p><span style="font-family: verdana;"><br /></span></p>In principle this is some kind of a rotated stacked bar chart where the segments are separated from each other. So my version looks a bit different but you will see it's similar, here you go:</span><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqadk3nRTMkn6BFPZN9xoBoZIQsmfIF7nsah37fhbs4PMhE0zpgkPNcrO9qOgBsDGpn2xreWpqkg4uDYHo3p_344FIBDJ5-Ojk73a9oYUmBTLs5nWYn8YX2hAyV7FgtFgRIQEjrg_rhVA/s2048/PanelBarChart.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1226" data-original-width="2048" height="384" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqadk3nRTMkn6BFPZN9xoBoZIQsmfIF7nsah37fhbs4PMhE0zpgkPNcrO9qOgBsDGpn2xreWpqkg4uDYHo3p_344FIBDJ5-Ojk73a9oYUmBTLs5nWYn8YX2hAyV7FgtFgRIQEjrg_rhVA/w640-h384/PanelBarChart.png" width="640" /></a></div><br /><span style="font-family: verdana;">So in the upper chart you see the server load of our 3 servers for this week (of course the numbers don't make sense and are random but you get the idea).</span><p></p><p><span style="font-family: verdana;">You need to define categories (here it's the days of the week). Now you need to add a series of chart items for each category and each server.</span></p><p><span style="font-family: verdana;">In upper chart eh bars are colored by the categories (workdays = gray, weekend = red). This colors can be defined in the categories. </span></p><p><span style="font-family: verdana;">If you switch this feature off (colorByCategory = false [default]) it will use the colors of the items in each series.</span></p><p><span style="font-family: verdana;">The chart will show the name of the series on the far left column and the sum of each series on the far right column.</span></p><p><span style="font-family: verdana;">On top of each category column it will show the sum per category (in this case per day). On the upper right corner it will show the overall sum of all values.</span></p><p><span style="font-family: verdana;">Well that's good but sometimes you would like to compare the current values with values from another point in time e.g. last week or last year.</span></p><p><span style="font-family: verdana;">To make this possible you can switch enableComparison to true and as you might already thought you need to define the data of the things you would like to compare.</span></p><p><span style="font-family: verdana;">To be able to compare data you need the exact same number of series in the listOfSeries and the comparisonListOfSeries. In addition the items in the each lisOfSeries need to use the same categories that you use in the </span><span style="font-family: verdana;">comparisonListOfSeries.</span></p><p><span style="font-family: verdana;">When you fullfill these requirements the chart will now show name and the comparisonName in the upper left corner (yes you can set the colors for both of them separately). Now you will also see the items for both series of data and if you like you can also define the colors for the series sums and the category sums separately.</span></p><p><span style="font-family: verdana;">The chart is interactive in the way that you can click on each item and a little popup will show you some text and the value of the item. The text that will be shown in the popup could either be the name of the item or the description of the item. If you set both (name and description) it will take the description.</span></p><p><span style="font-family: verdana;">You can find the code for the example above in the PanelBarChartTest class in the test package of the current jdk17 branch of my charts library.</span></p><p><span style="font-family: verdana;">The latest release is available on <a href="https://github.com/HanSolo/charts/releases" target="_blank">github</a> (at the moment it is 17.0.11) and also on maven central.</span></p><p><span style="font-family: verdana;">That's it for today...so keep coding... :)</span></p><p><span style="font-family: verdana;"><br /></span></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-77437705184331356492021-09-20T15:17:00.001+02:002021-09-20T15:17:12.118+02:00Mission Timer X<p><span style="font-family: verdana;">Aloha,</span></p><p><span style="font-family: verdana;">Last week I've watched the live stream of SpaceX Mission "<a href="https://inspiration4.com/" target="_blank">Inspiration4</a>". When following the countdown on the screen I saw a nice control on the bottom of the screen...a mission timer. Well <a href="https://www.jug-muenster.de/swing-apollo-space-program-mission-timer-280/">my very first Swing control</a> I've created was the mission timer of the Apollo missions. As you can see I always was fascinated by those things... :)</span></p><p><span style="font-family: verdana;">So here is the screenshot I took:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQTGeaT3NehDaoxmUs49lRsh76n-pwgtrgeAwOYaRz7sEoP6g3uzfh2XOX_br76LP5u_lvhvec92HO7nNssq9DLDZIiJMLHpCMQDa_Y0lfvT39LKBOdoBpvkIv-Oqz1C3XxJcA1zJLYmI/s2436/IMG_2535.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1125" data-original-width="2436" height="296" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQTGeaT3NehDaoxmUs49lRsh76n-pwgtrgeAwOYaRz7sEoP6g3uzfh2XOX_br76LP5u_lvhvec92HO7nNssq9DLDZIiJMLHpCMQDa_Y0lfvT39LKBOdoBpvkIv-Oqz1C3XxJcA1zJLYmI/w640-h296/IMG_2535.PNG" width="640" /></a></div><br /><span style="font-family: verdana;">The thing I really liked is the idea of having a circle where the upcoming events moving around. Very nice design. Of course there are details like colors, dots within the event circles etc. So I just had this screenshot and tried to re-create this control in JavaFX.</span><p></p><p><span style="font-family: verdana;">First thing to do (as always)...create a good vector drawing of the control.</span></p><p><span style="font-family: verdana;">So here you go:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2kYPRM54QxC_Cog35hh-CyDCyDShUU_eFm52TK40XvToFMY13EEIOFwaFX-iO1QV4W-IRc-Hx9ls6yqkIjMTgOce8ntTNHdQrmHwYj-aRqZT8e5xDfVz4PLOPh3ID92RS8wXp6lrAS8Q/s400/SpaceX_MissionTimer.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="82" data-original-width="400" height="132" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2kYPRM54QxC_Cog35hh-CyDCyDShUU_eFm52TK40XvToFMY13EEIOFwaFX-iO1QV4W-IRc-Hx9ls6yqkIjMTgOce8ntTNHdQrmHwYj-aRqZT8e5xDfVz4PLOPh3ID92RS8wXp6lrAS8Q/w640-h132/SpaceX_MissionTimer.png" width="640" /></a></div><br /><span style="font-family: verdana;">My version also supports days which is the reason why I have the format in the way you see it on the image above. Another thing which I might improve is the changing colors of the items. In my version the color depends on the angle of the item on the circle but I think fading the item color might be better...we will see, maybe I will add this later on.</span><p></p><p><span style="font-family: verdana;">Also the font is not really the same as the one used by SpaceX. I've tried to find an appropriate one and decided to use the one in the image above.</span></p><p><span style="font-family: verdana;">The intersting thing about this control is that the width of the control is the only thing I used to do all the calculations. This was needed to make sure the aspect ratio of the control is always the same.</span></p><p><span style="font-family: verdana;">The MissionTimerX control also fires events of the type MissionTimerXEvent.PROCESSED. These events will be fired once an item reaches the center of the control which means it happens :)</span></p><p><span style="font-family: verdana;">Here is a little gif that shows the control in action...</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggR-QMFearKtuZChg_Che_9lcXJJNBkx0K_xlmhZfnjZoVcMc9FE4pX-jNSvYA3EpTDpZmF1EqC5Q6xfCoqulcumPh5hMEZ9f0rqZuX5e4Nh3w6n8e5ehkzn63XoWL4mJOVYbGxUVveck/s1489/MissionTimerX.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="349" data-original-width="1489" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggR-QMFearKtuZChg_Che_9lcXJJNBkx0K_xlmhZfnjZoVcMc9FE4pX-jNSvYA3EpTDpZmF1EqC5Q6xfCoqulcumPh5hMEZ9f0rqZuX5e4Nh3w6n8e5ehkzn63XoWL4mJOVYbGxUVveck/w640-h150/MissionTimerX.gif" width="640" /></a></div><br /><span style="font-family: verdana;">And as always the code is available over at </span><a href="https://github.com/HanSolo/missiontimerx" style="font-family: verdana;" target="_blank">github</a><span style="font-family: verdana;">.</span><p></p><p><span style="font-family: verdana;">Well...that's it...so keep coding...</span></p><p><br /></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-14076887512509441482021-07-07T15:30:00.001+02:002021-07-07T15:30:00.195+02:00SpinnerTileSkin<p> <span style="font-family: verdana;">Aloha,</span></p><p><span style="font-family: verdana;">I finally found some time to continue working on TilesFX. There was an <a href="https://github.com/HanSolo/tilesfx/issues/113" target="_blank">issue</a> in the <a href="https://github.com/HanSolo/tilesfx" target="_blank">TilesFX repo</a> over at github that I would like to do for a long time but never really found the time.</span></p><p><span style="font-family: verdana;">So I now added a new skin called SpinnerTileSkin which is based on this issue/request. It does not look exactly like the requested one but I think it's close enough.</span></p><p><span style="font-family: verdana;">In principle the skin shows a numerical value that when changed will spin through the numbers from 0-9 as if they where on a wheel.</span></p><p><span style="font-family: verdana;">Because a screenshot won't really show the effect, here is a little video:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4SWjYlYulncdbfoYxUgPBXzfR9K5FZ7H93rZCXZhKh4UdecrzK44AB4uNwiIBjMqAU1ZwUR4yeubr1tJFVv2T_s06-H3m_I1V54Gwykwf0BXQH8Gp47eKKCPWyohxL_Q8mJWt69tf2uc/s908/SpinnerTileSkin.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="908" data-original-width="852" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4SWjYlYulncdbfoYxUgPBXzfR9K5FZ7H93rZCXZhKh4UdecrzK44AB4uNwiIBjMqAU1ZwUR4yeubr1tJFVv2T_s06-H3m_I1V54Gwykwf0BXQH8Gp47eKKCPWyohxL_Q8mJWt69tf2uc/w600-h640/SpinnerTileSkin.gif" width="600" /></a></div><br /><span style="font-family: verdana;">As you can see it is nothing really special but sometimes it might be exactly what you need.</span><p></p><p><span style="font-family: verdana;">To set it up you simple need the following code:</span></p><pre style="background-color: white;"><span style="font-family: courier; font-size: x-small;">Tile tile = TileBuilder.create()<br /> .<span style="color: #ff7500;">skinType</span>(SkinType.<span style="font-style: italic;">SPINNER</span>)<br /> .<span style="color: #ff7500;">prefSize</span>(<i>300, 300</i>)<br /> .<span style="color: #ff7500;">title</span>(<span style="color: magenta;">"SpinnerTile"</span>)<br /> .<span style="color: #ff7500;">minValue</span>(-50)<br /> .<span style="color: #ff7500;">maxValue</span>(50)<br /> .<span style="color: #ff7500;">value</span>(0)<br /> .<span style="color: #ff7500;">decimals</span>(2)<span style="color: green; font-style: italic;"><br /></span><span style="color: green; font-style: italic;"> </span>.<span style="color: #ff7500;">text</span>(<span style="color: magenta;">"Animated number spinner"</span>)<br /> .<span style="color: #ff7500;">animated</span>(<span style="color: #0004ff;">false</span>)<br /> .<span style="color: #ff7500;">build</span>();</span></pre><pre style="background-color: white;"><span style="font-family: courier; font-size: x-small;">tile.<span style="color: #ff7500;">currentValueProperty</span>().<span style="color: #ff7500;">addListener</span>((o, ov, nv) -> {<br /> <span style="color: #0004ff;">if </span>(nv.<span style="color: #ff7500;">doubleValue</span>() < 0) {<br /> tile.<span style="color: #ff7500;">setValueColor</span>(Tile.<span style="font-style: italic;">RED</span>);<br /> } <span style="color: #0004ff;">else </span>{<br /> tile.<span style="color: #ff7500;">setValueColor</span>(Tile.<span style="font-style: italic;">FOREGROUND</span>);<br /> }<br />});</span></pre><p><br /></p><p><span style="font-family: verdana;">Switching the color from white to red in case the number is negative is done by the listener attached to the currentValueProperty of the tile and is not the standard behavior.</span></p><p><span style="font-family: verdana;">Because I'm preparing my libraries for the upcoming JDK17 LTS release, this skin can only be found in the <a href="https://github.com/HanSolo/tilesfx/tree/jdk16" target="_blank">JDK16 branch</a> of TilesFX. Not sure if I will backport it to the JDK11 master branch.</span></p><p><span style="font-family: verdana;">At the moment the JDK16 branch is not available on Maven Central but I will probably create a release in the coming days...so stay tuned...and keep coding :)</span></p><p><span style="font-family: verdana;"><br /></span></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-66526742546116795522021-07-02T09:48:00.005+02:002021-07-02T09:48:32.424+02:00BubbleGridChart<p> <span style="font-family: verdana;">Aloha,</span></p><p><span style="font-family: verdana;">Last week I was playing around with some data and could not find the right chart to visualize it.</span></p><p><span style="font-family: verdana;">To give you an idea about the data, let's take the harvest of some fruits as an example. You will have ripe and unripe fruits, fruits that have been eaten by birds or caterpillars. Some of them might have been damaged by hail or did not get enough water, others might be rotten or mouldy. For each fruit you have those different numbers and you have the sum of each fruit and the sum of all fruits.</span></p><p><span style="font-family: verdana;">The best way to compare all those numbers would be a matrix style chart. So I've stumbled upon the so called Bubble Grid Chart. So here is an example that I've found on the web:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjW_uxojXpTjgYCTCnjKMwPKLz3WV_1lrKUai7dqhLMtUZ29pxq1yIckDq08xIf3i__j-uYKY9ng0Ii_0XBEEtwCV90B8a01wNqG2IfjkFF4DxYBYJ5dTct1VD4Lsj-p9hIEietwGD-bJyL/s673/BubbleGridChartFinal.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="602" data-original-width="673" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjW_uxojXpTjgYCTCnjKMwPKLz3WV_1lrKUai7dqhLMtUZ29pxq1yIckDq08xIf3i__j-uYKY9ng0Ii_0XBEEtwCV90B8a01wNqG2IfjkFF4DxYBYJ5dTct1VD4Lsj-p9hIEietwGD-bJyL/s320/BubbleGridChartFinal.png" width="320" /></a></div><span style="font-family: verdana;">As you can see it shows the fruit data that I described above.</span><p></p><p><span style="font-family: verdana;">The value of each crosspoint e.g. 90 Apples that are ripe will be visualized by the size of the bubble. Sizing the bubbles is a bit tricky because you would like to avoid having a few big bubbles and a lot of tiny bubbles. So you need to make sure that the size of the bubbles has no linear relationship to it's value.</span></p><p><span style="font-family: verdana;">In addition the max size of a bubble is given by either the height of the y-category items or the width of the x-category items, depends on which is smaller.</span></p><p><span style="font-family: verdana;">I also would like to have a grid in the background to make it easier to find specific coordinates. So, long story short, here is my version of the BubbleGridChart:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhR0OXhjlvpteQo4rBeOFL72Y9BYcgUJqcFHjtw7ibRA5AJvshmfUjOx8tXvt7hKwXhxEibXp4gzbwRN6hXdR55HfbCF0zCbJg5jWPJQEpaEC0CdPAX3VsHjAu0OnmMekTCG9etwlSE2y0/s1788/Bubble+Grid+Chart+2021-07-02+08-52-38.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1216" data-original-width="1788" height="436" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhR0OXhjlvpteQo4rBeOFL72Y9BYcgUJqcFHjtw7ibRA5AJvshmfUjOx8tXvt7hKwXhxEibXp4gzbwRN6hXdR55HfbCF0zCbJg5jWPJQEpaEC0CdPAX3VsHjAu0OnmMekTCG9etwlSE2y0/w640-h436/Bubble+Grid+Chart+2021-07-02+08-52-38.png" width="640" /></a></div><br /><span style="font-family: verdana;">As you can see I've decided to put the x-category items on the bottom and also added the ability to show not only the values on the bubbles but also on the rows and columns of the chart. I really was impressed on how much information you can get out of one chart. At a glance you can see that the number of all ripe fruits is 215 which is 43% of all fruits. Because in this example the number of each fruit was always 100 you cannot really compare by the x-category but this could be different.</span><p></p><p><span style="font-family: verdana;">You can see that 90 Apples have been ripe which is 18% of all fruits. This information will be shown in a little info that will popup when you click on the bubble. In addition I've added the ability to sort the chart in x- and y-direction by either their indices or their values.</span></p><p><span style="font-family: verdana;">To be able to sort the items by their index you have to define it upfront.</span></p><p><span style="font-family: verdana;">Let me show you how to set up the x- and y-category items for the chart above.</span></p><p><span style="font-family: verdana;">Y-Category Items:</span></p><p><span style="font-family: courier; font-size: x-small;">ChartItem ripe = ChartItemBuilder.create().<span style="color: #ffa400;">name</span>(<span style="color: #ff00fe;">"Ripe"</span>).<span style="color: #ffa400;">index</span>(0).<span style="color: #ffa400;">fill</span>(Color.<i>BLUE</i>).<span style="color: #ffa400;">build</span>();</span></p><p><span style="font-family: courier; font-size: x-small;">ChartItem unripe = ChartItemBuilder.create().<span style="color: #ffa400;">name</span>(<span style="color: #ff00fe;">"Unripe"</span>).<span style="color: #ffa400;">index</span>(0).<span style="color: #ffa400;">fill</span>(Color.<i>RED</i>).<span style="color: #ffa400;">build</span>();</span></p><p><span style="font-family: verdana;">X-Category Items:</span></p><p><span style="font-family: courier; font-size: x-small;">ChartItem peaches = ChartItemBuilder.create().<span style="color: #ffa400;">name</span>(<span style="color: #ff00fe;">"Peaches"</span>).<span style="color: #ffa400;">index</span>(0).<span style="color: #ffa400;">fill</span>(Color.<i>ORANGERED</i>).<span style="color: #ffa400;">build</span>();</span></p><p><span style="font-family: courier; font-size: x-small;">ChartItem apples = ChartItemBuilder.create().<span style="color: #ffa400;">name</span>(<span style="color: #ff00fe;">"Apples"</span>).<span style="color: #ffa400;">index</span>(1).<span style="color: #ffa400;">fill</span>(Color.<i>LIMEGREEN</i>).<span style="color: #ffa400;">build</span>();</span></p><p><span style="font-family: verdana;">BubbleChart Items:</span></p><p><span style="font-family: courier; font-size: x-small;">BubbleGridChartItem peaches1 = BubbleGridChartBuilder.create().<span style="color: #ffa400;">categoryXItem</span>(peaches).<span style="color: #ffa400;">categoryYItem</span>(ripe).<span style="color: #ffa400;">value</span>(60).<span style="color: #ffa400;">fill</span>(Color.<i>ORANGERED</i>).<span style="color: #ffa400;">build</span>();</span></p><p><span style="font-family: courier; font-size: x-small;">BubbleGridChartItem peaches2 = BubbleGridChartBuilder.create().<span style="color: #ffa400;">categoryXItem</span>(peaches).<span style="color: #ffa400;">categoryYItem</span>(unripe).<span style="color: #ffa400;">value</span>(5).<span style="color: #ffa400;">fill</span>(Color.<i>ORANGERED</i>).<span style="color: #ffa400;">build</span>();</span></p><p><span style="font-family: courier; font-size: x-small;">BubbleGridChartItem apples1 = BubbleGridChartBuilder.create().<span style="color: #ffa400;">categoryXItem</span>(apples).<span style="color: #ffa400;">categoryYItem</span>(ripe).<span style="color: #ffa400;">value</span>(90).<span style="color: #ffa400;">fill</span>(Color.<i>LIMEGREEN</i>).<span style="color: #ffa400;">build</span>();</span></p><div><p><span style="font-family: courier; font-size: x-small;">BubbleGridChartItem apples2 = BubbleGridChartBuilder.create().<span style="color: #ffa400;">categoryXItem</span>(apples).<span style="color: #ffa400;">categoryYItem</span>(ripe).<span style="color: #ffa400;">value</span>(0).<span style="color: #ffa400;">fill</span>(Color.<i>LIMEGREEN</i>).<span style="color: #ffa400;">build</span>();</span></p></div><div><span style="font-family: verdana;">The index that you define will later be used to sort the items, just make sure you don't have duplicate indices because I do not check that at the moment (I just needed something that works quickly). So you first create the x- and y-category items and from those you create the actual BubbleGridChart items that will be used to visualize the chart.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">The BubbleGridChart itself can be create as follows:</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: courier; font-size: x-small;">BubbleGridChart bubbleGridChart = </span></div><div><span style="font-family: courier; font-size: x-small;"> BubbleGridChartBuidler.create()</span></div><div><span style="font-family: courier; font-size: x-small;"> .<span style="color: #ffa400;">chartBackground</span>(Color.web(<span style="color: #ff00fe;">"#0e0e0e"</span>))</span></div><div><span style="font-family: courier; font-size: x-small;"> .<span style="color: #ffa400;">textColor</span>(Color.<i>WHITE</i>)</span></div><div><span style="font-family: courier; font-size: x-small;"> .<span style="color: #ffa400;">gridColor</span>(Color.rgb(255, 255, 255, 0.1))</span></div><div><span style="font-family: courier; font-size: x-small;"> .<span style="color: #ffa400;">showGrid</span>(<span style="color: #2b00fe;">true</span>)</span></div><div><span style="font-family: courier; font-size: x-small;"> .<span style="color: #ffa400;">showValues</span>(<span style="color: #2b00fe;">true</span>)</span></div><div><span style="font-family: courier; font-size: x-small;"> .<span style="color: #ffa400;">showPercentage</span>(<span style="color: #2b00fe;">true</span>)</span></div><div><span style="font-family: courier; font-size: x-small;"> .<span style="color: #ffa400;">items</span>(bubbleGridChartItems)</span></div><div><span style="font-family: courier; font-size: x-small;"> .<span style="color: #ffa400;">sortXCategoryItemsByIndexAscending</span>()</span></div><div><span style="font-family: courier; font-size: x-small;"> .<span style="color: #ffa400;">sortYCategoryItemsByIndexDescending</span>()</span></div><div><span style="font-family: courier; font-size: x-small;"> .<span style="color: #ffa400;">useXCategoryFill</span>()</span></div><div><span style="font-family: courier; font-size: x-small;"> .<span style="color: #ffa400;">useGradientFill</span>(<span style="color: #2b00fe;">false</span>)</span></div><div><span style="font-family: courier; font-size: x-small;"> .<span style="color: #ffa400;">gradient</span>(<span style="color: #2b00fe;">new</span> LinearGradient(0, 0, 1, 0, <span style="color: #2b00fe;">true</span>, </span></div><div><span style="font-family: courier; font-size: x-small;"> CycleMethod.<i>NO_CYCLE</i>, </span></div><div><span style="font-family: courier; font-size: x-small;"> <span style="color: #2b00fe;">new</span> Stop(0, Color.<i>BLUE</i>), </span></div><div><span style="font-family: courier; font-size: x-small;"> <span style="color: #2b00fe;">new</span> Stop(1, Color.<i>RED</i>))</span></div><div><span style="font-family: courier; font-size: x-small;"> .build();</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Sorting the categories via the build only works if you also provide the items, otherwise you have to call the sorting methods like sortXCategoryItemsByIndexAscending() after you have added the bubbleGridChartItems. For me the charts now works fine but you might have other requirements, so please do not hesitate to file issues/request over at <a href="https://github.com/HanSolo/charts" target="_blank">github</a>.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">The BubbleGridChart can be find in the current jdk16 branch which is also available on <a href="https://search.maven.org/artifact/eu.hansolo.fx/charts" target="_blank">maven central</a>. The jdk16 branch is more a temporary branch that I use to test stuff before JDK17 will come out in September. Because this will be the next long term stable version I will create a jdk17 branch in the future which will become the new main branch then.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">That's it for today, so enjoy the upcoming weekend and...keep coding... :)</span></div><div><span style="font-family: verdana;"><br /></span></div>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-30892016718703549812021-06-16T13:03:00.005+02:002021-06-16T13:03:35.945+02:00Fun Selector<p> <span style="font-family: verdana;">Aloha,</span></p><p><span style="font-family: verdana;">Time flies, it's already mid of June so it's time for another little blogpost about a fun control I've created last week.</span></p><p><span style="font-family: verdana;">It's a selector between two states and the fun is the animation when switching between the two states.</span></p><p><span style="font-family: verdana;">Here is a little demonstration of the control...</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://github.com/HanSolo/funselector/raw/main/funselector.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="416" data-original-width="664" src="https://github.com/HanSolo/funselector/raw/main/funselector.gif" /></a></div><br /><span style="font-family: verdana;">So the light green ball defines the selected state. It's not really something special but I like the idea of using animations in a fun way. Always keep in mind that such effects are nice in tools that you use once or twice but you don't want to use these things in a business app every day :)</span><p></p><p><span style="font-family: verdana;">As always the code is available over at <a href="https://github.com/HanSolo/funselector" target="_blank">github</a>.</span></p><p><span style="font-family: verdana;">And that's it for today...so keep coding...</span></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-11606709051646701272021-04-09T12:41:00.006+02:002021-04-09T12:41:28.374+02:00Friday Fun LXIII - JDKMon<p> <span style="font-family: verdana;">Aloha,</span></p><p><span style="font-family: verdana;">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.</span></p><p><span style="font-family: verdana;">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.</span></p><p><span style="font-family: verdana;">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. </span></p><p><span style="font-family: verdana;">On MacOS this folder usually can be found at </span><span style="font-family: courier;">/Library/Java/JavaVirtualMachines</span><span style="font-family: verdana;"> where on Windows it can be </span><span style="font-family: courier;">C:\Program Files\Java</span><span style="font-family: verdana;"> and on Linux it might be </span><span style="font-family: courier;">/usr/lib/jvm</span><span style="font-family: verdana;">. But like mentioned you can choose the folder JDKMon should look for JDK's.</span></p><p><span style="font-family: verdana;">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 <a href="https://github.com/dustinkredmond/FXTrayIcon" target="_blank">FXTrayIcon</a> 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.</span></p><p><span style="font-family: verdana;">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:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuqXlN61nSp7KqsrRPHo2JNE3v34iirLP0eC6q5FdEvLRAn-TW_tSpZPR5vq0SLPyY_VcgUx-kB9x-L2BtRxRGK7DhuXXoR2NVk_QjW9N3V8FZd5-JONnX_dvkkAhmPWwg0wF4aChAPgk/s774/screenshot.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="652" data-original-width="774" height="539" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuqXlN61nSp7KqsrRPHo2JNE3v34iirLP0eC6q5FdEvLRAn-TW_tSpZPR5vq0SLPyY_VcgUx-kB9x-L2BtRxRGK7DhuXXoR2NVk_QjW9N3V8FZd5-JONnX_dvkkAhmPWwg0wF4aChAPgk/w640-h539/screenshot.png" width="640" /></a></div><br /><span style="font-family: verdana;">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.</span><p></p><p><span style="font-family: verdana;">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:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2QAv520O31rHIzWmZ60Igk8NVJ_bCtEswMnLWR1NhjHv98juq2vFfV3q5NfvHi5VHLACBg-AVf21EEIAA6tjmni5WTei9xfXziQyzTBrKale03d5mswkyWWeLRUYl8jp2ZE4RQTw4ReA/s238/JDKMonWin.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="232" data-original-width="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2QAv520O31rHIzWmZ60Igk8NVJ_bCtEswMnLWR1NhjHv98juq2vFfV3q5NfvHi5VHLACBg-AVf21EEIAA6tjmni5WTei9xfXziQyzTBrKale03d5mswkyWWeLRUYl8jp2ZE4RQTw4ReA/s0/JDKMonWin.PNG" /></a></div><span style="font-family: verdana;"><p><span style="font-family: verdana;"><br /></span></p>The supported distributions at the moment are:</span><p></p><p><span style="font-family: verdana;">- Adoptium (but there are no packages yet)</span></p><p><span style="font-family: verdana;">- AdoptOpenJDK HotSpot</span></p><p><span style="font-family: verdana;">- AdoptOpenJDK OpenJ9</span></p><p><span style="font-family: verdana;">- Corretto</span></p><p><span style="font-family: verdana;">- Dragonwell</span></p><p><span style="font-family: verdana;">- GraalVM CE</span></p><p><span style="font-family: verdana;">- Liberica</span></p><p><span style="font-family: verdana;">- Liberica Native</span></p><p><span style="font-family: verdana;">- Mandrel</span></p><p><span style="font-family: verdana;">- Microsoft Build of OpenJDK*</span></p><p><span style="font-family: verdana;">- OJDK Build</span></p><p><span style="font-family: verdana;">- Oracle JDK (no direct download)</span></p><p><span style="font-family: verdana;">- Oracle OpenJDK</span></p><p><span style="font-family: verdana;">- RedHat (no direct download)</span></p><p><span style="font-family: verdana;">- SAP Machine</span></p><p><span style="font-family: verdana;">- Trava</span></p><p><span style="font-family: verdana;">- Zulu</span></p><p><span style="font-family: verdana; font-size: x-small;">* 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.</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;">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.</span></p><p><span style="font-family: verdana;">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.</span></p><p><span style="font-family: verdana;">As always the source code is available over at <a href="https://github.com/HanSolo/JDKMon" target="_blank">github</a> where you can also find the installers/jars in the <a href="https://github.com/HanSolo/JDKMon/releases">releases</a> section.</span></p><p><span style="font-family: verdana;">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 <a href="https://github.com/HanSolo/JDKMon/actions" target="_blank">actions</a> section.</span></p><p><span style="font-family: verdana;">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.</span></p><p><span style="font-family: verdana;">That's it for today, so enjoy the upcoming weekend and...keep coding... </span></p><p><span style="font-family: verdana;"><br /></span></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com1tag:blogger.com,1999:blog-6885067460687713893.post-20859083998068251652021-02-06T09:06:00.004+01:002021-02-06T09:13:36.769+01:00"Poor man's" dark mode detection <p> <span style="font-family: verdana;">Aloha,</span></p><p><span style="font-family: verdana;">I recently read a lot about how to detect the dark mode on MacOS or Windows from Java which is really interesting when you develop desktop applications in Java (yes they still exists).</span></p><p><span style="font-family: verdana;">And there are different ways in figuring this out, one of them is to use <a href="https://github.com/Dansoftowner/jSystemThemeDetector" target="_blank">jSystemThemeDetector</a> which not only detects the theme but also gives you the ability to listen to changes of the theme. This little library makes use of different other libraries like OSHI, JFA, Jetbrains Annotations and JNA. </span></p><p><span style="font-family: verdana;">Because I also wrote a little tool that helps me to figure out the system color theme I thought I might share this with you.</span></p><p><span style="font-family: verdana;">So my approach is a bit different in the way that I simply make use of the already existing operating systems tools to get the information I need. If you don't need to listen to theme switches (which is usually the case because users do not change their theme all the time) but only need to know if the operating system is currently using the dark theme or the light theme then you might want to use my approach.</span></p><p><span style="font-family: verdana;">I simply call operating system routines on the command line using Java's Runtime.getRuntime().exec() method and parse the result.</span></p><p><span style="font-family: verdana;">Because I'm on MacOS I've also added the ability to get the current accent color that is used in MacOS which is useful if your application should be as close to the native MacOS apps as possible.</span></p><p><span style="font-family: verdana;">Because the JavaFX stage does not recognize the current MacOS theme means you have to draw the window frame on your own dependent on the current theme but that's fine. I might add another blogpost about the native looking MacOS windows frame I've created.</span></p><p><span style="font-family: verdana;">The thing that I like most about my little tool is that it is only one class that offers some utility methods and everything is plain Java without any dependencies. </span></p><p><span style="font-family: verdana;">It works on MacOS and Windows 10 and for those of you that are interested in that tool, I've created a little <a href="https://gist.github.com/HanSolo/7cf10b86efff8ca2845bf5ec2dd0fe1d" target="_blank">gist</a>.</span></p><p><span style="font-family: verdana;">At the moment this utility class is made for JavaFX but it should be easy to change the Color definitions from JavaFX to Java Swing if you need them :)</span></p><p><span style="font-family: verdana;">That's it for today, enjoy your weekend and keep coding... :)</span></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-56445132418993716592021-01-06T08:21:00.003+01:002021-01-06T08:21:29.261+01:00Neumorphism...just for the fun of it...<p> <span style="font-family: verdana;">Aloha,</span></p><p><span style="font-family: verdana;">First of all happy new year to all of you. I took this week off which gave me some time to play around with new stuff and as you can already guess it's about neumorphism.</span></p><p><span style="font-family: verdana;">Well you might ask yourself what the heck is neumorphism and how is it related to ui stuff. If you remember the early days of the the iPhone, there was a ui style that was called skeumorphism which means the ui design looked really realistic. Sometimes a bit too realistic but to be honest I'm still a fan of skeumorphism, especially if you take a look at older people that are not that used to modern technology. They really have a hard time to understand all the new technology. But if you show them a ui that looks like things they know from the real world, they recognize them and know how to use them.</span></p><p><span style="font-family: verdana;">At some point the whole skeumorphism was a bit too much and Apple decided to radically change the complete ui design to the so called flat ui. And to be honest the first version was simply terrible. Even for experienced users the ui was not self explaining any more but you have to learn that colored text might be a button but doesn't have to be. I really like the clean style of a flat ui but it comes with a drawback when you look at the usability. The self explanation of a ui simply got lost. </span></p><p><span style="font-family: verdana;">Over the years Apple learned that the pure flat ui was a bit too flat and decided to make the whole thing a bit more usable again which leads us to the current ui design (which still is hard to learn for unexperienced people).</span></p><p><span style="font-family: verdana;">At some point neumorphism became a thing which is a bit like a mixture from skeumorphism and flat ui design. So there is no real definition of skeumorphism and for that reason you will find ui's that look not all the same as in the flat ui era but have different looks (which is good).</span></p><p><span style="font-family: verdana;">If you are interested in some examples you might want to take a look at <a href="https://dribbble.com/tags/neumorphism" target="_blank">dribbble</a>.</span></p><p><span style="font-family: verdana;">Neumorphism makes use of shadows to define ui component borders which gives a button in principle two states an embossed or rised and a sunken state. In combination with rounded corners the whole ui becomes a more soft touch which can work for specific apps.</span></p><p><span style="font-family: verdana;">The problem with neumorphism is that it does not work with all color combinations. This is because of the shadows it uses, they won't work on white or black backgrounds. On some colors the shadows work great on others not so neumorphism is not the general solution for ui design but just another ui design style that can be useful and refreshing for some applications.</span></p><p><span style="font-family: verdana;">So I've spend every day around 1.5 hours in the early morning to play around with JavaFX and neumorphism to get figure out how to make it work. I would love to use css for this because in principle this would mean you could simply load another css file and you are done...BUT...unfortunately in the JavaFX css you cannot chain effects as you can do in code.</span></p><p><span style="font-family: verdana;">Meaning to say in JavaFX code you can chain effects like a DropShadow that can have an input of another DropShadow. With this you can simply create an effect of a sunken button etc.</span></p><p><span style="font-family: verdana;">But this is not possible in JavaFX css where you can only have one effect on one node which is not enough :(</span></p><p><span style="font-family: verdana;">So for that reason I've decided to create my own controls (at least a few to play around with). And again I used the JavaFX Canvas node for that. I found myself more and more using the Canvas node for all kinds of things simply because it saves nodes on the scene graph, it's fast and if you need it you can simply port it to HTML.</span></p><p><span style="font-family: verdana;">And this is what I came up with...</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgOWVTnDcNVelnbY_KV6LDMNb-B6YxK9cU3bSBiToa3Eh50GtVLJQlL0T2dUHe1ahuMx5oS8T34_vNtEjxQuAM8Fz-rAW-XDwtVR3ZgZMNUXRddCsPgVUltHL92ySPjdmHVJcRBv1QdjY/s1452/neumorphic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="704" data-original-width="1452" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgOWVTnDcNVelnbY_KV6LDMNb-B6YxK9cU3bSBiToa3Eh50GtVLJQlL0T2dUHe1ahuMx5oS8T34_vNtEjxQuAM8Fz-rAW-XDwtVR3ZgZMNUXRddCsPgVUltHL92ySPjdmHVJcRBv1QdjY/w640-h310/neumorphic.png" width="640" /></a></div><br /><span style="font-family: verdana;">I've created a Button, ToggleButton, TextField, RadioButton, CheckBox, ChoiceBox, Switch and a Container.</span><p></p><p><span style="font-family: verdana;">Just keep in mind that these controls are not meant to be used in production because they miss some features but they might work for some demos (at least I will use them for demos) :)</span></p><p><span style="font-family: verdana;">The Container component which you can see in the lower row of components can be used to give other controls the ability to make use of the shadow effects. If you would like to use a circular Medusa gauge in a neumorphic ui you could add it to a circular Container.</span></p><p><span style="font-family: verdana;">To see how it can be used you might want to take a look at the Demo class.</span></p><p><span style="font-family: verdana;">The code is as always available over at <a href="https://github.com/HanSolo/neumorphic" target="_blank">github</a>.</span></p><p><span style="font-family: verdana;">So that's it for today and don't forget...keep coding...</span></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-88045944999796811052020-12-27T07:51:00.000+01:002020-12-27T07:51:15.092+01:00JavaFX monitor component<p> <span style="font-family: verdana;">Aloha,</span></p><p><span style="font-family: verdana;">There was always something I would like to do but never found the time to do it...a control that looks more or less like a heart rate monitor. Well it's not really about monitoring the heart rate but I like the heart rate signal, so it's more about the older tube based oscilloscopes. And the thing that makes me want to create such a control is the fade out of the beam when it moves across the screen. In the tube based devices this was coming from a metallized layer that was applied to the front of the glass tube. When electrons hit this layer, it absorbed a part of the kinetic energy of the electron and released it in the form of light (so called fluorescence). The layer on the glass is metallized so that the electric charge of the electrons can flow off.</span></p><p><span style="font-family: verdana;">Now when the electron beam was moving across the screen the electrons left behind a fluorescent line which formed the signal. Ok so that is the thing that I would like to create...not an oscilloscope but this fading effect when visualizing a signal.</span></p><p><span style="font-family: verdana;">So what we need is a line that fades out over time...sounds easy (and in the end the solution is easy) but getting the right idea on how to implement it took me some walks with the dog :)</span></p><p><span style="font-family: verdana;">The first idea is you simple draw a line and apply a horizontal linear gradient to the stroke that fades out to transparency. As long as the signal has a predominantly horizontal orientation this works really fine. But as soon as the signal also has significant vertical values we will run into a problem. Let me try to visualize this in a little graphic:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgewnUcJLOkIzixL2NLx_Y5plVZV4-UUo0UXFE3R0ThlfpqwMqfrVFyA_PQtS-E3D39f8K52nfvCzbNVhXp297R6574V7gCDV5YuoRhdQODXOyfyrKK07uAvmSR929DYk2f_pYdmbhbco/s327/example.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="261" data-original-width="327" height="319" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgewnUcJLOkIzixL2NLx_Y5plVZV4-UUo0UXFE3R0ThlfpqwMqfrVFyA_PQtS-E3D39f8K52nfvCzbNVhXp297R6574V7gCDV5YuoRhdQODXOyfyrKK07uAvmSR929DYk2f_pYdmbhbco/w400-h319/example.jpg" width="400" /></a></div><br /><span style="font-family: verdana;">On the picture above you see two signals that have the same horizontal width but a different signal length. On top I've added two rectangles that shows the horizontal linear gradient that is used to fade out the signal. The effect of fading out should be related to the length of the line and not to the width of the signal. </span><p></p><p><span style="font-family: verdana;">To achieve this behavior I need to fade out the signal along the line and not only in horizontal direction. So the fade out should follow the line. My idea was to use a queue with a fixed size, when filled up to the given size it should remove the head element when a new element was added to the end. With such a queue in place I could simply draw a line between each element of the queue and fade out these line segments when I'm iterating over them during the drawing process.</span></p><p><span style="font-family: verdana;">I simply created a FixedSizeQueue that is based on an ArrayBlockingQueue and added the "auto-remove" functionality in the add() method. So the queue looks like this:</span></p><pre style="background-color: white; font-family: "JetBrains Mono", monospace;"><span style="font-size: x-small;"><span style="color: #0004ff;">public class </span>FixedSizeQueue<<span style="color: #20999d;">E</span>> <span style="color: #0004ff;">extends </span>ArrayBlockingQueue<<span style="color: #20999d;">E</span>> {<br /> <span style="color: #0004ff;">private int </span>size;<br /> <br /> <span style="color: #0004ff;">public </span>FixedSizeQueue(<span style="color: #0004ff;">final int </span>size) {<br /> <span style="color: #0004ff;">super</span>(size);<br /> <span style="color: #0004ff;">this</span>.size = size;<br /> }<br /> <br /> <span style="color: olive;">@Override </span><span style="color: #0004ff;">public boolean </span>add(<span style="color: #0004ff;">final </span><span style="color: #20999d;">E </span>element) {<br /> <span style="color: #0004ff;">if </span>(<span style="color: #0004ff;">super</span>.<span style="color: #ff7500;">size</span>() == <span style="color: #0004ff;">this</span>.size) { <span style="color: #0004ff;">this</span>.<span style="color: #ff7500;">remove</span>(); }<br /> <span style="color: #0004ff;">return super</span>.<span style="color: #ff7500;">add</span>(element);<br /> }<br /><br /> <span style="color: #0004ff;">public </span><span style="color: #20999d;">E </span>getElementAt(<span style="color: #0004ff;">final int </span>index) {<br /> <span style="color: #0004ff;">if </span>(index < 0 || index > <span style="color: #ff7500;">size</span>() - 1) { <span style="color: #0004ff;">throw new </span>IllegalArgumentException(<span style="color: magenta;">"Index out of bounds."</span>); }<br /> <span style="color: #0004ff;">return </span>(<span style="color: #20999d;">E</span>) <span style="color: #ff7500;">toArray</span>()[index];<br /> }<br />}</span></pre><p><span style="font-family: verdana;">With this queue in place I can now use it as some kind of a signal buffer. I was playing around with different factors to fade out the signal but ended up with a simply linear approach. So I calculate the opacity factor by simply dividing 1/noOfElements. And when iterating through the points in the queue I just multiply the current index with the opacity factor as you can see in the code snippet below:</span></p><pre style="background-color: white; font-family: "JetBrains Mono", monospace;"><pre style="font-family: "JetBrains Mono", monospace;"><span style="font-size: x-small;"><span style="color: #0004ff;">final </span>Point[] points = queue.<span style="color: #ff7500;">toArray</span>(<span style="color: #0004ff;">new </span>Point[0]);<br /><span style="color: #0004ff;">double </span>opacityFactor = 1.0 / points.length;<br /><span style="color: #0004ff;">for </span>(<span style="color: #0004ff;">int </span>i = 0; i < length - 1; i += 1) {<br /> <span style="color: #0004ff;">if </span>(points[i].<span style="color: #b509ff;">x </span>< points[i + 1].<span style="color: #b509ff;">x</span>) {<br /> lineCtx.<span style="color: #ff7500;">setStroke</span>(Color.color(red, green, blue, i * opacityFactor));<br /> lineCtx.<span style="color: #ff7500;">strokeLine</span>(points[i].<span style="color: #b509ff;">x</span>, points[i].<span style="color: #b509ff;">y</span>, points[i + 1].<span style="color: #b509ff;">x</span>, points[i + 1].<span style="color: #b509ff;">y</span>);<br /> }<br />}</span></pre></pre><p><span style="font-family: verdana;">You could because I draw the lines from left to right the opacity of the elements will in this case fade in the line segments by increasing their opacity from 0 - 1.</span></p><p><span style="font-family: verdana;">So the result looks good enough to me:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYKAMDZP3oFjB-QuqdLNIl5zUD2htv7j8vOaFx5R_DMVB1vE1ADlQSrdQkoaAgSnnTa23CsyCTyEFYo0dgnxqw3SGVIksRTsXWpGY83so2fJlIGNP1zQ_YPimxXsHlt9HEwKn9WlmtZDs/s1082/JavaFX+Monitor+2020-12-27+06-57-04.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1082" data-original-width="1040" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYKAMDZP3oFjB-QuqdLNIl5zUD2htv7j8vOaFx5R_DMVB1vE1ADlQSrdQkoaAgSnnTa23CsyCTyEFYo0dgnxqw3SGVIksRTsXWpGY83so2fJlIGNP1zQ_YPimxXsHlt9HEwKn9WlmtZDs/w385-h400/JavaFX+Monitor+2020-12-27+06-57-04.png" width="385" /></a></div><br /><span style="font-family: verdana;">But this is only the effect of fading out a line along it's elements...what about the rest of the control?</span><p></p><p><span style="font-family: verdana;">For the monitor control I use three Canvas nodes and an ImageView that are placed inside a Region. So the whole control uses 5 nodes on the scene graph. </span></p><p><span style="font-family: verdana;">It's good practice to think about which elements in your control needs to be drawn and when. This is sometimes a bit boring because with the given compute power of today's machines you won't really see a big difference between the optimized drawing and the let's call it brute force drawing. Optimized drawing only draws the elements that are needed where brute force drawing simply draws everything everytime. A good way of testing your controls is to run it on an older Raspberry Pi. This device is fast for it's size but slow compared to your desktop computer and on such a Raspberry Pi you will directly get a visual feedback on how efficient your code is. On an older Pi everything counts...means if you only draw the stuff that is important you will really see the effect on the Pi.</span></p><p><span style="font-family: verdana;">So as I mentioned I use three Canvas nodes</span></p><p></p><ul style="text-align: left;"><li><span style="font-family: verdana;">background (rect filled with background color and raster with text)</span></li><li><span style="font-family: verdana;">line (the line segments)</span></li><li><span style="font-family: verdana;">dot (the leading dot with the glow effect)</span></li></ul><div><span style="font-family: verdana;">You might argue I should draw the dot in the same Canvas as the line but this only works for the mode where I fade out the signal line. The monitor control also has another mode where it always leaves the signal on the screen and only removes the part in front of the current position. This mode is common in todays heart rate monitors as you know it from the local hospital. For this mode I don't want to clear the background of the line Canvas on each draw and so I need to draw the dot on a separate Canvas.</span></div><p></p><p><span style="font-family: verdana;">Because I separated the background with the raster from the foreground with the line and the dot I only need to draw it when either a parameter has changed (e.g. the background color etc.) or when the control was resized. This is what I mean with optimized drawing, I could also draw the background all the time and put it in the same Canvas as the line but this is just not efficient.</span></p><p><span style="font-family: verdana;">In addition to the Canvas nodes I also use an ImageView to overlay the whole control with what I call a crystal overlay. The main idea is to make the whole UI look more realistic. The main thing here is noise...adding noise to a surface makes it look more natural because in the real world nearly no surface is perfect. For example if you take a look at a liquid crystal display (lcd), you will see some kind of structure on the background. This structure I try to imitate with a an image that contains semi-transparent noise. If you simply put such an image on top of your control it will look more realistic.</span></p><p><span style="font-family: verdana;">Here is an example screenshot of the monitor control with both variants:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCyiuAkQ7WGPC3GLNOfHbfsNWy8O1c46uCTSRNZkE3bAEVU66oxbo56xG8LovRH32GOJQkIohyr06Oo-RNqoYvI0Plx90ML2QbhfcZxaFyHlXyD9KN13HLvFialaYDG86b6keiL4M2228/s1004/crystal.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="832" data-original-width="1004" height="331" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCyiuAkQ7WGPC3GLNOfHbfsNWy8O1c46uCTSRNZkE3bAEVU66oxbo56xG8LovRH32GOJQkIohyr06Oo-RNqoYvI0Plx90ML2QbhfcZxaFyHlXyD9KN13HLvFialaYDG86b6keiL4M2228/w400-h331/crystal.jpg" width="400" /></a></div><br /><span style="font-family: verdana;">As you can see the upper image has the crystal overlay effect switched off where the lower image has it switched on. In the monitor control you can decide if you would like to use it or not.</span><p></p><p><span style="font-family: verdana;">So in principle we simply stack all the different layers over each other and so get the final result.</span></p><p><span style="font-family: verdana;">I have also added some themes that contain common used color combinations for oscilloscopes but you can of course also set all the different colors separately.</span></p><p><span style="font-family: verdana;">There is a Demo class in the code that gives you the ability to play around with the parameters and if you would like to see it in action here is a little video:</span></p><p><span style="font-family: verdana;"><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/nfRG7_M7GTs" width="560"></iframe></span></p><p><span style="font-family: verdana;">As always the code/binary is available in the following places:</span></p><p><span style="font-family: verdana;"><a href="https://github.com/HanSolo/monitor" target="_blank">github</a></span></p><p><span style="font-family: verdana;"><a href="https://bintray.com/hansolo/monitor" target="_blank">bintray</a></span></p><p><span style="font-family: verdana;"><a href="https://search.maven.org/artifact/eu.hansolo/monitor" target="_blank">maven central</a></span></p><p><span style="font-family: verdana;">I guess that's it for today...so keep coding...</span></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-45246793159818613232020-12-04T15:39:00.001+01:002020-12-04T15:39:09.239+01:00Worldmap connections...<p><span style="font-family: verdana;">Aloha,</span></p><p><span style="font-family: verdana;">Everytime I'm sitting on an airplane I'm taking out the magazine where it shows the flight connections of the airline on a worldmap. Somehow I really like that kind of visualization.</span></p><p><span style="font-family: verdana;">For those that don't know what I'm talking about, it's something like this:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://lp-cms-production.imgix.net/news/2019/04/map3.jpg?auto=format&fit=crop&sharp=10&vib=20&ixlib=react-8.6.4&w=850&q=50&dpr=2" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="414" data-original-width="800" height="331" src="https://lp-cms-production.imgix.net/news/2019/04/map3.jpg?auto=format&fit=crop&sharp=10&vib=20&ixlib=react-8.6.4&w=850&q=50&dpr=2" width="640" /></a></div><span style="font-family: verdana;"><p><span style="font-family: verdana;"><br /></span></p>When I've created the last update of my charts library I stumbled upon my worldmap and thought by myself...why not creating such a visualization by using the worldmap I already have.</span><p></p><p><span style="font-family: verdana;">You can choose this kind of visualization for different kind of data, e.g. simple connections, weighted connections to visualize a flow, incoming/outgoing connections and more.</span></p><p><span style="font-family: verdana;">Long story short...I've added the ability to cover some use cases I came up with. The first one is just a visualization of simple connections without showing any direction.</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNSJd5k2Ig29HxBwFtBc2lN4S0H2qqYNYhuR8BaBq3ZFdolBJpCsb9Aq226TpalcZ65emh_Adf9Vlum9mDqYySnkW8XDW1OAFzazB1stifaqukGJHCoKkNfTY5Hd4kLc0T5zPiyBZlwv0/s2018/Worldmap+Connections+2020-12-04+14-49-16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1386" data-original-width="2018" height="440" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNSJd5k2Ig29HxBwFtBc2lN4S0H2qqYNYhuR8BaBq3ZFdolBJpCsb9Aq226TpalcZ65emh_Adf9Vlum9mDqYySnkW8XDW1OAFzazB1stifaqukGJHCoKkNfTY5Hd4kLc0T5zPiyBZlwv0/w640-h440/Worldmap+Connections+2020-12-04+14-49-16.png" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><span style="font-family: verdana;">This really only shows the connections between points on the map. Nice but what if we would like to use colors and also show the direction? No problem, in this case it could look as follows...</span><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1scMuRmilF_16kZ4MZ8clS3-0Umfp1oAQCKbwsDYb5Yt0nHF15z3W__RksP7gMfoEcEbIa-c3_yhTvHhDJkgdFNi0ZIZIejZRPgjlTPtE-IISp9HGVib7hwU2czBRP3g37b4yqiXqXkU/s2018/Worldmap+Connections+2020-12-04+15-08-09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1386" data-original-width="2018" height="440" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1scMuRmilF_16kZ4MZ8clS3-0Umfp1oAQCKbwsDYb5Yt0nHF15z3W__RksP7gMfoEcEbIa-c3_yhTvHhDJkgdFNi0ZIZIejZRPgjlTPtE-IISp9HGVib7hwU2czBRP3g37b4yqiXqXkU/w640-h440/Worldmap+Connections+2020-12-04+15-08-09.png" width="640" /></a></div><span style="font-family: verdana;">Not bad but what if we would like to indicate the points with the highest volume of outgoing connections? No problem, here you go...</span><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1taJrKqSLVSeN8vJcm6FoaKdq9yZkwQqHeNDCSdr0IP50L2dZWrfGQ6o-bMl2rgJwCWsxpiaD04MxrX6h4_e95QY9fOAV5QrmHfTm2x34MLDBw3yyalutxRGeqXVZyrbxW_6cTq5ksKs/s2018/Worldmap+Connections+2020-12-04+15-25-44.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1386" data-original-width="2018" height="440" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1taJrKqSLVSeN8vJcm6FoaKdq9yZkwQqHeNDCSdr0IP50L2dZWrfGQ6o-bMl2rgJwCWsxpiaD04MxrX6h4_e95QY9fOAV5QrmHfTm2x34MLDBw3yyalutxRGeqXVZyrbxW_6cTq5ksKs/w640-h440/Worldmap+Connections+2020-12-04+15-25-44.png" width="640" /></a></div><br /><span style="font-family: verdana;">Hmm...so far so good but what if we would like to visualize a data flow from one point to another? Therefor the connection itself need to have a value which defines the thickness of the connection. Well...here it is...</span><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVbq0sO_WR6NB6ZQj1C4vjpcZY3dKm_X-fHBB1q8JH-ECKawIsDwUlBHG2vgXx3orLBb50pDetg7w9Kmq4oijwAu_ByQGWt15bpcXXjYlHNstaIUHIHO2W8R6LY6gfjc9a3nEfy-QQ_kM/s2018/Worldmap+Connections+2020-12-04+15-28-20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1386" data-original-width="2018" height="440" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVbq0sO_WR6NB6ZQj1C4vjpcZY3dKm_X-fHBB1q8JH-ECKawIsDwUlBHG2vgXx3orLBb50pDetg7w9Kmq4oijwAu_ByQGWt15bpcXXjYlHNstaIUHIHO2W8R6LY6gfjc9a3nEfy-QQ_kM/w640-h440/Worldmap+Connections+2020-12-04+15-28-20.png" width="640" /></a></div><span style="font-family: verdana;">Ok that's nice...so what about gradients...would it not be nice to have the connections change their color on their way from their startpoint to their endpoint? Yes it would be...so...</span><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghs0o1OmSvaJTKO5LxGdAA-P5g4BS5zaVe_7ORzOJzOp2KmVDNdUWf6t0LtP47btlftLKuxW5FH6zJVjKNKa5ZtjiDhv29Ksfz6tDghHBujY8CuGBYtFV2TzPNlXAwfmD9Uz0QHm9p5nI/s2018/Worldmap+Connections+2020-12-04+15-31-39.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1386" data-original-width="2018" height="440" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghs0o1OmSvaJTKO5LxGdAA-P5g4BS5zaVe_7ORzOJzOp2KmVDNdUWf6t0LtP47btlftLKuxW5FH6zJVjKNKa5ZtjiDhv29Ksfz6tDghHBujY8CuGBYtFV2TzPNlXAwfmD9Uz0QHm9p5nI/w640-h440/Worldmap+Connections+2020-12-04+15-31-39.png" width="640" /></a></div><br /><span style="font-family: verdana;">Allright...with this I covered all things I've implemented so far and as always I have no idea if someone has a use for that because I simply created it for the fun of it.</span><p></p><p><span style="font-family: verdana;">But if you can use it and you need more features...just let me know and I will try to make it happen.</span></p><p><span style="font-family: verdana;">If you are interested in how the above charts have been done you can find the code for it in the WorldmapConnectionsTest.java file in the test package.</span></p><p><span style="font-family: verdana;">In principle you can add MapPoint objects with a name, a color and latitude/longitude coordinates to show the points. The connections are of type MapConnection which takes 2 MapPoints, a value, a color or gradient and a tooltip text.</span></p><p><span style="font-family: verdana;">Attention: The connections are not clickable yet...so there is no tooltip and no user interaction implemented right now but it might follow.</span></p><p><span style="font-family: verdana;">The code can be found over at github at: <a href="https://github.com/HanSolo/charts" target="_blank">https://github.com/HanSolo/charts</a></span></p><p><span style="font-family: verdana;">You will find the changes in the dev branch.</span></p><p><span style="font-family: verdana;">That's it for today, so enjoy your upcoming weekend...and keep coding...</span></p><p><span style="font-family: verdana;"><br /></span></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com1tag:blogger.com,1999:blog-6885067460687713893.post-40743445919256576412020-11-21T08:06:00.001+01:002020-11-21T08:06:28.449+01:00Arc charts...<p><span style="font-family: verdana;">Aloha,</span></p><p><span style="font-family: verdana;">I really need to post more often, it's already November...</span></p><p><span style="font-family: verdana;">When I was looking for some interesting charts that I might add to my JavaFX charts library I stumbled upon an arc diagram. It's a chart that is very specific and cannot be used to visualize all kinds of data but it's great to visualize interactions or flows between items. More info about this diagram can be found at <a href="https://en.wikipedia.org/wiki/Arc_diagram">wikipedia</a>.</span></p><p><span style="font-family: verdana;">As always this chart was just created for the fun of it and not because I really need it, so I always try to make it as useful as possible but I don't know the special needs in the industry. So whenever you find something that can improve the handling of these charts, please let me know and I will try to make it better.</span></p><p><span style="font-family: verdana;">The hardest part is always to find some data to play with and this time I've found an <a href="https://github.com/evelinag/StarWars-social-network/tree/master/networks" target="_blank">interesting dataset</a> about the interactions between the characters in the movie Star Wars Episode I.</span></p><p><span style="font-family: verdana;">So I gave it a try and here are some results by using my new arcchart:</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQUocpn-Zq4BcUdam_4GJ6_uFIyYAjZFO9j5g5nkj5D9Cb7FN-Qbi0tQIbZxctLe_DpQCqBPnA4hFxTJkjNCl47sgxXStJ5slasH8Ak63bEk14pX4_CqVFlDpQYzyfadOmSDNrCZz8Zqc/s1374/Arc+Chart+2020-11-21+07-23-53.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1374" data-original-width="1240" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQUocpn-Zq4BcUdam_4GJ6_uFIyYAjZFO9j5g5nkj5D9Cb7FN-Qbi0tQIbZxctLe_DpQCqBPnA4hFxTJkjNCl47sgxXStJ5slasH8Ak63bEk14pX4_CqVFlDpQYzyfadOmSDNrCZz8Zqc/w578-h640/Arc+Chart+2020-11-21+07-23-53.png" width="578" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgx7nkTh7Is8X9id17c9Yo8t7QMM2t2N71-kvw5_N-N4T-Gt-_yr4oKJOU69zLTuURW3V_2DK-Cfm0liiCbPZ5PH_FkAkOff_jiZ_NPU7nUvahjVHEaGnfnWN4DaT7rZhY-PZD24FNe8Go/s1374/Arc+Chart+2020-11-21+07-23-17.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1374" data-original-width="1240" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgx7nkTh7Is8X9id17c9Yo8t7QMM2t2N71-kvw5_N-N4T-Gt-_yr4oKJOU69zLTuURW3V_2DK-Cfm0liiCbPZ5PH_FkAkOff_jiZ_NPU7nUvahjVHEaGnfnWN4DaT7rZhY-PZD24FNe8Go/w578-h640/Arc+Chart+2020-11-21+07-23-17.png" width="578" /></a></div><br /><span style="font-family: verdana;"><br /></span><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFf56QwrFXLbtVk5TONCMAlD0zN7GOGdLpPBlCfhHqt_KyAYFzfAj_igyYwnvpWlmFPIyaRlDGRXStxZDrhebsRPIjD2iJ8jU-MSUE78cz-6Hbfs2EZYrCNDKo9e9kONkpdZsrlRGI_sU/s1374/Arc+Chart+2020-11-21+07-25-04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1374" data-original-width="1240" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFf56QwrFXLbtVk5TONCMAlD0zN7GOGdLpPBlCfhHqt_KyAYFzfAj_igyYwnvpWlmFPIyaRlDGRXStxZDrhebsRPIjD2iJ8jU-MSUE78cz-6Hbfs2EZYrCNDKo9e9kONkpdZsrlRGI_sU/w578-h640/Arc+Chart+2020-11-21+07-25-04.png" width="578" /></a></div><br /><span style="font-family: verdana;">As you can see there are different options one can enable/disable in this chart.</span><p></p><p><span style="font-family: verdana;">Each connection between two items can have a value which can be used to weight the connection (wider strokes mean bigger values => stronger connection). One could also weight the dots of each item by the sum of their connection values. If you click on an item it will highlight all it's connections.</span></p><p><span style="font-family: verdana;">In the last image you see that you can also make use of the full circle for connections. In this case you read the chart clockwise, meaning to say outgoing connections to the right will be in the upper part and outgoing connections to the left will be in the lower part.</span></p><p><span style="font-family: verdana;">The items are sorted by the sum of their connection values from left to right. As you can see in the image above, Anakin has the most interactions in Episode I, followed by Jar Jar and Qui Gon.</span></p><p><span style="font-family: verdana;">The most interesting part is that creating the whole new chart took not longer than 3-4h last night which again shows how productive you can be by using JavaFX...love it :)</span></p><p><span style="font-family: verdana;">In addition one could also create a cluster to group items. For this I've simply created another chart without any meaning just to show the clustering. So in this case the clusters are europe and asia. The items in each cluster are sorted by the sum of their connection values from left to right.</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2Ct-FhXxLHIC0-GgTMAdM4IZepXqPX_0CHiW-Vr_HfwuEgPfro4XhZlFsOUmgFUNaYJXHhqTs7fX20-4iWmeVRCkv_8QP_RkgFKcOB3JmBUgFA8K3-gaLuvP2FL7vi4zs44p2ehSXUKg/s1394/Arc+Chart+2020-11-21+07-54-43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1296" data-original-width="1394" height="596" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2Ct-FhXxLHIC0-GgTMAdM4IZepXqPX_0CHiW-Vr_HfwuEgPfro4XhZlFsOUmgFUNaYJXHhqTs7fX20-4iWmeVRCkv_8QP_RkgFKcOB3JmBUgFA8K3-gaLuvP2FL7vi4zs44p2ehSXUKg/w640-h596/Arc+Chart+2020-11-21+07-54-43.png" width="640" /></a></div><br /><span style="font-family: verdana;">If you have clustered items all connections from the cluster will get the color of the cluster. A cluster will be created as follows:</span><p></p><p><span style="font-family: courier; font-size: x-small;">Cluster europe = new Cluster("Europe", Color.BLUE, germany, france, italy, spain);</span></p><p><span style="font-family: verdana;">Where germany, france, italy and spain are PlotItems that can be created as follows:</span></p><p><span style="font-family: courier; font-size: x-small;">PlotItem germany = new PlotItem("GERMANY", 1_250_000, Color.RED);</span></p><p><span style="font-family: verdana;">The connections between the items can be created as follows:</span></p><p><span style="font-family: courier; font-size: x-small;">germany.addToOutgoing(india, 150_000);</span></p><p><span style="font-family: verdana;">If you would like to highlight a specific connection you can get it as follows:</span></p><p><span style="font-family: courier; font-size: x-small;">arcChart.getConnection(germany, india).setFill(Color.GREEN);</span></p><p><span style="font-family: verdana;">Oh and the new ArcChart is part of the last release of my charts library which you can find at:</span></p><p></p><ul style="text-align: left;"><li><span style="font-family: verdana;"><a href="https://github.com/HanSolo/charts" target="_blank">github</a></span></li><li><span style="font-family: verdana;"><a href="https://bintray.com/hansolo/charts/charts" target="_blank">bintray</a></span></li><li><span style="font-family: verdana;"><a href="https://search.maven.org/search?q=g:eu.hansolo.fx.charts" target="_blank">maven central</a></span></li></ul><p></p><p><span style="font-family: verdana;">So that's it for today...enjoy the upcoming weekend and...keep coding...</span></p><p><br /></p>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-59738041980796668762020-06-11T17:44:00.001+02:002020-06-11T17:44:43.182+02:00TouchSlider control<span style="font-family: verdana, sans-serif;">Aloha,</span><br />
<span style="font-family: verdana, sans-serif;"><br /></span>
<span style="font-family: verdana, sans-serif;">Yesterday evening and this morning I've spent some time on creating a new control...a touch slider. To be honest I can't remember why I had the idea to create one but who cares...now it's ready :)</span><br />
<span style="font-family: verdana, sans-serif;">So here is a screenshot of it...</span><br />
<span style="font-family: verdana, sans-serif;"><br /></span>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxL5bEiJU_NCYfj4Itbdn0XNZjlzZZ6-r3AlbOzPDHZOqdWzjFBsP5BklPoxgdNHvE3XvymvMAOQ3h9emYNroJ80Mr4nZK7a-m25ag7Sbe-HkiV8vPZAm2T6UdqgdNW1VKCB94ld9Ig3A/s1324/TouchSlider.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1324" data-original-width="1280" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxL5bEiJU_NCYfj4Itbdn0XNZjlzZZ6-r3AlbOzPDHZOqdWzjFBsP5BklPoxgdNHvE3XvymvMAOQ3h9emYNroJ80Mr4nZK7a-m25ag7Sbe-HkiV8vPZAm2T6UdqgdNW1VKCB94ld9Ig3A/s320/TouchSlider.png" /></a></div><span style="font-family: verdana, sans-serif;"><div><span style="font-family: verdana, sans-serif;"><br /></span></div>So it's not really fancy but it works and is customizable in many ways. So first of all one can set the orientation to either horizontal or vertical. </span><div><span style="font-family: verdana, sans-serif;">You can set a name and you can also define if that name should be visible or not. In addition you can define if the current value should be visible or not.</span></div><div><span style="font-family: verdana, sans-serif;">The color of the bar background, the bar itself, the thumb, the value text, the name text and the zero indicator are also adjustable.</span></div><div><span style="font-family: verdana, sans-serif;">This control is based on the JavaFX Canvas node which means it does not use a lot of nodes. But even if it is based on the Canvas node it is still styleable by CSS. If you take a look at the Demo class in the github repo you will figure out that the black and blue sliders are styled using code but the red slider on the right side ist styled using CSS.</span></div><div><span style="font-family: verdana, sans-serif;">This works because JavaFX offers so called StyleableProperties which bridge the gap between code and CSS.</span></div><div><span style="font-family: verdana, sans-serif;">The plan is to use this slider in a specific Tile for TilesFX for control e.g. IoT projects.</span></div><div><span style="font-family: verdana, sans-serif;">Here is a little video of the slider in action...</span></div><div><span style="font-family: verdana, sans-serif;"><br /></span></div><div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dyxTD3JOwf-KiualnQKDgFjHcNDvJmJc5shWCUJ1hXUZhuLoDfFff31YeGufn5FNlLn01bAUYEkfE-xwhoDJw' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div><span style="font-family: verdana, sans-serif;"><br /></span></div><div><span style="font-family: verdana, sans-serif;">In addition to the above mentioned features you can also define if the bar should start at the zero position, this might come in handy if you have to control values that go from a negative to a positive value.</span></div><div><font face="verdana, sans-serif">In this control I've used a little bit different approach to set the min and max values. Here you can define the min value and the range. The value property of the slider will always be in the range from 0 - 1 but if you call the getSliderValue you will get the "real" value based on the min value and the range back.</font></div><div><font face="verdana, sans-serif">The slider will fire TouchSliderEvents when you drag it so that you can attach an observer to it and react on the events.</font></div><div><font face="verdana, sans-serif">Well that's in principle it and as always you can find the code at <a href="https://github.com/HanSolo/TouchSlider" target="_blank">github</a>...</font></div><div><span style="font-family: verdana, sans-serif;"><br /></span></div><div><span style="font-family: verdana, sans-serif;">That's it for today...so keep coding...<br /></span><span style="font-family: verdana, sans-serif;"><br /></span></div>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com0tag:blogger.com,1999:blog-6885067460687713893.post-24030027975370764572020-06-05T09:00:00.001+02:002020-06-05T09:00:11.439+02:00And another one...TilesFX 11.38<span style="font-family: verdana, sans-serif;">Aloha,</span><br />
<span style="font-family: verdana, sans-serif;"><br /></span><span style="font-family: verdana, sans-serif;">Here we go again...the last couple of evenings/nights I've spend creating new skins for TilesFX and because in the meantime I have 4 new skins it's time for another release of TilesFX :)</span><div><font face="verdana, sans-serif"><br /></font></div><div><span style="font-family: verdana, sans-serif;">So let's take a look at the new skins that I have added.</span></div><div><span style="font-family: verdana, sans-serif;"><br /></span></div><div><span style="font-family: verdana, sans-serif;"><b>ColorTileSkin</b></span></div><div><font face="verdana, sans-serif">This skin always visualizes the current value in percentage and the background color will change according to the value.</font></div><div><font face="verdana, sans-serif">The different colors that will be used are predefined by using sections.</font></div><div><font face="verdana, sans-serif">The default sections that will be used if nothing else is defined are:</font></div><div><pre style="background-color: white; font-family: "jetbrains mono", monospace;"><span style="color: #0004ff;">new </span>Section(0.00, 0.25, ColorSkin.<span style="font-style: italic;">GREEN</span>),<br /><span style="color: #0004ff;">new </span>Section(0.25, 0.50, ColorSkin.<span style="font-style: italic;">YELLOW</span>),<br /><span style="color: #0004ff;">new </span>Section(0.50, 0.75, ColorSkin.<span style="font-style: italic;">ORANGE</span>),<br /><span style="color: #0004ff;">new </span>Section(0.75, 1.00, ColorSkin.<span style="font-style: italic;">RED</span>)</pre><div><span style="font-family: verdana, sans-serif;">With this sections in place the color will for example be orange if the value is between 50% and 75% of the range that is defined by minValue and maxValue of the tile.</span></div><div><span style="font-family: verdana, sans-serif;">Of course you can override the sections with your own sections but keep in mind that the values of the sections should be in the range of 0.0 to 1.0 as shown in the example above.</span></div><div><span style="font-family: verdana, sans-serif;">That's fine but sometimes you would prefer having more finegrained colors for the values. One solution would be to define lot's of sections but you could also make use of the gradientStops feature in TilesFX.</span></div><div><span style="font-family: verdana, sans-serif;">For this you simply define the color stops you would like to use for the interpolation as follows in the TileBuilder:</span></div><div><span style="font-family: verdana, sans-serif;"><br /></span></div><div><span style="font-family: verdana, sans-serif;"><font face="verdana, sans-serif"><div><pre style="background-color: white; font-family: "jetbrains mono", monospace;"><pre style="font-family: "jetbrains mono", monospace;">tile1 = TileBuilder.create().<span style="color: #ff7500;">skinType</span>(SkinType.<span style="font-style: italic;">COLOR</span>)<br /> .<span style="color: #ff7500;">prefSize</span>(<span style="font-style: italic;">WIDTH</span>, <span style="font-style: italic;">HEIGHT</span>)<br /> .<span style="color: #ff7500;">title</span>(<span style="color: magenta;">"Color (Sections)"</span>)<br /> .<span style="color: #ff7500;">description</span>(<span style="color: magenta;">"CPU temp"</span>)<br /> .<span style="color: #ff7500;">text</span>(<span style="color: magenta;">"Text"</span>)<br /> .<span style="color: #ff7500;">unit</span>(<span style="color: magenta;">"</span><span style="color: navy;">\u0025</span><span style="color: magenta;">C"</span>)<br /> .<span style="color: #ff7500;">animated</span>(<span style="color: #0004ff;">true</span>)<br /> .<span style="color: #ff7500;">build</span>();<br /><br />tile2 = TileBuilder.create().<span style="color: #ff7500;">skinType</span>(SkinType.<span style="font-style: italic;">COLOR</span>)<br /> .<span style="color: #ff7500;">prefSize</span>(<span style="font-style: italic;">WIDTH</span>, <span style="font-style: italic;">HEIGHT</span>)<br /> .<span style="color: #ff7500;">title</span>(<span style="color: magenta;">"Color (Gradient)"</span>)<br /> .<span style="color: #ff7500;">description</span>(<span style="color: magenta;">"CPU temp"</span>)<br /> .<span style="color: #ff7500;">text</span>(<span style="color: magenta;">"Text"</span>)<br /> .<span style="color: #ff7500;">unit</span>(<span style="color: magenta;">"</span><span style="color: navy;">\u0025</span><span style="color: magenta;">C"</span>)<br /> .<span style="color: #ff7500;">animated</span>(<span style="color: #0004ff;">true</span>)<br /> .<span style="color: #ff7500;">gradientStops</span>(<span style="color: #0004ff;">new </span>Stop(0.0, Medium.<span style="font-style: italic;">GREEN</span>),<br /> <span style="color: #0004ff;">new </span>Stop(0.2, Medium.<span style="font-style: italic;">GREEN_YELLOW</span>),<br /> <span style="color: #0004ff;">new </span>Stop(0.4, Medium.<span style="font-style: italic;">YELLOW_ORANGE</span>),<br /> <span style="color: #0004ff;">new </span>Stop(0.6, Medium.<span style="font-style: italic;">ORANGE</span>),<br /> <span style="color: #0004ff;">new </span>Stop(0.8, Medium.<span style="font-style: italic;">ORANGE_RED</span>),<br /> <span style="color: #0004ff;">new </span>Stop(1.0, Medium.<span style="font-style: italic;">RED</span>))<br /> .<span style="color: #ff7500;">fillWithGradient</span>(<span style="color: #0004ff;">true</span>)<br /> .<span style="color: #ff7500;">build</span>();</pre></pre></div></font></span></div><div><font face="verdana, sans-serif">In principle it would also have been enough to define the 0.0, 0.5 and 1.0 values with for example green, yellow and red to get a similar result.</font></div><div><font face="verdana, sans-serif">Long story short, here is a little video of the result where the section approach is on the left side and the gradient approach on the right side:</font></div><div><font face="verdana, sans-serif"><br /></font></div><div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dzr9YdoS7mLvFM_-Jn5uTJ57mQU3eFbanHoSUxFT9yw8R1JMaMIiXCeGUrDyamIZ5J3Dp8NnGAEzoxsqyWuTg' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div><font face="verdana, sans-serif"><br /></font></div><div><font face="verdana, sans-serif"><br /></font></div><div><font face="verdana, sans-serif"><b>TurnoverTileSkin</b></font></div><div><font face="verdana, sans-serif">The next new skin that I have added is the TurnoverTileSkin. This skin can be used to show an image with a value and a text and visualize a ranking and a special effect if the given threshold was reached.</font></div><div><font face="verdana, sans-serif">The effect I'm talking about is the RotationEffect that I also have added to the library. To make use of the ranking you have to use the Rank class to define the ranks you need.</font></div><div><font face="verdana, sans-serif">As an example let's define the ranks, first, second and third as follows:</font></div><div><pre style="background-color: white; font-family: "jetbrains mono", monospace;">Rank first = <span style="color: #0004ff;">new </span>Rank(Ranking.<span style="font-style: italic;">FIRST</span>, Color.<span style="font-style: italic;">GOLD</span>);<br />Rank second = <span style="color: #0004ff;">new </span>Rank(Ranking.<span style="font-style: italic;">SECOND</span>, Color.<span style="font-style: italic;">SILVER</span>);<br />Rank third = <span style="color: #0004ff;">new </span>Rank(Ranking.<span style="font-style: italic;">THIRD</span>, Color.web(<span style="color: magenta;">"#cd7f32"</span>));</pre></div><div><font face="verdana, sans-serif">So each rank has a ranking (the Ranking enum goes from first to tenth) and with a color.</font></div><div><font face="verdana, sans-serif">In the following little video I created 5 tiles for some persons and put them in an HBox container. As you will see in the video the ranking changes and will be visualized when the values change. </font></div><div><font face="verdana, sans-serif">In addition you will see the rotation effect kick in as soon as one of the person value reaches the threshold of 500. So here is the video to give you an idea...</font></div><div><font face="verdana, sans-serif"><br /></font></div><div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dy-0y3XSO9gs3znBRA1BtlTm_0KPNFIutq-xhd8ScwkIJrQns_zlKnofRVKOj7ReTe88sZBlTTxxdJvygWJNA' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div><font face="verdana, sans-serif"><br /></font></div><div><font face="verdana, sans-serif">Here is some code that gives you an idea how I handle the ranking in the video above.</font></div><div><pre style="background-color: white; font-family: "JetBrains Mono", monospace;"><pre style="font-family: "jetbrains mono", monospace;"><span style="color: #0004ff;">private </span>Tile createPersonTile(<span style="color: #0004ff;">final </span>String title, <span style="color: #0004ff;">final </span>String name, <span style="color: #0004ff;">final </span>Image image) {<br /> <span style="color: #0004ff;">return </span>TileBuilder.create()<br /> .<span style="color: #ff7500;">skinType</span>(SkinType.<span style="font-style: italic;">TURNOVER</span>)<br /> .<span style="color: #ff7500;">prefSize</span>(300, 300)<br /> .<span style="color: #ff7500;">title</span>(title)<br /> .<span style="color: #ff7500;">unit</span>(<span style="color: magenta;">"$"</span>)<br /> .<span style="color: #ff7500;">image</span>(image)<br /> .<span style="color: #ff7500;">text</span>(name)<br /> .<span style="color: #ff7500;">maxValue</span>(2000)<br /> .<span style="color: #ff7500;">threshold</span>(500) <span style="color: green; font-style: italic;">// Will trigger the rotationEffect when reached<br /></span><span style="color: green; font-style: italic;"> </span>.<span style="color: #ff7500;">animated</span>(<span style="color: #0004ff;">true</span>)<br /> .<span style="color: #ff7500;">build</span>();<br />}<br /><br /><span style="color: #0004ff;">private void </span>checkHighscores(<span style="color: #0004ff;">final </span>List<Tile> persons) {<br /> List<Tile> sorted = persons.<span style="color: #ff7500;">stream</span>()<br /> .<span style="color: #ff7500;">sorted</span>(Comparator.comparingDouble(Tile::<span style="color: #ff7500;">getValue</span>).<span style="color: #ff7500;">reversed</span>())<br /> .<span style="color: #ff7500;">collect</span>(Collectors.toList());<br /> sorted.<span style="color: #ff7500;">get</span>(0).<span style="color: #ff7500;">setRank</span>(first);<br /> sorted.<span style="color: #ff7500;">get</span>(0).<span style="color: #ff7500;">setValueColor</span>(first.<span style="color: #ff7500;">getColor</span>());<br /> sorted.<span style="color: #ff7500;">get</span>(0).<span style="color: #ff7500;">setUnitColor</span>(first.<span style="color: #ff7500;">getColor</span>());<br /><br /> sorted.<span style="color: #ff7500;">get</span>(1).<span style="color: #ff7500;">setRank</span>(second);<br /> sorted.<span style="color: #ff7500;">get</span>(1).<span style="color: #ff7500;">setValueColor</span>(second.<span style="color: #ff7500;">getColor</span>());<br /> sorted.<span style="color: #ff7500;">get</span>(1).<span style="color: #ff7500;">setUnitColor</span>(second.<span style="color: #ff7500;">getColor</span>());<br /><br /> sorted.<span style="color: #ff7500;">get</span>(2).<span style="color: #ff7500;">setRank</span>(third);<br /> sorted.<span style="color: #ff7500;">get</span>(2).<span style="color: #ff7500;">setValueColor</span>(third.<span style="color: #ff7500;">getColor</span>());<br /> sorted.<span style="color: #ff7500;">get</span>(2).<span style="color: #ff7500;">setUnitColor</span>(third.<span style="color: #ff7500;">getColor</span>());<br /><br /> <span style="color: #0004ff;">for </span>(<span style="color: #0004ff;">int </span>i = 3 ; i < sorted.<span style="color: #ff7500;">size</span>() ; i++) {<br /> sorted.<span style="color: #ff7500;">get</span>(i).<span style="color: #ff7500;">setRank</span>(Rank.<span style="font-style: italic;">DEFAULT</span>);<br /> sorted.<span style="color: #ff7500;">get</span>(i).<span style="color: #ff7500;">setValueColor</span>(Tile.<span style="font-style: italic;">FOREGROUND</span>);<br /> sorted.<span style="color: #ff7500;">get</span>(i).<span style="color: #ff7500;">setUnitColor</span>(Tile.<span style="font-style: italic;">FOREGROUND</span>);<br /> }<br />}</pre></pre></div><div><font face="verdana, sans-serif">The createPersonTile method is used to create the tile for each person and the checkHighscores method is always called after the values have been set.</font></div><div><font face="verdana, sans-serif"><br /></font></div><div><font face="verdana, sans-serif"><b>FluidTileSkin</b></font></div><div><font face="verdana, sans-serif">The next new skin that was added is the FluidTileSkin. This skin shows the current value and visualizes it by filling the background of the tile with a fluid like effect.</font></div><div><font face="verdana, sans-serif">The code that is used in the following video looks as follows:</font></div><div><font face="verdana, sans-serif"><br /></font></div><div><pre style="background-color: white; font-family: "jetbrains mono", monospace;">tile = TileBuilder.create().<span style="color: #ff7500;">skinType</span>(SkinType.<span style="font-style: italic;">FLUID</span>)<br /> .<span style="color: #ff7500;">prefSize</span>(<span style="font-style: italic;">WIDTH</span>, <span style="font-style: italic;">HEIGHT</span>)<br /> .<span style="color: #ff7500;">title</span>(<span style="color: magenta;">"Fluid"</span>)<br /> .<span style="color: #ff7500;">text</span>(<span style="color: magenta;">"Waterlevel"</span>)<br /> .<span style="color: #ff7500;">unit</span>(<span style="color: magenta;">"</span><span style="color: navy;">\u0025</span><span style="color: magenta;">"</span>)<br /> .<span style="color: #ff7500;">decimals</span>(0)<br /> .<span style="color: #ff7500;">barColor</span>(Tile.<span style="font-style: italic;">BLUE</span>) <span style="color: green; font-style: italic;">// defines the fluid color<br /></span><span style="color: green; font-style: italic;"> </span>.<span style="color: #ff7500;">animated</span>(<span style="color: #0004ff;">true</span>)<br /> .<span style="color: #ff7500;">build</span>();</pre></div><div><font face="verdana, sans-serif"><br /></font></div><div><font face="verdana, sans-serif">If you would like to see a more colorful fluid you can make use of sections or the gradientStops like shown above in the ColorTileSkin example.</font></div><div><font face="verdana, sans-serif">Here is how it looks like in action...</font></div><div><font face="verdana, sans-serif"><br /></font></div><div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dx8i_0z8nN9cGqn3ELTxFnu7X3fjlpazHIfzNEECMVXe-Xj8gna_gS1y0Y7yvxRpkNFbf0ekIpQs8wYRPMNgg' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div><font face="verdana, sans-serif"><br /></font></div><div><font face="verdana, sans-serif"><br /></font></div><div><font face="verdana, sans-serif"><b>FireSmokeTileSkin</b></font></div><div><font face="verdana, sans-serif">The last skin I have added is the FireSmokeTileSkin. Well this is another fun skin to visualize the point where the value exceeds the defined threshold.</font></div><div><font face="verdana, sans-serif">As you might guess by the name the preferred usage for this skin might be visualizing a temperature value. </font></div><div><font face="verdana, sans-serif">The code used for the next video looks as follows...</font></div><div><pre style="background-color: white; font-family: "jetbrains mono", monospace;">tile = TileBuilder.create().<span style="color: #ff7500;">skinType</span>(SkinType.<span style="font-style: italic;">FLUID</span>)<br /> .<span style="color: #ff7500;">prefSize</span>(<span style="font-style: italic;">WIDTH</span>, <span style="font-style: italic;">HEIGHT</span>)<br /> .<span style="color: #ff7500;">title</span>(<span style="color: magenta;">"Fire Smoke"</span>)<br /> .<span style="color: #ff7500;">text</span>(<span style="color: magenta;">"CPU temp"</span>)<br /> .<span style="color: #ff7500;">unit</span>(<span style="color: magenta;">"</span><span style="color: navy;">\u00b0</span><span style="color: magenta;">C"</span>)<br /> .<span style="color: #ff7500;">threshold</span>(70) <span style="color: green; font-style: italic;">// triggers the fire and smoke effect<br /></span><span style="color: green; font-style: italic;"> </span>.<span style="color: #ff7500;">decimals</span>(0)<br /> .<span style="color: #ff7500;">animated</span>(<span style="color: #0004ff;">true</span>)<br /> .<span style="color: #ff7500;">build</span>();</pre></div><div><font face="verdana, sans-serif">The best way to describe this skin is to see it in action...so here you go...</font></div><div><font face="verdana, sans-serif"><br /></font></div><div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dziI_IqZEPR3afke8dscJ9zXXfiKL83nG9D08JSbnTdaRA8C_VJJdcIo3kuELKQtQMt1JqZU2WuLoVmT9Wv_Q' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div><font face="verdana, sans-serif"><br /></font></div><div><font face="verdana, sans-serif">As you can see the fire and smoke starts when the value exceeds the threshold of 70 and stops when the value falls back below 70.</font></div><div><font face="verdana, sans-serif"><br /></font></div><div><font face="verdana, sans-serif">Well that was a lot of new stuff and as always you can find the latest version of TilesFX at</font></div><div><font face="verdana, sans-serif"><br /></font></div><div><font face="verdana, sans-serif"><a href="https://github.com/HanSolo/tilesfx" target="_blank">github</a></font></div><div><font face="verdana, sans-serif"><br /></font></div><div><font face="verdana, sans-serif"><a href="https://bintray.com/hansolo/tilesfx" target="_blank">bintray</a></font></div><div><font face="verdana, sans-serif"><br /></font></div><div><font face="verdana, sans-serif"><a href="https://search.maven.org/search?q=tilesfx" target="_blank">maven central</a></font></div><div><font face="verdana, sans-serif"><br /></font></div><div><font face="verdana, sans-serif">That's it for today...so keep coding and stay healthy...</font></div><div><br /></div></div>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com5tag:blogger.com,1999:blog-6885067460687713893.post-91854998247971105712020-06-02T08:06:00.001+02:002020-06-02T08:06:26.979+02:00Touch me...<font face="verdana">Aloha,</font><div><font face="verdana"><br /></font></div><div><font face="verdana">Last weekend I've spend a lot of time in front of the TV to follow the flight of SpaceX Crew Dragon to the International Space Station.</font></div><div><font face="verdana">It's great to see that finally a commercial company is able to send people to the lower orbit and to the ISS.</font></div><div><font face="verdana">When I saw the touch user interface of the Crew Dragon I've got the idea to create a little touch joystick control in JavaFX.</font></div><div><font face="verdana">I've did not spend a lot of time to think through the functionality so there is as always room for improvement :)</font></div><div><font face="verdana">So I took my standard approach, first draw the control in a vector program which looked like this:</font></div><div><font face="verdana"><br /></font></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj42lQRNb1GYeNlFpQD83qJ8FfAIw_aC9UnfiMBzk8PFrok9msBRnB8PA1Ojmj_4JqIq1ZkSghAdGMMK8EawBRu6W88urphOX656EmY-hb2GhX1Cpo7OF3TSx-HftNPilNXPibHCT4z_HA/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1364" data-original-width="1806" height="484" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj42lQRNb1GYeNlFpQD83qJ8FfAIw_aC9UnfiMBzk8PFrok9msBRnB8PA1Ojmj_4JqIq1ZkSghAdGMMK8EawBRu6W88urphOX656EmY-hb2GhX1Cpo7OF3TSx-HftNPilNXPibHCT4z_HA/w640-h484/Affinity+Designer+-+TouchJoystick+%2528200.0%2525%2529+2020-06-02+07-45-41.png" width="640" /></a></div><font face="verdana"><br /></font></div><div><font face="verdana">As you can see it's not a hyper fancy control but it should be useful first :)</font></div><div><font face="verdana">The idea was to have a touch point that can be dragged around. A little ring outside that indicates that you touched the control (because your finger might block your view on the touch point itself).</font></div><div><font face="verdana">8 buttons around the control that can be used to make small adjustments with given directions.</font></div><div><font face="verdana">I've also added an indicator made out of chevron shapes. The more you drag the point to one direction the longer the bar of chevrons will get. First I thought to let it go from the center of the control to the touch point but if you use your finger to drag the point, your hand might block your view and so I've decided to put the chevron bar on the opposite side of the touch point to make it as visible as possible.</font></div><div><font face="verdana">I've created a little video that shows how the control works...</font></div><div><font face="verdana"><br /></font></div><div><font face="verdana"><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/rJpiv0mKh5U" width="320" youtube-src-id="rJpiv0mKh5U"></iframe></div></font></div><div><font face="verdana"><br /></font></div><div><font face="verdana">As always you can find the code over at <a href="https://github.com/HanSolo/touchjoystick" target="_blank">github</a>...</font></div><div><font face="verdana"><br /></font></div><div><font face="verdana">If you would like to start the demo you just have to make sure you are on Java 11 and then you can execute </font><font face="courier">gradle Demo</font><font face="verdana"> on the console.</font></div><div><font face="verdana"><br /></font></div><div><font face="verdana">That's it for today...so keep coding...</font></div><div><font face="verdana"><br /></font></div>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com1tag:blogger.com,1999:blog-6885067460687713893.post-35066494410380869832020-05-29T06:00:00.009+02:002020-05-29T07:03:19.387+02:00TilesFX 11.37<font face="verdana"><span style="font-family: "trebuchet ms", sans-serif;">Aloha,</span><br />
<span style="font-family: "trebuchet ms", sans-serif;"><br /></span>
<span style="font-family: "trebuchet ms", sans-serif;">Time for some new stuff...</span><br />
<span style="font-family: "trebuchet ms", sans-serif;">Last weekend I've stumbled upon some dashboards that had some interesting visualizations. The first one was a visualization of sales cycle steps that looked as follows:</span><br /><span style="font-family: "trebuchet ms", sans-serif;"><br /></span>
</font><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_T-KHPQ_b1xSxZiVsrbTCSIyGdDgg82aRLS9EMygaMY6Ru3CH0ILzstVEJgXtKgWPgFJDxJdxcYL6cT3bQbKc0dPDg9aggyZzwpdzVid2eNRorjU67BgAyuxr3BVgFAp6iWKok7NNUPg/" style="margin-left: 1em; margin-right: 1em;"><font face="verdana"><img border="0" data-original-height="1014" data-original-width="1125" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_T-KHPQ_b1xSxZiVsrbTCSIyGdDgg82aRLS9EMygaMY6Ru3CH0ILzstVEJgXtKgWPgFJDxJdxcYL6cT3bQbKc0dPDg9aggyZzwpdzVid2eNRorjU67BgAyuxr3BVgFAp6iWKok7NNUPg/s320/IMG_1240.jpg" width="320" /></font></a></div><font face="verdana"><span style="font-family: "trebuchet ms", sans-serif;"><br /></span><span style="font-family: "trebuchet ms", sans-serif;">The interesting part here for me is the chart where each bar starts where the last bar ended. I took a look at TilesFX but did not found something that made stuff like this possible in an easy way. So I decided to add a new skin that I called CycleStepTileskin (I know I'm not good in naming things). Here is how it looks like:</span></font><div><span style="font-family: "trebuchet ms", sans-serif;"><font face="verdana"><br /></font></span></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLSiBPKdrc-YOls7zHti5B6SUfQgExaWRawV8IBJVVACzIHMWzyHy6LTqYzi-6x072rJbTD0L3iUwzB71ooLg1WjnpiZbUFGejcOUOU0p_c5VQkIYy4JUyLezkFQCu5ff2z1R_n9d0aQY/" style="margin-left: 1em; margin-right: 1em;"><font face="verdana"><img border="0" data-original-height="872" data-original-width="1042" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLSiBPKdrc-YOls7zHti5B6SUfQgExaWRawV8IBJVVACzIHMWzyHy6LTqYzi-6x072rJbTD0L3iUwzB71ooLg1WjnpiZbUFGejcOUOU0p_c5VQkIYy4JUyLezkFQCu5ff2z1R_n9d0aQY/s320/Test+2020-05-28+10-47-40.png" width="320" /></font></a></div><span style="font-family: "trebuchet ms", sans-serif;"><font face="verdana"><br /></font></span></div><div><span style="font-family: "trebuchet ms", sans-serif;"><font face="verdana">To be honest I'm not sure if this is useful but you never know right :)</font></span></div><div><span style="font-family: "trebuchet ms", sans-serif;"><font face="verdana"><br /></font></span></div><div><span style="font-family: "trebuchet ms", sans-serif;"><font face="verdana">The other tile I've found was nothing really special but I realized it has something that I missed in TilesFX...flags. This is what I'm talking about...</font></span></div><div><span style="font-family: "trebuchet ms", sans-serif;"><font face="verdana"><br /></font></span></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwSXgqY2RbxkGncKieu5jyGIWbafY7UMWGLIXo1d5g_losYK1VkfK_hnNcPueOVQp_ow7uIwtM-ioAaKflveMjfEsvidplxUPMMj9G7OMT-jq6MAe8C-AId6DBg8EdJaPUKPAci-DzRDI/" style="margin-left: 1em; margin-right: 1em;"><font face="verdana"><img border="0" data-original-height="734" data-original-width="610" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwSXgqY2RbxkGncKieu5jyGIWbafY7UMWGLIXo1d5g_losYK1VkfK_hnNcPueOVQp_ow7uIwtM-ioAaKflveMjfEsvidplxUPMMj9G7OMT-jq6MAe8C-AId6DBg8EdJaPUKPAci-DzRDI/s320/flags.png" /></font></a></div><span style="font-family: "trebuchet ms", sans-serif;"><font face="verdana"><br /></font></span></div><div><font face="trebuchet ms, sans-serif">Not everyone needs flags but sometimes they come in really handy and so I was looking for flag images that I could use. Lucky me I've found free flags over at <a href="https://www.flaticon.com/packs/countrys-flags/2?k=1590473895072" target="_blank">flaticon</a> and added them to TilesFX. I've added them as enum and control.</font></div><div><font face="trebuchet ms, sans-serif">So if you need an icon image you can now simply do something like follows:</font></div><div><font face="trebuchet ms, sans-serif"><br /></font></div><div><pre style="background-color: white; font-family: "jetbrains mono", monospace;">ImageView flagImage = <span style="color: #0004ff;">new </span>ImageView(Flag.<span style="font-style: italic;">GERMANY</span>.<span style="color: #ff7500;">getImage</span>(48));</pre></div><div><font face="trebuchet ms, sans-serif"><br /></font></div><div><font face="trebuchet ms, sans-serif">The Flag enum has two methods to get an image, the standard getImage() method always returns a flag image with the size of 30x30 px. If you need a different size you can simply call the getImage(SIZE) method to get the flag in the size you want it.</font></div><div><font face="trebuchet ms, sans-serif">The flag images are always square!</font></div><div><font face="trebuchet ms, sans-serif">The other class I've added is called FlagIcon which is more or less a wrapper around an ImageView of the given image. Meaning to say it behaves like a control, so it will be resized automatically BUT it will resize the image based on the given flagSize. So if you have a flag of size 48 px and you increase the size of the FlagIcon it will get pixelated.</font></div><div><font face="trebuchet ms, sans-serif">So if you would like to avoid pixelation you need to set the FlagSize to a bigger value but that will also increase memory consumption...so it's up to you.</font></div><div><font face="trebuchet ms, sans-serif">In the end the FlagIcon is just a convenience class that comes in handy if you simply need a flag somewhere. You can use it as follows:</font></div><div><font face="trebuchet ms, sans-serif"><br /></font></div><div><pre style="background-color: white; font-family: "jetbrains mono", monospace;">FlagIcon flagIcon = <span style="color: #0004ff;">new </span>FlagIcon(Flag.<span style="font-style: italic;">SINGAPORE</span>, 24);</pre></div><div><font face="trebuchet ms, sans-serif"><br /></font></div><div><font face="trebuchet ms, sans-serif">This will give you a control that shows a flag that is based on an image with 24px.</font></div><div><font face="trebuchet ms, sans-serif"><br /></font></div><div><font face="trebuchet ms, sans-serif">As an example of what you can do with that I've simply added a tile that shows the Covid-19 cases of some countries based on data from Mai 26th 2020. For this tile I simply used the custom tile skin and added a VBox with some HBoxes. If you are interested in how I did that, please take a look at the Demo class in the TilesFX project.</font></div><div><font face="trebuchet ms, sans-serif"><br /></font></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7oTuLFhuOeRmeHm-nMh707LSO2zv1Ybq7J9U02Wr_PRQsYtkUye48A61OLYRQdyp3IvGHLqwRmu7qteSQ-rliHBr5HVAIxHnvY8py5gJvT1i1PNR5kB_c7U2Y6Aegjjo3oe7s9phIEBE/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1966" data-original-width="2990" height="420" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7oTuLFhuOeRmeHm-nMh707LSO2zv1Ybq7J9U02Wr_PRQsYtkUye48A61OLYRQdyp3IvGHLqwRmu7qteSQ-rliHBr5HVAIxHnvY8py5gJvT1i1PNR5kB_c7U2Y6Aegjjo3oe7s9phIEBE/w640-h420/TilesFX.png" width="640" /></a></div><font face="trebuchet ms, sans-serif"><br /></font></div><div><font face="trebuchet ms, sans-serif"><br /></font></div><div><font face="trebuchet ms, sans-serif">Here is a little bigger screenshot of it...</font></div><div><font face="trebuchet ms, sans-serif"><br /></font></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIz2R9sANe0T9u25uCugpbwvPj4nH-xhSbbv_OHYZ8axsG-BGlhu3gUbQ3Nc6JCIseNBgM86Fim8aFnbsxNpwH3cirtI5fDdBReZko-LOkDkxHOD7mwfnBy1vEv_TxBQj7Ll4dUZQ2YiA/" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="584" data-original-width="540" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIz2R9sANe0T9u25uCugpbwvPj4nH-xhSbbv_OHYZ8axsG-BGlhu3gUbQ3Nc6JCIseNBgM86Fim8aFnbsxNpwH3cirtI5fDdBReZko-LOkDkxHOD7mwfnBy1vEv_TxBQj7Ll4dUZQ2YiA/s320/Test+2020-05-28+11-22-20.png" /></a></div></div><div><br /></div><div><font face="trebuchet ms, sans-serif">That's it for today so as always you can find the code over at <a href="https://github.com/HanSolo/tilesfx" target="_blank">github</a> and the binary either at <a href="https://bintray.com/hansolo/tilesfx" target="_blank">bintray</a> or at <a href="https://search.maven.org/artifact/eu.hansolo/tilesfx" target="_blank">maven central</a>.</font></div><div><font face="trebuchet ms, sans-serif"><br /></font></div><div><font face="trebuchet ms, sans-serif">That's it for today...so keep coding and stay healthy...</font></div><div><br /></div>Han Solohttp://www.blogger.com/profile/12574196875988408685noreply@blogger.com1