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}