/*
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.cos;
import static org.jwildfire.base.mathlib.MathLib.fabs;
import static org.jwildfire.base.mathlib.MathLib.pow;
import static org.jwildfire.base.mathlib.MathLib.sin;
import static org.jwildfire.base.mathlib.MathLib.sqr;
import static org.jwildfire.base.mathlib.MathLib.sqrt;
import static org.jwildfire.base.mathlib.MathLib.tanh;
import org.jwildfire.create.tina.base.Layer;
import org.jwildfire.create.tina.base.XForm;
import org.jwildfire.create.tina.base.XYZPoint;
public class Waves2BFunc extends VariationFunc {
private static final long serialVersionUID = 1L;
private static final String PARAM_FREQX = "freqx";
private static final String PARAM_FREQY = "freqy";
private static final String PARAM_PWX = "pwx";
private static final String PARAM_PWY = "pwy";
private static final String PARAM_SCALEX = "scalex";
private static final String PARAM_SCALEINFX = "scaleinfx";
private static final String PARAM_SCALEY = "scaley";
private static final String PARAM_SCALEINFY = "scaleinfy";
private static final String PARAM_UNITY = "unity";
private static final String PARAM_JACOK = "jacok";
private static final String[] paramNames = { PARAM_FREQX, PARAM_FREQY, PARAM_PWX, PARAM_PWY, PARAM_SCALEX, PARAM_SCALEINFX, PARAM_SCALEY, PARAM_SCALEINFY, PARAM_UNITY, PARAM_JACOK };
private double freqx = 2.0;
private double freqy = 2.0;
private double pwx = 1.0;
private double pwy = 1.0;
private double scalex = 1.0;
private double scaleinfx = 1.0;
private double scaley = 1.0;
private double scaleinfy = 1.0;
private double unity = 1.0;
private double jacok = 0.25;
@Override
public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) {
// Waves2B by dark-beam, http://dark-beam.deviantart.com/art/Waves2b-UPDATE-FIX-456744888
double CsX = 1.0;
double CsY = 1.0;
double JCB_sn;
CsX = safediv(unity, (unity + sqr(pAffineTP.x)));
CsX = CsX * _six + scaleinfx;
CsY = safediv(unity, (unity + sqr(pAffineTP.y)));
CsY = CsY * _siy + scaleinfy;
// x
if (pwx >= 0 && pwx < 1e-4) { // jacobi sn
JCB_sn = Jacobi_elliptic(pAffineTP.y * freqx, jacok);
pVarTP.x += pAmount * (pAffineTP.x + CsX * JCB_sn);
}
else if (pwx < 0 && pwx > -1e-4) { // bessel j1
pVarTP.x += pAmount * (pAffineTP.x + CsX * cern.jet.math.Bessel.j1(pAffineTP.y * freqx));
}
else { // NORMAL!
pVarTP.x += pAmount * (pAffineTP.x + CsX * sin(sgn(pAffineTP.y) * pow(fabs(pAffineTP.y) + 1e-10, pwx) * freqx));
}
// y
if (pwy >= 0 && pwy < 1e-4) { // jacobi sn
JCB_sn = Jacobi_elliptic(pAffineTP.x * freqy, jacok);
pVarTP.y += pAmount * (pAffineTP.y + CsY * JCB_sn);
}
else if (pwy < 0 && pwy > -1e-4) { // bessel j1
pVarTP.y += pAmount * (pAffineTP.y + CsY * cern.jet.math.Bessel.j1(pAffineTP.x * freqy));
}
else { // NORMAL!
pVarTP.y += pAmount * (pAffineTP.y + CsY * sin(sgn(pAffineTP.x) * pow(fabs(pAffineTP.x) + 1e-10, pwy) * freqy));
}
if (pContext.isPreserveZCoordinate()) {
pVarTP.z += pAmount * pAffineTP.z;
}
}
@Override
public String[] getParameterNames() {
return paramNames;
}
@Override
public Object[] getParameterValues() {
return new Object[] { freqx, freqy, pwx, pwy, scalex, scaleinfx, scaley, scaleinfy, unity, jacok };
}
@Override
public void setParameter(String pName, double pValue) {
if (PARAM_FREQX.equalsIgnoreCase(pName))
freqx = pValue;
else if (PARAM_FREQY.equalsIgnoreCase(pName))
freqy = pValue;
else if (PARAM_PWX.equalsIgnoreCase(pName))
pwx = limitVal(pValue, -10.0, 10.0);
else if (PARAM_PWY.equalsIgnoreCase(pName))
pwy = limitVal(pValue, -10.0, 10.0);
else if (PARAM_SCALEX.equalsIgnoreCase(pName))
scalex = pValue;
else if (PARAM_SCALEINFX.equalsIgnoreCase(pName))
scaleinfx = pValue;
else if (PARAM_SCALEY.equalsIgnoreCase(pName))
scaley = pValue;
else if (PARAM_SCALEINFY.equalsIgnoreCase(pName))
scaleinfy = pValue;
else if (PARAM_UNITY.equalsIgnoreCase(pName))
unity = pValue;
else if (PARAM_JACOK.equalsIgnoreCase(pName))
jacok = limitVal(pValue, -1.0, 1.0);
else
throw new IllegalArgumentException(pName);
}
private double safediv(double q, double r) {
if (r < 1e-10)
return 1 / r;
return q / r;
}
private double sgn(double x) {
// if (x == 0.0) return x;
return (x < 0 ? -1.0 : 1.0);
}
@Override
public String getName() {
return "waves2b";
}
private double _six, _siy;
@Override
public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) {
_six = scalex - scaleinfx;
_siy = scaley - scaleinfy;
}
double Jacobi_elliptic(double uu, double emmc) {
double sn = 0.0;
// Code is taken from IROIRO++ library,
// released under CC share-alike license.
// less accurate for faster rendering (still very precise)
final double CA = 0.0003; // (The accuracy is the square of CA.)
double a, b, c = 0, d = 0, em[] = new double[13], en[] = new double[13];
double dn, cn;
int bo;
int l = 0;
int ii;
int i;
// LOGICAL bo
// main
double emc = emmc;
double u = uu;
if (emc != 0.0) {
bo = 0;
if (emc < 0.0)
bo = 1;
if (bo != 0) {
d = 1.0 - emc;
emc = -emc / d;
d = sqrt(d);
u = d * u;
}
a = 1.0;
dn = 1.0;
// for(i=0; i<13; i++){ original
for (i = 0; i < 8; i++) {
l = i;
em[i] = a;
emc = sqrt(emc);
en[i] = emc;
c = 0.5 * (a + emc);
if (fabs(a - emc) <= CA * a)
break;
emc = a * emc;
a = c;
}
u = c * u;
sn = sin(u);
cn = cos(u);
if (sn != 0.0) {
a = cn / sn;
c = a * c;
for (ii = l; ii >= 0; --ii) {
b = em[ii];
a = c * a;
c = dn * c;
dn = (en[ii] + a) / (b + a);
a = c / b;
}
a = 1.0 / sqrt(c * c + 1.0);
if (sn < 0.0)
sn = -a;
else
sn = a;
cn = c * sn;
}
if (bo != 0) {
a = dn;
dn = cn;
cn = a;
sn = sn / d;
}
}
else {
// cn = 1.0/cosh(u);
// dn = cn;
sn = tanh(u);
}
return sn;
}
}