All Downloads are FREE. Search and download functionalities are using the official Maven repository.

prng.image.Marble Maven / Gradle / Ivy

The newest version!
package prng.image;

import java.awt.Color;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.Random;

public class Marble extends BasePainter {

  public interface Op {

    int getHue(double x, double y);
  }



  public static class Comb implements Op {

    Op next;

    double sharpness;

    double shift;

    double spacing;

    double unitX;

    double unitY;


    Comb(Op op, Random rand) {
      double angle = Math.PI * 2 * rand.nextDouble();
      unitX = Math.cos(angle);
      unitY = Math.sin(angle);
      shift = 32 + (96 * rand.nextDouble());
      sharpness = 1 + (24 * rand.nextDouble());
      next = op;
      spacing = 256.0 / (10 * rand.nextDouble());
    }


    @Override
    public int getHue(double x, double y) {
      double dist = Math.abs(((x - 256) * unitX) + ((y - 256) * unitY));
      dist = (spacing / 2) - Math.abs((dist % spacing) - (spacing / 2));
      double scale = (shift * sharpness) / (dist + sharpness);
      double nx = x + (scale * unitY);
      double ny = y - (scale * unitX);
      return next.getHue(nx, ny);
    }
  }



  public static class Ink implements Op {

    double area;

    double centX;

    double centY;

    int hue;

    Op next;


    Ink(Op ink, double scale, Random rand) {
      next = ink;
      centX = rand.nextInt(512);
      centY = rand.nextInt(512);

      double theta = Math.atan2(centX - 256, centY - 256);
      hue = rand.nextInt(128);

      if (ink != null) {
        while (hue == ink.getHue(centX, centY)) {
          hue = rand.nextInt(128);
        }
      }

      area = 0;
      while (area < scale) {
        area += 4 * scale * (1 + (2 * rand.nextGaussian()));
      }
    }


    @Override
    public int getHue(double x, double y) {
      double dx = x - centX;
      double dy = y - centY;
      double dist2 = (dx * dx) + (dy * dy);

      if (dist2 <= area) {
        return hue;
      }
      if (next == null) {
        return 128;
      }

      double fact = Math.sqrt(1 - (area / dist2));
      double ox = centX + (dx * fact);
      double oy = centY + (dy * fact);
      return next.getHue(ox, oy);
    }
  }



  public static class Wave implements Op {

    double amplitude;

    Op next;

    double unitX;

    double unitY;

    double wavelength;


    public Wave(Op op, Random rand) {
      next = op;
      double angle = Math.PI * 2 * rand.nextDouble();
      unitX = Math.cos(angle);
      unitY = Math.sin(angle);
      amplitude = 8 + (64 * rand.nextDouble());
      wavelength = 8 + (48 * rand.nextDouble());
    }


    @Override
    public int getHue(double x, double y) {
      double dist = ((x - 256) * unitX) + ((y - 256) * unitY);
      double eff = amplitude * Math.sin(dist / wavelength);
      double nx = x - (eff * unitY);
      double ny = y + (eff * unitX);
      return next.getHue(nx, ny);
    }
  }



  public class Ripple {

    double centX;

    double centY;

    double min;

    double wavelength;


    Ripple(Random rand, float min) {
      centX = rand.nextInt(512);
      centY = rand.nextInt(512);
      wavelength = 8 + (24 * rand.nextDouble());
      this.min = min;
    }


    public float getModifier(double x, double y) {
      double dist = Math.hypot(x - centX, y - centY);
      return (float) (min
          + ((1 - min) * 0.5 * (1 + Math.cos(dist / wavelength))));
    }
  }


  public Marble() {
    // do nothing
  }


  public Marble(Random rand) {
    super(rand);
  }


  @Override
  public void create() {
    BufferedImage image = new BufferedImage(512, 512,
        BufferedImage.TYPE_INT_RGB);

    double scale = 1000;
    Op ink = new Ink(null, scale, rand);
    double area = ((Ink) ink).area;
    for (int i = 0; i < 100; i++) {
      ink = new Ink(ink, scale, rand);
      area += ((Ink) ink).area;
      scale *= 0.975;
    }

    switch (rand.nextInt(3)) {
      case 0:
        ink = new Wave(ink, rand);
        break;
      case 1:
        ink = new Comb(ink, rand);
        break;
      default:
    }

    double size = Math.sqrt(area);
    Rectangle2D bound = new Rectangle2D.Double(256 - size, 256 - size, size * 2, size * 2);

    int[] pallette = new int[129];
    float base = rand.nextFloat();
    for (int i0 = 0; i0 < 4; i0++) {
      float s = 1.0f - 0.1f * i0;
      for (int i1 = 0; i1 < 4; i1++) {
        float b = 1.0f - 0.1f * i1;
        int o = 8 * (i0 * 4 + i1);
        pallette[o] = Color.HSBtoRGB(base, s, b);
        pallette[o + 1] = Color.HSBtoRGB(base + 0.08f, s, b);
        pallette[o + 2] = Color.HSBtoRGB(base + 0.16f, s, b);
        pallette[o + 3] = Color.HSBtoRGB(base + 0.24f, s, b);
        pallette[o + 4] = Color.HSBtoRGB(base + 0.5f, s, b);
        pallette[o + 5] = Color.HSBtoRGB(base + 0.58f, s, b);
        pallette[o + 6] = Color.HSBtoRGB(base + 0.66f, s, b);
        pallette[o + 7] = Color.HSBtoRGB(base + 0.74f, s, b);
      }
    }
    pallette[128] = 0;

    Ripple sat = (rand.nextInt(3) == 0) ? new Ripple(rand, 0.2f) : null;
    Ripple bright = (rand.nextInt(3) == 0) ? new Ripple(rand, 0.5f) : null;
    boolean doRipple = (sat != null) || (bright != null);
    float[] hsv = new float[3];
    for (int x = 0; x < 512; x++) {
      for (int y = 0; y < 512; y++) {
        double x0 = bound.getMinX() + x * bound.getWidth() / 512;
        double y0 = bound.getMinY() + y * bound.getHeight() / 512;
        int hue = ink.getHue(x0, y0);
        int rgb = pallette[hue];
        if (doRipple) {
          hsv = Color.RGBtoHSB((rgb & 0xff0000) >> 16, (rgb & 0xff00) >> 8, (rgb & 0xff), hsv);
          if (sat != null) {
            hsv[1] *= sat.getModifier(x0, y0);
          }
          if (bright != null) {
            hsv[2] *= bright.getModifier(x0, y0);
          }
          rgb = Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]);
        }
        image.setRGB(x, y, rgb);
      }
    }

    myImage = image;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy