Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save adamjmurray/621594 to your computer and use it in GitHub Desktop.
Save adamjmurray/621594 to your computer and use it in GitHub Desktop.
Mobius transformation in Processing: Ruby vs Java

As you might expect, the Ruby version is much cleaner but also much slower. Not suitable for real time work, but I'd certainly prefer using Ruby's Complex objects with inline arithmetic for any offline rendering. We could speed up the Ruby version by not using Complex objects, but that's kind of the whole point of using Ruby for this sort of thing...

Ruby Version

load_libraries 'opengl'
include_package "processing.opengl"
require 'complex'
I = Complex::I

SIZE = 101
MIN = -SIZE/2
MAX = SIZE/2

COLORS = []

def setup
  size SIZE,SIZE
  colorMode HSB,SIZE    
  for x in 0...SIZE
    COLORS << []
    for y in 0...SIZE
      COLORS[x] << color( x, 2*y, 2*(SIZE-y) )  # hue, saturation, brightness
    end
  end
end

def draw
  translate MAX,MAX
  
  a = 1
  b = 0
  c = mouseY.to_f/SIZE/50
  d = 1 + mouseX.to_f/SIZE*10 * I
  
  for real in MIN...MAX
    for imag in MIN...MAX
      z = real + imag*I      
      stroke color_at( (z*d - b)/(a - z*c) ) # inverse Mobius transform
      point real,imag
    end
  end
end

def color_at(z)
  COLORS[ (z.real-MIN)%SIZE ][ (z.imag-MIN)%SIZE ]
end

Java Version

import processing.opengl.*;

float[] A = new float[]{ 1, 0 };
float[] B = new float[]{ 0, 0 };
float[] C = new float[]{ 0, 0 };
float[] D = new float[]{ 1, 0 };

int SIZE = 251;
int MIN = -SIZE/2;
int MAX = SIZE/2;

color[][] VALS = new color[SIZE][SIZE];
float[] z   = new float[2];
float[] tmp = new float[2];

void setup() {
  size(SIZE, SIZE);
  colorMode(HSB, SIZE);
  
  for (int x=0; x<SIZE; x++) {
    for(int y=0; y<SIZE; y++) {
      VALS[x][y] = color(x, 2*y, 2*(SIZE-y));
    }
  }    
}

void draw() {
  translate(MAX,MAX);
  
  D[1] = (float)mouseX/SIZE*10;
  C[0] = (float)mouseY/SIZE/100;
  
  for (int real = MIN; real < MAX; real++) {
    for (int imag = MIN; imag < MAX; imag++) {
      z[0] = real;
      z[1] = imag;
      // Calculate (z*D - B)/(A - z*C)
      tmp[0] = -real;
      tmp[1] = -imag;
      cmul(tmp,C);
      tmp[0] += A[0]; tmp[1] += A[1]; // add tmp + A, inlined cadd(tmp,A);
      cmul(z,D);
      z[0] -= B[0]; z[1] -= B[1]; // subtract z - B, inlined csub(z,B);
      cdiv(z,tmp);

      stroke(value(z));
      point(real,imag);
    }
  }
}
  
color value(float[] z) {
  return value((int)z[0], (int)z[1]);
}

color value(int real, int imag) {
  int rIdx = (real-MIN) % SIZE;
  if(rIdx < 0) rIdx *= -1;
  int iIdx = (imag-MIN) % SIZE;
  if(iIdx < 0) iIdx *= -1;
  return VALS[rIdx][iIdx];
}


/*
Utilities for maniuplating a complex number represented as a two-element float array.
All operations are in-place for efficiency.
The first argument will be modified in place.

All equations can be found at http://en.wikipedia.org/wiki/Complex_number
*/

// Adds 2 complex numbers in place
void cadd(float[] z, float[] w) {
  z[0] += w[0];
  z[1] += w[1];
}

// Subtracts 2 complex numbers in place
void csub(float[] z, float[] w) {
  z[0] -= w[0];
  z[1] -= w[1];
}

// Multiplies 2 complex numbers in place
void cmul(float[] z, float[] w) {
  float real = z[0]*w[0] - z[1]*w[1];
  float imag = z[1]*w[0] + z[0]*w[1];
  z[0] = real;
  z[1] = imag;
}

// Divides 2 complex numbers in place
void cdiv(float[] z, float[] w) {
  float denominator = w[0]*w[0] + w[1]*w[1];
  float real = (z[0]*w[0] + z[1]*w[1])/denominator;
  float imag = (z[1]*w[0] - z[0]*w[1])/denominator;
  z[0] = real;
  z[1] = imag;
}

String ctos(float[] z) {
  return z[0] + "+" + z[1]+"i";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment