/** * */ 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.Point; 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.controlers.ControleurMolette; 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 _RNAComponents; private ArrayList _RNAConnections; private Hashtable,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; private static double scaleFactorDefault = 0.7; private double scaleFactor = scaleFactorDefault; private TemplateEditor _editor; public double getScaleFactor() { return scaleFactor; } public void setScaleFactor(double scaleFactor) { this.scaleFactor = scaleFactor; } public Color nextBackgroundColor() { Color c = BackgroundColors[_nextBackgroundColor++]; _nextBackgroundColor = _nextBackgroundColor % BackgroundColors.length; return new Color(c.getRed(),c.getBlue(),c.getGreen(),50); } public TemplatePanel(TemplateEditor parent) { _editor = parent; init(); } public RNATemplate getTemplate() { return _template; } List getRNAComponents() { return _RNAComponents; } private void init() { _ui = new TemplateEditorPanelUI(this); _RNAComponents = new ArrayList(); _RNAConnections = new ArrayList(); _helixToConnection = new Hashtable,Connection>(); _template = new RNATemplate(); setBackground(Color.WHITE); MouseControler mc = new MouseControler(this,_ui); addMouseListener(mc); addMouseMotionListener(mc); addMouseWheelListener(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; if (_selected instanceof Helix) { _editor.flipButtonEnable(); } else { _editor.flipButtonDisable(); } } 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() { _editor.flipButtonDisable(); _selected = null; } public GraphicalTemplateElement getElement(RNATemplateElement t) { for(GraphicalTemplateElement t2: _RNAComponents) if (t==t2.getTemplateElement()) return t2; return null; } public GraphicalTemplateElement getElementAt(double x, double y) { return getElementAt(x, y, null); } public GraphicalTemplateElement getElementAt(double x, double 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(double x, double y) { return getElementCloseTo(x, y, null); } public GraphicalTemplateElement getElementCloseTo(double x, double 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(c._h1,c._edge1), c); _helixToConnection.put(new Couple(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(c._h1,c._edge1), c); _helixToConnection.put(new Couple(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(c._h1,c._edge1)); _helixToConnection.remove(new Couple(c._h2,c._edge2)); System.out.println("[A]"+c); c._h1.detach(c._edge1); } public boolean isInCycle(GraphicalTemplateElement el, GraphicalTemplateElement.RelativePosition edge) { Stack > p = new Stack>(); Hashtable,Integer> alreadySeen = new Hashtable,Integer>(); p.add(new Couple(el,edge)); while(!p.empty()) { Couple 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 otherEnd = new Couple(c2.first,next); if (!alreadySeen.containsKey(otherEnd)) { p.push(otherEnd); } else { Couple 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,Integer> buildConnectedComponents() { HashMap,Integer> alreadySeen = new HashMap,Integer>(); int numConnectedComponents = 0; for (GraphicalTemplateElement el : this._RNAComponents) { for (GraphicalTemplateElement.RelativePosition edge : el.getConnectedEdges()) { Couple c = new Couple(el,edge); if (!alreadySeen.containsKey(c)) { Stack > p = new Stack>(); p.add(c); p.add(new Couple(el,el.getConnectedEdge(edge))); while(!p.empty()) { Couple 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 otherEnd = new Couple(c2.first,next); p.push(otherEnd); Couple 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 getPartner(GraphicalTemplateElement h, GraphicalTemplateElement.RelativePosition edge) { Connection c = getConnection(h, edge); if (c != null) { if ((c._h1==h)&&(c._edge1==edge)) { return new Couple(c._h2,c._edge2); } else { return new Couple(c._h1,c._edge1); } } else { return null; } } public Connection getConnection(GraphicalTemplateElement h, Helix.RelativePosition edge) { Couple target = new Couple(h,edge); if (_helixToConnection.containsKey(target)) { return _helixToConnection.get(target); } else { return null; } } private boolean isConnected(Helix h, GraphicalTemplateElement.RelativePosition edge) { Couple 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(); // Debug code to show drawing area // g.setColor(Color.red); // g.fillRect(0,0,getWidth(),getHeight()); // g.setColor(Color.white); // g.fillRect(10,10,getWidth()-20,getHeight()-20); g.setColor(Color.white); g.fillRect(0,0,getWidth(),getHeight()); Graphics2D g2d = (Graphics2D) g; g2d.scale(scaleFactor, scaleFactor); 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); } } } /** * Get the bounding rectangle of the RNA components, always including the origin (0,0). */ public Rectangle getBoundingRectange() { int minX = 0; int maxX = 0; int minY = 0; int maxY = 0; for(int i=0;i graphical template element mapping Map map = new HashMap(); // First, we load elements { Iterator 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 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); } } zoomFit(); //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); } } private void zoomFinish() { rescale(); repaint(); } public void zoomIn() { scaleFactor *= 1.2; zoomFinish(); } public void zoomOut() { scaleFactor /= 1.2; zoomFinish(); } public void zoomReset() { scaleFactor = scaleFactorDefault; zoomFinish(); } public void zoomFit() { if (_RNAComponents.isEmpty()) { zoomReset(); } else { Rectangle rect = getBoundingRectange(); double areaW = (rect.width + 100); double areaH = (rect.height + 100); // make it cover at least the space visible in the window scaleFactor = 1; scaleFactor = Math.min(scaleFactor, _editor.getJp().getViewport().getSize().width / areaW); scaleFactor = Math.min(scaleFactor, _editor.getJp().getViewport().getSize().height / areaH); zoomFinish(); } } public void translateView(Point trans) { int newX = _editor.getJp().getHorizontalScrollBar().getValue() - trans.x; int newY = _editor.getJp().getVerticalScrollBar().getValue() - trans.y; newX = Math.max(0, Math.min(newX, _editor.getJp().getHorizontalScrollBar().getMaximum())); newY = Math.max(0, Math.min(newY, _editor.getJp().getVerticalScrollBar().getMaximum())); _editor.getJp().getHorizontalScrollBar().setValue(newX); _editor.getJp().getVerticalScrollBar().setValue(newY); } }