/*
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 static org.jwildfire.base.mathlib.MathLib.M_PI;
import static org.jwildfire.base.mathlib.MathLib.cos;
import static org.jwildfire.base.mathlib.MathLib.fabs;
import static org.jwildfire.base.mathlib.MathLib.sin;
import static org.jwildfire.base.mathlib.MathLib.trunc;
import org.jwildfire.create.tina.base.Layer;
import org.jwildfire.create.tina.base.XForm;
import org.jwildfire.create.tina.base.XYZPoint;
public class Hexnix3DFunc extends VariationFunc {
private static final long serialVersionUID = 1L;
private static final String PARAM_MAJP = "majp";
private static final String PARAM_SCALE = "scale";
private static final String PARAM_ZLIFT = "zlift";
private static final String PARAM_3SIDE = "3side";
private static final String[] paramNames = { PARAM_MAJP, PARAM_SCALE, PARAM_ZLIFT, PARAM_3SIDE };
private double majp = 1.0; // establishes 1 or 2 planes, and if 2, the distance between them
private double scale = 0.25; // scales the effect of X and Y
private double zlift = 0.0; // scales the effect of Z axis within the snowflake
private double _3side = 0.67; // scales the triangle within the hex - the 120 degree figure
private double _seg60x[] = new double[6];
private double _seg60y[] = new double[6];
private double _seg120x[] = new double[3];
private double _seg120y[] = new double[3];
private int _rswtch; // for choosing between 6 or 3 segments to a plane
private int _fcycle; // markers to count cycles...
private int _bcycle;
@Override
public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) {
/* hexnix3D by Larry Berlin, http://aporev.deviantart.com/art/3D-Plugins-Collection-One-138514007?q=gallery%3Aaporev%2F8229210&qo=15 */
if (_fcycle > 5) {// Resets the cyclic counting
_fcycle = 0;
_rswtch = (int) trunc(pContext.random() * 3.0); // Chooses new 6 or 3 nodes
}
if (_bcycle > 2) {
_bcycle = 0;
_rswtch = (int) trunc(pContext.random() * 3.0); // Chooses new 6 or 3 nodes
}
double lrmaj = pAmount; // Sets hexagon length radius - major plane
double smooth = 1.0;
double smRotxTP = 0.0;
double smRotyTP = 0.0;
double smRotxFT = 0.0;
double smRotyFT = 0.0;
double gentleZ = 0.0;
if (fabs(pAmount) <= 0.5) {
smooth = pAmount * 2.0;
}
else {
smooth = 1.0;
}
double boost = 0.0; // Boost is the separation distance between the two planes
int posNeg = 1;
int loc60;
int loc120;
double scale = this.scale;
double scale3 = this._3side;
if (pContext.random() < 0.5) {
posNeg = -1;
}
// Determine whether one or two major planes
int majplane = 0;
double abmajp = fabs(this.majp);
if (abmajp <= 1.0) {
majplane = 0; // 0= 1 plate active 1= transition to two plates active 2= defines two plates
boost = 0.0;
}
else if (abmajp > 1.0 && abmajp < 2.0) {
majplane = 1;
boost = 0.0;
}
else {
majplane = 2;
boost = (abmajp - 2.0) * 0.5; // distance above and below XY plane
}
// Creating Z factors relative to the planes
if (majplane == 0) {
pVarTP.z += smooth * pAffineTP.z * scale * this.zlift; // single plate instructions
}
else if (majplane == 1 && this.majp < 0.0) {// Transition for reversing plates because majp is negative value
if (this.majp < -1.0 && this.majp >= -2.0) {
gentleZ = (abmajp - 1.0); // Set transition smoothing values 0.00001 to 1.0
}
else {
gentleZ = 1.0; // full effect explicit default value
}
// Begin reverse transition - starts with pVarTP.z==pVarTP.z proceeds by gradual negative
if (posNeg < 0) {
pVarTP.z += -2.0 * (pVarTP.z * gentleZ); // gradually grows negative plate, in place, no boost,
}
}
if (majplane == 2 && this.majp < 0.0) {// Begin the splitting operation, animation transition is done
if (posNeg > 0) {// The splitting operation positive side
pVarTP.z += (smooth * (pAffineTP.z * scale * this.zlift + boost));
}
else {// The splitting operation negative side
pVarTP.z = (pVarTP.z - (2.0 * smooth * pVarTP.z)) + (smooth * posNeg * (pAffineTP.z * scale * this.zlift + boost));
}
}
else {// majp > 0.0 The splitting operation
pVarTP.z += smooth * (pAffineTP.z * scale * this.zlift + (posNeg * boost));
}
if (this._rswtch <= 1) {// Occasion to build using 60 degree segments
loc60 = (int) trunc(pContext.random() * 6.0); // random nodes selection
//loc60 = this.fcycle; // sequential nodes selection - seems to create artifacts that are progressively displaced
smRotxTP = (smooth * scale * pVarTP.x * _seg60x[loc60]) - (smooth * scale * pVarTP.y * _seg60y[loc60]);
smRotyTP = (smooth * scale * pVarTP.y * _seg60x[loc60]) + (smooth * scale * pVarTP.x * _seg60y[loc60]);
smRotxFT = (pAffineTP.x * smooth * scale * _seg60x[loc60]) - (pAffineTP.y * smooth * scale * _seg60y[loc60]);
smRotyFT = (pAffineTP.y * smooth * scale * _seg60x[loc60]) + (pAffineTP.x * smooth * scale * _seg60y[loc60]);
pVarTP.x = pVarTP.x * (1.0 - smooth) + smRotxTP + smRotxFT + smooth * lrmaj * _seg60x[loc60];
pVarTP.y = pVarTP.y * (1.0 - smooth) + smRotyTP + smRotyFT + smooth * lrmaj * _seg60y[loc60];
this._fcycle += 1;
}
else { // Occasion to build on 120 degree segments
loc120 = (int) trunc(pContext.random() * 3.0); // random nodes selection
//loc120 = this.bcycle; // sequential nodes selection - seems to create artifacts that are progressively displaced
smRotxTP = (smooth * scale * pVarTP.x * _seg120x[loc120]) - (smooth * scale * pVarTP.y * _seg120y[loc120]);
smRotyTP = (smooth * scale * pVarTP.y * _seg120x[loc120]) + (smooth * scale * pVarTP.x * _seg120y[loc120]);
smRotxFT = (pAffineTP.x * smooth * scale * _seg120x[loc120]) - (pAffineTP.y * smooth * scale * _seg120y[loc120]);
smRotyFT = (pAffineTP.y * smooth * scale * _seg120x[loc120]) + (pAffineTP.x * smooth * scale * _seg120y[loc120]);
pVarTP.x = pVarTP.x * (1.0 - smooth) + smRotxTP + smRotxFT + smooth * lrmaj * scale3 * _seg120x[loc120];
pVarTP.y = pVarTP.y * (1.0 - smooth) + smRotyTP + smRotyFT + smooth * lrmaj * scale3 * _seg120y[loc120];
this._bcycle += 1;
}
/*
Rotations need to interchange smoothly between x and y values (pseudo code)
new x = in_x*cos(r) - in_y*sin(r) + movedLoci_x;
new y = in_y*cos(r) + in_x*sin(r) + movedLoci_y;
*/
}
@Override
public String[] getParameterNames() {
return paramNames;
}
@Override
public Object[] getParameterValues() {
return new Object[] { majp, scale, zlift, _3side };
}
@Override
public void setParameter(String pName, double pValue) {
if (PARAM_MAJP.equalsIgnoreCase(pName))
majp = pValue;
else if (PARAM_SCALE.equalsIgnoreCase(pName))
scale = pValue;
else if (PARAM_ZLIFT.equalsIgnoreCase(pName))
zlift = pValue;
else if (PARAM_3SIDE.equalsIgnoreCase(pName))
_3side = pValue;
else
throw new IllegalArgumentException(pName);
}
@Override
public String getName() {
return "hexnix3D";
}
@Override
public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) {
/* Set up two major angle systems */
_rswtch = (int) trunc(pContext.random() * 3.0); // Chooses 6 or 3 nodes
double hlift = sin(M_PI / 3.0); // sin(60)
_fcycle = 0;
_bcycle = 0;
_seg60x[0] = 1.0;
_seg60x[1] = 0.5;
_seg60x[2] = -0.5;
_seg60x[3] = -1.0;
_seg60x[4] = -0.5;
_seg60x[5] = 0.5;
_seg60y[0] = 0.0;
_seg60y[1] = -hlift;
_seg60y[2] = -hlift;
_seg60y[3] = 0.0;
_seg60y[4] = hlift;
_seg60y[5] = hlift;
_seg120x[0] = 0.0; // These settings cause the 3-node setting to
_seg120x[1] = cos(7.0 * M_PI / 6.0); // rotate 30 degrees relative to the hex structure.
_seg120x[2] = cos(11.0 * M_PI / 6.0); //
_seg120y[0] = -1.0;
_seg120y[1] = 0.5;
_seg120y[2] = 0.5;
}
}