/* 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_PI_2; import static org.jwildfire.base.mathlib.MathLib.acos; 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.iabs; 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 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 BubbleT3DFunc extends VariationFunc { private static final long serialVersionUID = 1L; private static final String PARAM_NUMBER_OF_STRIPES = "number_of_stripes"; private static final String PARAM_RATIO_OF_STRIPES = "ratio_of_stripes"; private static final String PARAM_ANGLE_OF_HOLE = "angle_of_hole"; private static final String PARAM_EXPONENT_Z = "exponent_z"; private static final String PARAM_SYMMETRY_Z = "symmetry_z"; private static final String PARAM_MODUS_BLUR = "modus_blur"; private static final String[] paramNames = { PARAM_NUMBER_OF_STRIPES, PARAM_RATIO_OF_STRIPES, PARAM_ANGLE_OF_HOLE, PARAM_EXPONENT_Z, PARAM_SYMMETRY_Z, PARAM_MODUS_BLUR }; private int number_of_stripes = 0; private double ratio_of_stripes = 1.0; private double angle_of_hole = 0.0; private double exponent_z = 1.0; private int symmetry_z = 0; private int modus_blur = 0; @Override public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) { /* bubbleT3D by FractalDesire, http://fractaldesire.deviantart.com/art/bubbleT3D-plugin-188587423 */ double x = pAffineTP.x, y = pAffineTP.y, z = pAffineTP.z; double xTmp, yTmp, angTmp, angRot, fac; double rad = (sqr(x) + sqr(y)) / 4 + 1; double angXY, angZ; // calculation of stripes angXY = atan2(x, y); if (angXY < 0) angXY += M_2PI; if (number_of_stripes != 0) { while (angXY > angStrip2) { angXY -= angStrip2; } if (invStripes == FALSE) { if (angXY > angStrip1) { if (modus_blur == FALSE) { x = 0.0; y = 0.0; } else { if (ratio_of_stripes == 1) { xTmp = c * x - s * y; yTmp = s * x + c * y; x = xTmp; y = yTmp; } else { angRot = (angXY - angStrip1) / (angStrip2 - angStrip1); angRot = angXY - angRot * angStrip1; s = sin(angRot); c = cos(angRot); xTmp = c * x - s * y; yTmp = s * x + c * y; x = xTmp; y = yTmp; } } } } else { if (angXY < angStrip1) { if (modus_blur == FALSE) { x = 0.0; y = 0.0; } else { if (ratio_of_stripes == 1) { xTmp = c * x - s * y; yTmp = s * x + c * y; x = xTmp; y = yTmp; } else { angRot = (angXY - angStrip1) / (angStrip1); angRot = angXY - angRot * (angStrip2 - angStrip1); s = sin(angRot); c = cos(angRot); xTmp = c * x - s * y; yTmp = s * x + c * y; x = xTmp; y = yTmp; } } } } } //calculation of holes x = x / rad; y = y / rad; if ((x != 0) || (y != 0)) { z = 2 / pow(rad, exponent_z) - 1; if (exponent_z <= 2) angZ = M_PI - acos((z / (x * x + y * y + z * z))); else angZ = M_PI - atan2(sqr(x * x + y * y), z); } else { z = 0.0; angZ = 0.0; } // no symmetry to z-axis if (symmetry_z == FALSE) { if (invHole == FALSE) { if (angZ > angle_of_hole) { if ((modus_blur == FALSE) || (exponent_z != 1.0)) { x = 0.0; y = 0.0; z = 0.0; } else { angTmp = (M_PI - angZ) / angHoleComp * angle_of_hole - M_PI_2; angZ -= M_PI_2; fac = cos(angTmp) / cos(angZ); x = x * fac; y = y * fac; z = z * (sin(angTmp) / sin(angZ)); } } } else { if (angZ < angle_of_hole) { if ((modus_blur == FALSE) || (exponent_z != 1.0)) { x = 0.0; y = 0.0; z = 0.0; } else { angTmp = M_PI - angZ / angHoleComp * angle_of_hole - M_PI_2; angZ -= M_PI_2; fac = cos(angTmp) / cos(angZ); x = x * fac; y = y * fac; z = z * (sin(angTmp) / sin(angZ)); } } } } // symmetry to z-axis else { if ((angZ > angle_of_hole) || (angZ < (M_PI - angle_of_hole))) { if ((modus_blur == FALSE) || (exponent_z != 1.0)) { x = 0.0; y = 0.0; z = 0.0; } else { if (angZ > angle_of_hole) { angTmp = (M_PI - angZ) / angHoleComp * (M_PI - 2 * angHoleComp) + angHoleComp - M_PI_2; } else { angTmp = M_PI_2 - (angZ / angHoleComp * (M_PI - 2 * angHoleComp) + angHoleComp); } angZ -= M_PI_2; fac = cos(angTmp) / cos(angZ); x = x * fac; y = y * fac; z = z * (sin(angTmp) / sin(angZ)); } } } pVarTP.x += pAmount * x; pVarTP.y += pAmount * y; pVarTP.z += pAmount * z; } @Override public String[] getParameterNames() { return paramNames; } @Override public Object[] getParameterValues() { return new Object[] { number_of_stripes, ratio_of_stripes, angle_of_hole, exponent_z, symmetry_z, modus_blur }; } @Override public String[] getParameterAlternativeNames() { return new String[] { "number_of_stripes", "ratio_of_stripes", "angle_of_hole", "exponentZ", "_symmetryZ", "_modusBlur" }; } @Override public void setParameter(String pName, double pValue) { if (PARAM_NUMBER_OF_STRIPES.equalsIgnoreCase(pName)) { number_of_stripes = Tools.FTOI(pValue); if (number_of_stripes < 0) number_of_stripes = 0; } else if (PARAM_RATIO_OF_STRIPES.equalsIgnoreCase(pName)) ratio_of_stripes = limitVal(pValue, 0.0, 2.0); else if (PARAM_ANGLE_OF_HOLE.equalsIgnoreCase(pName)) angle_of_hole = pValue; else if (PARAM_EXPONENT_Z.equalsIgnoreCase(pName)) exponent_z = pValue; else if (PARAM_SYMMETRY_Z.equalsIgnoreCase(pName)) symmetry_z = Tools.FTOI(pValue); else if (PARAM_MODUS_BLUR.equalsIgnoreCase(pName)) modus_blur = Tools.FTOI(pValue); else throw new IllegalArgumentException(pName); } @Override public String getName() { return "bubbleT3D"; } private static final int TRUE = 1; private static final int FALSE = 0; private double angHoleComp; private double angStrip, angStrip1, angStrip2; private int invStripes, invHole; private double s, c; @Override public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) { // calculation of constants of stripes if (number_of_stripes < 0) { number_of_stripes = iabs(number_of_stripes); invStripes = TRUE; } else { invStripes = FALSE; } if (number_of_stripes != 0) { angStrip = M_PI / (double) number_of_stripes; angStrip2 = 2 * angStrip; s = sin(angStrip); c = cos(angStrip); if (ratio_of_stripes < 0.01) ratio_of_stripes = 0.01; if (ratio_of_stripes > 1.99) ratio_of_stripes = 1.99; angStrip1 = ratio_of_stripes * angStrip; } // calculation of constants of holes if (symmetry_z == TRUE) { if (angle_of_hole < 0.0) angle_of_hole = fabs(angle_of_hole); if (angle_of_hole > 179.9) angle_of_hole = 179.9; } if (angle_of_hole < 0) { angle_of_hole = fabs(angle_of_hole); invHole = TRUE; angle_of_hole = (angle_of_hole / 360 * M_2PI) / 2; } else { invHole = FALSE; angle_of_hole = M_PI - (angle_of_hole / 360 * M_2PI) / 2; } angHoleComp = M_PI - angle_of_hole; } }