/*
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.floor;
import org.jwildfire.base.Tools;
import org.jwildfire.create.tina.base.Layer;
import org.jwildfire.create.tina.base.XForm;
import org.jwildfire.create.tina.base.XYZPoint;
public class ScramblyFunc extends VariationFunc {
private static final long serialVersionUID = 1L;
private static final String PARAM_L = "l";
private static final String PARAM_SEED = "seed";
private static final String PARAM_BYROWS = "byrows";
private static final String PARAM_CELLSIZE = "cellsize";
private static final String[] paramNames = { PARAM_L, PARAM_SEED, PARAM_BYROWS, PARAM_CELLSIZE };
private int l = 3;
private int seed = 51;
private int byrows = 0;
private double cellsize = 0.1;
@Override
public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) {
/* scrambly by dark-beam, http://dark-beam.deviantart.com/art/Scrambly-463910011 */
double Vx = pAffineTP.x;
double Vy = pAffineTP.y;
int Ix;
int Iy;
int swp;
Vx += _mzcella;
Vy += _mzcella;
Vx *= _cellainv;
Vy *= _cellainv;
Ix = (int) floor(Vx);
Iy = (int) floor(Vy);
if (_current_l <= 0 || Ix < 0 || Ix >= _current_l || Iy < 0 || Iy >= _current_l) {
pVarTP.x += pAmount * pAffineTP.x;
pVarTP.y += pAmount * pAffineTP.y;
}
else {
Vx -= Ix;
Vy -= Iy;
// randomizing int parts:
swp = Ix + _current_l * Iy;
swp = (int) _mx_rd[swp];
Iy = swp / _current_l;
Ix = swp % _current_l;
// add int parts to fractional:
Vx += Ix;
Vy += Iy;
Vx *= _cella;
Vy *= _cella;
Vx -= _mzcella;
Vy -= _mzcella;
pVarTP.x += pAmount * Vx;
pVarTP.y += pAmount * Vy;
}
if (pContext.isPreserveZCoordinate()) {
pVarTP.z += pAmount * pAffineTP.z;
}
}
@Override
public String[] getParameterNames() {
return paramNames;
}
@Override
public Object[] getParameterValues() {
return new Object[] { l, seed, byrows, cellsize };
}
@Override
public void setParameter(String pName, double pValue) {
if (PARAM_L.equalsIgnoreCase(pName))
l = Tools.FTOI(pValue);
else if (PARAM_SEED.equalsIgnoreCase(pName))
seed = Tools.FTOI(pValue);
else if (PARAM_BYROWS.equalsIgnoreCase(pName))
byrows = Tools.FTOI(pValue);
else if (PARAM_CELLSIZE.equalsIgnoreCase(pName))
cellsize = limitVal(pValue, 1.0e-20, 1.0e20);
else
throw new IllegalArgumentException(pName);
}
@Override
public String getName() {
return "scrambly";
}
private static final int MX_L = 25; // set it lower on low memory machines
private static final int MX_L2 = 626; // set it lower on low memory machines
private int _current_l;
private double _cella;
private double _cellainv;
private double _mzcella;
private int _mx_rd[];
@Override
public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) {
_mx_rd = new int[MX_L2];
int curL = this.l;
int seed = this.seed;
int byrows = this.byrows;
int j;
int LL, LL2;
LL = curL * SGN(curL);
if (LL < 3)
LL = 3; // min
else if (LL > MX_L)
LL = MX_L; // max
LL2 = LL * LL;
_current_l = LL;
if (seed >= 0 && seed <= 50) {
for (j = 0; j < LL2; j++) {
_mx_rd[j] = (seed + j + 1) % LL2; // shifts the matrix (interesting effect)
}
}
else {
for (j = 0; j < LL2; j++) {
_mx_rd[j] = j;
}
if (byrows == 0) {
mx_randflip(0, LL2, seed, _mx_rd); // scrambles the whole matrix
}
else {
for (j = 0; j < LL; j++) {
mx_randflip(LL * j, LL * (1 + j), seed + j, _mx_rd); // scrambles row by row
}
}
}
_cella = this.cellsize / _current_l;
_mzcella = 0.5 * this.cellsize;
_cellainv = 1.0 / _cella;
}
private void mx_randflip(int idxmin, int idxmax, int seed, int[] mxrdp) {
int i, j, prn, ridx;
prn = 1;
for (j = idxmin;; j++) { // 1 means that I used a custom cycle break rule.
prn = prn * 1103515245 + 12345; // jwildfire source code :D
prn = prn & 0xFFFF0000 |
((prn << 8) & 0xFF00) | ((prn >> 8) & 0x00FF); // flippy bytes
prn = (prn & 4) != 0 ? prn - seed : prn ^ seed;
prn = (prn < 0) ? -prn : prn; // neg are troubleful
ridx = 1 + j;
if (idxmax > ridx) {
ridx += prn % (idxmax - ridx);
}
else {
//printf("STOP> %i %i %i\n",ridx, idxmin , idxmax);
break;
}
//printf("!! %i ",ridx);
i = mxrdp[ridx];
mxrdp[ridx] = mxrdp[j];
mxrdp[j] = i;
//printf("-> %i\n",mx_rd[j]);
}
//printf("rflip end-> %i\n",mx_rd[j]);
}
private int SGN(int x) {
if (x < 0) {
return -1;
}
else if (x > 0) {
return 1;
}
else {
return 0;
}
}
}