/*
JWildfire - an image and animation processor written in Java
Copyright (C) 1995-2011 Andreas Maschke
This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with this software;
if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jwildfire.create.tina.variation;
import static org.jwildfire.base.mathlib.MathLib.EPSILON;
import static org.jwildfire.base.mathlib.MathLib.M_2PI;
import static org.jwildfire.base.mathlib.MathLib.cos;
import static org.jwildfire.base.mathlib.MathLib.floor;
import static org.jwildfire.base.mathlib.MathLib.sin;
import static org.jwildfire.base.mathlib.MathLib.sqrt;
import org.jwildfire.base.Tools;
import org.jwildfire.create.tina.base.Layer;
import org.jwildfire.create.tina.base.XForm;
import org.jwildfire.create.tina.base.XYZPoint;
public class DCPerlinFunc extends VariationFunc {
private static final long serialVersionUID = 1L;
public static final String PARAM_SHAPE = "shape";
private static final String PARAM_MAP = "map";
private static final String PARAM_SELECT_CENTRE = "select_centre";
private static final String PARAM_SELECT_RANGE = "select_range";
public static final String PARAM_CENTRE = "centre";
public static final String PARAM_RANGE = "range";
private static final String PARAM_EDGE = "edge";
private static final String PARAM_SCALE = "scale";
public static final String PARAM_OCTAVES = "octaves";
public static final String PARAM_AMPS = "amps";
public static final String PARAM_FREQS = "freqs";
private static final String PARAM_Z = "z";
private static final String PARAM_SELECT_BAILOUT = "select_bailout";
private static final String[] paramNames = { PARAM_SHAPE, PARAM_MAP, PARAM_SELECT_CENTRE, PARAM_SELECT_RANGE, PARAM_CENTRE, PARAM_RANGE, PARAM_EDGE, PARAM_SCALE, PARAM_OCTAVES, PARAM_AMPS, PARAM_FREQS, PARAM_Z, PARAM_SELECT_BAILOUT };
private int shape = 0;
private int map = 0;
private double select_centre = 0.0;
private double select_range = 1.0;
private double centre = 0.25;
private double range = 0.25;
private double edge = 0.0;
private double scale = 1.0;
private int octaves = 2;
private double amps = 2.0;
private double freqs = 2.0;
private double z = 0.0;
private int select_bailout = 10;
private final static int SHAPE_SQUARE = 0;
private final static int SHAPE_DISC = 1;
private final static int SHAPE_BLUR = 2;
private final static int MAP_FLAT = 0;
private final static int MAP_SPHERICAL = 1;
private final static int MAP_HSPHERE = 2;
private final static int MAP_QSPHERE = 3;
private final static int MAP_BUBBLE = 4;
private final static int MAP_BUBBLE2 = 5;
@Override
public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) {
/* dc_perlin by slobo777, http://slobo777.deviantart.com/art/dc-perlin-Apophysis-Plugin-186190256 */
double V[] = new double[3];
double Vx = 0.0, Vy = 0.0, Col, r, theta, s, c, p, e;
int t;
t = 0;
do {
// Default edge value
e = 0.0;
// Assign Vx, Vy according to shape
switch (shape) {
case SHAPE_SQUARE:
Vx = (1.0 + this.edge) * (pContext.random() - 0.5);
Vy = (1.0 + this.edge) * (pContext.random() - 0.5);
r = Vx * Vx > Vy * Vy ? Vx : Vy;
if (r > 1.0 - this.edge) {
e = 0.5 * (r - 1.0 + this.edge) / this.edge;
}
break;
case SHAPE_DISC:
r = pContext.random() + pContext.random();
r = (r > 1.0) ? 2.0 - r : r;
r *= (1.0 + this.edge);
if (r > 1.0 - this.edge) {
e = 0.5 * (r - 1.0 + this.edge) / this.edge;
}
theta = pContext.random() * M_2PI;
s = sin(theta);
c = cos(theta);
Vx = 0.5 * r * s;
Vy = 0.5 * r * c;
break;
case SHAPE_BLUR:
r = (1.0 + this.edge) * pContext.random();
if (r > 1.0 - this.edge) {
e = 0.5 * (r - 1.0 + this.edge) / this.edge;
}
theta = pContext.random() * M_2PI;
s = sin(theta);
c = cos(theta);
Vx = 0.5 * r * s;
Vy = 0.5 * r * c;
break;
default: // nothing to do
break;
}
// Assign V for noise vector position according to map
switch (this.map) {
case MAP_FLAT:
V[0] = this.scale * Vx;
V[1] = this.scale * Vy;
V[2] = this.scale * this.z;
break;
case MAP_SPHERICAL:
r = 1.0 / (Vx * Vx + Vy * Vy + EPSILON);
V[0] = this.scale * Vx * r;
V[1] = this.scale * Vy * r;
V[2] = this.scale * this.z;
break;
case MAP_HSPHERE:
r = 1.0 / (Vx * Vx + Vy * Vy + 0.5);
V[0] = this.scale * Vx * r;
V[1] = this.scale * Vy * r;
V[2] = this.scale * this.z;
break;
case MAP_QSPHERE:
r = 1.0 / (Vx * Vx + Vy * Vy + 0.25);
V[0] = this.scale * Vx * r;
V[1] = this.scale * Vy * r;
V[2] = this.scale * this.z;
break;
case MAP_BUBBLE:
r = 0.25 - (Vx * Vx + Vy * Vy);
if (r < 0.0) {
r = sqrt(-r);
}
else {
r = sqrt(r);
}
V[0] = this.scale * Vx;
V[1] = this.scale * Vy;
V[2] = this.scale * (r + this.z);
break;
case MAP_BUBBLE2:
r = 0.25 - (Vx * Vx + Vy * Vy);
if (r < 0.0) {
r = sqrt(-r);
}
else {
r = sqrt(r);
}
V[0] = this.scale * Vx;
V[1] = this.scale * Vy;
V[2] = this.scale * (2 * r + this.z);
break;
}
p = NoiseTools.perlinNoise3D(V, this.amps, this.freqs, this.octaves);
// Add edge effects
if (p > 0.0) {
e = p * (1.0 + e * e * 20.0) + 2.0 * e;
}
else {
e = p * (1.0 + e * e * 20.0) - 2.0 * e;
}
}
while ((e < _notch_bottom || e > _notch_top) && t++ < this.select_bailout);
// Add blur effect to transform
pVarTP.x += pAmount * Vx;
pVarTP.y += pAmount * Vy;
if (pContext.isPreserveZCoordinate()) {
pVarTP.z += pAmount * pAffineTP.z;
}
// Calculate and add direct colour
Col = this.centre + this.range * p;
pVarTP.color = Col - floor(Col);
}
@Override
public String[] getParameterNames() {
return paramNames;
}
@Override
public Object[] getParameterValues() {
return new Object[] { shape, map, select_centre, select_range, centre, range, edge, scale, octaves, amps, freqs, z, select_bailout };
}
@Override
public void setParameter(String pName, double pValue) {
if (PARAM_SHAPE.equalsIgnoreCase(pName))
shape = limitIntVal(Tools.FTOI(pValue), 0, 2);
else if (PARAM_MAP.equalsIgnoreCase(pName))
map = limitIntVal(Tools.FTOI(pValue), 0, 5);
else if (PARAM_SELECT_CENTRE.equalsIgnoreCase(pName))
select_centre = limitVal(pValue, -1.0, 1.0);
else if (PARAM_SELECT_RANGE.equalsIgnoreCase(pName))
select_range = limitVal(pValue, 0.1, 2.0);
else if (PARAM_CENTRE.equalsIgnoreCase(pName))
centre = pValue;
else if (PARAM_RANGE.equalsIgnoreCase(pName))
range = pValue;
else if (PARAM_EDGE.equalsIgnoreCase(pName))
edge = pValue;
else if (PARAM_SCALE.equalsIgnoreCase(pName))
scale = pValue;
else if (PARAM_OCTAVES.equalsIgnoreCase(pName))
octaves = limitIntVal(Tools.FTOI(pValue), 1, 5);
else if (PARAM_AMPS.equalsIgnoreCase(pName))
amps = pValue;
else if (PARAM_FREQS.equalsIgnoreCase(pName))
freqs = pValue;
else if (PARAM_Z.equalsIgnoreCase(pName))
z = pValue;
else if (PARAM_SELECT_BAILOUT.equalsIgnoreCase(pName))
select_bailout = limitIntVal(Tools.FTOI(pValue), 2, 1000);
else
throw new IllegalArgumentException(pName);
}
@Override
public String getName() {
return "dc_perlin";
}
private double _notch_bottom, _notch_top;
@Override
public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) {
_notch_bottom = select_centre - select_range;
_notch_bottom = (_notch_bottom > 0.75) ? 0.75 : _notch_bottom;
_notch_bottom = (_notch_bottom < -2.0) ? -3.0 : _notch_bottom;
_notch_top = select_centre + select_range;
_notch_top = (_notch_top < -0.75) ? -0.75 : _notch_top;
_notch_top = (_notch_top > 3.0) ? 3.0 : _notch_top;
}
}