/* 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.atan2; import static org.jwildfire.base.mathlib.MathLib.cos; import static org.jwildfire.base.mathlib.MathLib.exp; import static org.jwildfire.base.mathlib.MathLib.fabs; import static org.jwildfire.base.mathlib.MathLib.log; 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 PostSmartCropFunc extends VariationFunc { private static final long serialVersionUID = 1L; private static final String PARAM_POWER = "power"; private static final String PARAM_RADIUS = "radius"; private static final String PARAM_ROUNDSTR = "roundstr"; private static final String PARAM_ROUNDWIDTH = "roundwidth"; private static final String PARAM_DISTORTION = "distortion"; private static final String PARAM_EDGE = "edge"; private static final String PARAM_SCATTER = "scatter"; private static final String PARAM_OFFSET = "offset"; private static final String PARAM_CROPMODE = "cropmode"; private static final String PARAM_STATIC = "static"; private static final String[] paramNames = { PARAM_POWER, PARAM_RADIUS, PARAM_ROUNDSTR, PARAM_ROUNDWIDTH, PARAM_DISTORTION, PARAM_EDGE, PARAM_SCATTER, PARAM_OFFSET, PARAM_CROPMODE, PARAM_STATIC }; private double power = 4.0; private double radius = 1.0; private double roundstr = 0.0; private double roundwidth = 1.0; private double distortion = 1.0; private double edge = 0.0; private double scatter = 0.0; private double offset = 0.0; private int cropmode = 1; private int _static = 2; @Override public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) { // post_smartcrop by Zy0rg, http://zy0rg.deviantart.com/art/SmartCrop-267036043 final double xi, yi, zi; if (_static == 2) { xi = pVarTP.x; yi = pVarTP.y; zi = pVarTP.z; } else { xi = pAffineTP.x; yi = pAffineTP.y; zi = pAffineTP.z; } double ang = atan2(yi, xi); double rad = sqrt(xi * xi + yi * yi); if (post_scrop_radial) { double edge = this.edge * (pContext.random() - 0.5); double xang = ang / M_2PI + 1 + edge; xang = (xang - (int) xang) * M_2PI; if ((xang > post_scrop_workpower) == post_scrop_mode) { if (cropmode == 2) { if ((_static == 2) || (_static == -1)) { pVarTP.x = post_scrop_x; pVarTP.y = post_scrop_y; pVarTP.z = post_scrop_z; pVarTP.color = post_scrop_c; return; } pVarTP.x += post_scrop_x; pVarTP.y += post_scrop_y; pVarTP.z += post_scrop_z; pVarTP.color = post_scrop_c; return; } double a = ((int) (pContext.random() * 2.0)) > 0 ? post_scrop_workpower + (pContext.random() * scatter + offset + edge) * M_PI : -(pContext.random() * scatter + offset + edge) * M_PI; double s = sin(a); double c = cos(a); if ((_static == 2) || (_static == -1)) { pVarTP.x = pAmount * rad * c; pVarTP.y = pAmount * rad * s; pVarTP.z = pAmount * zi; return; } pVarTP.x += pAmount * rad * c; pVarTP.y += pAmount * rad * s; pVarTP.z += pAmount * zi; return; } } else { double coeff; if (distortion == 0.0) coeff = 1; else { double xang = (ang + M_PI) / post_scrop_alpha; xang = xang - (int) xang; xang = (xang < 0.5) ? xang : 1 - xang; coeff = 1 / cos(xang * post_scrop_alpha); if (roundstr != 0.0) { final double wwidth = ((roundwidth != 1.0) ? exp(log(xang * 2) * roundwidth) : (xang * 2)) * post_scrop_roundcoeff; coeff = fabs((1 - wwidth) * coeff + wwidth); } if (distortion != 1.0) coeff = exp(log(coeff) * distortion); } final double xr = coeff * ((edge != 0.0) ? post_scrop_workradius + edge * (pContext.random() - 0.5) : post_scrop_workradius); if ((rad > xr) == post_scrop_mode) { if (cropmode == 2) { if ((_static == 2) || (_static == -1)) { pVarTP.x = post_scrop_x; pVarTP.y = post_scrop_y; pVarTP.z = post_scrop_z; pVarTP.color = post_scrop_c; return; } pVarTP.x += post_scrop_x; pVarTP.y += post_scrop_y; pVarTP.z += post_scrop_z; pVarTP.color = post_scrop_c; return; } final double rdc = (cropmode > 0 ? 1 : 0) * xr + coeff * (pContext.random() * scatter + offset); double s = sin(ang); double c = cos(ang); if ((_static == 2) || (_static == -1)) { pVarTP.x = pAmount * rdc * c; pVarTP.y = pAmount * rdc * s; pVarTP.z = pAmount * zi; return; } pVarTP.x += pAmount * rdc * c; pVarTP.y += pAmount * rdc * s; pVarTP.z += pAmount * zi; return; } } post_scrop_x = pAmount * xi; post_scrop_y = pAmount * yi; post_scrop_z = pAmount * zi; if (cropmode == 2) post_scrop_c = pVarTP.color; if (_static > 0) { pVarTP.x = post_scrop_x; pVarTP.y = post_scrop_y; pVarTP.z = post_scrop_z; return; } pVarTP.x += post_scrop_x; pVarTP.y += post_scrop_y; pVarTP.z += post_scrop_z; return; } @Override public String[] getParameterNames() { return paramNames; } @Override public Object[] getParameterValues() { return new Object[] { power, radius, roundstr, roundwidth, distortion, edge, scatter, offset, cropmode, _static }; } @Override public void setParameter(String pName, double pValue) { if (PARAM_POWER.equalsIgnoreCase(pName)) power = pValue; else if (PARAM_RADIUS.equalsIgnoreCase(pName)) radius = pValue; else if (PARAM_ROUNDSTR.equalsIgnoreCase(pName)) roundstr = pValue; else if (PARAM_ROUNDWIDTH.equalsIgnoreCase(pName)) roundwidth = pValue; else if (PARAM_DISTORTION.equalsIgnoreCase(pName)) distortion = pValue; else if (PARAM_EDGE.equalsIgnoreCase(pName)) edge = pValue; else if (PARAM_SCATTER.equalsIgnoreCase(pName)) scatter = pValue; else if (PARAM_OFFSET.equalsIgnoreCase(pName)) offset = pValue; else if (PARAM_CROPMODE.equalsIgnoreCase(pName)) cropmode = limitIntVal(Tools.FTOI(pValue), 0, 2); else if (PARAM_STATIC.equalsIgnoreCase(pName)) _static = limitIntVal(Tools.FTOI(pValue), -1, 2); else throw new IllegalArgumentException(pName); } @Override public String getName() { return "post_smartcrop"; } private boolean post_scrop_mode, post_scrop_radial; private double post_scrop_workradius, post_scrop_workpower, post_scrop_alpha, post_scrop_roundcoeff; private double post_scrop_x, post_scrop_y, post_scrop_z, post_scrop_c; @Override public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) { post_scrop_mode = (power > 0) == (radius > 0); post_scrop_workradius = fabs(radius); post_scrop_workpower = fabs(power); if (post_scrop_workpower < 2) { post_scrop_workpower = post_scrop_workpower * M_PI; post_scrop_radial = true; } else { post_scrop_radial = false; post_scrop_alpha = M_2PI / post_scrop_workpower; post_scrop_roundcoeff = roundstr / sin(post_scrop_alpha / 2.0) / post_scrop_workpower * 2.0; } post_scrop_x = post_scrop_y = post_scrop_z = post_scrop_c = 0.0; } @Override public String[] getParameterAlternativeNames() { return new String[] { "post_scrop_power", "post_scrop_radius", "post_scrop_roundstr", "post_scrop_roundwidth", "post_scrop_distortion", "post_scrop_edge", "post_scrop_scatter", "post_scrop_offset", "post_scrop_cropmode", "post_scrop_static" }; } @Override public int getPriority() { return 1; } }