/* 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 org.jwildfire.base.Tools; import org.jwildfire.base.mathlib.GfxMathLib; import org.jwildfire.base.mathlib.MathLib; import org.jwildfire.base.mathlib.VecMathLib.VectorD; 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.DisplacementMapHolder; import org.jwildfire.create.tina.variation.FlameTransformationContext; import org.jwildfire.create.tina.variation.RessourceType; import org.jwildfire.create.tina.variation.VariationFunc; public class YPlot3DWFFunc extends VariationFunc { 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_DISPL_AMOUNT = "displ_amount"; private static final String PARAM_BLEND_DISPLMAP = "blend_displ_map"; private static final String RESSOURCE_FORMULA = "formula"; private static final String RESSOURCE_COLORMAP_FILENAME = "colormap_filename"; private static final String RESSOURCE_DISPL_MAP_FILENAME = "displ_map_filename"; 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[] paramNames = { PARAM_PRESET_ID, PARAM_XMIN, PARAM_XMAX, PARAM_YMIN, PARAM_YMAX, PARAM_ZMIN, PARAM_ZMAX, PARAM_DIRECT_COLOR, PARAM_COLOR_MODE, PARAM_BLEND_COLORMAP, PARAM_DISPL_AMOUNT, PARAM_BLEND_DISPLMAP, 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, RESSOURCE_DISPL_MAP_FILENAME }; private int preset_id = -1; private double xmin = -3.0; private double xmax = 2.0; private double ymin = -4.0; private double ymax = 4.0; private double zmin = -2.0; private double zmax = 2.0; private int direct_color = 1; private int color_mode = CM_Z; 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 static final int CM_COLORMAP = 0; private static final int CM_X = 1; private static final int CM_Y = 2; private static final int CM_Z = 3; private static final int CM_XZ = 4; private String formula; private ColorMapHolder colorMapHolder = new ColorMapHolder(); private DisplacementMapHolder displacementMapHolder = new DisplacementMapHolder(); private YPlot3DFormulaEvaluator evaluator; @Override public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) { if (evaluator == null) { return; } double randU = pContext.random(); double randV = pContext.random(); double x = _xmin + randU * _dx; double z = _zmin + randV * _dz; double y = evaluator.evaluate(x, z); if (displacementMapHolder.isActive()) { double epsx = _dx / 100.0; double x1 = x + epsx; double y1 = evaluator.evaluate(x1, z); double epsz = _dz / 100.0; double z1 = z + epsz; double y2 = evaluator.evaluate(x, z1); VectorD av = new VectorD(epsx, y1 - y, 0); VectorD bv = new VectorD(0.0, y2 - y, epsz); VectorD n = VectorD.cross(av, bv); n.normalize(); double iu = GfxMathLib.clamp(randU * (displacementMapHolder.getDisplacementMapWidth() - 1.0), 0.0, displacementMapHolder.getDisplacementMapWidth() - 1.0); double iv = GfxMathLib.clamp(displacementMapHolder.getDisplacementMapHeight() - 1.0 - randV * (displacementMapHolder.getDisplacementMapHeight() - 1.0), 0, displacementMapHolder.getDisplacementMapHeight() - 1.0); int ix = (int) MathLib.trunc(iu); int iy = (int) MathLib.trunc(iv); double d = displacementMapHolder.calculateImageDisplacement(ix, iy, iu, iv) * _displ_amount; pVarTP.x += pAmount * n.x * d; pVarTP.y += pAmount * n.y * d; pVarTP.z += pAmount * n.z * d; } if (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_COLORMAP: if (colorMapHolder.isActive()) { double iu = GfxMathLib.clamp(randU * (colorMapHolder.getColorMapWidth() - 1.0), 0.0, colorMapHolder.getColorMapWidth() - 1.0); double iv = GfxMathLib.clamp(colorMapHolder.getColorMapHeight() - 1.0 - randV * (colorMapHolder.getColorMapHeight() - 1.0), 0, colorMapHolder.getColorMapHeight() - 1.0); 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_XZ: pVarTP.color = (x - _xmin) / _dx * (z - _zmin) / _dz; break; default: case CM_Z: pVarTP.color = (z - _zmin) / _dz; break; } if (pVarTP.color < 0.0) pVarTP.color = 0.0; else if (pVarTP.color > 1.0) pVarTP.color = 1.0; } 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[] { preset_id, xmin, xmax, ymin, ymax, zmin, zmax, direct_color, color_mode, colorMapHolder.getBlend_colormap(), displacementMapHolder.getDispl_amount(), displacementMapHolder.getBlend_displ_map(), 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; } else if (PARAM_ZMAX.equalsIgnoreCase(pName)) { zmax = pValue; } 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_DISPL_AMOUNT.equalsIgnoreCase(pName)) { displacementMapHolder.setDispl_amount(pValue); } else if (PARAM_BLEND_DISPLMAP.equalsIgnoreCase(pName)) { displacementMapHolder.setBlend_displ_map(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 throw new IllegalArgumentException(pName); } @Override public String getName() { return "yplot3d_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), (displacementMapHolder.getDispl_map_filename() != null ? displacementMapHolder.getDispl_map_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 if (RESSOURCE_DISPL_MAP_FILENAME.equalsIgnoreCase(pName)) { displacementMapHolder.setDispl_map_filename(pValue != null ? new String(pValue) : ""); displacementMapHolder.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 if (RESSOURCE_DISPL_MAP_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; private double _displ_amount; @Override public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) { colorMapHolder.init(); uvColors = pLayer.getPalette().createRenderPalette(pContext.getFlameRenderer().getFlame().getWhiteLevel()); displacementMapHolder.init(); _displ_amount = displacementMapHolder.getDispl_amount() / 255.0; _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; evaluator = null; if (!formula.isEmpty()) { String code = "import static org.jwildfire.base.mathlib.MathLib.*;\r\n" + "\r\n" + " public double evaluate(double x,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 + ";\r\n" + " }\r\n"; try { evaluator = YPlot3DFormulaEvaluator.compile(code); } catch (Exception e) { evaluator = null; e.printStackTrace(); System.out.println(code); throw new IllegalArgumentException(e); } } } public YPlot3DWFFunc() { super(); preset_id = WFFuncPresetsStore.getYPlot3DWFFuncPresets().getRandomPresetId(); refreshFormulaFromPreset(preset_id); } private void validatePresetId() { if (preset_id >= 0) { YPlot3DWFFuncPreset preset = WFFuncPresetsStore.getYPlot3DWFFuncPresets().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)) { preset_id = -1; } } } private void refreshFormulaFromPreset(int presetId) { YPlot3DWFFuncPreset preset = WFFuncPresetsStore.getYPlot3DWFFuncPresets().getPreset(presetId); formula = preset.getFormula(); xmin = preset.getXmin(); xmax = preset.getXmax(); ymin = preset.getYmin(); ymax = preset.getYmax(); 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; } }