/*
JWildfire - an image and animation processor written in Java
Copyright (C) 1995-2014 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.cos;
import static org.jwildfire.base.mathlib.MathLib.cosh;
import static org.jwildfire.base.mathlib.MathLib.fabs;
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.XForm;
import org.jwildfire.create.tina.base.XYZPoint;
public class JacCnFunc extends VariationFunc {
private static final long serialVersionUID = 1L;
private static final String PARAM_K = "k";
private static final String[] paramNames = { PARAM_K };
private double k = 0.5;
@Override
public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) {
// Jacobi-elliptic-CN by dark-beam, http://dark-beam.deviantart.com/art/Jacobi-elliptic-sn-cn-and-dn-Apoplugins-460783612
double NumX, NumY, Denom;
Jacobi_elliptic_result jac_x = new Jacobi_elliptic_result();
Jacobi_elliptic(pAffineTP.x, k, jac_x);
Jacobi_elliptic_result jac_y = new Jacobi_elliptic_result();
Jacobi_elliptic(pAffineTP.y, 1.0 - k, jac_y);
NumX = jac_x.cn * jac_y.cn;
NumY = -jac_x.dn * jac_x.sn * jac_y.dn * jac_y.sn;
Denom = sqr(jac_x.sn) * sqr(jac_y.sn) * k + sqr(jac_y.cn);
Denom = pAmount / (EPSILON + Denom);
pVarTP.x += Denom * NumX;
pVarTP.y += Denom * NumY;
if (pContext.isPreserveZCoordinate()) {
pVarTP.z += pAmount * pAffineTP.z;
}
}
@Override
public String[] getParameterNames() {
return paramNames;
}
@Override
public Object[] getParameterValues() {
return new Object[] { k };
}
@Override
public void setParameter(String pName, double pValue) {
if (PARAM_K.equalsIgnoreCase(pName))
k = limitVal(pValue, -1.0, 1.0);
else
throw new IllegalArgumentException(pName);
}
@Override
public String getName() {
return "jac_cn";
}
public static class Jacobi_elliptic_result {
public double sn, cn, dn;
public void clear() {
sn = cn = dn = 0.0;
}
}
public static void Jacobi_elliptic(double uu, double emmc, Jacobi_elliptic_result res) {
// Code is taken from IROIRO++ library,
// released under CC share-alike license.
res.clear();
// 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.0, d = 0.0, em[] = new double[13], en[] = new double[13];
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;
res.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;
res.sn = sin(u);
res.cn = cos(u);
if (res.sn != 0.0) {
a = res.cn / res.sn;
c = a * c;
for (ii = l; ii >= 0; --ii) {
b = em[ii];
a = c * a;
c = res.dn * c;
res.dn = (en[ii] + a) / (b + a);
a = c / b;
}
a = 1.0 / sqrt(c * c + 1.0);
if (res.sn < 0.0)
(res.sn) = -a;
else
res.sn = a;
res.cn = c * (res.sn);
}
if (bo != 0) {
a = res.dn;
res.dn = res.cn;
res.cn = a;
res.sn = (res.sn) / d;
}
}
else {
res.cn = 1.0 / cosh(u);
res.dn = res.cn;
(res.sn) = tanh(u);
}
}
}