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

prng.image.Fractal Maven / Gradle / Ivy

package prng.image;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.util.Random;

/** Create a fractal image file using a provided random number generator */
public class Fractal extends BasePainter {

    /**
     * Number of vertices in the TSP polygon
     */
    public static final int VERTICES = 64;


    /**
     * Create 2D linearly-interpolated Perlin noise
     *
     * @param rand
     *            source of randomness
     * @return noise values
     */
    public static int[][] createNoise(Random rand) {
        float[][] r = new float[128][128];
        int scale = 64;
        int len = 2;
        while( len <= r.length ) {
            // create seed points for the current scale
            float[][] b = new float[len + 1][len + 1];
            for(int x = 0;x <= len;x++) {
                for(int y = 0;y <= len;y++) {
                    b[x][y] = rand.nextFloat() * scale;
                }
            }

            // interpolate the seed points to the grid
            int v = 128 / len;
            for(int x = 0;x < len;x++) {
                for(int y = 0;y < len;y++) {
                    // corners of the seed points in this square
                    float b00 = b[x][y];
                    float b01 = b[x][y + 1];
                    float b10 = b[x + 1][y];
                    float b11 = b[x + 1][y + 1];

                    // for each point in the seed square
                    for(int i = 0;i < v;i++) {
                        float fi = (float) i / v;
                        for(int j = 0;j < v;j++) {
                            float fj = (float) j / v;
                            // calculate local contribution
                            r[(x * v) + i][(y * v)
                                    + j] += (b00 * (1 - fi) * (1 - fj))
                                            + (b10 * fi * (1 - fj))
                                            + (b01 * (1 - fi) * fj)
                                            + (b11 * fi * fj);
                        }
                    }
                }
            }
            scale /= 2;
            len *= 2;
        }

        // find max and minimum for rescaling
        float max = r[0][0];
        float min = r[0][0];
        for(int i = 0;i < 128;i++) {
            for(int j = 0;j < 128;j++) {
                float v = r[i][j];
                if( v > max ) {
                    max = v;
                }
                if( v < min ) {
                    min = v;
                }
            }
        }
        float size = max - min;

        // rescale up to integer grid
        int[][] ri = new int[128][128];
        for(int i = 0;i < 128;i++) {
            for(int j = 0;j < 128;j++) {
                ri[i][j] = (int) ((255.0 * (r[i][j] - min)) / size);
            }
        }

        return ri;
    }


    /**
     * Create a route which is a TSP solution for a set of random points in one
     * quarter of the field. The route enters and leaves on different boundaries
     * of the quarter, so by reflections a complete symmetric polygon can be
     * created.
     *
     * @param rand
     *            random source
     * @return points for one quarter of the polygon
     */
    public static Point2D[] createPoly(Random rand) {
        // create the vertices
        Point2D[] points = new Point2D[VERTICES + 1];
        for(int i = 0;i < VERTICES;i++) {
            Point2D p;
            boolean isUsed;
            do {
                p = new Point2D.Double(rand.nextInt(256), rand.nextInt(256));
                isUsed = false;
                for(int j = 0;j < i;j++) {
                    if( p.equals(points[j]) ) {
                        isUsed = true;
                        break;
                    }
                }
            } while( isUsed );
            points[i] = p;
        }

        // create the edge vertices at start and end
        points[0].setLocation(256, rand.nextInt(256));
        points[VERTICES] = new Point2D.Double(rand.nextInt(256), 256);

        Point2D s1, e1, s2, e2;

        // repeatedly apply the 2-opt rule to improve the TSP solution
        boolean isImproved = true;
        while( isImproved ) {
            isImproved = false;
            outer: for(int i = 1;i < (VERTICES - 1);i++) {
                s1 = points[i - 1];
                e1 = points[i];
                double len1 = s1.distance(e1);
                for(int j = i + 1;j < VERTICES;j++) {
                    s2 = points[j];
                    e2 = points[j + 1];
                    double origLen = len1 + s2.distance(e2);
                    double newLen = s1.distance(s2) + e1.distance(e2);

                    // if new route is shorter, reverse i to j
                    if( (newLen < origLen) ) {
                        while( i < j ) {
                            Point2D t = points[i];
                            points[i] = points[j];
                            points[j] = t;
                            i++;
                            j--;
                        }
                        isImproved = true;
                        break outer;
                    }
                }
            }
        }

        return points;
    }


    public Fractal() {
        // do nothing
    }


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


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

        // draw Perlin noise background
        int[][] red = createNoise(rand);
        int[][] green = createNoise(rand);
        int[][] blue = createNoise(rand);
        for(int i = 0;i < 128;i++) {
            for(int j = 0;j < 128;j++) {
                graphics.setColor(
                        new Color(red[i][j], green[i][j], blue[i][j]));
                graphics.fillRect(i * 4, j * 4, 4, 4);
            }
        }

        // create the polygon
        Point2D[] points = createPoly(rand);
        Path2D path = new Path2D.Double(Path2D.WIND_NON_ZERO,
                (4 * VERTICES) + 2);

        // draw 1st quarter
        path.moveTo(points[0].getX(), points[0].getY());
        for(int i = 1;i < points.length;i++) {
            Point2D p = points[i];
            path.lineTo(p.getX(), p.getY());
        }

        // draw 2nd quarter
        for(int i = points.length - 2;i > 0;i--) {
            Point2D p = points[i];
            path.lineTo(p.getX(), 512 - p.getY());
        }

        // draw 3rd quarter
        for(int i = 0;i < points.length;i++) {
            Point2D p = points[i];
            path.lineTo(512 - p.getX(), 512 - p.getY());
        }

        // draw final quarter
        for(int i = points.length - 2;i > 0;i--) {
            Point2D p = points[i];
            path.lineTo(512 - p.getX(), p.getY());
        }
        path.closePath();

        // fill the polygon with a translucent black
        graphics.setColor(new Color(0, 0, 0, 64));
        graphics.fill(path);

        // draw the edge of the polygon in solid black
        graphics.setStroke(new BasicStroke(4, BasicStroke.CAP_ROUND,
                BasicStroke.JOIN_ROUND));
        graphics.setColor(Color.BLACK);
        graphics.draw(path);

        // create a 5x5 Gaussian blur filter
        float[] blurMatrix = new float[] { 1, 4, 7, 4, 1, 4, 16, 26, 16, 4, 7,
                26, 41, 26, 7, 4, 16, 26, 16, 4, 1, 4, 7, 4, 1 };
        for(int i = 0;i < 25;i++) {
            blurMatrix[i] /= 273f;
        }
        BufferedImageOp op = new ConvolveOp(new Kernel(5, 5, blurMatrix),
                ConvolveOp.EDGE_NO_OP, null);

        // blur the image
        myImage = op.filter(image, null);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy