001/*
002Copyright 2006 Jerry Huxtable
003
004Licensed under the Apache License, Version 2.0 (the "License");
005you may not use this file except in compliance with the License.
006You may obtain a copy of the License at
007
008   http://www.apache.org/licenses/LICENSE-2.0
009
010Unless required by applicable law or agreed to in writing, software
011distributed under the License is distributed on an "AS IS" BASIS,
012WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013See the License for the specific language governing permissions and
014limitations under the License.
015*/
016
017package com.jhlabs.math;
018
019import java.awt.image.*;
020import java.util.*;
021
022public class CellularFunction2D implements Function2D {
023
024        public float distancePower = 2;
025        public boolean cells = false;
026        public boolean angular = false;
027        private float[] coefficients = { 1, 0, 0, 0 };
028        private Random random = new Random();
029        private Point[] results = null;
030        
031        public CellularFunction2D() {
032                results = new Point[2];
033                for (int j = 0; j < results.length; j++)
034                        results[j] = new Point();
035        }
036        
037        public void setCoefficient(int c, float v) {
038                coefficients[c] = v;
039        }
040        
041        public float getCoefficient(int c) {
042                return coefficients[c];
043        }
044        
045        class Point {
046                int index;
047                float x, y;
048                float distance;
049        }
050        
051        private float checkCube(float x, float y, int cubeX, int cubeY, Point[] results) {
052                random.setSeed(571*cubeX + 23*cubeY);
053                int numPoints = 3 + random.nextInt() % 4;
054                numPoints = 4;
055
056                for (int i = 0; i < numPoints; i++) {
057                        float px = random.nextFloat();
058                        float py = random.nextFloat();
059                        float dx = Math.abs(x-px);
060                        float dy = Math.abs(y-py);
061                        float d;
062                        if (distancePower == 1.0f)
063                                d = dx + dy;
064                        else if (distancePower == 2.0f)
065                                d = (float)Math.sqrt(dx*dx + dy*dy);
066                        else
067                                d = (float)Math.pow(Math.pow(dx, distancePower) + Math.pow(dy, distancePower), 1/distancePower);
068
069                        // Insertion sort
070                        for (int j = 0; j < results.length; j++) {
071                                if (results[j].distance == Double.POSITIVE_INFINITY) {
072                                        Point last = results[j];
073                                        last.distance = d;
074                                        last.x = px;
075                                        last.y = py;
076                                        results[j] = last;
077                                        break;
078                                } else if (d < results[j].distance) {
079                                        Point last = results[results.length-1];
080                                        for (int k = results.length-1; k > j; k--)
081                                                results[k] = results[k-1];
082                                        last.distance = d;
083                                        last.x = px;
084                                        last.y = py;
085                                        results[j] = last;
086                                        break;
087                                }
088                        }
089                }
090                return results[1].distance;
091        }
092        
093        public float evaluate(float x, float y) {
094                for (int j = 0; j < results.length; j++)
095                        results[j].distance = Float.POSITIVE_INFINITY;
096
097                int ix = (int)x;
098                int iy = (int)y;
099                float fx = x-ix;
100                float fy = y-iy;
101
102                float d = checkCube(fx, fy, ix, iy, results);
103                if (d > fy)
104                        d = checkCube(fx, fy+1, ix, iy-1, results);
105                if (d > 1-fy)
106                        d = checkCube(fx, fy-1, ix, iy+1, results);
107                if (d > fx) {
108                        checkCube(fx+1, fy, ix-1, iy, results);
109                        if (d > fy)
110                                d = checkCube(fx+1, fy+1, ix-1, iy-1, results);
111                        if (d > 1-fy)
112                                d = checkCube(fx+1, fy-1, ix-1, iy+1, results);
113                }
114                if (d > 1-fx) {
115                        d = checkCube(fx-1, fy, ix+1, iy, results);
116                        if (d > fy)
117                                d = checkCube(fx-1, fy+1, ix+1, iy-1, results);
118                        if (d > 1-fy)
119                                d = checkCube(fx-1, fy-1, ix+1, iy+1, results);
120                }
121
122                float t = 0;
123                for (int i = 0; i < 2; i++)
124                        t += coefficients[i] * results[i].distance;
125                if (angular)
126                        t += Math.atan2(fy-results[0].y, fx-results[0].x) / (2*Math.PI) + 0.5;
127                return t;
128        }
129        
130}