X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=srcjar%2Ffr%2Forsay%2Flri%2Fvarna%2Fapplications%2FtemplateEditor%2FTemplatePanel.java;fp=srcjar%2Ffr%2Forsay%2Flri%2Fvarna%2Fapplications%2FtemplateEditor%2FTemplatePanel.java;h=ce477169495faaaf5f61129e4828d0810bc6f2c3;hb=ec8f3cedf60fb1feed6d34de6b49f6bfa78b9dd8;hp=0000000000000000000000000000000000000000;hpb=056dad85a910551cc95e44d451a61f6b8c4dd35d;p=jalview.git diff --git a/srcjar/fr/orsay/lri/varna/applications/templateEditor/TemplatePanel.java b/srcjar/fr/orsay/lri/varna/applications/templateEditor/TemplatePanel.java new file mode 100644 index 0000000..ce47716 --- /dev/null +++ b/srcjar/fr/orsay/lri/varna/applications/templateEditor/TemplatePanel.java @@ -0,0 +1,662 @@ +/** + * + */ +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); + } +}