HTML5 Canvases & Transforms

Browsers supporting HTML5 allow you to draw on the browser’s screen without preparing an image file before. Drawing on a canvas is done using the wonderful Javascript language. If you want to draw a 2-dimensional image on canvas element ‘cnv’, get the drawing context using:

var ctx = cnv.getContext("2d")

And use that context to draw everything using the standard functions:

moveTo, lineTo, arc, fillRect, etc.

Learn more about drawing here.

You can use the functions to create more complicated shapes easily thanks to transform functions:

Transformations: Linear Algebra Made Easy

The origin(0,0) of the canvas is defined to be the top-left pixel of the canvas element. And the coordinates are given in number of pixels. This can change by using transforms.

The transformation functions are:

  • scale(x,y): this will make every shape x times wider and y time taller.
  • translate(x,y): now coordinate (0,0) will be shifted x units to the right, and y units down.
  • rotate(angle): will rotete the axes by given angle in radians.
  • transform(a,b,c,d,e,f): If you want to use a transformation matrix
  • setTransfrom(a,b,c,d,e,f): reset all transform, and perform transform(a,b,c,d,e,f).

a,b,c,d,e,f are values in the matrix:

Transform Matrix
The values a,b,c,d are used for rotating and scaling. e,f for translating.

Other useful methods of the context are:

  • ctx.save() – to save current transform in a stack (Last In First Out).
  • ctx.restore() – to retrieve the trnasform from the top of the stack.

An Example – The Koch Snowflake

The algorithm for drawing the Koch Snowflake can be found in the post Drawing The Koch Snowflake Fractal With GIMP.

Here’s an example in Javascript:

        function drawSide(ctx, len){
          if (len > 1) {
            var thirdOfLen = len / 3.;

            var rotationAngles = [0, -Math.PI / 3, 2 * Math.PI / 3., -Math.PI / 3];

            rotationAngles.forEach(function(val){
              
              if (val != 0){
                ctx.translate(thirdOfLen, 0);
                ctx.rotate(val);
              }
              ctx.save();
              drawSide(ctx, thirdOfLen);
              ctx.restore();
            });

          } else {
            ctx.moveTo(0,0);
            ctx.lineTo(len,0);
            //ctx.stroke();
          } 
        }

        ctx.translate(startX, startY);
        for (i=0; i<3; i++){
          ctx.save();
          drawSide(ctx, sideLength);
          ctx.restore();
          ctx.translate(sideLength,0);
          ctx.rotate(2*Math.PI/3);
        }
        ctx.stroke();
      }

Warning: using ctx.stroke() after every little line you draw might make your code inefficient, and slow down your browser.

Transformation functions are also part of the Processing language.

Advertisement

Processing: Sketches – Your Multi-Programs

When you start Processing, you get an editor with a sketch name. You will probably want another name for your program, a name that means something. So, to change the name generated by the tool, you choose file->save as. Choose a name, and the tool will create a folder with that name, and a file named <name>.pde.

When you run your sketch – by clicking the button with the triange or selecting ‘sketch->Run …’ (Run In Browser, Run on Device, Run In Emulator, Run, etc. Depending on the mode) – Processing will parse the ‘.pde’ files and the program used for running will create an instance each time you run it.

Setup and Draw

If you want have things to do upon initiation of your program, define a function named ‘void setup()‘.  To size your canvas call size(width, height). It is recommended to use ‘size’ only once in the setup function, and the whole sketch. A setup command is not called in run time. It is recommended to set the frame rate inside the setup function by calling ‘frameRate(rate)‘, the rate is the number of time the function ‘draw()‘ is supposed to be call every second. If you want an action to take place each time a frame changes, write a “draw” function in one of  your ‘.pde’ files.

More Than Just Pure Processing

Sometimes, pure Processing is not enough for your application. For example, sound functions do not exist in pure Processing. There are libraries, such as Maxim,  that extend Processing.

Another reason to use more than pure Processing is will to use native widgets (HTML elements, Android Menus & Dialogs, etc.). In pure Processing, you would have to draw the button and detect in the ‘mouseClicked‘ function if the mouse pointer is inside the button.

Here’s a little video example of using the android menu to change the number of polygon sides:

In addition, I’ve created a Javascript module, and added buttons to the HTML page displayed when running in Javascript mode.

The following sections will explain how to do it.

 The Common Part: Just Draw a Polygon

The sommon part that draws the polygon is found in a ‘.pde’ file in the ‘polygon’ sketch directory. The function ‘draw’ detects changes in the global variable n, the number of sides.

The functions ‘translate’ and ‘rotate’ make drawing rotated shapes and lines easier.

In addition, the module contains a call to a function named ‘alert’. This function exists in Javascript, so I’ve made another in the Android module using Toasts.

Following is the code:

// This program draws a regular polygon with n sides.
int n;
float halfVertexAngle; // Will be used for moving from the center
                      // of the sreen to a vertex of the polygon.
                                          
float rotateAngle;     

void set_no_of_sides(int inputN){
  if (inputN < 3){
    alert("Polygons must have at least 3 sides. Try again");
    return;
  }
  n=inputN;
  halfVertexAngle = radians(180) / n; 
  rotateAngle = radians(360./n);
}

void setup(){
  //size(640,480);
  set_no_of_sides(8);
  background(0xffffff00);
  strokeWeight(1);
}

void draw(){
    background(0xffffff00);

  // Center coordinates of the screen and circumbscribed circle.

  int centerX = width / 2;
  int centerY = height / 2;
  
  
  float radius = min(width / 2, height/2);  // The radius of the circumscribed circle.
 
  // Use the cosine theorem to find the length of a side. 
  float side = sqrt(2 * radius * radius * (1 - cos(2 * halfVertexAngle)));
  
  translate(centerX, centerY);  // Move the origin of axes to the center of the screen.
  rotate (-halfVertexAngle);    // Rotate the axes to find the first vertex without
                                // dealing with trigonometry.
  translate(0, radius);
  rotate(halfVertexAngle);      // Rotate back to make the first side horizontal.
  
  for (int i=0; i<n; i ++){
    line(0,0,-side,0);
    translate(-side,0);
    rotate(rotateAngle);
  }
}

The Android Module

I found that in my Linux system Android files are created in the ‘/tmp’ directory. Processing Android mode generates an extension of the class PApplet from your pde files. The good news is that PApplet extends the android Activity class, which makes adding event handlers easy.

Additional functions to a class extending PApplet should be written in a separate pde file. Following is the extension’s code:

import android.os.Bundle;
import android.widget.Toast;
import android.view.Menu;
import android.view.MenuItem;
import android.app.AlertDialog;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.content.DialogInterface;

AlertDialog.Builder alertDialog=null;

public void alert(String text){
  Toast toast=Toast.makeText(getApplicationContext(), text, Toast.LENGTH_LONG);
  toast.show();
}

public void onCreate(Bundle savedInstanceState){
  super.onCreate(savedInstanceState);
  
}

 public boolean onCreateOptionsMenu (Menu menu){
   super.onCreateOptionsMenu (menu);
   menu.add(Menu.NONE, 37, Menu.NONE, "Change Number of sides");
   return true;
 }
 
 private void createAlertDialog(){
     // Create a dialog to change the number of polygon sides.
  alertDialog = new AlertDialog.Builder(this);
  alertDialog.setTitle("Polygon Settings");
  alertDialog.setMessage("Enter number of polygon sides:");
  final EditText input = new EditText(polygon.this);
  LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                                    LinearLayout.LayoutParams.MATCH_PARENT,
                                    LinearLayout.LayoutParams.MATCH_PARENT);
  input.setLayoutParams(lp);
  alertDialog.setView(input);
  alertDialog.setPositiveButton("Accept",
    new DialogInterface.OnClickListener(){
      public void onClick(DialogInterface dialog, int which){
        int numberParsed;
        try {
          numberParsed = Integer.parseInt(input.getText().toString());
          set_no_of_sides(numberParsed);
        } catch (NumberFormatException ex){
          alert("Not an integer. Try again.");
        }
      }
    });

  alertDialog.setNegativeButton("Cancel",
    new DialogInterface.OnClickListener(){
      public void onClick(DialogInterface dialog, int which){
      }
    });

 }
 
 public boolean onMenuItemSelected(int featureId, MenuItem menuitem){
   super.onMenuItemSelected(featureId, menuitem);
   switch (menuitem.getItemId()){
     case 37:
       createAlertDialog();
       alertDialog.show();
       break;
     default:
       break;
   }
   return true;
 }

Notes:

  • If you have an Android module, but you want to run in another mode, renaming the Android mode’s pde file with a ‘.bak’ extension is recommended.
  • If the Android module is not installed, you can install it from a Processing editor window, by selecting <mode button> -> Add more…

Javascript Functions & Changes to the HTML Page

You can change the appearance of your output HTML page by editing the file <sketch-name>/template/template.html . If the file and directory do not exist yet, you can create them by choosing “Javascript->Start Custom Template”. If they do exist use “Javascript-.Show Custom Template”.

For example, I’ve added a button in the HTML template as follows:

<input type="button" value="Change Number of Sides" onclick="get_no_of_sides(this);" />

In addition, I’ve added an ‘onload’ event to change the size of the canvas after the polygon object is created. This function works fine in my favorite browser, Firefox. Fortunately, from ‘.js’ module one can call ‘size’ to resize the canvas:

    <body onload="new_size(640, 480);">

From this page you can learn about accessing Processing from Javascript.

Following is my javascript code defined in ‘js_functions.js’:

function new_size(w, h){
  var divElement=document.getElementById('content');
  divElement.addEventListener("DOMSubtreeModified", function(event, func){
    if (window.Processing.getInstanceById("polygon")){
      divElement.removeEventListener("DOMSubtreeModified", arguments.callee);
      divElement.style.width = w + "px";
      window.Processing.getInstanceById("polygon").size(w,h);
    }
  });

function get_no_of_sides(obj){

  var k=prompt("How many sides?");
  var num=Number(k);
  if (isNaN(num) || num != parseInt(k)){
    alert("Please type an integer");
    return;
  }

  this.Processing.getInstanceById('polygon').set_no_of_sides(num);
}

Now, when you run your application, the product will be found in ‘<sketch-name>/web-export’.
Learn more about Javascript modules here.

The Processing Language…

Processing is a programming language used mainly for animations and graphical applications. If your program is written in pure Processing, the same code can be run in many tools that use their own language: Windows with Java applets, HTML pages with Javascript, Android applications, etc. Running an application  is done by choosing a mode from the Processing GUI tool.

Here’s a screen shot of a Processing Window containing a program to draw a regular polygon:

processingGUI
Drawing a Polygon

Now, to run the program , push the “Play” button, or choose Sketch->Run, and you’ll see the result:

regularPolygon
The polygon in a Java applet.

Now, to run this on Android, I commented out the call to the function ‘size’, following is the result on in an emulator window:

polygonInAndroid
Polygon in Android

You can add to your programs command specific to the mode in which you run. For example you can  add an ‘alert’ command in Javascript mode. Better do it in separate files.

You can download Processing here.

To learn more about pure Processing function go to the function reference.