Skip to main content

SimpleFX Examples

SimpleFX Examples.

The ZenGuitar!

Zenguitar written in SimpleFX
Image of the Zen-Guitar

Last week, we released the SimpleFX-Zenguitar on Github.

The ZenGuitar is a creation of, and was originally implemented and demonstrated by, James Weaver, at Oracle. Take a look at his great blogpost here!

The ZenGuitar is an application, which nicely demonstrates the multi-touch- and 3D-capabilities of JavaFX. It accesses some simple parts of the JFugue-library, using Midi to play a number of different instruments, including a guitar, obviously.

Together with James Weaver, we re-implemented the original version, written in Java8, with SimpleFX, to demonstrate some of the benefits using the powerful features of Invariant-Definitions, Expression-Binding, Imply-Statement etc. of SimpleFX.

SimpleFX is a DSL for JavaFX written in Scala. It encourages Expression-Binding and a Declerative Programming style. It makes writing advanced JavaFX programs very easy and less error-prone, it encourages writing clear and concise code, as well as improving readability and quality of the source-code.

See how simple it is with SimpleFX to use Expression-Binding, and see how the implementation, as a complementary effect, reaches an abstraction level you probably have not seen before. Take a look yourself, make a short study of the interesting "Zenguitar" example.


when(muteMode) ==> { // When in muteMode, define the invariants for rotation and scaling.
    Δ(guitarAngleX) <-- (Δ(pin.dragDistance.y + pin.touchScrollDistance.y)/ 3) // Angle-changes === the changes
    Δ(guitarAngleY) <-- (Δ(pin.dragDistance.x + pin.touchScrollDistance.x)/-3) // of the drag- and scroll-distances.    
    Δ(guitarAngleZ) <--  Δ(pin.rotateAngleSum)                                 // Angle-change === rotation-change.
    guitar.scaleXYZ <-- (prev(guitar.scaleX) *                                 // Defining the scaling parameters.
                        (1.0 + (Δ(pin.mouseWheelDistance.y) / 400))  *            
                        guitar.zoomFactorProduct / prev(guitar.zoomFactorProduct)).to3D

    when(guitar.scaleX < .25) --> {                                            // When scale goes below 25%, reset.
      muteMode = false                                                         // Muting is set to off.
      guitarAngleXYZ  := (0,0,0)  in GUITAR_RESET_DURATION                     // Any rotation is reset, progressively.
      guitar.scaleXYZ := 1.0.to3D in GUITAR_RESET_DURATION                     // Any scaling  is reset, progressively.

What does this code-snippet really do?

It defines, that

(a) when the guitar is in muteMode, the 3-dimensional angles are to be changed through the dragDistance's, the touchScrollDistance's or the rotateAngle. The dragDistance's are defined through standard mouse-dragging-operations, the touchScrollDistance's through standard touch-screen-operations and the rotateAngleSum through standard, multi-touch rotation operations. And,

(b) the scaling(zoom-effect) of the entire guitar is defined through

  • the last scale-value *
    (1.0 + the last change of the mouseWheelDistance in the y-dimension/400) *
    the relation between the last zoomFactor and the previous zoomFactor

(c) the reset of all rotation and zooming, defined through the 3-dimensional angles- and scaling-values, being set back to (0, 0, 0) and (1.0, 1.0, 1.0) accordingly. Because Progressive Assignments are used, the reset happens nicely animated.

Description of the used features:

  • Imply Operator (==>), brief explanation:

    when(condition) ==> { body }

    Any time the condition changes from false to true, the body is executed.
    Any time the condition changes from true to false, the body is "cleaned up".
    "Cleaned up" here means, that all Bindings, Triggers, Progressive Assignments etc. defined in the body are automatically disposed.
    Therefore, when used correctly, a false condition always results in an "undo" of whatever happened in the last body as the condition was true. And, whatever is defined in the body. it can be regarded as an invariant for condition being true. How and when the condition turned true does not matter.

  • The Trigger Operator (-->), brief explanation:

    Bindable --> { expression }

    Any time when Bindable's value changes, expression is being executed. The initial execution takes place at declaration-time. It works in many senses very similar to the Imply-Operator, but is simpler (see SimpleFX Tutorials for a precise definition).

  • The Binding Operator (<-- ), brief explanation:

    Bindable <-- { expression }

    Bindable is a SimpleFX Property. Any time when a Bindable, which is used inside of expression, changes, the expression is re-calculated and returns a new value for Bindable . SimpleFX auto-detects all the Bindables being used inside of expression and guarantuees a performant "monitoring" of any change happening to any of them. As the Binding is declared, the first, initial value of expression is calculated and used to set the initial value of Bindable. Therefore, expression can be considered an invariant definition of Bindable's value.

  • Using Deltas:

    Δ(Bindable) <-- Δ(expression)

    Bindables come with an inherent support for Δ ("Deltas"), which are being treated as Bindables themselves. A Bindable's Δ represents the change to a Bindable since it's last scheduled frame(see SimpleFX Tutorials for a precise definition). prev(Bindable) represents the last Δ-value of Bindable.
    There are some interesting motivations for often using the Δ-version of an invariant-definition (or Binding) for Bindables. In the case above, we want the angles to be updated only when the guitar is in muteMode. Any change to the dragging-properties, scroll-distances and rotate-angles should ONLY apply, as long as the guitar is in muteMode (otherwise, we would have unwanted effects, like the guitar would rotate as we were playing, for instance.
    In SimpleFX it is super simple to use Delta-definitions and Bindings together. First of all, just replace = with <-- and you have a Binding instead of an assignment! And, secondly, in combination with the Delta (Δ), the zoom- and rotate-events can be defined in one single expression>

  • Progressive Assignments:

    Bindable := value in duration

    The Bindable is set to value during a period of duration. This means, Bindable is interpolated, during a period of duration and reaching the end-value of value at the end of the duration. Specific Interpolators, a startValue and an onFinished-Lamda are also supported.

Hello World!

package samples.hello

import simplefx.core._
import simplefx.all._

object HelloSimpleFX extends SimpleFXApp {
  root = new Label("""

  Hello all UI-Developer-Colleagues, Scala-Enthusiasts, Java-Enthusiasts, 
  JavaFX-Fans, ScalaFX-Fans, Visage-Fans, all Fans of a little bit more 
  Functional than Java, all Fans of creative DSLs, friends of Declarative- and 
  Reactive Programming, all friends of short, precise, expressive, concise and  
  non-verbose code!

  We are getting very close to the launch of SimpleFX! ...
  In the meantime, have fun studying the Samples and the different infos available! 
  Stay in!  And, please revisit this blog the next coming days ...

  We really hope you will learn to love it as much as we do!