/* 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 java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.jwildfire.base.Tools; import org.jwildfire.base.mathlib.MathLib; import org.jwildfire.create.tina.base.Layer; import org.jwildfire.create.tina.base.XForm; import org.jwildfire.create.tina.base.XYZPoint; public abstract class AbstractFractWFFunc extends VariationFunc { private static final long serialVersionUID = 1L; private static final String PARAM_MAX_ITER = "max_iter"; private static final String PARAM_XMIN = "xmin"; private static final String PARAM_XMAX = "xmax"; private static final String PARAM_YMIN = "ymin"; private static final String PARAM_YMAX = "ymax"; private static final String PARAM_BUDDHABROT_MODE = "buddhabrot_mode"; private static final String PARAM_BUDDHABROT_MIN_ITER = "buddhabrot_min_iter"; private static final String PARAM_DIRECT_COLOR = "direct_color"; private static final String PARAM_SCALEZ = "scalez"; private static final String PARAM_CLIP_ITER_MIN = "clip_iter_min"; private static final String PARAM_CLIP_ITER_MAX = "clip_iter_max"; private static final String PARAM_MAX_CLIP_ITER = "max_clip_iter"; private static final String PARAM_SCALE = "scale"; private static final String PARAM_OFFSETX = "offsetx"; private static final String PARAM_OFFSETY = "offsety"; private static final String PARAM_OFFSETZ = "offsetz"; private static final String PARAM_Z_FILL = "z_fill"; private static final String PARAM_Z_LOGSCALE = "z_logscale"; protected int max_iter = 100; protected double xmin = -1.6; protected double xmax = 1.6; protected double ymin = -1.2; protected double ymax = 1.2; protected int direct_color = 1; protected double scalez = 1.0; protected int clip_iter_min = 3; protected int clip_iter_max = -5; protected double scale = 3.0; protected double offsetx = 0.0; protected double offsety = 0.0; protected double offsetz = 0.0; protected int max_clip_iter = 3; protected int buddhabrot_mode = 0; protected int buddhabrot_min_iter = 7; protected double z_fill = 0.0; protected int z_logscale = 0; public AbstractFractWFFunc() { initParams(); } public abstract class Iterator implements Serializable { private static final long serialVersionUID = 1L; protected double xs, ys; public int currIter, maxIter; public double startX, startY; public double currX, currY; public double nextX, nextY; public void init(double pX0, double pY0) { xs = ys = 0; startX = currX = pX0; startY = currY = pY0; currIter = 0; } public void setCurrPoint(double pX, double pY) { currX = pX; currY = pY; currIter++; } protected boolean bailout() { return (xs + ys >= 4.0); } protected int iterate(double pStartX, double pStartY, int pMaxIter) { init(pStartX, pStartY); int currIter = 0; while ((currIter++ < pMaxIter) && !bailout()) { nextIteration(); } return currIter; } protected boolean preBuddhaIterate(double pStartX, double pStartY, int pMaxIter) { init(pStartX, pStartY); int currIter = 0; while ((currIter++ < pMaxIter) && !bailout()) { nextIteration(); } if (bailout()) { maxIter = currIter; return maxIter > buddhabrot_min_iter; } else { return false; } } protected abstract void nextIteration(); } @Override public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) { chooseNewPoint = true; } private boolean chooseNewPoint; @Override public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) { if (buddhabrot_mode > 0) transformBuddhabrot(pContext, pXForm, pAffineTP, pVarTP, pAmount); else transformIterate(pContext, pXForm, pAffineTP, pVarTP, pAmount); } public void transformBuddhabrot(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) { Iterator iterator = getIterator(); double x0, y0; if (chooseNewPoint) { while (true) { x0 = (xmax - xmin) * pContext.random() + xmin; y0 = (ymax - ymin) * pContext.random() + ymin; if (iterator.preBuddhaIterate(x0, y0, max_iter)) { break; } } chooseNewPoint = false; iterator.init(x0, y0); for (int skip = 0; skip < buddhabrot_min_iter; skip++) { iterator.nextIteration(); } } iterator.nextIteration(); if (iterator.currIter >= iterator.maxIter) { chooseNewPoint = true; } pVarTP.x += scale * pAmount * (iterator.currX + offsetx); pVarTP.y += scale * pAmount * (iterator.currY + offsety); pVarTP.color += (double) iterator.currIter / (double) iterator.maxIter; if (pVarTP.color < 0) pVarTP.color = 0; else if (pVarTP.color > 1.0) pVarTP.color = 1.0; } public void transformIterate(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) { pVarTP.doHide = false; Iterator iterator = getIterator(); double x0 = 0.0, y0 = 0.0; int iterCount = 0; for (int i = 0; i < max_clip_iter; i++) { x0 = (xmax - xmin) * pContext.random() + xmin; y0 = (ymax - ymin) * pContext.random() + ymin; iterCount = iterator.iterate(x0, y0, max_iter); if ((clip_iter_max < 0 && iterCount >= (max_iter + clip_iter_max)) || (clip_iter_min > 0 && iterCount <= clip_iter_min)) { if (i == max_clip_iter - 1) { // pVarTP.x = pVarTP.y = pVarTP.z = -120000.0 * (pContext.random() + 0.5); // pVarTP.rgbColor = true; // pVarTP.redColor = pVarTP.greenColor = pVarTP.blueColor = 0; pVarTP.doHide = true; return; } } else { break; } } pVarTP.x += scale * pAmount * (x0 + offsetx); pVarTP.y += scale * pAmount * (y0 + offsety); double z; if (z_logscale == 1) { z = scale * pAmount * (scalez / 10 * MathLib.log10(1.0 + (double) iterCount / (double) max_iter) + offsetz); if (z_fill > MathLib.EPSILON && pContext.random() < z_fill) { double prevZ = scale * pAmount * (scalez / 10 * MathLib.log10(1.0 + (double) (iterCount - 1) / (double) max_iter) + offsetz); z = (prevZ - z) * pContext.random() + z; } } else { z = scale * pAmount * (scalez / 10 * ((double) iterCount / (double) max_iter) + offsetz); if (z_fill > MathLib.EPSILON && pContext.random() < z_fill) { double prevZ = scale * pAmount * (scalez / 10 * ((double) (iterCount - 1) / (double) max_iter) + offsetz); z = (prevZ - z) * pContext.random() + z; } } pVarTP.z += z; if (direct_color != 0) { pVarTP.color += (double) iterCount / (double) max_iter; if (pVarTP.color > 1.0) pVarTP.color -= 1.0; if (pVarTP.color < 0) pVarTP.color = 0; else if (pVarTP.color > 1.0) pVarTP.color = 1.0; } } protected abstract Iterator getIterator(); protected abstract void initParams(); protected abstract void addCustomParameterNames(List<String> pList); protected abstract void addCustomParameterValues(List<Object> pList); protected abstract boolean setCustomParameter(String pName, double pValue); @Override public String[] getParameterNames() { List<String> lst = new ArrayList<String>(); lst.add(PARAM_MAX_ITER); lst.add(PARAM_XMIN); lst.add(PARAM_XMAX); lst.add(PARAM_YMIN); lst.add(PARAM_YMAX); lst.add(PARAM_BUDDHABROT_MODE); addCustomParameterNames(lst); lst.add(PARAM_DIRECT_COLOR); lst.add(PARAM_SCALEZ); lst.add(PARAM_CLIP_ITER_MIN); lst.add(PARAM_CLIP_ITER_MAX); lst.add(PARAM_MAX_CLIP_ITER); lst.add(PARAM_BUDDHABROT_MIN_ITER); lst.add(PARAM_SCALE); lst.add(PARAM_OFFSETX); lst.add(PARAM_OFFSETY); lst.add(PARAM_OFFSETZ); lst.add(PARAM_Z_FILL); lst.add(PARAM_Z_LOGSCALE); return lst.toArray(new String[0]); } @Override public Object[] getParameterValues() { List<Object> lst = new ArrayList<Object>(); lst.add(max_iter); lst.add(xmin); lst.add(xmax); lst.add(ymin); lst.add(ymax); lst.add(buddhabrot_mode); addCustomParameterValues(lst); lst.add(direct_color); lst.add(scalez); lst.add(clip_iter_min); lst.add(clip_iter_max); lst.add(max_clip_iter); lst.add(buddhabrot_min_iter); lst.add(scale); lst.add(offsetx); lst.add(offsety); lst.add(offsetz); lst.add(z_fill); lst.add(z_logscale); return lst.toArray(); } @Override public void setParameter(String pName, double pValue) { if (PARAM_MAX_ITER.equalsIgnoreCase(pName)) max_iter = Tools.FTOI(pValue); else if (PARAM_XMIN.equalsIgnoreCase(pName)) xmin = pValue; else if (PARAM_XMAX.equalsIgnoreCase(pName)) xmax = pValue; else if (PARAM_YMIN.equalsIgnoreCase(pName)) ymin = pValue; else if (PARAM_YMAX.equalsIgnoreCase(pName)) ymax = pValue; else if (PARAM_DIRECT_COLOR.equalsIgnoreCase(pName)) direct_color = Tools.FTOI(pValue); else if (PARAM_SCALEZ.equalsIgnoreCase(pName)) scalez = pValue; else if (PARAM_CLIP_ITER_MIN.equalsIgnoreCase(pName)) clip_iter_min = Tools.FTOI(pValue); else if (PARAM_CLIP_ITER_MAX.equalsIgnoreCase(pName)) clip_iter_max = Tools.FTOI(pValue); else if (PARAM_MAX_CLIP_ITER.equalsIgnoreCase(pName)) max_clip_iter = Tools.FTOI(pValue); else if (PARAM_SCALE.equalsIgnoreCase(pName)) scale = pValue; else if (PARAM_OFFSETX.equalsIgnoreCase(pName)) offsetx = pValue; else if (PARAM_OFFSETY.equalsIgnoreCase(pName)) offsety = pValue; else if (PARAM_OFFSETZ.equalsIgnoreCase(pName)) offsetz = pValue; else if (PARAM_BUDDHABROT_MODE.equalsIgnoreCase(pName)) buddhabrot_mode = Tools.FTOI(pValue); else if (PARAM_BUDDHABROT_MIN_ITER.equalsIgnoreCase(pName)) buddhabrot_min_iter = Tools.FTOI(pValue); else if (PARAM_Z_FILL.equalsIgnoreCase(pName)) z_fill = limitVal(pValue, 0, 1); else if (PARAM_Z_LOGSCALE.equalsIgnoreCase(pName)) z_logscale = limitIntVal(Tools.FTOI(pValue), 0, 1); else if (!setCustomParameter(pName, pValue)) throw new IllegalArgumentException(pName); } }