Archive | 2011

Spice picker

6 Dec

I made this sketch to demonstrate how to identify complicated shapes with the mouse. It’s easy enough to figure out what the mouse has clicked on if you are clicking on rectangles or ellipses, but in this image we have 5 bodies which are difficult to define using simple geometry.

The trick is to use a second image which uses a solid color for each shape. This image, called spice_girls_map.png, is shown below. It is the same size as the original image.

I prepared this second image in photoshop, although you could use Gimp (which is free) or any paint software available to you. In this image, black is used for the background, and then successively lighter shades of gray are used for each spice girl. I chose each shade to be have a RGB values 32 higher than the previous shade, like so:

Background 0,0,0
Scary Spice 32,32,32
Baby Spice 64,64,64
Sporty Spice 96,96,96
and so on.

Also, I was very careful not to use anti-aliased edges on these shapes. You just want the pure colors, otherwise you might get selection errors when you click on the edges of shapes.

This technique allows 7 objects to be detected. Fortunately, there are only 5 spice girls. If I had more objects to deal with, I would have placed the colors closer together. The reason I spread the colors apart is so I can easily differentiate them by eye. Using this technique, you can pick out 255 different shapes using shades of gray. Using multiple color components, you can pick out many more.

In the sketch, I load both images.

  void setup()
  {
    size(475, 578);
    colorImage = loadImage("/assets/spice_girls.jpg");
    mapImage = loadImage("/assets/spice_girls_map.png");
  }

In the draw routine, I render the color image, and draw the text of the last clicked-on selection.

  void draw()
  {
    image(colorImage, 0,0);
    fill(255);
    text(lastSelection, 20, 20);
  }

The magic happens in the mouseClicked routine. First I get the mouse coordinates.

  int px = constrain(mouseX,0,width);
  int py = constrain(mouseY,0,height);

I use constrain to make sure the point is onscreen, otherwise, when I sample the pixel from the map, I may generate an invalid array index.

  int v = mapImage.pixels[py*mapImage.width + px];

At this point, assuming I clicked on Scary Spice, I have an integer which represents the color I clicked on (FF202020 in hex). I am only interested in the red component, so I extract that, and then divide by 32 to get a number which goes 0,1,2,3 etc…

  v = red(v) / 32;

Old time hacker note: When I first wrote this sketch, I wrote that line like so (which does the same thing):

  v = (v & 0xFF) >> 5;

Old habits die hard, I guess.

I use constrain to insure the value is in the range I’m looking for. This step is probably unnecessary, but there’s always a chance I was sloppy when preparing the map image.

  v = (int) constrain(v, 0, spiceVarieties.length-1);

spiceVarieties is a global array with labels for each of the spices I am selecting.

  String[] spiceVarieties = {"No","Scary","Baby","Sporty","Ginger","Posh"};

I can now use v to index into it.

  lastSelection = spiceVarieties[v] + " Spice";