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