Sketches in multiple windows

The multisketch package aims to make it as simple as possible to create multiple Processing sketches each sitting in their own window. Existing sketches can be placed in their own windows with minimal change of code. Sketches in separate windows can communicate with each other with minimal use of system resources when windows are not active or visible.

 

Creating multiple sketches in Processing

To create multiple sketches you will then need to import the multisketch package with the line

 
import org.gicentre.utils.multisketch.*;
 

For full details of the methods available see the multisketch API reference. See also, the MultiSketchExample supplied in the examples folder of the giCentre utilities library.

To create a sketch in its own window, simply place the normal Processing code inside a class that extends EmbeddedSketch. The only other change required is that the first line of the draw() method contains the line super.draw(). This ensures that the animation loop of the sketch is not active when the window is minimised or hidden.

As an example, consider the following sketch to draw some moving text:

Embedded sketch in its own window

// Simple embedded sketch that can be placed in its own window. 
// Version 1.3, 5th November, 2013. 

class AnotherSketch extends EmbeddedSketch 
{ 
  float textScale;  

  // Initialises the sketch ready to display some animated text.
  void setup() 
  {
    size(300, 300);
    textFont(createFont("SansSerif", 24), 24);
    textAlign(CENTER, CENTER);
    fill(20, 120, 20);
    textScale = 0;
  }

  // Displays some text and animates a change in size.
  void draw() 
  { 
    super.draw();   // Should be the first line of draw(). 
    background(200, 255, 200); 

    pushMatrix(); 
    translate(width/2, height/2); 
    scale(0.1+sin(textScale), 1); 
    text("Hello again", 0, 0); 
    popMatrix(); 

    textScale += 0.02;
  }
}
 

The only differences between this code and a normal Processing sketch are in the highlighted lines which wrap the sketch inside a class and ensure drawing does not happen if window is not active.

To run the embedded sketch, it needs to be passed to a PopupWindow object inside a normal Processing sketch. For example the following when combined with the one above will create two separate animated sketches. The main sketch below links to the embedded sketch with the two highlighted lines:

Main sketch window

import org.gicentre.utils.multisketch.*;  

// Simple example to put two sketches in separate windows. 
// Version 1.3, 5th November, 2013. 

float rotationAngle; 

// Sets up this sketch and adds another sketch in a separate window.  
void setup() 
{ 
  size(300, 300); 
  textFont(createFont("Serif", 32), 32); 
  textAlign(CENTER, CENTER); 
  fill(120, 20, 20); 
  rotationAngle = 0; 

  PopupWindow win = new PopupWindow(this, new AnotherSketch()); 
  win.setVisible(true);
} 

// Displays some text and animates its rotation.  
void draw() 
{ 
  background(255, 200, 200); 

  pushMatrix(); 
  translate(width/2, height/2); 
  rotate(rotationAngle); 
  text("Hello world", 0, 0); 
  popMatrix(); 

  rotationAngle += 0.01;
} 

Embedding sketches in a single window 

If you wish to have more than one sketch in the same window, you can create a SketchPanel instead of a PopupWindow. These panels can be laid out inside your sketch as you might any other Java component.

Note that if you do this, you will probably want to set noLoop() in your main Processing sketch since it does nothing other than hold the embedded sketches. By default, sketches embedded in a SketchPanel are 'inactive' meaning they will not animate. To start the sketches animating (if you need to), call setIsActive(true). The following provides a simple example laying out two embedded sketches in a single window. This assumes you have created the two separate sketches ASketch and AnotherSketch elsewhere, each extending the class EmbeddedSketch.

Two sketches embedded in a single window

import org.gicentre.utils.multisketch.*;  
import java.awt.*;              // For panel layout.
 
// Simple example to embed two sketches in a single window. 
// Version 1.3, 5th November, 2013. 

void setup() 
{ 
  size(1200,600); 
  setLayout(new GridLayout(0,2)); 
  noLoop(); 
     
  ASketch       sketch1 = new ASketch(); 
  AnotherSketch sketch2 = new AnotherSketch(); 
   
  SketchPanel sp1 = new SketchPanel(this,sketch1); 
  add(sp1); 
  sketch1.setIsActive(true); 
   
  SketchPanel sp2 = new SketchPanel(this,sketch2); 
  add(sp2); 
  sketch2.setIsActive(true); 
}

Linked windows

Because any embedded sketches are created inside a parent sketch, getting multiple sketches to communicate with each other is easy. Simply create methods in the embedded sketch that send or receive any messages that need to be passed between sketches. Additionally, built-in variables associated with a sketch such as width and height may be addressed directly by prefixing them with the name of the EmbeddedSketch object.

The following example moves a line in the left-hand sketch in response to mouse movements, and then draws a continuation of that line in the second sketch:

Two sketches, two windows and a line to join them.

import org.gicentre.utils.multisketch.*;  

// Example to show how sketches in separate windows can communicate. 
// Version 1.2, 5th November 2013. 

RightSketch rightSketch;    // The embedded sketch. 

// Creates a separate sketch in its own window.
void setup() 
{ 
  size(300, 300); 
  stroke(120, 20, 20); 
  strokeWeight(4); 

  rightSketch = new RightSketch(); 
  PopupWindow win = new PopupWindow(this, rightSketch); 
  win.setVisible(true);
} 

// Draws line from current mouse position towards the centre  
// of the other sketch. 
void draw() 
{ 
  background(255, 200, 200); 

  float xOffset = width-mouseX + rightSketch.width/2.0; 
  float yOffset = rightSketch.height/2-mouseY; 
  line(mouseX, mouseY, mouseX+xOffset, mouseY+yOffset); 
  float angle = atan(yOffset/xOffset); 

  // Tell the other sketch to draw a line from one edge to its centre.  
  rightSketch.setLine(0, mouseY+(width-mouseX)*tan(angle));
}
// Simple embedded sketch that draws a line to the centre of the window 
// Version 1.2, 5th November, 2013. 

public class RightSketch extends EmbeddedSketch 
{   
  float x1, y1;    // Start point of the line. 

  void setup() 
  { 
    size(300, 300); 
    stroke(20, 120, 20); 
    strokeWeight(2);
  } 

  // Draws line from current start point to centre of the sketch. 
  void draw() 
  { 
    super.draw();   // Should be the first line of draw(). 
    background(200, 255, 200); 
    line(x1, y1, width/2, height/2);
  } 

  // Sets the start point of the line. 
  void setLine(float x1, float y1) 
  { 
    this.x1 = x1; 
    this.y1 = y1;
  }
}
 

Loading files with Processing methods

While most sketches require little modification to be embedded in their own window, there are a few exceptions that need to be dealt with differently.

Processing methods that involve loading files from the default ./data folder will not work in an embedded sketch by default (they don't look in the correct folder). These include:

  • loadShape()
  • loadBytes()
  • loadStrings()
  • loadImage()
  • loadPixels()
  • loadFont()

To use these methods, the embedded sketch needs to be told to look in the same folder as the main sketch. To do this, call the setParentSketch() method somewhere in your main sketch. For example if you have created an embedded sketch called embeddedSketch, you would call the following in your main sketch

 
embeddedSketch.setParentSketch(this);

Calling Processing methods inside external classes

 If your sketch makes use of external classes (i.e. classes defined in their own tab window outside of any sketch), calling processing methods from within them can lead to ambiguous references to the sketch to which they apply. This is particularly the case for methods that affect the state of a sketch, such as pushMatrix(), popMatrix(), scale(), translate(), rotate(), fontSize() etc. Note this is not a problem if these methods are called from within an embedded sketch (such as in the first example above).

To overcome the problem, the sketch to which the methods are to be applied should be passed to the class that wishes to use them. This can be thought of as providing the context to the class that needs to operate within a particular sketch.