/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/*
* Waterfall.java
*
* Created on Dec 7, 2009, 5:48:11 PM
*/
package granolasdr.plot;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.nio.DoubleBuffer;
/**
*
* @author matt
*/
public class Waterfall extends javax.swing.JPanel {
private BufferedImage offscreen = null;
private BufferedImage onscreen = null;
private Graphics bufferGraphics;
private int height = 0;
private int width = 0;
private int refreshScale = 4;
private double leftEdge = 0.0;
private double rightEdge = 1.0;
private DoubleBuffer data;
// private int max = 0;
private double amp = 15;
private double floor = 0.0;
private double[] temp = null;
private int[] newPix = null;
private int currentRaster = 0;
private Marker[] markers = new Marker[2];
private TimeLine timeLine = new TimeLine();
public static boolean usingCuda = true;
private class TimeLine {
private int[] pixels = new int[4096];
private double timeValue = 0.0;
private int yCoord = 0;
public double computeAmplitude(int rgb) {
int blue = rgb & 0xFF;
int green = (rgb >>> 8) & 0xFF;
int red = (rgb >>> 16) & 0xFF;
double delta = 1024.0 / (amp - floor);
double value = 0;
if (red == 0 && blue == 0 && green == 0) {
value = 0;
} else if (red == 0 && green == 0) {
value = blue - 128;
} else if (blue == 255 && red == 0) {
value = green + 128;
} else if (green == 255 && (red + blue) == 256) {
value = red + 384;
} else if (red == 255 && blue == 0) {
value = 896 - green;
} else if (green == 0 && blue == 0) {
value = 1152 - red;
} else {
value = 1024;
}
value = (value / delta) + floor;
return (value);
}
public void setTimeLine(double time) {
// first clean up the old time line (if any)
if (yCoord != 0) {
try {
offscreen.setRGB(0, yCoord, width, 1, pixels, 0, 0);
} catch (Exception ex) {
}
}
if (time > 0.0 && time < 1.0) {
// store the new time line to the restoration array
timeValue = time;
yCoord = (int) (timeValue * height);
offscreen.getRGB(0, yCoord, width, 1, pixels, 0, 0);
for (int x = 0; x < width; x++) {
pixels[x] ^= 0x00FFFFFF;
}
offscreen.setRGB(0, yCoord, width, 1, pixels, 0, 0);
for (int x = 0; x < width; x++) {
pixels[x] ^= 0x00FFFFFF;
}
}
}
public void getTimeLine(double[] array) {
double xScale = (double) (width - 1) / (double) array.length;
double[] values = new double[width];
for (int x = 0; x < width; x++) {
values[x] = computeAmplitude(pixels[x]);
}
for (int x = 0; x < array.length; x++) {
double xPosition = x * xScale;
int xIndex = (int) Math.floor(xPosition);
double xWeight = xPosition - xIndex;
array[x] = ((1.0 - xWeight) * values[xIndex]) + (xWeight * values[xIndex + 1]);
}
}
}
private class Marker {
public double time;
public double frequency;
public int color = 0xFFFFFF;
public int shape = 1;
public boolean inUse = false;
public int markWidth = 8;
public int markHeight = 16;
private int[][] pixelQueue = null;
private double effectiveFrequency = 0.0;
private int xCoord = 0;
private int yCoord = 0;
private int compliment = 0x00;
public void drawMarker() {
effectiveFrequency = (frequency - leftEdge) / (rightEdge - leftEdge);
compliment = color ^ 0x00FFFFFF;
if (inUse && frequency > leftEdge && frequency < rightEdge && time >= 0 && time <= 1.0) {
xCoord = (int) (width * effectiveFrequency);
yCoord = (int) (height * time);
updateQueue();
erase();
switch (shape) {
// case 1, diamond shaped
case 1:
for (int y = 0; y < markHeight + 1; y++) {
for (int x = -markWidth; x < markWidth; x++) {
if ((y >>> 1) >= Math.abs(x)) {
try {
if ((y >>> 1) != Math.abs(x)) {
offscreen.setRGB(xCoord + x, yCoord + y, color);
offscreen.setRGB(xCoord + x, yCoord + ((markHeight << 1) - y - 1), color);
} else {
offscreen.setRGB(xCoord + x, yCoord + y, compliment);
offscreen.setRGB(xCoord + x, yCoord + ((markHeight << 1) - y - 1), compliment);
}
} catch (Exception ex) {
System.err.println("could not draw marker diamond");
}
}
}
}
break;
// case 3: cross shaped
case 2:
// vertical part
for (int y = 0; y < markHeight + refreshScale; y++) {
for (int x = -2; x < 3; x++) {
try {
if (x == -2 || x == 2 || y == 0 || y == markHeight - 1) {
offscreen.setRGB(xCoord + x, yCoord + y, compliment);
} else {
offscreen.setRGB(xCoord + x, yCoord + y, color);
}
} catch (Exception ex) {
System.err.println("could not draw marker plus");
}
}
}
// horizontal part
for (int y = (markHeight >>> 1) - 2; y <= (markHeight >>> 1) + 2; y++) {
for (int x = -markWidth; x < markWidth; x++) {
try {
if (x < -1 || x > 1) {
if (x == -markWidth || x == (markWidth - 1) || y == ((markHeight >>> 1) - 2) || y == ((markHeight >>> 1) + 2)) {
offscreen.setRGB(xCoord + x, yCoord + y, compliment);
} else {
offscreen.setRGB(xCoord + x, yCoord + y, color);
}
}
} catch (Exception ex) {
System.err.println("could not draw marker plus");
}
}
}
break;
// default: triangle shaped
default:
for (int y = 0; y < markHeight - 1; y++) {
for (int x = -markWidth; x < markWidth; x++) {
if (Math.abs(x) <= (y >>> 1)) {
try {
if ((y >>> 1) == Math.abs(x)) {
offscreen.setRGB(xCoord + x, yCoord + y, compliment);
} else {
offscreen.setRGB(xCoord + x, yCoord + y, color);
}
} catch (Exception ex) {
System.err.println("could not draw marker triangle");
}
}
}
}
for (int x = -markWidth; x < markWidth; x++) {
try {
offscreen.setRGB(xCoord + x, yCoord + markHeight - 1, compliment);
} catch (Exception ex) {
}
}
break;
}
}
}
private void updateQueue() {
if (pixelQueue == null || pixelQueue.length < (markWidth << 1) || pixelQueue[0].length < ((markHeight + refreshScale) << 1)) {
pixelQueue = new int[markWidth << 1][(markHeight + refreshScale) << 1];
for (int y = 0; y <= (markHeight << 1); y++) {
for (int x = -markWidth; x < markWidth; x++) {
try {
pixelQueue[x + markWidth][y] = offscreen.getRGB(xCoord + x, yCoord + y);
} catch (Exception ex) {
System.err.println(ex.toString());
pixelQueue[x + markWidth][y] = color;
}
}
}
} else {
for (int y = (markHeight << 1) + refreshScale; y >= refreshScale; y--) {
for (int x = 0; x < (markWidth << 1); x++) {
pixelQueue[x][y] = pixelQueue[x][y - refreshScale];
}
}
for (int y = 0; y < refreshScale; y++) {
for (int x = -markWidth; x < markWidth; x++) {
try {
pixelQueue[x + markWidth][y] = offscreen.getRGB(xCoord + x, yCoord + y);
} catch (Exception ex) {
pixelQueue[x + markWidth][y] = color;
}
}
}
}
}
public void erase() {
if (pixelQueue != null) {
for (int y = 0; y <= (markHeight << 1) + refreshScale; y++) {
for (int x = -markWidth; x < markWidth; x++) {
try {
offscreen.setRGB(xCoord + x, yCoord + y, pixelQueue[x + markWidth][y]);
} catch (Exception ex) {
}
}
}
// pixelQueue = null;
}
}
public int getPixel() {
try {
return (offscreen.getRGB(xCoord, yCoord));
} catch (Exception ex) {
return (0xFFFFFF);
}
}
public int getCover(int x, int y) {
if (!inUse) {
return (0);
}
x -= xCoord;
y -= yCoord;
if (y >= 0 && y <= markHeight) {
if (x >= -markWidth && x <= markWidth) {
return (pixelQueue[x][y]);
}
}
return (0);
}
}
/**
* Creates new form Waterfall
*/
public Waterfall() {
initComponents();
}
public void setMarker(int index, double time, double freq) {
setMarker(index, time, freq, 0xFFFFFF, 1, 16);
}
public void setMarker(int index, double time, double freq, int color, int shape, int size) {
if (index < 2 && index >= 0) {
if (markers[index] == null) {
markers[index] = new Marker();
}
if (time >= 0) {
if (markers[index].inUse) {
markers[index].erase();
}
markers[index].inUse = true;
markers[index].frequency = freq;
markers[index].time = time;
markers[index].color = color;
markers[index].shape = shape;
markers[index].markHeight = size;
markers[index].markWidth = size >>> 1;
markers[index].drawMarker();
} else {
markers[index].erase();
markers[index].inUse = false;
}
}
}
public double getMarkerAmplitude(int index) {
return (timeLine.computeAmplitude(markers[index].getPixel()));
}
public double[] getXAxis() {
double[] returnValue = new double[2];
returnValue[0] = leftEdge;
returnValue[1] = rightEdge;
return (returnValue);
}
public void setWidth(double start, double end) {
leftEdge = start;
rightEdge = end;
}
public void setAmp(double a) {
amp = a;
}
public void setFloor(double a) {
floor = a;
}
public double getAmp() {
return amp;
}
public double getFloor() {
return floor;
}
public void setData(DoubleBuffer d) {
data = d;
}
public void setTimeLine(double time) {
timeLine.setTimeLine(time);
repaint();
}
public void setRefreshScale(int scale) {
refreshScale = scale;
}
public int getRefreshScale() {
return (refreshScale);
}
public void getTimeLine(double[] array) {
timeLine.getTimeLine(array);
}
public synchronized void updateData() {
if (currentRaster <= 0) {
bufferGraphics.copyArea(0, 0, width, height - refreshScale, 0, refreshScale);
currentRaster = refreshScale;
}
currentRaster--;
data.rewind();
double delta = 1024.0 / (amp - floor);
int leftScale = (int) (leftEdge * data.capacity());
int rightScale = (int) (rightEdge * data.capacity());
// JPL: this thing tends to die whenever we change the display,
// rewrite to handle exceptions gracefully
try {
if (temp == null || temp.length < data.capacity()) {
temp = new double[data.capacity()];
}
data.get(temp, 0, data.capacity());
if (newPix == null || newPix.length < width) {
newPix = new int[width];
} else {
for (int cnt = 0; cnt < width; cnt++) {
double xPosition = leftScale + (cnt * ((double) (rightScale - leftScale - 1) / (double) width));
int xIndex = (int) Math.floor(xPosition);
double xScale = xPosition - xIndex;
double value = ((1.0 - xScale) * temp[xIndex]) + (xScale * temp[xIndex + 1]);
int red = 0;
int green = 0;
int blue = 0;
int v = (int) ((value - floor) * delta);
blue = 384 - Math.abs(v - 256);
blue = Math.max(blue, 0);
blue = Math.min(blue, 255);
blue += 256 * (Math.abs(v) / 1024);
green = 384 - Math.abs(v - 512);
green = Math.max(green, 0);
green = Math.min(green, 255);
green += 256 * (Math.abs(v) / 1024);
red = 384 - Math.abs(v - 768);
red = Math.max(red, 0);
red = Math.min(red, 255);
red += 128 * (Math.abs(v) / 1024);
newPix[cnt] = 0xff000000 | (red << 16) | (green << 8) | blue;
}
}
offscreen.setRGB(0, currentRaster, width, 1, newPix, 0, width);
if (currentRaster == 0) {
for (int i = 0; i < markers.length; i++) {
if (markers[i] != null && markers[i].inUse) {
markers[i].drawMarker();
}
}
}
onscreen = offscreen;
repaint();
} catch (Exception ex) {
System.err.println("exception: " + ex.toString() + " thrown whilst redrawing the waterfall");
height = getHeight();
width = getWidth();
}
}
@Override
public synchronized void paint(Graphics g) {
if (onscreen != null) {
g.drawImage(offscreen, 0, 0, this);
super.paint(g);
}
}
private void createBackground() {
offscreen = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
bufferGraphics = offscreen.getGraphics();
}
@Override
public void doLayout() {
super.doLayout();
height = getHeight();
width = getWidth();
// System.out.println("Height " + height + " Width " + width);
createBackground();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
setOpaque(false);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 300, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}