/*
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.transform;
import org.jwildfire.base.Property;
import org.jwildfire.base.PropertyCategory;
import org.jwildfire.base.mathparser.JEPWrapper;
import org.jwildfire.image.WFImage;
import org.nfunk.jep.Node;
import com.l2fprod.common.beans.editor.ComboBoxPropertyEditor;
public class ZPlot2DTransformer extends Mesh3DTransformer {
public enum Axis {
X, Y, XY, RADIAL
};
@Property(description = "Damping of the effect", category = PropertyCategory.SECONDARY)
private double damping;
@Property(description = "Damping on/off", category = PropertyCategory.SECONDARY)
private boolean damp;
@Property(description = "X-coordinate of the effect origin (for damping)", category = PropertyCategory.SECONDARY)
private double originX;
@Property(description = "Y-coordinate of the effect origin (for damping)", category = PropertyCategory.SECONDARY)
private double originY;
@Property(description = "Z-coordinate of the effect origin (for damping)", category = PropertyCategory.SECONDARY)
private double originZ;
@Property(description = "Propagation axis of the effect", editorClass = AxisEditor.class)
private Axis axis;
@Property(description = "Minimum of the X-range")
private double xMin;
@Property(description = "Maximum of the X-range")
private double xMax;
@Property(description = "Formula for the amplitude")
private String formula;
@Override
protected void transformMesh(Mesh3D pMesh3D, int pImageWidth, int pImageHeight) {
int pCount = pMesh3D.getPCount();
int width = pImageWidth;
int height = pImageHeight;
double x[] = pMesh3D.getX();
double y[] = pMesh3D.getY();
double z[] = pMesh3D.getZ();
JEPWrapper parser = new JEPWrapper();
parser.addVariable("x", 0.0);
Node node = parser.parse(formula);
// Don't calculate the actual bounding box because this may cause unexpected results if the object was deformed by another tansformer before
double objXMin = -(double) width / 2.0;
// double objXMax = (double) width / 2.0;
double objYMin = -(double) height / 2.0;
// double objYMax = (double) height / 2.0;
double objXSize = (double) width;
double objYSize = (double) height;
double dx = this.xMax - this.xMin;
if (this.axis == Axis.X) {
if (!this.damp) {
for (int i = 0; i < pCount; i++) {
double xx = ((x[i] - objXMin) * dx) / objXSize + xMin;
parser.setVarValue("x", xx);
double amp = (Double) parser.evaluate(node);
z[i] -= amp;
}
}
else {
for (int i = 0; i < pCount; i++) {
double xx = ((x[i] - objXMin) * dx) / objXSize + xMin;
parser.setVarValue("x", xx);
double amp = (Double) parser.evaluate(node);
double dxx = ((x[i] - objXMin) * dx) / objXSize + xMin - originX;
double drr = Math.abs(dxx);
double dmp = drr * damping;
amp *= Math.exp(dmp);
z[i] -= amp;
}
}
}
else if (this.axis == Axis.Y) {
if (!this.damp) {
for (int i = 0; i < pCount; i++) {
double yy = ((y[i] - objYMin) * dx) / objYSize + xMin;
parser.setVarValue("x", yy);
double amp = (Double) parser.evaluate(node);
z[i] -= amp;
}
}
else {
for (int i = 0; i < pCount; i++) {
double yy = ((y[i] - objYMin) * dx) / objYSize + xMin;
parser.setVarValue("x", yy);
double amp = (Double) parser.evaluate(node);
double dyy = ((y[i] - objYMin) * dx) / objYSize + xMin - originY;
double drr = Math.abs(dyy);
double dmp = drr * damping;
amp *= Math.exp(dmp);
z[i] -= amp;
}
}
}
else if (this.axis == Axis.XY) {
if (!this.damp) {
for (int i = 0; i < pCount; i++) {
double xx = ((x[i] - objXMin) * dx) / objXSize + xMin;
double yy = ((y[i] - objYMin) * dx) / objYSize + xMin;
double rr = Math.sqrt(xx * xx + yy * yy);
parser.setVarValue("x", rr);
double amp = (Double) parser.evaluate(node);
z[i] -= amp;
}
}
else {
for (int i = 0; i < pCount; i++) {
double xx = ((x[i] - objXMin) * dx) / objXSize + xMin;
double yy = ((y[i] - objYMin) * dx) / objYSize + xMin;
double rr = Math.sqrt(xx * xx + yy * yy);
parser.setVarValue("x", rr);
double amp = (Double) parser.evaluate(node);
double dxx = ((x[i] - objXMin) * dx) / objXSize + xMin - originX;
double dyy = ((y[i] - objYMin) * dx) / objYSize + xMin - originY;
double drr = Math.sqrt(dxx * dxx + dyy * dyy);
double dmp = drr * damping;
amp *= Math.exp(dmp);
z[i] -= amp;
}
}
}
else { /* radial */
if (!this.damp) {
for (int i = 0; i < pCount; i++) {
double xx = ((x[i] - objXMin) * dx) / objXSize + xMin - originX;
double yy = ((y[i] - objYMin) * dx) / objYSize + xMin - originY;
double zz = z[i] - originZ;
double rr = Math.sqrt(xx * xx + yy * yy + zz * zz);
parser.setVarValue("x", rr);
double amp = (Double) parser.evaluate(node);
double vx, vy, vz;
if (rr > 0.00001) {
vx = xx / rr;
vy = yy / rr;
vz = zz / rr;
}
else {
vx = vy = 0.0;
vz = 1.0;
}
x[i] += vx * amp;
y[i] += vy * amp;
z[i] += vz * amp;
}
}
else {
for (int i = 0; i < pCount; i++) {
double xx = ((x[i] - objXMin) * dx) / objXSize + xMin - originX;
double yy = ((y[i] - objYMin) * dx) / objYSize + xMin - originY;
double zz = z[i] - originZ;
double rr = Math.sqrt(xx * xx + yy * yy + zz * zz);
parser.setVarValue("x", rr);
double amp = (Double) parser.evaluate(node);
double drr = rr;
double dmp = drr * damping;
amp *= Math.exp(dmp);
double vx, vy, vz;
if (rr > 0.00001) {
vx = xx / rr;
vy = yy / rr;
vz = zz / rr;
}
else {
vx = vy = 0.0;
vz = 1.0;
}
x[i] += vx * amp;
y[i] += vy * amp;
z[i] += vz * amp;
}
}
}
}
@Override
public void initDefaultParams(WFImage pImg) {
super.initDefaultParams(pImg);
damping = -0.5;
damp = true;
originX = 0.0;
originY = 0.0;
originZ = 0.0;
axis = Axis.XY;
xMin = -5.0;
xMax = 3.0;
formula = "60*sin(2*x*x)";
// 40*(sin(x)+2*sin(2*x)+1*sin(4*x)), x=-5..5
beta = 45.0;
zoom = 1.2;
}
public double getDamping() {
return damping;
}
public void setDamping(double damping) {
this.damping = damping;
}
public boolean isDamp() {
return damp;
}
public void setDamp(boolean damp) {
this.damp = damp;
}
public double getOriginX() {
return originX;
}
public void setOriginX(double originX) {
this.originX = originX;
}
public double getOriginY() {
return originY;
}
public void setOriginY(double originY) {
this.originY = originY;
}
public double getOriginZ() {
return originZ;
}
public void setOriginZ(double originZ) {
this.originZ = originZ;
}
public Axis getAxis() {
return axis;
}
public void setAxis(Axis axis) {
this.axis = axis;
}
public static class AxisEditor extends ComboBoxPropertyEditor {
public AxisEditor() {
super();
setAvailableValues(new Axis[] { Axis.X, Axis.Y, Axis.XY, Axis.RADIAL });
}
}
public double getXMin() {
return xMin;
}
public void setXMin(double xMin) {
this.xMin = xMin;
}
public double getXMax() {
return xMax;
}
public void setXMax(double xMax) {
this.xMax = xMax;
}
public String getFormula() {
return formula;
}
public void setFormula(String formula) {
this.formula = formula;
}
@Override
public void initPresets() {
addPreset("60*sin(2*x*x)").
addProperty("formula", "60*sin(2*x*x)").
addProperty("axis", Axis.XY).
addProperty("xMin", -5.0).
addProperty("xMax", 3.0);
addPreset("40*(sin(x)+2*sin(2*x)+1*sin(4*x))").
addProperty("formula", "40*(sin(x)+2*sin(2*x)+1*sin(4*x))").
addProperty("axis", Axis.XY).
addProperty("xMin", -5.0).
addProperty("xMax", 5.0);
}
}