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...
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
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";
}