/** * */ 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); } } }