/*
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.M_1_PI;
import static org.jwildfire.base.mathlib.MathLib.M_2PI;
import static org.jwildfire.base.mathlib.MathLib.atan2;
import static org.jwildfire.base.mathlib.MathLib.cos;
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 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;
// Rosoni JWildfire variation by DarkBeam, July 2014
// (for more details, see JWildfire forum post: http://jwildfire.org/forum/viewtopic.php?f=23&t=1449)
// From dark-beam's post:
// In some situations (if you mangle with values a bit and sweetiter is = 0) some pixels will turn black.
// It's not a common situation and I think it's ok. It should never happen if you choose a correct sweetiter value.
// Commonly sweetiter should be = maxiter / 2. But different ones can be tried.
//
// minor changes by CozyG
public class RosoniFunc extends VariationFunc {
private static final long serialVersionUID = 1L;
private static final String PARAM_MAXITER = "maxiter";
private static final String PARAM_SWEETITER = "sweetiter";
private static final String PARAM_ALTSHAPES = "altshapes";
private static final String PARAM_CUTOFF = "cutoff";
private static final String PARAM_RADIUS = "radius";
private static final String PARAM_DX = "dx";
private static final String PARAM_DY = "dy";
private static final String[] paramNames = { PARAM_MAXITER, PARAM_SWEETITER, PARAM_ALTSHAPES, PARAM_CUTOFF, PARAM_RADIUS, PARAM_DX, PARAM_DY };
private int maxiter = 25;
private int sweetiter = 3;
private int altshapes = 0;
private double cutoff = 1.0;
private double radius = 0.4;
private double dx = 0.6;
private double dy = 0.0;
@Override
public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) {
double x = pAffineTP.x;
double y = pAffineTP.y;
double r = sqrt(sqr(x) + sqr(y)) - cutoff;
if (cutoff < 0.0)
r = Math.max(fabs(x), fabs(y)) + cutoff;
boolean cerc = r > 0.0;
if (cerc) {
pVarTP.x += pAmount * x;
pVarTP.y += pAmount * y;
if (pContext.isPreserveZCoordinate()) {
pVarTP.z += pAmount * pAffineTP.z;
}
return;
}
cerc = (dx > 0.0);
int i;
double xrt = x, yrt = y, swp, r2 = xrt;
double sweetx = xrt, sweety = yrt;
for (i = 0; i < maxiter; i++) {
if (altshapes == 0) {
r2 = sqr(xrt - dx) + sqr(yrt - dy) - sqr(radius); // circle
if (radius < 0.0)
r2 = Math.max(fabs(xrt - dx), fabs(y - dy)) + radius; // square
}
else {
r2 = ((xrt - dx) < 0.0) ? -(xrt - dx) : ((sqr(yrt - dy)) - sqr(xrt - dx) * (sqr(radius) - sqr(xrt - dx))); // lemniscate
if (radius < 0.0)
r2 = fabs(atan2(y - dy, xrt - dx)) * M_1_PI + radius; // angle
}
cerc ^= (r2 <= 0.0);
// rotate around to get the the sides!!! :D
if (i == sweetiter) {
sweetx = xrt;
sweety = yrt;
}
swp = xrt * _cosa - yrt * _sina;
yrt = xrt * _sina + yrt * _cosa;
xrt = swp;
}
if (cerc) {
if (sweetiter == 0) {
if (dy != 0)
pVarTP.x -= pAmount * sweetx;
else
pVarTP.x += pAmount * sweetx;
pVarTP.y -= pAmount * sweety;
}
else
{
pVarTP.x += pAmount * sweetx;
pVarTP.y += pAmount * sweety;
}
if (pContext.isPreserveZCoordinate()) {
pVarTP.z += pAmount * pAffineTP.z;
}
return;
}
pVarTP.x += pAmount * x;
pVarTP.y += pAmount * y;
if (pContext.isPreserveZCoordinate()) {
pVarTP.z += pAmount * pAffineTP.z;
}
}
@Override
public String[] getParameterNames() {
return paramNames;
}
@Override
public Object[] getParameterValues() {
return new Object[] { maxiter, sweetiter, altshapes, cutoff, radius, dx, dy };
}
@Override
public void setParameter(String pName, double pValue) {
if (PARAM_MAXITER.equalsIgnoreCase(pName))
maxiter = limitIntVal(Tools.FTOI(pValue), 1, 1024);
else if (PARAM_SWEETITER.equalsIgnoreCase(pName))
sweetiter = limitIntVal(Tools.FTOI(pValue), 0, 1023);
else if (PARAM_ALTSHAPES.equalsIgnoreCase(pName))
altshapes = Tools.FTOI(pValue);
else if (PARAM_CUTOFF.equalsIgnoreCase(pName))
cutoff = pValue;
else if (PARAM_RADIUS.equalsIgnoreCase(pName))
radius = pValue;
else if (PARAM_DX.equalsIgnoreCase(pName))
dx = pValue;
else if (PARAM_DY.equalsIgnoreCase(pName))
dy = pValue;
else
throw new IllegalArgumentException(pName);
}
@Override
public String getName() {
return "rosoni";
}
private double _sina, _cosa;
@Override
public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) {
double phi = M_2PI / (double) maxiter;
_sina = sin(phi);
_cosa = cos(phi);
}
}