/* 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 static org.jwildfire.base.mathlib.MathLib.trunc; 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 ParPlot2DWFFunc extends VariationFunc { private static final long serialVersionUID = 1L; private static final String PARAM_PRESET_ID = "preset_id"; private static final String PARAM_UMIN = "umin"; private static final String PARAM_UMAX = "umax"; private static final String PARAM_VMIN = "vmin"; private static final String PARAM_VMAX = "vmax"; 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 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_XFORMULA = "xformula"; private static final String RESSOURCE_YFORMULA = "yformula"; private static final String RESSOURCE_ZFORMULA = "zformula"; private static final String RESSOURCE_COLORMAP_FILENAME = "colormap_filename"; private static final String RESSOURCE_DISPL_MAP_FILENAME = "displ_map_filename"; private static final String[] paramNames = { PARAM_PRESET_ID, PARAM_UMIN, PARAM_UMAX, PARAM_VMIN, PARAM_VMAX, 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_XFORMULA, RESSOURCE_YFORMULA, RESSOURCE_ZFORMULA, RESSOURCE_COLORMAP_FILENAME, RESSOURCE_DISPL_MAP_FILENAME }; private static final int CM_COLORMAP = 0; private static final int CM_U = 1; private static final int CM_V = 2; private static final int CM_UV = 3; private int preset_id = -1; private double umin = 0.0; private double umax = 2.0 * Math.PI; private double vmin = 0.0; private double vmax = 2.0 * Math.PI; private int direct_color = 1; private int color_mode = CM_UV; 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 String xformula; private String yformula; private String zformula; private ColorMapHolder colorMapHolder = new ColorMapHolder(); private DisplacementMapHolder displacementMapHolder = new DisplacementMapHolder(); private ParPlot2DFormulaEvaluator 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 u = _umin + randU * _du; double v = _vmin + randV * _dv; double x = evaluator.evaluateX(u, v); double y = evaluator.evaluateY(u, v); double z = evaluator.evaluateZ(u, v); if (displacementMapHolder.isActive()) { double epsu = _du / 100.0; double u1 = u + epsu; double x1 = evaluator.evaluateX(u1, v); double y1 = evaluator.evaluateY(u1, v); double z1 = evaluator.evaluateZ(u1, v); double epsv = _dv / 100.0; double v1 = v + epsv; double x2 = evaluator.evaluateX(u, v1); double y2 = evaluator.evaluateY(u, v1); double z2 = evaluator.evaluateZ(u, v1); VectorD av = new VectorD(x1 - x, y1 - y, z1 - z); VectorD bv = new VectorD(x2 - x, y2 - y, z2 - z); 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) trunc(iu); int iy = (int) 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_V: pVarTP.color = (v - _vmin) / _dv; break; case CM_U: pVarTP.color = (u - _umin) / _du; 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; default: case CM_UV: pVarTP.color = (v - _vmin) / _dv * (u - _umin) / _du; 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, umin, umax, vmin, vmax, 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_UMIN.equalsIgnoreCase(pName)) { umin = pValue; validatePresetId(); } else if (PARAM_UMAX.equalsIgnoreCase(pName)) { umax = pValue; validatePresetId(); } else if (PARAM_VMIN.equalsIgnoreCase(pName)) { vmin = pValue; validatePresetId(); } else if (PARAM_VMAX.equalsIgnoreCase(pName)) { vmax = 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_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 "parplot2d_wf"; } @Override public String[] getRessourceNames() { return ressourceNames; } @Override public byte[][] getRessourceValues() { return new byte[][] { (xformula != null ? xformula.getBytes() : null), (yformula != null ? yformula.getBytes() : null), (zformula != null ? zformula.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_XFORMULA.equalsIgnoreCase(pName)) { xformula = pValue != null ? new String(pValue) : ""; validatePresetId(); } else if (RESSOURCE_YFORMULA.equalsIgnoreCase(pName)) { yformula = pValue != null ? new String(pValue) : ""; validatePresetId(); } else if (RESSOURCE_ZFORMULA.equalsIgnoreCase(pName)) { zformula = 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_XFORMULA.equalsIgnoreCase(pName)) { return RessourceType.BYTEARRAY; } else if (RESSOURCE_YFORMULA.equalsIgnoreCase(pName)) { return RessourceType.BYTEARRAY; } else if (RESSOURCE_ZFORMULA.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 _umin, _umax, _du; private double _vmin, _vmax, _dv; 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; _umin = umin; _umax = umax; if (_umin > _umax) { double t = _umin; _umin = _umax; _umax = t; } _du = _umax - _umin; _vmin = vmin; _vmax = vmax; if (_vmin > _vmax) { double t = _vmin; _vmin = _vmax; _vmax = t; } _dv = _vmax - _vmin; String code = "import static org.jwildfire.base.mathlib.MathLib.*;\r\n" + "\r\n" + " public double evaluateX(double u,double v) {\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 " + (xformula != null && !xformula.isEmpty() ? xformula : "0.0") + ";\r\n" + " }\r\n" + " public double evaluateY(double u,double v) {\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 " + (yformula != null && !yformula.isEmpty() ? yformula : "0.0") + ";\r\n" + " }\r\n" + " public double evaluateZ(double u,double v) {\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 " + (zformula != null && !zformula.isEmpty() ? zformula : "0.0") + ";\r\n" + " }\r\n"; try { evaluator = ParPlot2DFormulaEvaluator.compile(code); } catch (Exception e) { evaluator = null; e.printStackTrace(); System.out.println(code); throw new IllegalArgumentException(e); } } public ParPlot2DWFFunc() { super(); preset_id = WFFuncPresetsStore.getParPlot2DWFFuncPresets().getRandomPresetId(); refreshFormulaFromPreset(preset_id); } private void validatePresetId() { if (preset_id >= 0) { ParPlot2DWFFuncPreset preset = WFFuncPresetsStore.getParPlot2DWFFuncPresets().getPreset(preset_id); if (!preset.getXformula().equals(xformula) || !preset.getYformula().equals(yformula) || !preset.getZformula().equals(zformula) || (fabs(umin - preset.getUmin()) > EPSILON) || (fabs(umax - preset.getUmax()) > EPSILON) || (fabs(vmin - preset.getVmin()) > EPSILON) || (fabs(vmax - preset.getVmax()) > EPSILON)) { preset_id = -1; } } } private void refreshFormulaFromPreset(int presetId) { ParPlot2DWFFuncPreset preset = WFFuncPresetsStore.getParPlot2DWFFuncPresets().getPreset(presetId); xformula = preset.getXformula(); yformula = preset.getYformula(); zformula = preset.getZformula(); umin = preset.getUmin(); umax = preset.getUmax(); vmin = preset.getVmin(); vmax = preset.getVmax(); 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; } }