Simple Complex Math Class for Processing

Previously I presented some Newton Fractal ruby-processing sketches. Here is an example using vanilla processing:-

int imgx = 512;
int imgy = 512;
JComplex z;
JComplex COMPLEX = new JComplex(1.0E-6, 1.0E-6);
PImage img;
//Drawing area
float xa = -4.0;
float xb = 4.0;
float ya = -4.0;
float yb = 4.0;

int maxIt = 20; // max iterations allowed
float EPS = 1.0E-3; // max error allowed

JComplex func(JComplex z) {
  return z.mul(z.pow(3.0).div(6.0).sub(1.0));
}

void settings() {
  size(imgx, imgy);
}

void setup() {
  img = createImage(imgx, imgy, RGB);
  img.loadPixels();
  for (int y = 0; y < imgy; y++) {
    float zy = y*(yb - ya)/(imgy - 1) + ya;
    for (int x = 0; x < imgx; x++) {
      float zx = x*(xb - xa)/(imgx - 1) + xa;
      z = new JComplex(zx, zy);
      for (int i = 0; i < maxIt; i++) {
        // Complex numerical derivative
        JComplex dz = func(z.add(COMPLEX)).sub(func(z)).div(COMPLEX);
        JComplex z0 =  z.sub(func(z).div(dz));
        if (z0.sub(z).abs2() < EPS * EPS) {
          break;
        }
        z = z0;
        img.pixels[x + y * img.width] = color(i%5*64, i%17*16, i%9*32);
      }
    }
  }
  noLoop();
}

void draw() {
  image(img, 0, 0, img.width, img.height);
}

Here is the java class (just put it the processing ide)

import java.util.Objects;

// Code includes some ideas from 
// commons.apache.org/proper/commons-math/userguide/complex.html
public final class JComplex {

    private final double re;   // the real part
    private final double im;   // the imaginary part
    private final static JComplex ZERO = new JComplex(0, 0);
    private final static JComplex NAN = new JComplex(Double.NaN, Double.NaN);

    /**
     * create a new object with the given real and imaginary parts
     *
     * @param real
     * @param imag
     */
    public JComplex(double real, double imag) {
        re = real;
        im = imag;
    }

    /**
     * @return a string representation of the invoking Complex object
     */
    @Override
    public String toString() {
        return "JComplex(" + re + ", " + im + "i)";
    }

    /**
     *
     * @return abs/modulus/magnitude
     */
    public final double abs() {
        return Math.hypot(re, im);
    }

    /**
     *
     * @return square of abs/modulus/magnitude
     */
    public final double abs2() {
        return re * re + im * im;
    }

    /**
     *
     * @return angle/phase/argument, normalized to be between -pi and pi
     */
    public final double phase() {
        return Math.atan2(im, re);
    }

    /**
     *
     * @param b
     * @return a new Complex object whose value is (this + b)
     */
    public final JComplex add(JComplex b) {
        JComplex a = this;             // invoking object
        double real = a.re + b.re;
        double imag = a.im + b.im;
        return new JComplex(real, imag);
    }

    /**
     *
     * @param scalar
     * @return a new Complex object whose value is (this + scalar)
     */
    public final JComplex add(double scalar) {
        return new JComplex(re + scalar, im);
    }

    public final boolean zero() {
        return this.equals(ZERO);
    }

    /**
     *
     * @param b
     * @return a new Complex object whose value is (this - b)
     */
    public final JComplex sub(JComplex b) {
        JComplex a = this;
        double real = a.re - b.re;
        double imag = a.im - b.im;
        return new JComplex(real, imag);
    }

    /**
     *
     * @param scalar
     * @return a new Complex object whose value is (this - scalar)
     */
    public final JComplex sub(double scalar) {
        return new JComplex(re - scalar, im);
    }

    /**
     *
     * @param b
     * @return a new Complex object whose value is (this * b)
     */
    public final JComplex mul(JComplex b) {
        JComplex a = this;
        double real = a.re * b.re - a.im * b.im;
        double imag = a.re * b.im + a.im * b.re;
        return new JComplex(real, imag);
    }

    /**
     * Also known as scale
     *
     * @param b
     * @return a new Complex object whose value is (this * b)
     */
    public final JComplex mul(double b) {
        return new JComplex(re * b, im * b);
    }

    /**
     *
     * @return a new Complex object whose value is the conjugate of this
     */
    public final JComplex conjugate() {
        return new JComplex(re, -im);
    }

    /**
     *
     * @return a new Complex object whose value is the reciprocal of this
     */
    private JComplex reciprocal() {
        double scale = re * re + im * im; // self dot product
        return new JComplex(re / scale, -im / scale);
    }

    /**
     *
     * @param other
     * @return this^other
     */
    public final JComplex pow(JComplex other) {
        if (this.zero()) {
            if (other.zero()) {
                return ZERO;
            }
            return NAN;
        }
        return (this).log().mul(other).exp();
    }

    /**
     *
     * @param scalar
     * @return this^scalar
     */
    public final JComplex pow(double scalar) {
        if (this.zero()) {
            if (scalar == 0) {
                return ZERO;
            }
            return NAN;
        }
        return (this).log().mul(scalar).exp();
    }

    /**
     *
     * @return log
     */
    private JComplex log() {
        return new JComplex(Math.log(abs()), Math.atan2(im, re));
    }

    /**
     *
     * @return real part
     */
    public final double re() {
        return re;
    }

    /**
     *
     * @return imaginary part
     */
    public final double im() {
        return im;
    }

    /**
     *
     * @param b
     * @return a / b
     */
    public final JComplex div(JComplex b) {
        JComplex a = this;
        return a.mul(b.reciprocal());
    }

    /**
     *
     * @param b
     * @return a / b
     */
    public final JComplex div(double b) {
        if (b == 0) {
            return NAN;
        }
        return new JComplex(re / b, im / b);
    }

    /**
     *
     * @return a new Complex object whose value is the complex exponential of
     * this
     */
    public final JComplex exp() {
        return new JComplex(Math.exp(re) * Math.cos(im), Math.exp(re) * Math.sin(im));
    }

    @Override
    public final int hashCode() {
        return Objects.hash(re, im);
    }

    @Override
    public final boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final JComplex other = (JComplex) obj;
        if (Double.doubleToLongBits(this.re) != Double.doubleToLongBits(other.re)) {
            return false;
        }
        return Double.doubleToLongBits(this.im) == Double.doubleToLongBits(other.im);
    }
}

4 Likes