/*
JWildfire - an image and animation processor written in Java
Copyright (C) 1995-2017 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.sinAndCos;
import java.util.ArrayList;
import java.util.List;
import org.jwildfire.base.Prefs;
import org.jwildfire.base.mathlib.MathLib;
import org.jwildfire.base.mathlib.VecMathLib.Matrix3D;
import org.jwildfire.base.mathlib.VecMathLib.VectorD;
import org.jwildfire.create.tina.random.AbstractRandomGenerator;
import org.jwildfire.create.tina.random.MarsagliaRandomGenerator;
import odk.lang.DoubleWrapper;
public class DLA3DWFFuncIterator {
private DoubleWrapper sinPhi = new DoubleWrapper();
private DoubleWrapper cosPhi = new DoubleWrapper();
private DoubleWrapper sinTheta = new DoubleWrapper();
private DoubleWrapper cosTheta = new DoubleWrapper();
private final int thread_count;
private double innerRadius;
private double outerRadius;
private final AbstractRandomGenerator randGen;
private final int max_iter;
// private final int seed;
private final double glue_radius;
private final double force_x;
private final double force_y;
private final double force_z;
public DLA3DWFFuncIterator(int max_iter, int seed, double glue_radius, double force_x, double force_y, double force_z, boolean singleThread) {
super();
this.max_iter = max_iter;
// this.seed = seed;
this.glue_radius = glue_radius;
this.force_x = force_x;
this.force_y = force_y;
this.force_z = force_z;
this.thread_count = singleThread ? 1 : Prefs.getPrefs().getTinaRenderThreads();
randGen = new MarsagliaRandomGenerator();
randGen.randomize(seed);
}
public List<DLA3DWFFuncPoint> iterate() {
List<DLA3DWFFuncPoint> res = new ArrayList<>();
DLA3DWFFuncPoint initialPoint = new DLA3DWFFuncPoint();
res.add(initialPoint);
innerRadius = 3.0;
outerRadius = 3.0 * innerRadius;
while (res.size() < max_iter) {
double phi = randGen.random() * (M_PI + M_PI);
sinAndCos(phi, sinPhi, cosPhi);
double theta = randGen.random() * (M_PI + M_PI);
sinAndCos(theta, sinTheta, cosTheta);
double ci = innerRadius * sinTheta.value * cosPhi.value;
double cj = innerRadius * sinTheta.value * sinPhi.value;
double ck = innerRadius * cosTheta.value;
if (thread_count > 1) {
List<WalkerThread> threads = new ArrayList<>();
for (int i = 0; i < 2 * thread_count; i++) {
WalkerThread thread = new WalkerThread(ci, cj, ck, res);
threads.add(thread);
new Thread(thread).start();
}
boolean isDone = false;
while (!isDone) {
isDone = true;
for (WalkerThread thread : threads) {
if (!thread.isDone()) {
isDone = false;
try {
Thread.sleep(1);
}
catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
}
}
for (WalkerThread thread : threads) {
DLA3DWFFuncPoint nextPoint = thread.getRes();
if (nextPoint != null) {
res.add(nextPoint);
if (res.size() > 1000 && res.size() % 100 == 0) {
System.out.println("PNTS " + res.size());
}
}
}
}
else {
DLA3DWFFuncPoint nextPoint = nextWalk(ci, cj, ck, res);
if (nextPoint != null) {
res.add(nextPoint);
if (res.size() > 1000 && res.size() % 100 == 0) {
System.out.println("PNTS " + res.size());
}
}
}
}
for (DLA3DWFFuncPoint p : res) {
p.relDepth = (double) p.pathLength / (double) calcMaxPathLength(res, p);
if (p.parent != null) {
VectorD d = new VectorD(p.x - p.parent.x, p.y - p.parent.y, p.z - p.parent.z);
d.normalize();
p.elevation = MathLib.atan2(d.y, d.x) + MathLib.M_PI_2;
p.azimuth = -MathLib.atan2(d.z, MathLib.sqrt(d.x * d.x + d.y * d.y));
double sina = MathLib.sin(p.elevation);
double cosa = MathLib.cos(p.elevation);
double sinb = MathLib.sin(p.azimuth);
double cosb = MathLib.cos(p.azimuth);
Matrix3D a = new Matrix3D();
a.m[0][0] = cosb;
a.m[0][1] = 0;
a.m[0][2] = sinb;
a.m[1][0] = sina * sinb;
a.m[1][1] = cosa;
a.m[1][2] = -sina * cosb;
a.m[2][0] = -cosa * sinb;
a.m[2][1] = sina;
a.m[2][2] = cosa * cosb;
p.rotation = a;
}
}
return res;
}
private class WalkerThread implements Runnable {
private boolean done;
private DLA3DWFFuncPoint res;
private final double ci0;
private final double cj0;
private final double ck0;
private final List<DLA3DWFFuncPoint> points;
public WalkerThread(double ci0, double cj0, double ck0, List<DLA3DWFFuncPoint> points) {
super();
this.ci0 = ci0;
this.cj0 = cj0;
this.ck0 = ck0;
this.points = new ArrayList<>(points);
}
@Override
public void run() {
done = false;
try {
res = nextWalk(ci0, cj0, ck0, points);
}
finally {
done = true;
}
}
public boolean isDone() {
return done;
}
public DLA3DWFFuncPoint getRes() {
return res;
}
}
private DLA3DWFFuncPoint nextWalk(double ci0, double cj0, double ck0, List<DLA3DWFFuncPoint> res) {
int iter = 0;
final int maxWalk = 1000000;
double ci = ci0;
double cj = cj0;
double ck = ck0;
while (iter < maxWalk) {
double moveRadius = 0.9;
// phi = randGen.random() * (M_PI + M_PI);
// sinAndCos(phi, sinPhi, cosPhi);
// theta = randGen.random() * (M_PI + M_PI);
// sinAndCos(theta, sinTheta, cosTheta);
// final double moveRadius = 0.9;
//
// ci += moveRadius * sinTheta.value * cosPhi.value + gradx;
// cj += moveRadius * sinTheta.value * sinPhi.value + grady;
// ck += moveRadius * cosTheta.value + gradz;
switch ((int) (randGen.random() * 6)) {
case 0:
ci = ci + moveRadius + force_x;
break;
case 1:
cj = cj - moveRadius + force_y;
break;
case 2:
ci = ci - moveRadius + force_x;
break;
case 3:
ck = ck - moveRadius + force_z;
break;
case 4:
ck = ck + moveRadius + force_z;
break;
case 5:
cj = cj + moveRadius + force_y;
break;
default:
break;
}
double radius = MathLib.sqrt(MathLib.sqr(ci) + MathLib.sqr(cj) + MathLib.sqr(ck));
DLA3DWFFuncPoint parent = null;
double minRadius = Double.MAX_VALUE;
for (DLA3DWFFuncPoint r : res) {
double dr0 = MathLib.sqrt(MathLib.sqr(r.x - ci) + MathLib.sqr(r.y - cj) + MathLib.sqr(r.z - ck));
if (dr0 <= glue_radius) {
if (dr0 < minRadius) {
minRadius = dr0;
parent = r;
}
}
}
if (parent != null) {
DLA3DWFFuncPoint point = new DLA3DWFFuncPoint(parent, ci, cj, ck);
if (radius > innerRadius) {
innerRadius = radius;
outerRadius = 2.1 * innerRadius;
}
return point;
}
else if (radius > outerRadius) {
ci = ci0;
cj = cj0;
ck = ck0;
break;
}
iter++;
}
return null;
}
private int calcMaxPathLength(List<DLA3DWFFuncPoint> points, DLA3DWFFuncPoint p) {
int res = p.pathLength;
for (DLA3DWFFuncPoint r : points) {
DLA3DWFFuncPoint s = r;
while (s != null) {
if (s.parent == p) {
if (r.pathLength > res) {
res = r.pathLength;
}
break;
}
s = s.parent;
}
}
return res;
}
}