/* 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_2PI; import static org.jwildfire.base.mathlib.MathLib.M_PI; import static org.jwildfire.base.mathlib.MathLib.M_SQRT2; 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.fmod; 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 LazyJessFunc extends VariationFunc { private static final long serialVersionUID = 1L; private static final String PARAM_N = "n"; private static final String PARAM_SPIN = "spin"; private static final String PARAM_SPACE = "space"; private static final String PARAM_CORNER = "corner"; private static final String[] paramNames = { PARAM_N, PARAM_SPIN, PARAM_SPACE, PARAM_CORNER }; private int n = 4; private double spin = M_PI; private double space = 0.0; private int corner = 1; @Override public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) { /* lazyjess by FarDareisMai, http://fardareismai.deviantart.com/art/Apophysis-Plugin-Lazyjess-268929293 */ double theta, sina, cosa; double x = pAffineTP.x; double y = pAffineTP.y; double modulus = sqrt(x * x + y * y); // n==2 requires a special case if (n == 2) { if (fabs(x) < pAmount) {// If the input point falls inside the designated area... // ...then rotate it. theta = atan2(y, x) + spin; sina = sin(theta); cosa = cos(theta); x = pAmount * modulus * cosa; y = pAmount * modulus * sina; if (fabs(x) < pAmount) { pVarTP.x += x; pVarTP.y += y; } else {// If it is now part of a corner that falls outside the designated area... // ...then flip and rotate into place. theta = atan2(y, x) - spin + corner_rotation; sina = sin(theta); cosa = cos(theta); pVarTP.x += pAmount * modulus * cosa; pVarTP.y -= pAmount * modulus * sina; } } else { modulus = 1.0 + space / modulus; pVarTP.x += pAmount * modulus * x; pVarTP.y += pAmount * modulus * y; } } else { // Calculate the distance r from origin to the edge of the polygon at the angle of the input point. theta = atan2(y, x) + M_2PI; double theta_diff = fmod(theta + half_slice, pie_slice); double r = pAmount * M_SQRT2 * sin_vertex / sin(M_PI - theta_diff - vertex); if (modulus < r) { // Again, rotating points within designated area. theta = atan2(y, x) + spin + M_2PI; sina = sin(theta); cosa = cos(theta); x = pAmount * modulus * cosa; y = pAmount * modulus * sina; // Calculating r and modulus for our rotated point. theta_diff = fmod(theta + half_slice, pie_slice); r = pAmount * M_SQRT2 * sin_vertex / sin(M_PI - theta_diff - vertex); modulus = sqrt(x * x + y * y); if (modulus < r) { pVarTP.x += x; pVarTP.y += y; } else { // Again, flipping and rotating corners that fall outside the designated area. theta = atan2(y, x) - spin + corner_rotation + M_2PI; sina = sin(theta); cosa = cos(theta); pVarTP.x += pAmount * modulus * cosa; pVarTP.y -= pAmount * modulus * sina; } } else { modulus = 1.0 + space / modulus; pVarTP.x += pAmount * modulus * x; pVarTP.y += pAmount * modulus * y; } } if (pContext.isPreserveZCoordinate()) { pVarTP.z += pAmount * pAffineTP.z; } } @Override public String[] getParameterNames() { return paramNames; } @Override public Object[] getParameterValues() { return new Object[] { n, spin, space, corner }; } @Override public void setParameter(String pName, double pValue) { if (PARAM_N.equalsIgnoreCase(pName)) n = Tools.FTOI(pValue); else if (PARAM_SPIN.equalsIgnoreCase(pName)) spin = pValue; else if (PARAM_SPACE.equalsIgnoreCase(pName)) space = pValue; else if (PARAM_CORNER.equalsIgnoreCase(pName)) corner = Tools.FTOI(pValue); else throw new IllegalArgumentException(pName); } @Override public String getName() { return "lazyjess"; } private double vertex, sin_vertex, pie_slice, half_slice, corner_rotation; @Override public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) { if (n < 2) { n = 2; } vertex = M_PI * (n - 2) / (2 * n); sin_vertex = sin(vertex); pie_slice = M_2PI / n; half_slice = pie_slice / 2; corner_rotation = (corner - 1) * pie_slice; } }