/* 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.EPSILON; import static org.jwildfire.base.mathlib.MathLib.M_2PI; import static org.jwildfire.base.mathlib.MathLib.M_E; import static org.jwildfire.base.mathlib.MathLib.M_PI; import static org.jwildfire.base.mathlib.MathLib.asin; 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.log; 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; public abstract class AbstractFalloff3Func extends VariationFunc { private static final long serialVersionUID = 1L; private static final String PARAM_BLUR_TYPE = "blur_type"; private static final String PARAM_BLUR_SHAPE = "blur_shape"; private static final String PARAM_BLUR_STRENGTH = "blur_strength"; private static final String PARAM_MIN_DISTANCE = "min_distance"; private static final String PARAM_INVERT_DISTANCE = "invert_distance"; private static final String PARAM_MUL_X = "mul_x"; private static final String PARAM_MUL_Y = "mul_y"; private static final String PARAM_MUL_Z = "mul_z"; private static final String PARAM_MUL_C = "mul_c"; private static final String PARAM_CENTER_X = "center_x"; private static final String PARAM_CENTER_Y = "center_y"; private static final String PARAM_CENTER_Z = "center_z"; private static final String PARAM_ALPHA = "alpha"; private static final String[] paramNames = { PARAM_BLUR_TYPE, PARAM_BLUR_SHAPE, PARAM_BLUR_STRENGTH, PARAM_MIN_DISTANCE, PARAM_INVERT_DISTANCE, PARAM_MUL_X, PARAM_MUL_Y, PARAM_MUL_Z, PARAM_MUL_C, PARAM_CENTER_X, PARAM_CENTER_Y, PARAM_CENTER_Z, PARAM_ALPHA }; //adjustment coefficient private static final double SCATTER_ADJUST = 0.04; // blur types private static final int BT_GAUSSIAN = 0; private static final int BT_RADIAL = 1; private static final int BT_LOG = 2; // blur shapes private static final int BS_CIRCLE = 0; private static final int BS_SQUARE = 1; private int blur_type = 0; private int blur_shape = 0; private double blur_strength = 1.0; private double min_distance = 0.5; private int invert_distance = 0; private double mul_x = 1.0; private double mul_y = 1.0; private double mul_z = 0.0; private double mul_c = 0.0; private double center_x = 0.0; private double center_y = 0.0; private double center_z = 0.0; protected double alpha = 0.0; @Override public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) { /* falloff3 by Xyrus02, http://xyrus-02.deviantart.com/art/FallOff3-Plugin-for-Apophysis-7x-451048315 */ fillVIn(pAffineTP, pVarTP, v_in); double weight = pAmount; double d_0 = min_distance; Double4 random = new Double4(pContext.random(), pContext.random(), pContext.random(), pContext.random()); double radius; switch (blur_shape) { case BS_CIRCLE: radius = bs_circle(v_in, center); break; case BS_SQUARE: radius = bs_square(v_in, center); break; default: throw new IllegalArgumentException("unsupported blur_shape <" + blur_shape + ">"); } double dist = Math.min(((invert_distance != 0 ? Math.min(1 - radius, 0) : Math.min(radius, 0)) - d_0) * r_max, 0); Double4 v_out; switch (blur_type) { case BT_GAUSSIAN: v_out = bt_gaussian(v_in, mul, random, dist); break; case BT_RADIAL: v_out = bt_radial(v_in, mul, random, dist); break; case BT_LOG: v_out = bt_log(v_in, mul, random, dist); break; default: throw new IllegalArgumentException("unsupported blur_type <" + blur_type + ">"); } // write back output vector applyVOut(pAffineTP, pVarTP, v_out, weight); // write back output color pVarTP.color = fabs(fmod(v_out.c, 1.0)); } protected abstract void applyVOut(XYZPoint pAffineTP, XYZPoint pVarTP, Double4 pVOut, double weight); protected abstract void fillVIn(XYZPoint pAffineTP, XYZPoint pVarTP, Double4 pVIn); @Override public String[] getParameterNames() { return paramNames; } @Override public Object[] getParameterValues() { return new Object[] { blur_type, blur_shape, blur_strength, min_distance, invert_distance, mul_x, mul_y, mul_z, mul_c, center_x, center_y, center_z, alpha }; } @Override public void setParameter(String pName, double pValue) { if (PARAM_BLUR_TYPE.equalsIgnoreCase(pName)) blur_type = limitIntVal(Tools.FTOI(pValue), 0, 2); else if (PARAM_BLUR_SHAPE.equalsIgnoreCase(pName)) blur_shape = limitIntVal(Tools.FTOI(pValue), 0, 1); else if (PARAM_BLUR_STRENGTH.equalsIgnoreCase(pName)) { blur_strength = pValue; if (blur_strength < 1.0e-6) blur_strength = 1.0e-6; } else if (PARAM_MIN_DISTANCE.equalsIgnoreCase(pName)) { min_distance = pValue; if (min_distance < 0.0) min_distance = 0.0; } else if (PARAM_MUL_X.equalsIgnoreCase(pName)) mul_x = limitVal(pValue, 0.0, 1.0); else if (PARAM_MUL_Y.equalsIgnoreCase(pName)) mul_y = limitVal(pValue, 0.0, 1.0); else if (PARAM_MUL_Z.equalsIgnoreCase(pName)) mul_z = limitVal(pValue, 0.0, 1.0); else if (PARAM_MUL_C.equalsIgnoreCase(pName)) mul_c = limitVal(pValue, 0.0, 1.0); else if (PARAM_CENTER_X.equalsIgnoreCase(pName)) center_x = pValue; else if (PARAM_CENTER_Y.equalsIgnoreCase(pName)) center_y = pValue; else if (PARAM_CENTER_Z.equalsIgnoreCase(pName)) center_z = pValue; else if (PARAM_INVERT_DISTANCE.equalsIgnoreCase(pName)) invert_distance = limitIntVal(Tools.FTOI(pValue), 0, 1); else if (PARAM_ALPHA.equalsIgnoreCase(pName)) alpha = pValue; else throw new IllegalArgumentException(pName); } @Override public String getName() { return "falloff3"; } protected Double4 v_in; protected Double4 mul; protected Double3 center; protected double r_max; @Override public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) { v_in = new Double4(); mul = new Double4(mul_x, mul_y, mul_z, mul_c); center = new Double3(center_x, center_y, center_z); r_max = SCATTER_ADJUST * blur_strength; } protected static class Double4 { double x; double y; double z; double c; public Double4() { } public Double4(double pX, double pY, double pZ, double pC) { x = pX; y = pY; z = pZ; c = pC; } } protected static class Double3 { double x; double y; double z; public Double3() { } public Double3(double pX, double pY, double pZ) { x = pX; y = pY; z = pZ; } } private double sgnd(double x) { return x < 0 ? -1 : 1; } private double log_scale(double x) { return x == 0 ? 0 : log((fabs(x) + 1.0) * M_E) * sgnd(x) / M_E; } private double log_map(double x) { return x == 0 ? 0 : (M_E + log(x * M_E)) / 4.0 * sgnd(x); } private Double4 bt_gaussian(Double4 v_in, Double4 mul, Double4 random, double dist) { double sigma = dist * random.y * M_2PI; double phi = dist * random.z * M_PI; double rad = dist * random.x; double sigma_s = sin(sigma); double sigma_c = cos(sigma); double phi_s = sin(phi); double phi_c = cos(phi); return new Double4( v_in.x + mul.x * rad * sigma_c * phi_c, v_in.y + mul.y * rad * sigma_c * phi_s, v_in.z + mul.z * rad * sigma_s, v_in.c + mul.c * dist * random.c); } private Double4 bt_radial(Double4 v_in, Double4 mul, Double4 random, double dist) { if (v_in.x == 0 && v_in.y == 0 && v_in.z == 0) return v_in; double r_in = sqrt(sqr(v_in.x) + sqr(v_in.y) + sqr(v_in.z)); double a = mul.y * dist + alpha * dist / sqrt(r_max); double b = mul.z * dist; double sigma = asin(v_in.z / r_in) + b * random.z; double phi = atan2(v_in.y, v_in.x) + a * random.y; double r = r_in + mul.x * random.x * dist; double sigma_s = sin(sigma); double sigma_c = cos(sigma); double phi_s = sin(phi); double phi_c = cos(phi); return new Double4( r * sigma_c * phi_c, r * sigma_c * phi_s, r * sigma_s, v_in.c + mul.c * random.c * dist); } private Double4 bt_log(Double4 v_in, Double4 mul, Double4 random, double dist) { double coeff = r_max <= EPSILON ? dist : dist + alpha * (log_map(dist) - dist); return new Double4( v_in.x + log_map(mul.x) * log_scale(random.x) * coeff, v_in.y + log_map(mul.y) * log_scale(random.y) * coeff, v_in.z + log_map(mul.z) * log_scale(random.z) * coeff, v_in.c + log_map(mul.c) * log_scale(random.c) * coeff); } private double bs_circle(Double4 v_in, Double3 center) { double distance = sqrt( sqr(v_in.x - center.x) + sqr(v_in.y - center.y) + sqr(v_in.z - center.z)); return distance; } private double bs_square(Double4 v_in, Double3 center) { return Math.min(fabs(v_in.x - center.x), Math.min(fabs(v_in.y - center.y), (fabs(v_in.z - center.z)))); } }