There are many ways to write code that uses Trackmate data (via LusidOSC) to create rich interactions with your computer. While LusidOSC supports many programming languages (Java, MaxMSP, and any language with networking support via UDP or TCP), this section will focus on using Processing as a simple way to get started writing your own spatial applications with graphical, audio, and network support.

This section assumes that you have already installed and setup:

1. Understanding how LusidOSC works


Before you start coding, it's important to understands the basics of how data is sent from Trackmate to your application.

Instead of hard-coding specific function calls into the Tracker's code to interact with an application, data is simply streamed over a network connection (which can be on the same machine, or across the internet to somewhere remote) to any application listening for the the incoming information. All data, such as object position, rotation, ID, type, color, etc. is wrapped in a protocol (an agreed upon ordering and structure for the data), LusidOSC.

LusidOSC connects spatial interfaces with user-level applications via a simple, extendable protocol over a local or remote socket connection. With the help of a library, a few lines of code can be used to identify objects, examine relationships between them, and build up interesting interaction techniques. The details of the protocol (which will not be discussed here) can be found in the LusidOSC Specification v1.0 document.

Make sure to check out all of the examples in the LusidOSC Processing Bundle (and the application explanations/screenshots here) for help with how to explore the wide range of possibilities using processing.

Okay, now let's dive into the code!

2. Using the LusidOSC Processing library


In Processing, received LusidOSC data can be easily accessed and manipulated via a library using the following steps: The resulting code should look like this:
// we need to import the LusidOSC library and declare a LusidClient variable
import lusidOSC.*;
LusidClient lusidClient;

// setup: this gets called once when the application starts.
void setup() {
  // setup the processing display window size and type
  size(320,240);

  // Create an instance of the LusidClient. The LusidClient expects
  // an implementation of the 3 LusidOSC callback methods (see below).
  lusidClient = new LusidClient(this);
}

// -------------------------------------------------------------------
// these methods are called whenever a LusidOSC event occurs.
// -------------------------------------------------------------------
// called when an object is added to the scene
void addLusidObject(LusidObject lObj) {
}
// called when an object is removed from the scene
void removeLusidObject(LusidObject lObj) {
}
// called when an object is moved/updated
void updateLusidObject (LusidObject lObj) {
}

3. Ways to get Trackate tag data in Processing


There are two basic ways to get LusidOSC object data via the Processing library: push and pull.

Push
One way to use incoming object data is to be pushed, or notified via a callback, when information is received. This strategy is convenient when you are waiting for particular information; instead of constantly checking for new data (see the Pull method below), your application will remain idle until LusidOSC data is received. However, pushing is not always better as your application may get flooded if, for example, you perform an action on each tag and 50 new tags are added all at once - It really depends on what you want to do with the data.

The callbacks are automatically called each time appropriate data is received. To use them, simply add code to the appropriate methods.
// -------------------------------------------------------------------
// these methods are called whenever a LusidOSC event occurs.
// -------------------------------------------------------------------

// called when an object is added to the scene
void addLusidObject(LusidObject lObj) {
  // Print out lots of info when an object is added.
  println("add object: "+lObj.getUniqueID());
  println(" location = ("+lObj.getX()+","+lObj.getY()+","+lObj.getZ()+")");
  println(" rotation = ("+lObj.getRotX()+","+lObj.getRotY()+","+lObj.getRotZ()+")");
  println(" data = ("+lObj.getEncoding()+","+lObj.getData()+")");
}

// called when an object is removed from the scene
void removeLusidObject(LusidObject lObj) {
  // Print out a notification that object has been removed.
  println("remove object: "+lObj.getUniqueID());
}

// called when an object is moved/updated
void updateLusidObject (LusidObject lObj) {
  // Print out the ID of each updated object (lots of them!).
  println("update object: "+lObj.getUniqueID());
}


Pull
Sometimes you don't want to be bothered by large streams of incoming data, and instead, just want to have access to object information when requested. Choosing to pull data (often implemented as polling, or sending requests) gives you the flexibility to see a list of the objects currently present, along with their corresponding properties, and act accordingly.

A simple example is a program that displays dots on the screen corresponding to the location of each object. By requesting data via getLusidObjects() in the draw() method (a special method in Processing that is called every frame; usually 15-30 times a second, but you can set it to whatever you like), a list of the current objects can be accessed and used.
// draw: gets called every frame (~30 frames per second default)
// here, we retrieve an array of LusidObject from the LusidClient and
// then loop over both lists to draw the graphical feedback.
void draw() {
  // clear the background to white.
  background(255, 255, 255);
  // set the fill color
  fill(0, 0, 0);
  // get the list of all objects that are currently present
  LusidObject[] lusidObjectList = lusidClient.getLusidObjects();
  for (int i=0;i     LusidObject lObj = lusidObjectList[i];
    // shift the X and Y so they are centered on the screen.
    int x = width/2 + lObj.getX();
    int y = height/2 - lObj.getY();
    // now draw each object to the screen as a rectangle!
    rect(x, y, 10, 10);
  }
}

Another way to pull is to request a particular object by ID using the getLusidObject(uniqueID) method. Be careful, though, as it may return null if the object is not present.
// set the background to white if our specific object is present,
// otherwise make it black to indicate that we don't see it.
void draw(){
  // try to get the object if it is present...
  LusidObject lObj = lusidClient.getLusidObject("0x08D03B05BA29");
  if(lObj != null){
    println("Object is present! -- x:"+lObj.getX()+", y:"+lObj.getY());
    background(255,255,255);
  }else{
    println("Object was not found.");
    background(0,0,0);
  }
}

4. Quick interaction examples


Here are just a few of the ways you can map object properties to user interaction in an application.

Do something when any object is added
Use the addLusidObject() callback method.
// called when an object is added to the scene
void addLusidObject(LusidObject lObj) {
  // Do something here...
}

Do something when a specific object is added
Use the addLusidObject() callback method, and check for the object's uniqueID.
// called when an object is added to the scene
void addLusidObject(LusidObject lObj) {
  if(lObj.getUniqueID().equals("0x08D03B05BA29")){
    // Do something here...
  }
}

Use object rotation to directly set a variable
Use a specific object's rotation to set a variable directly, such as a volume knob. This example uses the updateLusidObject() callback method to make changes anytime the specified object is moved.
// called when an object is moved/updated
void updateLusidObject(LusidObject lObj) {
  if(lObj.getUniqueID().equals("0x08D03B05BA29")){
    // Now update the desired variable...
    volume = lObj.getRotZ() / TWO_PI;   }
}

Use object rotation to incrementally adjust a variable
Create a knob by mapping an object's rotation to a variable. The variable will be incrementally adjusted, such as a jog wheel or scroller. Be careful to catch the discontinuity (the angle goes between 0 and TWO_PI, and then immediately back to 0), and bound the variable with min/max to keep it in your desired range. This example primarily uses the updateLusidObject() callback method to make changes anytime the specified object is moved, as well as the addLusidObject() method to initialize the rotation.
// The jog wheel variables...
// here, we use an object's rotation to incrementally
// adjust a variable, tempo.
int tempo = 120;
int MIN_TEMPO = 60;
int MAX_TEMPO = 240;
float rotationThreshold = PI/16;
float lastUsedRotation = 0;

// called when an object is added
void addLusidObject(LusidObject lObj) {
  if(lObj.getUniqueID().equals("0x08D03B05BA29")){
    // object just added, set its initial rotation.
    lastUsedRotation = lObj.getRotZ();
  }
}
// called when an object is removed
void removeLusidObject(LusidObject lObj) {
  // don't need to do anything when object is removed.
}
// called when an object is moved/updated
void updateLusidObject (LusidObject lObj) {
  if(lObj.getUniqueID().equals("0x08D03B05BA29")){
    float rotDiff = lObj.getRotZ() - lastUsedRotation;
    if(abs(rotDiff) > PI){
      // handle discontinuity; edge case.
      rotDiff = rotDiff-(abs(rotDiff)/rotDiff)*TWO_PI;
    }
    if(abs(rotDiff) > PI/2){
    // rotation is too much, seems noisy and improbable.
    lastUsedRotation = lObj.getRotZ();
    }
    else{
      if(abs(rotDiff) > rotationThreshold){
        // rotation looks good! do something with it...
        int rotationTicks = (int)(rotDiff / rotationThreshold);
        tempo = max(MIN_TEMPO, min(MAX_TEMPO, tempo + rotationTicks));
        println("Tempo is now: " + tempo);
        lastUsedRotation = lObj.getRotZ();
      }
    }
  }
}

Use the distance between two objects
The distance between two objects can be calculated and used as an additional parameter. This example finds the distance between two specific objects, and then uses that distance to change the background color in the draw() method, called each frame.
void draw() {
  // get the objects, if they exist.
  LusidObject objA = lusidClient.getLusidObject("0x08D03B05BA29");
  LusidObject objB = lusidClient.getLusidObject("0x3F28629CA473");
  if(objA != null && objB != null){
    // now calculate the distance between the objects.
    float d = dist(objA.getX(), objA.getY(), objB.getX(), objB.getY());
    // update the background color using the distance.
    background(d,d,d);
    println("Distance between objects: " + d);
  }
}

More complex forms of interaction...
For more interaction techniques and working examples, take a look at the code in the LusidOSC Processing Bundle. There you will find a set of simple applications that grow in complexity,SimpleApp1-7, as well as six other applications designed to spotlight various functionality. See the bundle screenshot and descriptions for more information.

5. Extensions and other languages


Processing is just one of the many ways you can build application with Trackmate and LusidOSC. Here are some other ways you can program to help you work outside of Processing if you prefer.
Project Web Hosted by
SourceForge.net