package automenta.vivisect.timeline;
import automenta.vivisect.TreeMLData;
import com.google.common.primitives.Floats;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.math3.complex.Complex;
import org.apache.commons.math3.transform.DftNormalization;
import org.apache.commons.math3.transform.FastFourierTransformer;
import org.apache.commons.math3.transform.TransformType;
public class SpectrumChart extends LineChart {
//http://commons.apache.org/proper/commons-math/javadocs/api-3.3/org/apache/commons/math3/transform/FastFourierTransformer.html
//https://github.com/lessthanoptimal/BoofCV/blob/master/main/ip/test/boofcv/alg/transform/fft/TestGeneralPurposeFFT_F32_1D.java
//https://github.com/lessthanoptimal/BoofCV/blob/master/main/ip/src/boofcv/alg/transform/fft/GeneralPurposeFFT_F32_1D.java
FastFourierTransformer fft = new FastFourierTransformer(DftNormalization.STANDARD);
float barWidth = 0.5f;
private boolean updated = false;
static class Window {
float[] phase;
float[] magnitude;
Window(Complex[] input) {
final int vl = input.length;
magnitude = new float[vl];
phase = new float[vl];
for (int i = 0; i < vl; i++) {
float r = (float)input[i].getReal();
float c = (float)input[i].getImaginary();
magnitude[i] = (float)Math.sqrt( r*r + c*c );
phase[i] = (float)Math.atan2( c, r );
}
}
}
List<Window> windows = new ArrayList();
private int windowSize;
public SpectrumChart(TreeMLData t, int windowSize) {
super(t);
this.windowSize = windowSize;
}
protected void update() {
TreeMLData chart = data.get(0);
float length = chart.getEnd() - chart.getStart();;
int numWindows = (int)Math.ceil(length / windowSize);
//TODO dont remove existing windows
windows.clear();
int t = 0;
for (int w = 0; w < numWindows; w++) {
final int vl = windowSize;
double[] input = new double[vl];
for (int i = 0; i < vl; i++) {
input[i] = (float)chart.getData(t++);
}
Complex[] c = fft.transform(input, TransformType.FORWARD);
// phase = atan2( imaginary , real )
// magnitude = sqrt( real<sup>2</sup> + imaginary<sup>2</sup> )
windows.add(new Window(c));
}
}
public long cycleToWindow(long c) {
return c / windowSize;
}
public long windowToCycle(long w) {
return w * windowSize;
}
@Override
protected void drawData(TimelineVis l, float timeScale, float yScale, float y) {
TreeMLData chart = data.get(0);
if (!updated) {
update();
updated = true;
}
//int ccolor = chart.getColor();
l.g.noStroke();
long prevWindow = -1;
float yh = yScale / windowSize;
for (long t = l.cycleStart - (windowSize); t < l.cycleEnd; t++) {
if (t < 0) continue;
float x = t * timeScale;
long w = cycleToWindow(t);
if ((w != prevWindow) && (w < windows.size())) {
float t2 = t + windowSize;
float x2 = t2 * timeScale;
//draw window block
Window win = windows.get((int)w);
float magMax = Floats.max(win.magnitude);
float yy = yScale;
for (int f = 0; f < windowSize; f++) {
float m = win.magnitude[f] / magMax;
m = (0.25f + 0.75f * m);
float phase = (win.phase[f] + (float)Math.PI*2f) / ((float)Math.PI*2f);
l.g.fill((0.2f + 0.3f * phase) * 255f, 0.75f * 255f, m * 255f);
yy -= yh;
l.g.rect(x, y + yy, x2 - x, yh);
}
prevWindow = w;
}
}
}
// public static void main(String args[]) {
// float[] v= { 1, 2, 3, 2, 1, 2, 3, 1, 2, 1, 0, 2, 1, 2, 3 };
//
// float[] input = new float[v.length*2];
// for (int i = 0; i < v.length; i++)
// input[i*2] = v[i];
//
// GeneralPurposeFFT_F32_1D x = new GeneralPurposeFFT_F32_1D(v.length);
// x.complexForward(input);
//
//
// // phase = atan2( imaginary , real )
// // magnitude = sqrt( real<sup>2</sup> + imaginary<sup>2</sup> )
//
// float[] magnitude = new float[v.length];
// float[] phase = new float[v.length];
//
// for (int i = 0; i < v.length; i++) {
// float r = input[i*2];
// float c = input[i*2+1];
// magnitude[i] = (float)Math.sqrt( r*r + c*c );
// phase[i] = (float)Math.atan2( c, r );
// }
//
// System.out.println(Arrays.toString(magnitude));
// System.out.println(Arrays.toString(phase));
// }
}