How to Enable Mouse Wheel Support in controlP5 on Processing 2.2

I’m finally using controlP5 in earnest, and I noticed that two-finger trackpad scrolling over a knob didn’t dial the knob, like I’d hoped it would. (Thanks to Ben Oakes, again, for showing me – just yesterday afternoon – that Ubuntu’s volume icon supports this. That was the trigger that got me wondering about this.)

It turns out controlP5 does let you turn knobs with the mouse wheel, but, for some reason, it’s disabled by default. There’s an example sketch, “ControlP5mouseWheel”, that shows how to enable it, but it doesn’t work for me with Processing 2.2. The problematic part is the addMouseWheelListener() callback that mucks with java.awt.event stuff.

Here’s a Processing 2.2-style mouseWheel callback event that gets us back to turning knobs by scrolling:

// When working in desktop mode, you can add mousewheel 
// support for controlP5 by using Processing 2.2's built-in 
// support:
void mouseWheel(MouseEvent e) {
  // Processing's `getCount()` returns 1 when the mouseWheel 
  // is rotated down, and -1 when the mouseWheel is rotated 
  // up. Natural scrolling reverses this, so you need to 
  // negate the count value. Try removing the negation, if 
  // the scrolling is backwards for you.
  cp5.setMouseWheelRotation(-e.getCount());
}

While I was at it, I noticed that controlP5 is still hosted on the retired google-code platform, so I exported it to my github account. I expect Andreas will export it to his own soon enough, and I can fork his version then, and merge in my update to the ControlP5mouseWheel example sketch.

Advertisements

Circle Pictures Redux

Back in December, I posted some images I’d made by dropping non-overlapping circles on an image, larger or smaller based on the brightness of the underlying pixel at that spot. I called them Circle Pictures.

At the time, I was happiest with the black-and-white images; I liked the color ones less. I think I’ve figured out how to make the color images work better; a lot of it comes down to picking the right kind of image to start with. (High-contrast images of recognizable objects works well, especially isolated images.) I also used a broader range of circle sizes, added borders, and played with how the circle size is decided: based on brightness, or saturation, or the amount of blue…any data in the pixel is fair game. The code is basically the same, so I won’t bother re-posting it.

Here are some of the results, including a repeat of the sunflower images. I’ll be showing prints of some of these, and some others like them, at City Wide Open Studios at the Armory space, October 11 and 12, if you’d like to see them up-close.

Here are the source images:

Image Credits

The tree is my least-favorite outcome, but the source image is from John Buchanan’s poetry site. The gorilla source image is from what looks like a Rwanda tourism site. The sea turtle source image is by digivangelist. The turtle with fish source image is from wallpapersak.com. The yellow striped fish source image is by FarzinPhoto. The sea horse source image is from this National Geographic article.

 

Two Copyright-Free Photo Collections

I’m always looking for interesting photos to hack on, so I was glad to find these two sources of free images, both via Vormplus.

New Old Stock: “vintage photos from the public archives free of known copyright restrictions.” Old photos with the fuzzy haze you’d expect. Some have no indication of age besides the color fading, but others have elements that clearly mark them as old.

Unsplash: “Free (do whatever you want) hi-resolution photos.  10 new photos every 10 days.” You can subscribe via email, too.

Circle Pictures

Circle Pictures

Take an image.

Pick a random pixel. Over a plain background of the same size as the image, draw a circle over that spot: bigger if it’s lighter, smaller if it’s darker. (Or reverse it: big for dark, small for light.) Repeat, but don’t overlap the circles. After a while, you’ll run out of empty places for new circles, so stop.

overlapping circles

I screwed up the math for checking circle overlaps. At first, I was thinking: pack the circles as tight as you can, so only call it an overlap when the distance between the circle centers is less than the sum of their radii.

But then, from another part of my brain, I drew the circles at half-size, forgetting that Processing’s ellipse method takes width and height parameters, not a radius. So the circles were packed less densely, and I wasn’t clever enough to see why. I hacked it, and decided to count it as an overlap when the distance is bigger than the radius of the bigger circle. (Shrug. Hack.) It worked. And it looked cool.

Eventually I figured out my bug, and fixed it, but then the circles were too dense, so I went back to the happy accident.

I also tried color images. I think there’s potential here, but I like these less.

The source images are images of Paul Erdős, Jorge Luis Borges (twice), Henri de Toulouse-Lautrec, two sunflowers, and a cow.

Here’s the source. (I tried putting it on openprocessing.org, which I normally really like, but I had troubles with the images in JavaScript mode, and they no longer support Java mode.)

PImage image;
boolean dark = true;

ArrayList circles;

// Trailing average. When the average number of tries 
// to place a circle is too high,
// stop trying.
Averager averager;

String paul = "erdos.jpg";
String jorge1 = "borges1.jpg";
String jorge2 = "borges2.jpg";
String henri = "lautrec.jpg";
String sunflower = "sunflower.jpg";
String sunflower2 = "sunflower2.jpg";
String cow = "cow.png";

void setup() {
  image = loadImage(paul);
  image.resize(600, 0);
  size(image.width, image.height);

  ellipseMode(CENTER);
  noStroke();
  smooth();

  reset();
}

void reset() {
  circles = new ArrayList();
  averager = new Averager(20);
  background(dark ? 0 : 255);
}

void draw() {
  for (int i = 0; i < 10; i++) {
    drawRandomCircle();
    if (averager.average() > 100) {
      //save("7.dark.png");
      reset();
      break;
    }
  }
}

void drawRandomCircle() {
  //println(averager.average());
  Circle circ;
  int tries = 0;
  do {
    int x = floor(random(width));
    int y = floor(random(height));

    color c = image.get(x, y);
    float val = brightness(c);

    tries++;
    float circleSize = dark ? 
         map(val, 255, 0, 1, 60) : 
         map(val, 0, 255, 1, 60);
    circ = new Circle(x, y, c, circleSize);
  }
  while (overlaps (circ));

  averager.record(tries);

  circles.add(circ);
  circ.draw();
}

boolean overlaps(Circle c) {
  for (Circle other : circles) {
    if (c.overlaps(other)) {
      return true;
    }
  }
  return false;
}

class Circle {
  int x;
  int y;
  color c;
  float diameter;

  Circle(int x, int y, color c, float diameter) {
    this.x = x;
    this.y = y;
    this.c = c;
    this.diameter = diameter;
  }

  boolean overlaps(Circle other) {
    return dist(x, y, other.x, other.y) < max(diameter, other.diameter);
  }

  void draw() {
    fill(c);
    ellipse(x, y, diameter, diameter);
  }
}

class Averager {
  float[] values;
  int index = 0;
  Averager(int length) {
    values = new float[length];
  }

  void record(float value) {
    values[index] = value;
    index = (index + 1) % values.length;
  }

  float average() {
    float sum = 0;
    for (float val : values) {
      sum += val;
    }
    return sum / values.length;
  }
}

Cantor’s Snowflake

The Koch snowflake is a famous fractal.

The Koch Snowflake fractal

So is the Cantor set.

The Cantor set

Less famous, maybe, is Cantor dust, a version of the Cantor set made with squares instead of lines, which apparently earned it a much cooler name.

But as far as I know, we have no Cantor snowflake.

Since it’s Christmas, and since, in the odd quiet moments between holiday noise, Daniel Shiffman’s Nature of Code has been keeping me company, I wondered if we could make a Cantor snowflake.

Here’s what I came up with.

cantor-snowflake

As a bonus, it contains the Koch snowflake inside of it! I didn’t expect that.

I also rendered a Cantor snowflake PDF, which has a couple extra generations. It could make a nice bookmark.

Here’s the sourcecode, which is also running on openprocessing:

void setup() {
  size(1450, 300);

  background(255);
  noStroke();
  fill(0);

  cantorSnowflake(0, height/2, 140, 280);
}

void cantorSnowflake(float x, float y, float length, float sideStep) {
  if (length < 0.1) return;

  pushMatrix();

  hexagon(x, y, length);

  translate(sideStep, 0);

  for (int i = 0; i < 6; i++) {
    PVector point = vector(i * THIRD_PI, length * 2 / 3);
    cantorSnowflake(point.x, point.y, length / 3, sideStep);
  }

  popMatrix();
}

void hexagon(float centerX, float centerY, float length) {
  translate(centerX, centerY);

  beginShape();
  for (int i = 0; i < 6; i++) {
    hexPoint(vector(i * THIRD_PI, length));
  }
  endShape(CLOSE);
}

void hexPoint(PVector v) {
  vertex(v.x, v.y);
}

PVector vector(float rads, float length) {
  return new PVector(cos(rads) * length, sin(rads) * length);
}

Happy Christmas!

Symmetrical Portraits, Undone

Julian Wolkenstein’s Symmetrical Portraits project just made the rounds. I could’ve sworn I saw it on Brain Pickings, but I can’t find it now. Whatever, no matter.

It’s a weird-looking project: take a bunch of head-shots, cut them down the middle, and mirror each half, so one asymmetrical face becomes two symmetrical faces. It’s startling how much some of the pairs differ from each other. There’s a hypothesis that symmetry makes the people more attractive, but some of them are pretty uncanny:

So what’s a Processing goof-off going to do? Tear them apart, and put them back together. I don’t know whether the asymmetrical version is right, or whether it’s backwards, but I don’t think it really matters, unless you know the person in the photo. Click ’em for big versions.












Here’s the code I used to de-symmetry them. Note the mouse controls: I had to tweak some of them, especially that second one of the blond short-haired guy.

// 36_Wolkenstein_12.jpg
String[] files = new String[] {
  "01_v2", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"
};
PImage[] origImgs;

int imgIndex = 0;

void setup() {
  PImage img = load(files[0]);
  size(ceil(img.width * 1.5), img.height);

  origImgs = new PImage[files.length];

  for (int i = 0; i < files.length; i++) {
    origImgs[i] = load(files[i]);
  }
}

void draw() {
  PImage orig = origImgs[imgIndex];
  image(orig, 0, 0);

  int placeLine = round(orig.width * 1.25);
  int cropLine = round(orig.width * 0.75);

  int placeOffset = round(map(mouseX, 0, width, -20, 20));
  int cropOffset = round(map(mouseY, 0, height, -20, 20));

  image(
    orig.get(0, 0, round(orig.width * 0.5), orig.height),
    orig.width, 0);

  image(
    orig.get(
      cropLine + cropOffset,
      0, round(orig.width * 0.25), orig.height
    ),
    placeLine + placeOffset, 0);
}

void keyPressed() {
  if (key == ENTER) {
    save("fixed_" + files[imgIndex] + ".jpg");
  }
  imgIndex = (imgIndex + 1) % files.length;
}

PImage load(String chunk) {
  return loadImage("36_Wolkenstein_" + chunk + ".jpg");
}