/**
*
*/
package fr.orsay.lri.varna.applications.templateEditor;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.Point2D;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.undo.UndoManager;
import fr.orsay.lri.varna.applications.templateEditor.GraphicalTemplateElement.RelativePosition;
import fr.orsay.lri.varna.exceptions.ExceptionInvalidRNATemplate;
import fr.orsay.lri.varna.exceptions.ExceptionXmlLoading;
import fr.orsay.lri.varna.models.templates.RNATemplate;
import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateElement;
import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateHelix;
import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateUnpairedSequence;
import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateElement.EdgeEndPoint;
/**
* @author ponty
*
*/
public class TemplatePanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 3162771335587335679L;
private ArrayList<GraphicalTemplateElement> _RNAComponents;
private ArrayList<Connection> _RNAConnections;
private Hashtable<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>,Connection> _helixToConnection;
private TemplateEditorPanelUI _ui;
private RNATemplate _template;
private static Color[] BackgroundColors = {Color.blue,Color.red,Color.cyan,Color.green,Color.lightGray,Color.magenta,Color.PINK};
private int _nextBackgroundColor = 0 ;
public Color nextBackgroundColor()
{
Color c = BackgroundColors[_nextBackgroundColor++];
_nextBackgroundColor = _nextBackgroundColor % BackgroundColors.length;
return new Color(c.getRed(),c.getBlue(),c.getGreen(),50);
}
public TemplatePanel()
{
init();
}
public RNATemplate getTemplate()
{
return _template;
}
List<GraphicalTemplateElement> getRNAComponents() {
return _RNAComponents;
}
private void init()
{
_ui = new TemplateEditorPanelUI(this);
_RNAComponents = new ArrayList<GraphicalTemplateElement>();
_RNAConnections = new ArrayList<Connection>();
_helixToConnection = new Hashtable<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>,Connection>();
_template = new RNATemplate();
setBackground(Color.WHITE);
MouseControler mc = new MouseControler(this,_ui);
addMouseListener(mc);
addMouseMotionListener(mc);
_solidStroke = new BasicStroke(1.5f, BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND, 3.0f);
float[] dash = { 5.0f, 5.0f };
_dashedStroke = new BasicStroke(1.5f, BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND, 3.0f, dash, 0);
}
public void addUndoableEditListener(UndoManager manager)
{
_ui.addUndoableEditListener(manager);
}
public TemplateEditorPanelUI getTemplateUI()
{
return _ui;
}
public void flip(Helix h)
{
h.toggleFlipped();
}
public void addElement(GraphicalTemplateElement h)
{
_RNAComponents.add(h);
}
public void removeElement(GraphicalTemplateElement h)
{
_RNAComponents.remove(h);
try {
_template.removeElement(h.getTemplateElement());
} catch (ExceptionInvalidRNATemplate e) {
//e.printStackTrace();
}
}
private GraphicalTemplateElement _selected = null;
public GraphicalTemplateElement getSelected()
{
return _selected;
}
public void setSelected(GraphicalTemplateElement sel)
{
_selected = sel;
}
Helix.RelativePosition _relpos = Helix.RelativePosition.RP_OUTER;
public void setSelectedEdge(Helix.RelativePosition rel)
{
_relpos = rel;
}
public void unselectEdge(Helix.RelativePosition rel)
{
_relpos = rel;
}
Point2D.Double _mousePos = new Point2D.Double();
public void setPointerPos(Point2D.Double p)
{
_mousePos = p;
}
public void Unselect()
{
_selected = null;
}
public GraphicalTemplateElement getElement(RNATemplateElement t)
{
for(GraphicalTemplateElement t2: _RNAComponents)
if (t==t2.getTemplateElement())
return t2;
return null;
}
public GraphicalTemplateElement getElementAt(int x, int y)
{
return getElementAt(x, y, null);
}
public GraphicalTemplateElement getElementAt(int x, int y, GraphicalTemplateElement excluded)
{
GraphicalTemplateElement h = null;
for (int i=0; i<_RNAComponents.size();i++)
{
GraphicalTemplateElement h2 = _RNAComponents.get(i);
if ((
(h2.getRelativePosition(x, y)== Helix.RelativePosition.RP_CONNECT_END3)
|| (h2.getRelativePosition(x, y)== Helix.RelativePosition.RP_CONNECT_END5)
|| (h2.getRelativePosition(x, y)== Helix.RelativePosition.RP_CONNECT_START3)
|| (h2.getRelativePosition(x, y)== Helix.RelativePosition.RP_CONNECT_START5))
&& (excluded!=h2))
{
h = h2;
}
}
if (h==null)
{ h = getElementCloseTo(x, y, excluded);};
return h;
}
public GraphicalTemplateElement getElementCloseTo(int x, int y)
{
return getElementCloseTo(x, y, null);
}
public GraphicalTemplateElement getElementCloseTo(int x, int y, GraphicalTemplateElement excluded)
{
GraphicalTemplateElement h = null;
for (int i=0; i<_RNAComponents.size();i++)
{
GraphicalTemplateElement h2 = _RNAComponents.get(i);
if ((h2.getRelativePosition(x, y) != Helix.RelativePosition.RP_OUTER)
&& (excluded!=h2))
{
h = h2;
}
}
return h;
}
public void addConnection(Connection c)
{
_RNAConnections.add(c);
_helixToConnection.put(new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c._h1,c._edge1), c);
_helixToConnection.put(new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c._h2,c._edge2), c);
try {
c._h1.attach(c._h2, c._edge1, c._edge2);
c._h2.attach(c._h1, c._edge2, c._edge1);
} catch (ExceptionInvalidRNATemplate e) {
System.out.println(e.toString());// TODO Auto-generated catch block
}
}
public Connection addConnection(GraphicalTemplateElement h1, GraphicalTemplateElement.RelativePosition edge1,GraphicalTemplateElement h2, GraphicalTemplateElement.RelativePosition edge2)
{
if ((h1!=h2)&&(getPartner(h1,edge1)==null)&&(getPartner(h2,edge2)==null))
{
Connection c = new Connection(h1,edge1,h2,edge2);
addConnection(c);
return c;
}
return null;
}
/**
* When there is already a connection in the underlying RNATemplate
* and we want to create one at the graphical level.
*/
public void addGraphicalConnection(GraphicalTemplateElement h1, GraphicalTemplateElement.RelativePosition edge1,GraphicalTemplateElement h2, GraphicalTemplateElement.RelativePosition edge2) {
//System.out.println("Connecting " + h1 + " " + edge1 + " to " + h2 + " " + edge2);
Connection c = new Connection(h1,edge1,h2,edge2);
_RNAConnections.add(c);
_helixToConnection.put(new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c._h1,c._edge1), c);
_helixToConnection.put(new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c._h2,c._edge2), c);
c._h1.graphicalAttach(c._h2, c._edge1, c._edge2);
c._h2.graphicalAttach(c._h1, c._edge2, c._edge1);
}
public void removeConnection(Connection c)
{
_RNAConnections.remove(c);
_helixToConnection.remove(new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c._h1,c._edge1));
_helixToConnection.remove(new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c._h2,c._edge2));
System.out.println("[A]"+c);
c._h1.detach(c._edge1);
}
public boolean isInCycle(GraphicalTemplateElement el, GraphicalTemplateElement.RelativePosition edge)
{
Stack<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> > p = new Stack<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>>();
Hashtable<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>,Integer> alreadySeen = new Hashtable<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>,Integer>();
p.add(new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(el,edge));
while(!p.empty())
{
Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> c2 = p.pop();
if (alreadySeen.containsKey(c2))
{
return true;
}
else
{
alreadySeen.put(c2, new Integer(1));
}
GraphicalTemplateElement.RelativePosition next = c2.first.getConnectedEdge(c2.second);
Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> otherEnd = new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c2.first,next);
if (!alreadySeen.containsKey(otherEnd))
{
p.push(otherEnd);
}
else
{
Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> child = getPartner(c2.first,c2.second);
if (child!=null)
{
p.push(child);
}
}
}
return false;
}
private static Color[] _colors = {Color.gray,Color.pink,Color.cyan,Color.RED,Color.green,Color.orange};
public static Color getIndexedColor(int n)
{
return _colors[n%_colors.length];
}
public HashMap<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>,Integer> buildConnectedComponents()
{
HashMap<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>,Integer> alreadySeen = new HashMap<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>,Integer>();
int numConnectedComponents = 0;
for (GraphicalTemplateElement el : this._RNAComponents)
{
for (GraphicalTemplateElement.RelativePosition edge : el.getConnectedEdges())
{
Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> c = new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(el,edge);
if (!alreadySeen.containsKey(c))
{
Stack<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> > p = new Stack<Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>>();
p.add(c);
p.add(new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(el,el.getConnectedEdge(edge)));
while(!p.empty())
{
Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> c2 = p.pop();
if (!alreadySeen.containsKey(c2))
{
//System.out.println(" "+numConnectedComponents+" "+c2);
c2.first.setMainColor(c2.second, getIndexedColor(numConnectedComponents));
alreadySeen.put(c2, new Integer(numConnectedComponents));
GraphicalTemplateElement.RelativePosition next = c2.first.getConnectedEdge(c2.second);
Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> otherEnd = new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c2.first,next);
p.push(otherEnd);
Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> child = getPartner(c2.first,c2.second);
if (child!=null)
{ p.push(child); }
}
}
numConnectedComponents += 1;
}
}
}
return alreadySeen;
}
public boolean isInCycle(Connection c)
{
return isInCycle(c._h1,c._edge1);
}
public Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> getPartner(GraphicalTemplateElement h, GraphicalTemplateElement.RelativePosition edge)
{
Connection c = getConnection(h, edge);
if (c != null)
{
if ((c._h1==h)&&(c._edge1==edge))
{ return new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c._h2,c._edge2); }
else
{ return new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(c._h1,c._edge1); }
}
else
{
return null;
}
}
public Connection getConnection(GraphicalTemplateElement h, Helix.RelativePosition edge)
{
Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> target = new Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition>(h,edge);
if (_helixToConnection.containsKey(target))
{
return _helixToConnection.get(target);
}
else
{ return null; }
}
private boolean isConnected(Helix h, GraphicalTemplateElement.RelativePosition edge)
{
Couple<GraphicalTemplateElement,GraphicalTemplateElement.RelativePosition> partner = getPartner(h,edge);
return partner!=null;
}
// Aspects graphiques
private static final Color CYCLE_COLOR = Color.red;
private static final Color NON_EXISTANT_COLOR = Color.gray.brighter();
private static final Color CONTROL_COLOR = Color.gray.darker();
private static final Color BACKGROUND_COLOR = Color.white;
private Stroke _solidStroke;
private Stroke _dashedStroke;
private void drawConnections(Graphics2D g2d, Connection c)
{
GraphicalTemplateElement h1 = c._h1;
GraphicalTemplateElement.RelativePosition edge1 = c._edge1;
Point2D.Double p1 = h1.getEdgePosition(edge1);
GraphicalTemplateElement h2 = c._h2;
GraphicalTemplateElement.RelativePosition edge2 = c._edge2;
Point2D.Double p2 = h2.getEdgePosition(edge2);
if (isInCycle(c))
{
g2d.setColor(CYCLE_COLOR);
}
else
{
g2d.setColor(GraphicalTemplateElement.BACKBONE_COLOR);
}
g2d.drawLine((int)p1.x,(int)p1.y,(int)p2.x,(int)p2.y);
}
public void paintComponent(Graphics g)
{
rescale();
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
removeAll();
super.paintComponent(g2d);
buildConnectedComponents();
if (_selected!=null)
{
if (_relpos != GraphicalTemplateElement.RelativePosition.RP_OUTER)
{
Point2D.Double p = _selected.getEdgePosition(_relpos);
g2d.setStroke(_solidStroke);
g2d.drawLine((int)_mousePos.x, (int)_mousePos.y, (int)p.x, (int)p.y);
}
}
for (int i=0;i<_RNAConnections.size();i++)
{
Connection c = _RNAConnections.get(i);
drawConnections(g2d,c);
}
for (int i=0;i<_RNAComponents.size();i++)
{
GraphicalTemplateElement elem = _RNAComponents.get(i);
g2d.setColor(elem.getDominantColor());
g2d.fill(elem.getArea());
if (_selected == elem)
{
elem.draw(g2d,true);
}
else
{ elem.draw(g2d,false); }
}
}
private void rescale()
{
int minX = 0;
int maxX = 0;
int minY = 0;
int maxY = 0;
for(int i=0;i<this._RNAComponents.size();i++)
{
GraphicalTemplateElement h = _RNAComponents.get(i);
Polygon p = h.getBoundingPolygon();
Rectangle r = p.getBounds();
minX = Math.min(minX,r.x);
maxX = Math.max(maxX,r.x+r.width);
minY = Math.min(minY,r.y);
maxY = Math.max(maxY,r.y+r.height);
}
if ((minX<0)||(minY<0))
{
for(int i=0;i<this._RNAComponents.size();i++)
{
GraphicalTemplateElement h = _RNAComponents.get(i);
h.translate(minX<0 ? -minX : 0, minY<0 ? -minY : 0);
}
}
setPreferredSize(new Dimension(Math.max(maxX-minX,10)+100,Math.max(maxY-minY,10)+100));
revalidate();
}
/**
* Load an existing RNATemplate object in this panel.
*/
public void loadTemplate(RNATemplate template) {
_template = template;
_RNAComponents.clear();
_RNAConnections.clear();
_helixToConnection.clear();
// We need a template element -> graphical template element mapping
Map<RNATemplateElement, GraphicalTemplateElement> map = new HashMap<RNATemplateElement, GraphicalTemplateElement>();
// First, we load elements
{
Iterator<RNATemplateElement> iter = template.classicIterator();
while (iter.hasNext()) {
RNATemplateElement templateElement = iter.next();
if (templateElement instanceof RNATemplateHelix) {
RNATemplateHelix templateHelix = (RNATemplateHelix) templateElement;
Helix graphicalHelix = new Helix(templateHelix);
graphicalHelix.setDominantColor(nextBackgroundColor());
_RNAComponents.add(graphicalHelix);
map.put(templateHelix, graphicalHelix);
} else if (templateElement instanceof RNATemplateUnpairedSequence) {
RNATemplateUnpairedSequence templateSequence = (RNATemplateUnpairedSequence) templateElement;
UnpairedRegion graphicalSequence = new UnpairedRegion(templateSequence);
graphicalSequence.setDominantColor(nextBackgroundColor());
_RNAComponents.add(graphicalSequence);
map.put(templateSequence, graphicalSequence);
}
}
}
// Now, we load edges
{
Iterator<EdgeEndPoint> iter = template.makeEdgeList().iterator();
while (iter.hasNext()) {
EdgeEndPoint v1 = iter.next();
EdgeEndPoint v2 = v1.getOtherEndPoint();
GraphicalTemplateElement gte1 = map.get(v1.getElement());
GraphicalTemplateElement gte2 = map.get(v2.getElement());
RelativePosition rp1 = gte1.relativePositionFromEdgeEndPointPosition(v1.getPosition());
RelativePosition rp2 = gte2.relativePositionFromEdgeEndPointPosition(v2.getPosition());
addGraphicalConnection(gte1, rp1, gte2, rp2);
}
}
repaint();
}
/**
* Load a template from an XML file.
*/
public void loadFromXmlFile(File filename) {
try {
RNATemplate newTemplate = RNATemplate.fromXMLFile(filename);
loadTemplate(newTemplate);
} catch (ExceptionXmlLoading e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, e.getMessage(), "Template loading error", JOptionPane.ERROR_MESSAGE);
}
}
}