/* JWildfire - an image and animation processor written in Java Copyright (C) 1995-2016 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.plot; import static org.jwildfire.base.mathlib.MathLib.EPSILON; import static org.jwildfire.base.mathlib.MathLib.fabs; import static org.jwildfire.base.mathlib.MathLib.sqrt; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.jwildfire.base.Tools; import org.jwildfire.base.mathlib.GfxMathLib; 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; import org.jwildfire.create.tina.palette.RenderColor; import org.jwildfire.create.tina.variation.ColorMapHolder; import org.jwildfire.create.tina.variation.FlameTransformationContext; import org.jwildfire.create.tina.variation.RessourceType; import org.jwildfire.create.tina.variation.mesh.AbstractOBJMeshWFFunc; public class IsoSFPlot3DWFFunc extends AbstractOBJMeshWFFunc { private static final long serialVersionUID = 1L; private static final String PARAM_PRESET_ID = "preset_id"; 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_ZMIN = "zmin"; private static final String PARAM_ZMAX = "zmax"; private static final String PARAM_DIRECT_COLOR = "direct_color"; private static final String PARAM_COLOR_MODE = "color_mode"; private static final String PARAM_BLEND_COLORMAP = "blend_colormap"; private static final String PARAM_THICKNESS = "thickness"; private static final String PARAM_MAX_ITER = "max_iter"; private static final String PARAM_PARAM_A = "param_a"; private static final String PARAM_PARAM_B = "param_b"; private static final String PARAM_PARAM_C = "param_c"; private static final String PARAM_PARAM_D = "param_d"; private static final String PARAM_PARAM_E = "param_e"; private static final String PARAM_PARAM_F = "param_f"; private static final String RESSOURCE_FORMULA = "formula"; private static final String RESSOURCE_COLORMAP_FILENAME = "colormap_filename"; private static final String[] paramNames = { PARAM_PRESET_ID, PARAM_XMIN, PARAM_XMAX, PARAM_YMIN, PARAM_YMAX, PARAM_ZMIN, PARAM_ZMAX, PARAM_THICKNESS, PARAM_MAX_ITER, PARAM_DIRECT_COLOR, PARAM_COLOR_MODE, PARAM_BLEND_COLORMAP, PARAM_PARAM_A, PARAM_PARAM_B, PARAM_PARAM_C, PARAM_PARAM_D, PARAM_PARAM_E, PARAM_PARAM_F }; private static final String[] ressourceNames = { RESSOURCE_FORMULA, RESSOURCE_COLORMAP_FILENAME }; private static final int CM_COLORMAP_X = 0; private static final int CM_COLORMAP_Y = 1; private static final int CM_COLORMAP_Z = 2; private static final int CM_X = 3; private static final int CM_Y = 4; private static final int CM_Z = 5; private static final int CM_XY = 6; private static final int CM_YZ = 7; private static final int CM_ZX = 8; private static final int CM_XYZ = 9; private int preset_id = -1; private double xmin = -Math.PI; private double xmax = Math.PI; private double ymin = -Math.PI; private double ymax = Math.PI; private double zmin = -Math.PI; private double zmax = Math.PI; private int direct_color = 1; private int color_mode = CM_XYZ; private double param_a = 0.0; private double param_b = 0.0; private double param_c = 0.0; private double param_d = 0.0; private double param_e = 0.0; private double param_f = 0.0; private double thickness = 0.05; private int max_iter = 160; private String formula; private ColorMapHolder colorMapHolder = new ColorMapHolder(); private IsoSFPlot3DFormulaEvaluator evaluator; @Override public String[] getParameterNames() { return paramNames; } @Override public Object[] getParameterValues() { return new Object[] { preset_id, xmin, xmax, ymin, ymax, zmin, zmax, thickness, max_iter, direct_color, color_mode, colorMapHolder.getBlend_colormap(), param_a, param_b, param_c, param_d, param_e, param_f }; } @Override public void setParameter(String pName, double pValue) { if (PARAM_PRESET_ID.equalsIgnoreCase(pName)) { preset_id = Tools.FTOI(pValue); if (preset_id >= 0) { refreshFormulaFromPreset(preset_id); } } else if (PARAM_XMIN.equalsIgnoreCase(pName)) { xmin = pValue; validatePresetId(); } else if (PARAM_XMAX.equalsIgnoreCase(pName)) { xmax = pValue; validatePresetId(); } else if (PARAM_YMIN.equalsIgnoreCase(pName)) { ymin = pValue; validatePresetId(); } else if (PARAM_YMAX.equalsIgnoreCase(pName)) { ymax = pValue; validatePresetId(); } else if (PARAM_ZMIN.equalsIgnoreCase(pName)) { zmin = pValue; validatePresetId(); } else if (PARAM_ZMAX.equalsIgnoreCase(pName)) { zmax = pValue; validatePresetId(); } else if (PARAM_DIRECT_COLOR.equalsIgnoreCase(pName)) { direct_color = Tools.FTOI(pValue); } else if (PARAM_COLOR_MODE.equalsIgnoreCase(pName)) { color_mode = Tools.FTOI(pValue); } else if (PARAM_BLEND_COLORMAP.equalsIgnoreCase(pName)) { colorMapHolder.setBlend_colormap(limitIntVal(Tools.FTOI(pValue), 0, 1)); } else if (PARAM_PARAM_A.equalsIgnoreCase(pName)) { param_a = pValue; } else if (PARAM_PARAM_B.equalsIgnoreCase(pName)) { param_b = pValue; } else if (PARAM_PARAM_C.equalsIgnoreCase(pName)) { param_c = pValue; } else if (PARAM_PARAM_D.equalsIgnoreCase(pName)) { param_d = pValue; } else if (PARAM_PARAM_E.equalsIgnoreCase(pName)) { param_e = pValue; } else if (PARAM_PARAM_F.equalsIgnoreCase(pName)) { param_f = pValue; } else if (PARAM_MAX_ITER.equalsIgnoreCase(pName)) { max_iter = Tools.FTOI(pValue); if (max_iter < 1) { max_iter = 1; } } else if (PARAM_THICKNESS.equalsIgnoreCase(pName)) { thickness = pValue; if (thickness < EPSILON) { thickness = EPSILON; } } else throw new IllegalArgumentException(pName); } @Override public String getName() { return "isosfplot3d_wf"; } @Override public String[] getRessourceNames() { return ressourceNames; } @Override public byte[][] getRessourceValues() { return new byte[][] { (formula != null ? formula.getBytes() : null), (colorMapHolder.getColormap_filename() != null ? colorMapHolder.getColormap_filename().getBytes() : null) }; } @Override public void setRessource(String pName, byte[] pValue) { if (RESSOURCE_FORMULA.equalsIgnoreCase(pName)) { formula = pValue != null ? new String(pValue) : ""; validatePresetId(); } else if (RESSOURCE_COLORMAP_FILENAME.equalsIgnoreCase(pName)) { colorMapHolder.setColormap_filename(pValue != null ? new String(pValue) : ""); colorMapHolder.clear(); uvIdxMap.clear(); } else throw new IllegalArgumentException(pName); } @Override public RessourceType getRessourceType(String pName) { if (RESSOURCE_FORMULA.equalsIgnoreCase(pName)) { return RessourceType.BYTEARRAY; } else if (RESSOURCE_COLORMAP_FILENAME.equalsIgnoreCase(pName)) { return RessourceType.IMAGE_FILENAME; } else throw new IllegalArgumentException(pName); } private double _xmin, _xmax, _dx; private double _ymin, _ymax, _dy; private double _zmin, _zmax, _dz; @Override public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) { hits = new AtomicInteger(0); misses = new AtomicInteger(0); super.init(pContext, pLayer, pXForm, pAmount); _xmin = xmin; _xmax = xmax; if (_xmin > _xmax) { double t = _xmin; _xmin = _xmax; _xmax = t; } _dx = _xmax - _xmin; _ymin = ymin; _ymax = ymax; if (_ymin > _ymax) { double t = _ymin; _ymin = _ymax; _ymax = t; } _dy = _ymax - _ymin; _zmin = zmin; _zmax = zmax; if (_zmin > _zmax) { double t = _zmin; _zmin = _zmax; _zmax = t; } _dz = _zmax - _zmin; String code = "import static org.jwildfire.base.mathlib.MathLib.*;\r\n" + "\r\n" + " public double evaluate(double x,double y,double z) {\r\n" + " double pi = M_PI;\r\n" + " double param_a = " + param_a + ";\r\n" + " double param_b = " + param_b + ";\r\n" + " double param_c = " + param_c + ";\r\n" + " double param_d = " + param_d + ";\r\n" + " double param_e = " + param_e + ";\r\n" + " double param_f = " + param_f + ";\r\n" + " return " + (formula != null && !formula.isEmpty() ? formula : "0.0") + ";\r\n" + " }\r\n"; try { evaluator = IsoSFPlot3DFormulaEvaluator.compile(code); } catch (Exception e) { evaluator = null; e.printStackTrace(); System.out.println(code); throw new IllegalArgumentException(e); } } public IsoSFPlot3DWFFunc() { super(); preset_id = WFFuncPresetsStore.getIsoSFPlot3DWFFuncPresets().getRandomPresetId(); refreshFormulaFromPreset(preset_id); } private void validatePresetId() { if (preset_id >= 0) { IsoSFPlot3DWFFuncPreset preset = WFFuncPresetsStore.getIsoSFPlot3DWFFuncPresets().getPreset(preset_id); if (!preset.getFormula().equals(formula) || (fabs(xmin - preset.getXmin()) > EPSILON) || (fabs(xmax - preset.getXmax()) > EPSILON) || (fabs(ymin - preset.getYmin()) > EPSILON) || (fabs(ymax - preset.getYmax()) > EPSILON) || (fabs(zmin - preset.getZmin()) > EPSILON) || (fabs(zmax - preset.getZmax()) > EPSILON)) { preset_id = -1; } } } private void refreshFormulaFromPreset(int presetId) { IsoSFPlot3DWFFuncPreset preset = WFFuncPresetsStore.getIsoSFPlot3DWFFuncPresets().getPreset(presetId); formula = preset.getFormula(); xmin = preset.getXmin(); xmax = preset.getXmax(); ymin = preset.getYmin(); ymax = preset.getYmax(); zmin = preset.getZmin(); zmax = preset.getZmax(); param_a = preset.getParam_a(); param_b = preset.getParam_b(); param_c = preset.getParam_c(); param_d = preset.getParam_d(); param_e = preset.getParam_e(); param_f = preset.getParam_f(); } private RenderColor[] uvColors; protected Map<RenderColor, Double> uvIdxMap = new HashMap<RenderColor, Double>(); private double getUVColorIdx(int pR, int pG, int pB) { RenderColor pColor = new RenderColor(pR, pG, pB); Double res = uvIdxMap.get(pColor); if (res == null) { int nearestIdx = 0; RenderColor color = uvColors[0]; double dr, dg, db; dr = (color.red - pR); dg = (color.green - pG); db = (color.blue - pB); double nearestDist = sqrt(dr * dr + dg * dg + db * db); for (int i = 1; i < uvColors.length; i++) { color = uvColors[i]; dr = (color.red - pR); dg = (color.green - pG); db = (color.blue - pB); double dist = sqrt(dr * dr + dg * dg + db * db); if (dist < nearestDist) { nearestDist = dist; nearestIdx = i; } } res = (double) nearestIdx / (double) (uvColors.length - 1); uvIdxMap.put(pColor, res); } return res; } private static AtomicInteger hits, misses; @Override public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) { if (evaluator == null) { return; } pVarTP.doHide = true; double x = 0.0, y = 0.0, z = 0.0; for (int i = 0; i < max_iter; i++) { x = _xmin + pContext.random() * _dx; y = _ymin + pContext.random() * _dy; z = _zmin + pContext.random() * _dz; double e = evaluator.evaluate(x, y, z); if (fabs(e) <= thickness) { pVarTP.x += pAmount * x; pVarTP.y += pAmount * y; pVarTP.z += pAmount * z; pVarTP.doHide = false; break; } } // if (pVarTP.doHide) { // misses.addAndGet(1); // } // else { // hits.addAndGet(1); // } // int m = misses.get(); // int h = hits.get(); // int sum = m + h; // if (sum % 100000000 == 0) { // System.out.println("h: " + h + ", m: " + m + ", misses: " + Tools.FTOI(100.0 * (double) m / (double) (m + h)) + "%"); // } if (!pVarTP.doHide && direct_color > 0) { switch (color_mode) { case CM_X: pVarTP.color = (x - _xmin) / _dx; break; case CM_Y: pVarTP.color = (y - _ymin) / _dy; break; case CM_Z: pVarTP.color = (z - _zmin) / _dz; break; case CM_COLORMAP_X: case CM_COLORMAP_Y: case CM_COLORMAP_Z: if (colorMapHolder.isActive()) { double iu, iv; switch (color_mode) { case CM_COLORMAP_X: iu = GfxMathLib.clamp(y * (colorMapHolder.getColorMapWidth() - 1.0), 0.0, colorMapHolder.getColorMapWidth() - 1.0); iv = GfxMathLib.clamp(colorMapHolder.getColorMapHeight() - 1.0 - z * (colorMapHolder.getColorMapHeight() - 1.0), 0, colorMapHolder.getColorMapHeight() - 1.0); case CM_COLORMAP_Y: iu = GfxMathLib.clamp(z * (colorMapHolder.getColorMapWidth() - 1.0), 0.0, colorMapHolder.getColorMapWidth() - 1.0); iv = GfxMathLib.clamp(colorMapHolder.getColorMapHeight() - 1.0 - x * (colorMapHolder.getColorMapHeight() - 1.0), 0, colorMapHolder.getColorMapHeight() - 1.0); case CM_COLORMAP_Z: default: iu = GfxMathLib.clamp(x * (colorMapHolder.getColorMapWidth() - 1.0), 0.0, colorMapHolder.getColorMapWidth() - 1.0); iv = GfxMathLib.clamp(colorMapHolder.getColorMapHeight() - 1.0 - y * (colorMapHolder.getColorMapHeight() - 1.0), 0, colorMapHolder.getColorMapHeight() - 1.0); break; } int ix = (int) MathLib.trunc(iu); int iy = (int) MathLib.trunc(iv); colorMapHolder.applyImageColor(pVarTP, ix, iy, iu, iv); pVarTP.color = getUVColorIdx(Tools.FTOI(pVarTP.redColor), Tools.FTOI(pVarTP.greenColor), Tools.FTOI(pVarTP.blueColor)); } break; case CM_XY: pVarTP.color = (x - _xmin) / _dx * (y - _ymin) / _dy; break; case CM_YZ: pVarTP.color = (y - _ymin) / _dy * (z - _zmin) / _dz; break; case CM_ZX: pVarTP.color = (z - _zmin) / _dz * (x - _xmin) / _dx; break; default: case CM_XYZ: pVarTP.color = (x - _xmin) / _dx * (y - _ymin) / _dy * (z - _zmin) / _dz; break; } if (pVarTP.color < 0.0) pVarTP.color = 0.0; else if (pVarTP.color > 1.0) pVarTP.color = 1.0; } } }