/*
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.Tools.FTOI;
import static org.jwildfire.base.mathlib.MathLib.M_2PI;
import static org.jwildfire.base.mathlib.MathLib.M_PI;
import static org.jwildfire.base.mathlib.MathLib.atan2;
import static org.jwildfire.base.mathlib.MathLib.cos;
import static org.jwildfire.base.mathlib.MathLib.fabs;
import static org.jwildfire.base.mathlib.MathLib.pow;
import static org.jwildfire.base.mathlib.MathLib.sin;
import org.jwildfire.create.tina.base.Layer;
import org.jwildfire.create.tina.base.XForm;
import org.jwildfire.create.tina.base.XYZPoint;
public class XTrbFunc extends VariationFunc {
private static final long serialVersionUID = 1L;
private static final String PARAM_POWER = "power";
private static final String PARAM_RADIUS = "radius";
private static final String PARAM_WIDTH = "width";
private static final String PARAM_DIST = "dist";
private static final String PARAM_A = "a";
private static final String PARAM_B = "b";
private static final String[] paramNames = { PARAM_POWER, PARAM_RADIUS, PARAM_WIDTH, PARAM_DIST, PARAM_A, PARAM_B };
private int power = 2;
private double radius = 1.0;
private double width = 0.5;
private double dist = 1.0;
private double a = 1.0;
private double b = 1.0;
@Override
public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount) {
// xtrb by Xyrus02, http://xyrus02.deviantart.com/art/XTrb-Plugin-for-Apophysis-136800563
double Alpha, Beta, OffsetAl, OffsetBe, OffsetGa, X, Y;
int M, N;
// transfer to trilinear coordinates, normalized to real distances from triangle sides
{
DirectTrilinearTO to = new DirectTrilinearTO();
DirectTrilinear(pAffineTP.x, pAffineTP.y, to);
Alpha = to.Al;
Beta = to.Be;
}
M = (int) Math.floor(Alpha / S2a);
OffsetAl = Alpha - M * S2a;
N = (int) Math.floor(Beta / S2b);
OffsetBe = Beta - N * S2b;
OffsetGa = S2c - ac * OffsetAl - bc * OffsetBe;
if (OffsetGa > 0) {
HexTo to = new HexTo();
Hex(pContext, OffsetAl, OffsetBe, OffsetGa, to);
Alpha = to.Al1;
Beta = to.Be1;
}
else {
OffsetAl = S2a - OffsetAl;
OffsetBe = S2b - OffsetBe;
OffsetGa = -OffsetGa;
{
HexTo to = new HexTo();
Hex(pContext, OffsetAl, OffsetBe, OffsetGa, to);
Alpha = to.Al1;
Beta = to.Be1;
}
Alpha = S2a - Alpha;
Beta = S2b - Beta;
}
Alpha = Alpha + M * S2a;
Beta = Beta + N * S2b;
{
InverseTrilinearTO to = new InverseTrilinearTO();
InverseTrilinear(pContext, pAmount, Alpha, Beta, to);
X = to.x;
Y = to.y;
}
pVarTP.x += pAmount * X;
pVarTP.y += pAmount * Y;
if (pContext.isPreserveZCoordinate()) {
pVarTP.z += pAmount * pAffineTP.z;
}
}
@Override
public String[] getParameterNames() {
return paramNames;
}
@Override
public Object[] getParameterValues() {
return new Object[] { power, radius, width, dist, a, b };
}
@Override
public void setParameter(String pName, double pValue) {
if (PARAM_POWER.equalsIgnoreCase(pName))
power = FTOI(pValue);
else if (PARAM_RADIUS.equalsIgnoreCase(pName))
radius = pValue;
else if (PARAM_WIDTH.equalsIgnoreCase(pName))
width = pValue;
else if (PARAM_DIST.equalsIgnoreCase(pName))
dist = pValue;
else if (PARAM_A.equalsIgnoreCase(pName))
a = pValue;
else if (PARAM_B.equalsIgnoreCase(pName))
b = pValue;
else
throw new IllegalArgumentException(pName);
}
@Override
public String getName() {
return "xtrb";
}
private double width1, width2, width3;
private double sinC, cosC;
private double ab, ac, ba, bc, ca, cb;
private double Ha, Hb, Hc;
private double S2a, S2b, S2c, S2bc, S2ab, S2ac;
private int absN;
private double cN;
@Override
public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) {
double angle_Br = 0.047 + a;///180.0*M_PI; // angeles in radians
double angle_Cr = 0.047 + b;///180.0*M_PI;
double angle_Ar = M_PI - angle_Br - angle_Cr;
double sinA2, cosA2;
sinA2 = sin(0.5 * angle_Ar);
cosA2 = cos(0.5 * angle_Ar);
double sinB2, cosB2;
sinB2 = sin(0.5 * angle_Br);
cosB2 = cos(0.5 * angle_Br);
double sinC2, cosC2;
sinC2 = sin(0.5 * angle_Cr);
cosC2 = cos(0.5 * angle_Cr);
sinC = sin(angle_Cr);
cosC = cos(angle_Cr);
double a = radius * sinC2 / cosC2 + sinB2 / cosB2; //sides
double b = radius * (sinC2 / cosC2 + sinA2 / cosA2);
double c = radius * (sinB2 / cosB2 + sinA2 / cosA2);
width1 = 1.0 - width;
width2 = 2.0 * width;
width3 = 1.0 - width * width;
double S2 = radius * (a + b + c); //square
Ha = S2 / a / 6.0; //Hight div on 6.0
Hb = S2 / b / 6.0;
Hc = S2 / c / 6.0;
ab = a / b;// a div on b
ac = a / c;
ba = b / a;
bc = b / c;
ca = c / a;
cb = c / b;
S2a = 6.0 * Ha;
S2b = 6.0 * Hb;
S2c = 6.0 * Hc;
S2bc = S2 / (b + c) / 6.0;
S2ab = S2 / (a + b) / 6.0;
S2ac = S2 / (a + c) / 6.0;
absN = (int) fabs(power);
cN = dist / (double) power / 2.0;
}
private static class DirectTrilinearTO {
double Al, Be;
}
private void DirectTrilinear(double x, double y, DirectTrilinearTO res) {
double U = y + radius;
double V = x * sinC - y * cosC + radius;
res.Al = U;
res.Be = V;
}
private static class InverseTrilinearTO {
double x, y;
}
private void InverseTrilinear(FlameTransformationContext pContext, double pAmount, double Al, double Be, InverseTrilinearTO res) {
double inx = (Be - radius + (Al - radius) * cosC) / sinC;
double iny = Al - radius;
double angle = (atan2(iny, inx) + M_2PI * (pContext.random(Integer.MAX_VALUE) % (int) absN)) / power;
double r = pAmount * pow(inx * inx + iny * iny, cN);
double sina = sin(angle);
double cosa = cos(angle);
res.x = r * cosa;
res.y = r * sina;
}
private static class HexTo {
double Al1, Be1;
}
private void Hex(FlameTransformationContext pContext, double Al, double Be, double Ga, HexTo res) {
double Ga1, De1, R;
R = pContext.random();
if (Be < Al) {
if (Ga < Be) {
if (R >= width3) {
De1 = width * Be;
Ga1 = width * Ga;
}
else
{
Ga1 = width1 * Ga + width2 * Hc * Ga / Be;
De1 = width1 * Be + width2 * S2ab * (3 - Ga / Be);
}
res.Al1 = S2a - ba * De1 - ca * Ga1;
res.Be1 = De1;
}
else
{
if (Ga < Al)
{
if (R >= width3)
{
Ga1 = width * Ga;
De1 = width * Be;
}
else
{
De1 = width1 * Be + width2 * Hb * Be / Ga;
Ga1 = width1 * Ga + width2 * S2ac * (3 - Be / Ga);
}
res.Al1 = S2a - ba * De1 - ca * Ga1;
res.Be1 = De1;
}
else
{
if (R >= width3)
{
res.Al1 = width * Al;
res.Be1 = width * Be;
}
else
{
res.Be1 = width1 * Be + width2 * Hb * Be / Al;
res.Al1 = width1 * Al + width2 * S2ac * (3 - Be / Al);
}
}
}
}
else
{
if (Ga < Al)
{
if (R >= width3)
{
De1 = width * Al;
Ga1 = width * Ga;
}
else
{
Ga1 = width1 * Ga + width2 * Hc * Ga / Al;
De1 = width1 * Al + width2 * S2ab * (3 - Ga / Al);
}
res.Be1 = S2b - ab * De1 - cb * Ga1;
res.Al1 = De1;
}
else
{
if (Ga < Be)
{
if (R >= width3)
{
Ga1 = width * Ga;
De1 = width * Al;
}
else
{
De1 = width1 * Al + width2 * Ha * Al / Ga;
Ga1 = width1 * Ga + width2 * S2bc * (3 - Al / Ga);
}
res.Be1 = S2b - ab * De1 - cb * Ga1;
res.Al1 = De1;
}
else
{
if (R >= width3)
{
res.Be1 = width * Be;
res.Al1 = width * Al;
}
else
{
res.Al1 = width1 * Al + width2 * Ha * Al / Be;
res.Be1 = width1 * Be + width2 * S2bc * (3 - Al / Be);
}
}
}
}
}
}