001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019/*
020*
021
022Licensed under the Apache License, Version 2.0 (the "License");
023you may not use this file except in compliance with the License.
024You may obtain a copy of the License at
025
026   http://www.apache.org/licenses/LICENSE-2.0
027
028Unless required by applicable law or agreed to in writing, software
029distributed under the License is distributed on an "AS IS" BASIS,
030WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
031See the License for the specific language governing permissions and
032limitations under the License.
033*/
034
035package lucee.runtime.img.math;
036
037import java.util.Random;
038
039public class CellularFunction2D implements Function2D {
040
041        public float distancePower = 2;
042        public boolean cells = false;
043        public boolean angular = false;
044        private float[] coefficients = { 1, 0, 0, 0 };
045        private Random random = new Random();
046        private Point[] results = null;
047        
048        public CellularFunction2D() {
049                results = new Point[2];
050                for (int j = 0; j < results.length; j++)
051                        results[j] = new Point();
052        }
053        
054        public void setCoefficient(int c, float v) {
055                coefficients[c] = v;
056        }
057        
058        public float getCoefficient(int c) {
059                return coefficients[c];
060        }
061        
062        class Point {
063                int index;
064                float x, y;
065                float distance;
066        }
067        
068        private float checkCube(float x, float y, int cubeX, int cubeY, Point[] results) {
069                random.setSeed(571*cubeX + 23*cubeY);
070                int numPoints = 3 + random.nextInt() % 4;
071                numPoints = 4;
072
073                for (int i = 0; i < numPoints; i++) {
074                        float px = random.nextFloat();
075                        float py = random.nextFloat();
076                        float dx = Math.abs(x-px);
077                        float dy = Math.abs(y-py);
078                        float d;
079                        if (distancePower == 1.0f)
080                                d = dx + dy;
081                        else if (distancePower == 2.0f)
082                                d = (float)Math.sqrt(dx*dx + dy*dy);
083                        else
084                                d = (float)Math.pow(Math.pow(dx, distancePower) + Math.pow(dy, distancePower), 1/distancePower);
085
086                        // Insertion sort
087                        for (int j = 0; j < results.length; j++) {
088                                if (results[j].distance == Double.POSITIVE_INFINITY) {
089                                        Point last = results[j];
090                                        last.distance = d;
091                                        last.x = px;
092                                        last.y = py;
093                                        results[j] = last;
094                                        break;
095                                } else if (d < results[j].distance) {
096                                        Point last = results[results.length-1];
097                                        for (int k = results.length-1; k > j; k--)
098                                                results[k] = results[k-1];
099                                        last.distance = d;
100                                        last.x = px;
101                                        last.y = py;
102                                        results[j] = last;
103                                        break;
104                                }
105                        }
106                }
107                return results[1].distance;
108        }
109        
110        public float evaluate(float x, float y) {
111                for (int j = 0; j < results.length; j++)
112                        results[j].distance = Float.POSITIVE_INFINITY;
113
114                int ix = (int)x;
115                int iy = (int)y;
116                float fx = x-ix;
117                float fy = y-iy;
118
119                float d = checkCube(fx, fy, ix, iy, results);
120                if (d > fy)
121                        d = checkCube(fx, fy+1, ix, iy-1, results);
122                if (d > 1-fy)
123                        d = checkCube(fx, fy-1, ix, iy+1, results);
124                if (d > fx) {
125                        checkCube(fx+1, fy, ix-1, iy, results);
126                        if (d > fy)
127                                d = checkCube(fx+1, fy+1, ix-1, iy-1, results);
128                        if (d > 1-fy)
129                                d = checkCube(fx+1, fy-1, ix-1, iy+1, results);
130                }
131                if (d > 1-fx) {
132                        d = checkCube(fx-1, fy, ix+1, iy, results);
133                        if (d > fy)
134                                d = checkCube(fx-1, fy+1, ix+1, iy-1, results);
135                        if (d > 1-fy)
136                                d = checkCube(fx-1, fy-1, ix+1, iy+1, results);
137                }
138
139                float t = 0;
140                for (int i = 0; i < 2; i++)
141                        t += coefficients[i] * results[i].distance;
142                if (angular)
143                        t += Math.atan2(fy-results[0].y, fx-results[0].x) / (2*Math.PI) + 0.5;
144                return t;
145        }
146        
147}