/*
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.M_PI;
import static org.jwildfire.base.mathlib.MathLib.cos;
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 RippleFunc extends VariationFunc {
private static final long serialVersionUID = 1L;
private static final String PARAM_FREQUENCY = "frequency";
private static final String PARAM_VELOCITY = "velocity";
private static final String PARAM_AMPLITUDE = "amplitude";
private static final String PARAM_CENTERX = "centerx";
private static final String PARAM_CENTERY = "centery";
private static final String PARAM_PHASE = "phase";
private static final String PARAM_SCALE = "scale";
private static final String PARAM_FIXED_DIST_CALC = "fixed_dist_calc";
private static final String[] paramNames = { PARAM_FREQUENCY, PARAM_VELOCITY, PARAM_AMPLITUDE, PARAM_CENTERX, PARAM_CENTERY, PARAM_PHASE, PARAM_SCALE, PARAM_FIXED_DIST_CALC };
private double frequency = 2.0;
private double velocity = 1.0;
private double amplitude = 0.5;
private double centerx = 0.0;
private double centery = 0.0;
private double phase = 0.0;
private double scale = 1.0;
private int fixed_dist_calc = 0;
@Override
public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) {
// Ripple by Xyrus02, http://xyrus02.deviantart.com/art/Ripple-Plugin-for-Apophysis-154713493
//align input x, y to given center and multiply with scale
double x = (pAffineTP.x * _s) - centerx, y = (pAffineTP.y * _s) + centery;
// calculate distance from center but constrain it to EPS
double d = _fixed_dist_calc ? sqrt(x * x + y * y) : sqrt(x * x * y * y);
if (d < EPSILON)
d = EPSILON;
// normalize (x,y)
double nx = x / d, ny = y / d;
// calculate cosine wave with given frequency, velocity
// and phase based on the distance to center
double wave = cos(_f * d - _vxp);
// calculate the wave offsets
double d1 = wave * _pxa + d, d2 = wave * _pixa + d;
// we got two offsets, so we also got two new positions (u,v)
double u1 = (centerx + nx * d1), v1 = (-centery + ny * d1);
double u2 = (centerx + nx * d2), v2 = (-centery + ny * d2);
// interpolate the two positions by the given phase and
// invert the multiplication with scale from before
pVarTP.x = pAmount * (lerp(u1, u2, _p)) * _is;
pVarTP.y = pAmount * (lerp(v1, v2, _p)) * _is;
if (pContext.isPreserveZCoordinate()) {
pVarTP.z += pAmount * pAffineTP.z;
}
}
@Override
public String[] getParameterNames() {
return paramNames;
}
@Override
public Object[] getParameterValues() {
return new Object[] { frequency, velocity, amplitude, centerx, centery, phase, scale, fixed_dist_calc };
}
@Override
public void setParameter(String pName, double pValue) {
if (PARAM_FREQUENCY.equalsIgnoreCase(pName))
frequency = pValue;
else if (PARAM_VELOCITY.equalsIgnoreCase(pName))
velocity = pValue;
else if (PARAM_AMPLITUDE.equalsIgnoreCase(pName))
amplitude = pValue;
else if (PARAM_CENTERX.equalsIgnoreCase(pName))
centerx = pValue;
else if (PARAM_CENTERY.equalsIgnoreCase(pName))
centery = pValue;
else if (PARAM_PHASE.equalsIgnoreCase(pName))
phase = pValue;
else if (PARAM_SCALE.equalsIgnoreCase(pName))
scale = pValue;
else if (PARAM_FIXED_DIST_CALC.equalsIgnoreCase(pName))
fixed_dist_calc = Tools.FTOI(pValue);
else
throw new IllegalArgumentException(pName);
}
@Override
public String getName() {
return "ripple";
}
private double _f, _s, _p, _is, _pxa, _pixa, _vxp;
private boolean _fixed_dist_calc;
@Override
public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) {
// some variables are settled in another range for edit comfort
// - transform them
_f = frequency * 5;
double a = amplitude * 0.01;
_p = phase * M_2PI - M_PI;
// scale must not be zero
_s = scale == 0 ? EPSILON : scale;
// we will need the inverse scale
_is = 1.0 / _s;
// pre-multiply velocity+phase, phase+amplitude and (PI-phase)+amplitude
_vxp = velocity * _p;
_pxa = _p * a;
_pixa = (M_PI - _p) * a;
_fixed_dist_calc = fixed_dist_calc != 0;
}
private double lerp(double a, double b, double p) {
return a + (b - a) * p;
}
}