package timeflow.app.ui.filter;
import javax.swing.*;
import timeflow.data.time.Interval;
import timeflow.model.Display;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.text.*;
public class BabyHistogram extends JPanel {
private int[] buckets;
private double[] x;
private double min, max;
private int numDefined;
private int maxBucket;
private double value;
private static final DecimalFormat df=new DecimalFormat("###,###,###,###.##");
Point mouseHit=new Point();
Point mouse=new Point(-1,0);
enum Modify {START, END, POSITION, NONE};
Modify change=Modify.NONE;
Rectangle startRect=new Rectangle(-1,-1,0,0);
Rectangle endRect=new Rectangle(-1,-1,0,0);
Rectangle positionRect=new Rectangle(-1,-1,0,0);
Color sidePlain=Color.orange;
Color sideMouse=new Color(230,100,0);
double relLow=0, relHigh=1, originalLow, originalHigh;
public void setRelRange(double relLow, double relHigh)
{
this.relLow=Math.max(0,relLow);
this.relHigh=Math.min(1,relHigh);
repaint();
}
public void setTrueRange(double low, double high)
{
double span=max-min;
if (span<=0) // nothing much to do...
return;
setRelRange((low-min)/span, (high-min)/span);
}
public boolean isEverything()
{
return relLow==0 && relHigh==1;
}
public double getLow()
{
return abs(relLow);
}
public double getHigh()
{
return abs(relHigh);
}
private double abs(double x)
{
return x*max+(1-x)*min;
}
public BabyHistogram(final Runnable changeAction)
{
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
int mx=e.getX();
int my=e.getY();
int lx=lowX(), hx=highX();
int ox=0;
if (Math.abs(mx-lx)<Math.abs(mx-hx))
{
change=Modify.START;
ox=lx;
}
else
{
change=Modify.END;
ox=hx;
}
mouseHit.setLocation(ox,my);
mouse.setLocation(mx,my);
originalLow=relLow;
originalHigh=relHigh;
repaint();
}
@Override
public void mouseReleased(MouseEvent e) {
change=Modify.NONE;
repaint();
}});
addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
if (change==Modify.NONE)
return;
mouse.setLocation(e.getX(), e.getY());
int mouseDiff=mouse.x-mouseHit.x;
double relDiff=mouseDiff/(double)getSize().width;
switch (change)
{
case POSITION:
relLow=originalLow+relDiff;
relHigh=originalHigh+relDiff;
break;
case START: relLow=originalLow+relDiff;
break;
case END: relHigh=originalHigh+relDiff;
}
relLow=Math.max(0, relLow);
relHigh=Math.min(1, relHigh);
changeAction.run();
repaint();
}
});
}
public void setData(double[] x)
{
relLow=0;
relHigh=1;
this.x=x;
int n=x.length;
// do some quick checks on the data.
boolean positive=true;
min=Double.NaN;
max=Double.NaN;
numDefined=0;
for (int i=0; i<n; i++)
{
double m=x[i];
if (!Double.isNaN(m))
{
numDefined++;
positive &= m>0;
if (Double.isNaN(min))
{
min=m;
max=m;
value=m;
}
else
{
min=Math.min(m, min);
max=Math.max(m, max);
}
}
}
if (numDefined==0)
return;
if (min==max)
{
buckets=new int[1];
buckets[0]=numDefined;
maxBucket=numDefined;
return;
}
int numBuckets=(int)Math.min(50, 2*Math.sqrt(numDefined));
buckets=new int[numBuckets];
maxBucket=0;
for (int i=0; i<n; i++)
{
if (!Double.isNaN(x[i]))
{
int b=(int)((numBuckets-1)*(x[i]-min)/(max-min));
buckets[b]++;
maxBucket=Math.max(maxBucket, buckets[b]);
}
}
}
public void paintComponent(Graphics g)
{
int w=getSize().width;
int h=getSize().height;
g.setColor(Color.white);
g.fillRect(0,0,w,h);
if (x==null)
{
say(g, "No data");
return;
}
if (x.length==0)
{
say(g, "No values");
return;
}
if (numDefined==0)
{
say(g, "No defined values");
return;
}
int n=buckets.length;
if (n==1)
{
say(g, "All defined vals = "+df.format(value));
return;
}
// wow, if we got here we really have a histogram and not a degenerate mess!
Color bar=Display.barColor;
g.setColor(bar);
for (int i=0; i<n; i++)
{
int x1=(i*w)/n;
int x2=((i+1)*w)/n;
int y1=h-(buckets[i]*h)/maxBucket;
if (buckets[i]>0 && y1>h-2)
y1=h-2;
g.fillRect(x1,y1,x2-x1-1,h-y1);
}
// now draw thumb.
int thumb1=lowX();
int thumb2=highX();
g.setColor(Color.black);
g.drawLine(thumb1,0,thumb1,h);
g.drawLine(thumb2,0,thumb2,h);
g.setColor(new Color(235,235,235,160));
g.fillRect(0,0,thumb1,h);
g.fillRect(thumb2+1,0,w-thumb2-1,h);
g.setColor(Color.lightGray);
g.drawRect(0,0,w-1,h-1);
}
int lowX()
{
return (int)((getSize().width-1)*relLow);
}
int highX()
{
return (int)((getSize().width-1)*relHigh);
}
void say(Graphics g, String s)
{
g.setColor(Color.gray);
g.drawString(s,5,getSize().height-5);
}
public Dimension getPreferredSize()
{
return new Dimension(200,60);
}
}