/**
*
*/
package fr.unistra.pelican.gui.MultiViews;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import fr.unistra.pelican.Image;
import fr.unistra.pelican.util.ChartCreator;
import fr.unistra.pelican.util.Line;
/**
* <p>To display cut through a set of images and show the cut line over another Panel (transparency and event dispatching).
* <p>Chart can be customized (change color, add extra informations,...) by implementing the {@link ImageProfileChartCustomizer} interface and adding it to the properties of the image with keyword profileCustomizer
*
* @author Benjamin Perret
*
*/
public class ImProfile extends JPanel implements MouseMotionListener, MouseListener, MouseWheelListener,ChangeListener, ActionListener{
private Color color [] = {Color.black, Color.red, Color.blue, Color.green,Color.orange,Color.gray, Color.cyan, Color.pink, Color.yellow};
/**
* Image property keyword to store your own {@link ImageProfileChartCustomizer}
*/
public static final String profileCustomizer = "IMAGE_PROFILE_CUSTOMIZER";
private int x1;
private int x2;
private int y1;
private int y2;
private Point p1=null;
private View view;
private Point p2=null;
private Point selected=null;
boolean change=true;
public JFrame frame;
private ArrayList<View> views = new ArrayList<View>();
private BufferedImage im ;
private ImagePanel profile;
private boolean log=false;
private JFreeChart chart;
private JCheckBoxMenuItem logScale;
private JMenuItem dropAsMatlabPlot;
public void setLine(Point p1, Point p2)
{
if(this.p1!=null)
{
this.p1.x=p1.x;
this.p1.y=p1.y;
}else {
this.p1=new Point(p1);
}
if(this.p2!=null)
{
this.p2.x=p2.x;
this.p2.y=p2.y;
}else {
this.p2=new Point(p2);
}
}
public ImProfile()
{
this.addMouseListener(this);
this.addMouseMotionListener(this);
this.addMouseWheelListener(this);
profile=new ImagePanel();
buildPopUp();
frame=new JFrame("Profile");
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
frame.addComponentListener(new ComponentListener(){
@Override
public void componentHidden(ComponentEvent e) {
}
@Override
public void componentMoved(ComponentEvent e) {
}
@Override
public void componentResized(ComponentEvent e) {
refreshGraph();
}
@Override
public void componentShown(ComponentEvent e) {
}
});
frame.add(profile);
//frame.setLocationRelativeTo(null);
}
private void buildPopUp(){
//popup=new JPopupMenu();
logScale=new JCheckBoxMenuItem("log scale");
logScale.setSelected(log);
logScale.addActionListener(this);
dropAsMatlabPlot = new JMenuItem("Drop for Matlab Plot");
dropAsMatlabPlot.addActionListener(this);
profile.addPopUpOption("Profile view", logScale,dropAsMatlabPlot);
}
@Override
public void paint(Graphics g) {
if (frame.isVisible()) {
Component c = this.getParent().getParent();
if (c != null && c instanceof MagicPanel) {
View v = ((MagicPanel) c).getBg().getView();
if(v==null)
return;
if(v!=view)
{
change=true;
view=v;
}
if (p1 == null || p2 == null) {
int w = getWidth();
int h = getHeight();
int x1 = view.getAbsoluteXCoord(w / 4);
int x2 = view.getAbsoluteXCoord(3 * w / 4);
int y1 = view.getAbsoluteYCoord(h / 2);
int y2 = view.getAbsoluteYCoord(h / 2);
p1 = new Point(x1, y1);
p2 = new Point(x2, y2);
}
x1 = view.getRelativeXCoord(p1.x);
x2 = view.getRelativeXCoord(p2.x);
y1 = view.getRelativeYCoord(p1.y);
y2 = view.getRelativeYCoord(p2.y);
g.setColor(Color.red);
g.drawLine(x1, y1, x2, y2);
g.drawRect(x1 - 2, y1 - 2, 4, 4);
g.drawRect(x2 - 2, y2 - 2, 4, 4);
}
extractProfiles();
}
}
double [][] xSeries;
double [][] ySeries;
private void extractProfiles(){
if(change)
{
//System.out.println("extract");
int l=views.size();
String [] names = new String[l];
xSeries=new double[l][];
ySeries=new double[l][];
int i=0;
if (views.size() == 0) {
//System.out.println("nullin");
profile.setImage((Image) null);
} else {
for (View v : views) {
double[][] op = null;
names[i] = v.getImage().getName();
if (names[i] == null)
names[i] = "No Name";
Object o = null;// v.properties.get(PROFILE_SAVE);
if (o != null && o instanceof double[][]) {
op = (double[][]) o;
} else {
change = true;
op = extractLine(v, v.getDisplayedBand());
}
ySeries[i] = op[0];
xSeries[i] = op[1];
i++;
}
chart = ChartCreator.getXYChart("Profile view", names,"Distance", "Intensity", xSeries, ySeries);
XYItemRenderer renderer = chart.getXYPlot().getRenderer();
for(int j=0;j<Math.min(names.length, color.length);j++)
{
renderer.setSeriesPaint(j, color[j]);
}
int j=0;
for (View v : views) {
Image image=v.getImage();
Object o= image.getProperty(profileCustomizer);
if(o!=null && o instanceof ImageProfileChartCustomizer)
{
((ImageProfileChartCustomizer)o).customizeChart(chart, p1, p2,v,j);
}
j++;
}
refreshGraph();
change = false;
}
}
}
/**
* Implements this and add you own customizer in the image properties
* @author Benjamin Perret
*
*/
public abstract interface ImageProfileChartCustomizer{
/**
* Customize given chart knowing the position of the cut, the underlying view and the index of the serie
* @param chart the chart
* @param p1 origin of the cut
* @param p2 end of the cut
* @param v view of the image
* @param serieIndex index of the serie representing the cut
*/
public abstract void customizeChart(JFreeChart chart, Point p1, Point p2, View v, int serieIndex);
}
private void refreshGraph(){
if(chart!=null){
im=chart.createBufferedImage(profile.getWidth(), profile.getHeight());
profile.setImage(im);
View v=profile.setImage(im);
v.setAutoFitWindow(true);}
}
private double [][] extractLine(View v, int band)
{
double [][] res= new double [2][];
Image im=v.getPersistentImage();
int x1 = p1.x;//v.getAbsoluteXCoord(p1.x);
int x2 = p2.x;//v.getAbsoluteXCoord(p2.x);
int y1 = p1.y;//v.getAbsoluteYCoord(p1.y);
int y2 = p2.y;//v.getAbsoluteYCoord(p2.y);
Line l =new Line(new Point(x1,y1), new Point(x2,y2));
res[0]=l.imProfileDouble(im,band);
//System.out.println(l);
res[1] = new double[res[0].length];
double c=0.0;
double length=Math.sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
double step=length/(double)res[0].length;//not perfect but seems ok
if (log) {
for (int i = 0; i < res[0].length; i++) {
res[0][i] = Math.max(Math.log(res[0][i]), -2);
res[1][i] = c;
c += step;
}
}else{
for (int i = 0; i < res[0].length; i++) {
res[1][i] = c;
c += step;
}
}
return res;
}
/**
* @param e
* @return
* @see java.util.ArrayList#add(java.lang.Object)
*/
public boolean add(View e) {
boolean b=false;
if(!views.contains(e))
b= views.add(e);
change=true;
revalidate();
return b;
}
/**
*
* @see java.util.ArrayList#clear()
*/
public void clear() {
for (View v: views)
remove(v);
}
public void refresh()
{
change=true;
revalidate();
}
/**
* @param o
* @return
* @see java.util.ArrayList#remove(java.lang.Object)
*/
public boolean remove(Object o) {
boolean res=views.remove(o);
refresh();
repaint();
return res;
}
public void setVisible(boolean f)
{
frame.setVisible(f);
}
public void setVisible(boolean f, Point location)
{
frame.setLocation(location);
frame.setVisible(f);
}
public boolean isVisible()
{
return frame.isVisible();
}
public void mouseDragged(MouseEvent e) {
if (selected != null) {
selected.x = view.getAbsoluteXCoord(e.getX());
selected.y = view.getAbsoluteYCoord(e.getY());
change = true;
} else
redispatchMouseEvent(e);
}
public void mouseMoved(MouseEvent e) {
redispatchMouseEvent(e) ;
}
public void mouseClicked(MouseEvent e) {
redispatchMouseEvent(e) ;
}
public void mouseEntered(MouseEvent e) {
redispatchMouseEvent(e) ;
}
public void mouseExited(MouseEvent e) {
redispatchMouseEvent(e) ;
}
public void mousePressed(MouseEvent e) {
int x=e.getX();
int y=e.getY();
int dx=x-x1;
int dy=y-y1;
int dx2=x-x2;
int dy2=y-y2;
if(dx*dx+dy*dy<=25)
{
selected=p1;
}
else if(dx2*dx2+dy2*dy2<=25)
{
selected=p2;
}
else redispatchMouseEvent(e) ;
}
public void mouseReleased(MouseEvent e) {
selected=null;
redispatchMouseEvent(e) ;
}
private void redispatchMouseEvent(MouseEvent e) {
Point glassPanePoint = e.getPoint();
Container container = this.getParent();
Point containerPoint = SwingUtilities.convertPoint(this,
glassPanePoint, container);
if (containerPoint.y < 0) { // we're not in the content pane
// Could have special code to handle mouse events over
// the menu bar or non-system window decorations, such as
// the ones provided by the Java look and feel.
} else {
// The mouse event is probably over the content pane.
// Find out exactly which component it's over.
Component component = null;
/*
* SwingUtilities.getDeepestComponentAt( container,
* containerPoint.x, containerPoint.y);
*/
int nbcom = container.getComponentCount();
for (int i = 0; i < nbcom; i++) {
Component c = container.getComponent(i);
if (c != this) {
component = SwingUtilities.getDeepestComponentAt(c,
containerPoint.x, containerPoint.y);
break;
}
}
if ((component != null)) {
Point componentPoint = SwingUtilities.convertPoint(this,
glassPanePoint, component);
component
.dispatchEvent(new MouseEvent(component, e.getID(), e
.getWhen(), e.getModifiers(), componentPoint.x,
componentPoint.y, e.getClickCount(), e
.isPopupTrigger()));
}
}
// Update the glass pane if requested.
}
private void redispatchMouseWheelEvent(MouseWheelEvent e) {
Point glassPanePoint = e.getPoint();
Container container = this.getParent();
Point containerPoint = SwingUtilities.convertPoint(
this,
glassPanePoint,
container);
if (containerPoint.y < 0) { //we're not in the content pane
//Could have special code to handle mouse events over
//the menu bar or non-system window decorations, such as
//the ones provided by the Java look and feel.
} else {
//The mouse event is probably over the content pane.
//Find out exactly which component it's over.
Component component =null;
/*SwingUtilities.getDeepestComponentAt(
container,
containerPoint.x,
containerPoint.y);*/
int nbcom=container.getComponentCount();
for(int i=0;i<nbcom;i++)
{
Component c = container.getComponent(i);
if(c!=this)
{
component =SwingUtilities.getDeepestComponentAt(
c,
containerPoint.x,
containerPoint.y);
break;
}
}
if ((component != null)
) {
Point componentPoint = SwingUtilities.convertPoint(
this,
glassPanePoint,
component);
component.dispatchEvent(new MouseWheelEvent(component,
e.getID(),
e.getWhen(),
e.getModifiers(),
componentPoint.x,
componentPoint.y,
e.getClickCount(),
e.isPopupTrigger(),e.getScrollType(),e.getScrollAmount(),e.getWheelRotation()));
}
}
//Update the glass pane if requested.
}
public void mouseWheelMoved(MouseWheelEvent e) {
redispatchMouseWheelEvent(e);
}
/**
* @param o
* @return
* @see java.util.ArrayList#contains(java.lang.Object)
*/
public boolean contains(Object o) {
return views.contains(o);
}
public void stateChanged(ChangeEvent e) {
Object o=e.getSource();
if(o instanceof View)
{
change=true;
}
}
private void dropAsMatlabPlot(){
StringBuffer buf=new StringBuffer();
for(int i=0;i < xSeries.length;i++)
{
double [] xs=xSeries[i];
double [] ys=ySeries[i];
buf.append("X"+i+"=[");
for(int j=0;j<xs.length;j++)
{
buf.append(xs[j]);
if(j!=xs.length-1)
buf.append(",");
}
buf.append("];\n");
buf.append("Y"+i+"=[");
for(int j=0;j<ys.length;j++)
{
buf.append(ys[j]);
if(j!=ys.length-1)
buf.append(",");
}
buf.append("];\n");
}
buf.append("figure(2);\nhold on;\n");
for(int i=0;i < xSeries.length;i++)
buf.append("plot(X"+i+",Y"+i+");\n");
buf.append("hold off;\n");
System.out.println(buf);
}
@Override
public void actionPerformed(ActionEvent e) {
Object o=e.getSource();
if(o==logScale)
{
log=logScale.isSelected();
refresh();
repaint();
}else if(o==dropAsMatlabPlot)
{
dropAsMatlabPlot();
}
}
}