Archive for the ‘Processing to JavaFx’ Category

Comparing Processing to JavaFX via a Processing tutorial

April 21, 2010

I came across this Processing tutorial via flowingdata the other day written by Jer Thorp (an artist and educator from Vancouver, Canada), I think it is a very impressive tutorial. It’s a nice and easy introduction to Processing with some excellent results.

It has played on my mind since because I “cut my teeth” learning Processing and now I have ventured off into the world of JavaFX. I thought it might be interesting to compare, for each step of the tutorial, the equivalent steps required in JavaFX.

So here goes, I will give an overview of each step Jer details with the Processing code and then give the equivalent JavaFX code. The end result should be two applications doing the same thing but different languages.

In the first instance I needed to setup the SimpleSpreadsheetManager class, this wasn’t too difficult, I downloaded Jer’s code and then created a new JavaFX project in Netbeans. I had to setup the Google Data libraries, and then type in the code.

With that out of the way I can progress, Jar’s first Processing task is to define the dimensions and the background colour:

In Processing:

void setup()
{
    size(800,800);
    background(0);
    smooth();
}

In JavaFX:

Stage
{
   scene: Scene
   {
      width: 800
      height: 800
      fill: Color.BLACK;
   }
}

His next step is to retrieve the data from the spreadsheet, using a function getNumbers(), his Processing code is:

    int[] numbers = getNumbers();

My JavaFX code is:

    var numbers : Integer[] = getNumbers();

With the function getNumbers() being:

Processing

int[] getNumbers()
{
    println("Asking Google for numbers...");
    sm = new SimpleSpreadsheetManager();
    sm.init("RandomNumbers", googleUser, googlePass);
    sm.fetchSheetByKey(sUrl, 0);

    int n = sm.currentListEntries.size();
    int[] returnArray = new int[n];
    for (int i = 0; i < n; i++) {
        returnArray[i] = int(sm.getCellValue("Number", i));
    };
    println("Got " + n + " numbers.");
    return(returnArray);
}

and for JavaFX:

function getNumbers():Integer[]
{
    println("Asking Google for number...");
    sm = new SimpleSpreadsheetManager("randomnumbers", googleUser, googlePass);
    sm.fetchSheetByKey(sUrl,0);

    var n = sm.currentListEntries.size();
    var res : Integer[] = [];
    for (i in [0..<n])
    {
       insert Integer.parseInt(sm.getCellValue("Number", i)) into res;
    }
    return res;
}

Notice I changed the SimpleSpreadsheetManager by creating a new constructor rather than using the init function (just a personnel preference).

With the data retrieved it is a case of creating the “World’s easiest data visualisation!”, the Processing code is:

 fill(255,40);
 noStroke();
 for (int i = 0; i < numbers.length; i++)
 {
     ellipse(numbers[i] * 8, width/2, 8, 8);
 }

And to do the same in JavaFX it is:

 Group
 {
     content: for (i in [0..<numbers.size()])
     {
        Circle
        {
            centerX: numbers[i] * 8;
            centerY: st.scene.width/2;
            radius: 8;
            fill: Color.WHITE;
            opacity: 0.4;
        }
     }
 }

Slightly more code, but all very straight forward and easy.

Worlds easiest visualisation

Next up, Jer wants to compare the random numbers he received from his <a href=”http://www.twitter.com”>Twitter</a&gt; followers to that of the random numbers generated by Processing by expanding the previous code with:

 for (int i = 0; i < numbers.length; i++)
 {
     ellipse(ceil(random(0,99)) * 8, height / 2 + 20, 8, 8);
 }

JavaFX code is:

 Group
 {
    content:
    [
       for (i in [0..<numbers.size()])
       {
          Group
          {
             content:
             [
                Circle
                {
                    centerX: numbers[i] * 8;
                    centerY: bind st.scene.width/2;
                    radius: 8;
                    fill: Color.WHITE;
                    opacity: 0.4;
                },
                Circle
                {
                    centerX: Math.ceil((Math.random()*100)*8);
                    centerY: bind st.scene.width/2 + 40;
                    radius: 8;
                    fill: Color.WHITE;
                    opacity: 0.4;
                }
             ]
          }
       }
    ]
 }

Worlds easiest visualisation comparison

BarGraph

Jer now wants to produce a histogram, or bar graph, of the random number distribution. The Processing code for this is:

void barGraph(int[] nums, float y)
{
   //Make a list of number counts
   int[] counts = new int[100];
   //Fill it with zeros
   for (int i = 1; i < 100; i++)
   {
      counts[i] = 0;
   }
   //Tally the counts
   for (int i = 0; i < nums.length; i++)
   {
      counts[nums[i]] ++;
   }
   //Draw the bar graph
   for (int i = 0; i < counts.length; i++)
   {
      rect(i * 8, y, 8, -counts[i] * 10);
   }
}

And in JavaFX the same function looks like:

function barGraph(nums:Integer[], y: Float):Group
{
   var counts : Integer[] = [];

   for (i in [0..<100])
   {
      insert 0 into counts;
   }

   for (i in [0..<nums.size()])
   {
      counts[nums[i]]++;
   }

   return Group
   {
      content:
      [
          for (i in [0..<counts.size()])
          {
             Rectangle
             {
                x: i * 8;
                y: y - ((counts[i]) * 10);
                width: 8
                height: (counts[i]) * 10;
                fill: Color.WHITE;
                strokeWidth: 1.0
                stroke: Color.BLACK;
             }
          }
      ]
   }
}

Notice the slight difference in handling the direction of the rectangles, Processing relies on a negative height, and JavaFX subtracts a value from the value for y.

Next up Jer introduces colour to his bar chart by updating the final loop to be:

   for (int i = 0; i < counts.length; i++)
   {
      colorMode(HSB);
      fill(counts[i] * 30, 255, 255);
      rect(i * 8, y, 8, -counts[i] * 10);
   }

And JavaFx it is:

   for (i in [0..<counts.size()])
   {
       Rectangle
       {
           x: i * 8;
           y: y - ((counts[i]) * 10);
           width: 8
           height: (counts[i]) * 10;
           fill: Color.hsb((counts[i] * 30), 1.0, 1.0);
           strokeWidth: 1.0
           stroke: Color.BLACK;
       }
   }

Interestingly, you get different shades between the two! This, I presume, is down to how ColourMode(HSB) and Color.hsb differ, I openly admit to not being completely aware of the workings of these.

A final extension to this work, is comparing the histogram of the human generated random numbers to the system generated values, Jer’s Processing code:

int[] numbers = getNumbers();
//Draw the graph
barGraph(numbers, 100);

for (int i = 1; i < 7; i++)
{
   int[] randoms = getRandomNumbers(225);
   barGraph(randoms, 100 + (i * 130));
}

JavaFX code is:

   barGraph(numbers,100),
   for (i in [1..<7])
   {
      randoms = getRandomNumbers(255);
      barGraph(randoms, 100 + (i * 130))
   }

histogram comparison

Boxes and Numbers

Now we are finished with histograms and Jer moves us onto to a new visualisation technique, boxes. I quite like this, it is amazingly simple to code in Processing and looks really good. So the Processing code is:

void colorGrid(int[] nums, float x, float y, float s)
{
 //Make a list of number counts
 int[] counts = new int[100];
 //Fill it with zeros
 for (int i = 0; i < 100; i++)
 {
    counts[i] = 0;
 }
 //Tally the counts
 for (int i = 0; i < nums.length; i++)
 {
    counts[nums[i]] ++;
 }

 pushMatrix();
 translate(x,y);
 //Draw the grid
 for (int i = 0; i < counts.length; i++)
 {
    colorMode(HSB);
    fill(counts[i] * 30, 255, 255, counts[i] * 30);
    rect((i % 10) * s, floor(i/10) * s, s, s);
 }
 popMatrix();
}

And now the same code in JavaFX:

function colourGrid(nums:Integer[], x: Float, y:Float, s: Float):Group
{
   var counts : Integer[] = [];
   var maxValue = -1;

   for (i in [0..<100])
   {
      insert 0 into counts;
   }

   for (i in [0..<nums.size()])
   {
      counts[nums[i]]++;
      if (counts[nums[i]] > maxValue) maxValue = counts[nums[i]];
   }

   return Group
   {
     content:
     [
         for (i in [0..<counts.size()])
         {
             Rectangle
             {
                 x: (i mod 10) * s + 25;
                 y: Math.floor(i/10) * s + 25;
                 width: s
                 height: s
                 fill: Color.hsb(counts[i] * 30, 1.0, 1.0);
                 stroke: Color.BLACK;
                 strokeWidth: 0.5;
                 opacity: ((counts[i] as Number)/(maxValue as Number)) as Number
             }
         }
     ]
   }
}

Some interesting differences here, Processing handles the colouring differently and so I have calculated the opacity based on the ratio of the value to the maximum count. Again the colours are slightly different, but it still looks good.

boxes visualisation of random numbers

Finally Jer changes the boxes to the numbers, but maps the colours and opacity relevant to the frequency of the value, again quite simple but really nice. Processing code:

void colorGrid(int[] nums, float x, float y, float s)
{
 //Make a list of number counts
 int[] counts = new int[100];
 //Fill it with zeros
 for (int i = 0; i < 100; i++)
 {
    counts[i] = 0;
 }
 //Tally the counts
 for (int i = 0; i < nums.length; i++)
 {
    counts[nums[i]] ++;
 }

 pushMatrix();
 translate(x,y);
 //Draw the grid
 for (int i = 0; i < counts.length; i++)
 {
    colorMode(HSB);
    fill(counts[i] * 30, 255, 255, counts[i] * 30);
    textAlign(CENTER);
    textFont(label);
    textSize(s/2);
    text(i, (i % 10) * s, floor(i/10) * s);
 }
 popMatrix();
}

And JavaFX

function colourGrid(nums:Integer[], x: Float, y:Float, s: Float):Group
{
   var counts : Integer[] = [];
   var maxValue = -1;

   for (i in [0..<100])
   {
      insert 0 into counts;
   }

   for (i in [0..<nums.size()])
   {
      counts[nums[i]]++;
      if (counts[nums[i]] > maxValue) maxValue = counts[nums[i]];
   }

   return Group
   {
      content:
      [

         for (i in [0..<counts.size()])
         {
             Text
             {
                x: (i mod 10) * s + 50;
                y: Math.floor(i/10) * s + 75;
                content: counts[i].toString();
                opacity: ((counts[i] as Number)/(maxValue as Number)) as Number
                font: Font.font("Helvetic", FontWeight.REGULAR, s);
                textAlignment: TextAlignment.CENTER;
                fill: Color.hsb(counts[i] * 30, 1.0, 1.0);
             }
         }
      ]
   }
}

Random numbers

So that is it. I really like Jer’s tutorial, and the results. It is amazing how simple it is but you feel like you have achieved a lot. Converting the tutorial from Processing to JavaFX was quite straight forward, with no major surprises or problems.

If anyone wants the JavaFX code I would be happy to share? Thanks again to Jer for sharing his tutorial.