/* VARNA is a tool for the automated drawing, visualization and annotation of the secondary structure of RNA, designed as a companion software for web servers and databases. Copyright (C) 2008 Kevin Darty, Alain Denise and Yann Ponty. electronic mail : Yann.Ponty@lri.fr paper mail : LRI, bat 490 Universit� Paris-Sud 91405 Orsay Cedex France This file is part of VARNA version 3.1. VARNA version 3.1 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. VARNA version 3.1 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with VARNA version 3.1. If not, see http://www.gnu.org/licenses. */ package fr.orsay.lri.varna.views; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Point; import java.awt.Toolkit; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.awt.image.FilteredImageSource; import java.awt.image.ImageFilter; import java.awt.image.ImageProducer; import java.awt.image.RGBImageFilter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Hashtable; import java.util.Vector; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.stream.FileImageOutputStream; import javax.swing.JColorChooser; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.filechooser.FileFilter; import javax.swing.undo.UndoManager; import javax.swing.undo.UndoableEditSupport; import fr.orsay.lri.varna.VARNAPanel; import fr.orsay.lri.varna.applications.FileNameExtensionFilter; import fr.orsay.lri.varna.applications.VARNAPrinter; import fr.orsay.lri.varna.applications.templateEditor.TemplateEdits; import fr.orsay.lri.varna.applications.templateEditor.TemplatePanel; import fr.orsay.lri.varna.exceptions.ExceptionExportFailed; import fr.orsay.lri.varna.exceptions.ExceptionFileFormatOrSyntax; import fr.orsay.lri.varna.exceptions.ExceptionJPEGEncoding; import fr.orsay.lri.varna.exceptions.ExceptionLoadingFailed; import fr.orsay.lri.varna.exceptions.ExceptionNAViewAlgorithm; import fr.orsay.lri.varna.exceptions.ExceptionNonEqualLength; import fr.orsay.lri.varna.exceptions.ExceptionPermissionDenied; import fr.orsay.lri.varna.exceptions.ExceptionUnmatchedClosingParentheses; import fr.orsay.lri.varna.exceptions.ExceptionWritingForbidden; import fr.orsay.lri.varna.factories.RNAFactory; import fr.orsay.lri.varna.models.FullBackup; import fr.orsay.lri.varna.models.VARNAConfig; import fr.orsay.lri.varna.models.VARNAEdits; import fr.orsay.lri.varna.models.annotations.ChemProbAnnotation; import fr.orsay.lri.varna.models.annotations.HighlightRegionAnnotation; import fr.orsay.lri.varna.models.annotations.TextAnnotation; import fr.orsay.lri.varna.models.rna.ModeleBase; import fr.orsay.lri.varna.models.rna.ModeleBaseNucleotide; import fr.orsay.lri.varna.models.rna.ModeleBasesComparison; import fr.orsay.lri.varna.models.rna.ModeleBP; import fr.orsay.lri.varna.models.rna.RNA; public class VueUI { private VARNAPanel _vp; private File _fileChooserDirectory = null; private UndoableEditSupport _undoableEditSupport; public VueUI(VARNAPanel vp) { _vp = vp; _undoableEditSupport = new UndoableEditSupport(_vp); } public void addUndoableEditListener(UndoManager manager) { _undoableEditSupport.addUndoableEditListener(manager); } public void UIToggleColorMap() { if (_vp.isModifiable()) { _vp.setColorMapVisible(!_vp.getColorMapVisible()); _vp.repaint(); } } public Hashtable<Integer,Point2D.Double> backupAllCoords() { Hashtable<Integer,Point2D.Double> tmp = new Hashtable<Integer,Point2D.Double>(); for(int i=0;i<_vp.getRNA().getSize();i++) { tmp.put(i, _vp.getRNA().getCoords(i)); } return tmp; } public void UIToggleFlatExteriorLoop() { if (_vp.isModifiable() && _vp.getRNA().get_drawMode() == RNA.DRAW_MODE_RADIATE) { Hashtable<Integer,Point2D.Double> bck = backupAllCoords(); _undoableEditSupport.postEdit(new VARNAEdits.RedrawEdit(RNA.DRAW_MODE_RADIATE,_vp,!_vp.getFlatExteriorLoop())); _vp.setFlatExteriorLoop(!_vp.getFlatExteriorLoop()); _vp.reset(); _vp.drawRNA(_vp.getRNA(), RNA.DRAW_MODE_RADIATE); _vp.repaint(); _vp.fireLayoutChanged(bck); } } public void UIRadiate() { if (_vp.isModifiable()) { Hashtable<Integer,Point2D.Double> bck = backupAllCoords(); _undoableEditSupport.postEdit(new VARNAEdits.RedrawEdit(RNA.DRAW_MODE_RADIATE,_vp)); _vp.reset(); _vp.drawRNA(_vp.getRNA(), RNA.DRAW_MODE_RADIATE); _vp.repaint(); _vp.fireLayoutChanged(bck); } } public void UIMOTIFView() { if (_vp.isModifiable()) { Hashtable<Integer,Point2D.Double> bck = backupAllCoords(); _undoableEditSupport.postEdit(new VARNAEdits.RedrawEdit(RNA.DRAW_MODE_MOTIFVIEW,_vp)); _vp.reset(); _vp.drawRNA(_vp.getRNA(), RNA.DRAW_MODE_MOTIFVIEW); _vp.repaint(); _vp.fireLayoutChanged(bck); } } public void UILine() { if (_vp.isModifiable()) { Hashtable<Integer,Point2D.Double> bck = backupAllCoords(); _undoableEditSupport.postEdit(new VARNAEdits.RedrawEdit(RNA.DRAW_MODE_LINEAR,_vp)); _vp.reset(); _vp.drawRNA(_vp.getRNA(), RNA.DRAW_MODE_LINEAR); _vp.repaint(); _vp.fireLayoutChanged(bck); } } public void UICircular() { if (_vp.isModifiable()) { Hashtable<Integer,Point2D.Double> bck = backupAllCoords(); _undoableEditSupport.postEdit(new VARNAEdits.RedrawEdit(RNA.DRAW_MODE_CIRCULAR,_vp)); _vp.reset(); _vp.drawRNA(_vp.getRNA(), RNA.DRAW_MODE_CIRCULAR); _vp.repaint(); _vp.fireLayoutChanged(bck); } } public void UINAView() { if (_vp.isModifiable()) { Hashtable<Integer,Point2D.Double> bck = backupAllCoords(); _undoableEditSupport.postEdit(new VARNAEdits.RedrawEdit(RNA.DRAW_MODE_NAVIEW,_vp)); _vp.reset(); _vp.drawRNA(_vp.getRNA(), RNA.DRAW_MODE_NAVIEW); _vp.repaint(); _vp.fireLayoutChanged(bck); } } public void UIVARNAView() { if (_vp.isModifiable()) { Hashtable<Integer,Point2D.Double> bck = backupAllCoords(); _undoableEditSupport.postEdit(new VARNAEdits.RedrawEdit(RNA.DRAW_MODE_VARNA_VIEW,_vp)); _vp.reset(); _vp.drawRNA(_vp.getRNA(), RNA.DRAW_MODE_VARNA_VIEW); _vp.repaint(); _vp.fireLayoutChanged(bck); } } public void UIReset() { if (_vp.isModifiable()) { Hashtable<Integer,Point2D.Double> bck = backupAllCoords(); _undoableEditSupport.postEdit(new VARNAEdits.RedrawEdit(_vp.getRNA().get_drawMode(),_vp)); _vp.reset(); _vp.drawRNA(_vp.getRNA(), _vp.getRNA().get_drawMode()); _vp.repaint(); _vp.fireLayoutChanged(bck); } } private void savePath(JFileChooser jfc) { _fileChooserDirectory = jfc.getCurrentDirectory(); } private void loadPath(JFileChooser jfc) { if (_fileChooserDirectory != null) { jfc.setCurrentDirectory(_fileChooserDirectory); } } public void UIFile() throws ExceptionNonEqualLength { if (_vp.isModifiable()) { JFileChooser fc = new JFileChooser(); fc.setFileSelectionMode(JFileChooser.OPEN_DIALOG); fc.setDialogTitle("Open..."); loadPath(fc); if (fc.showOpenDialog(_vp) == JFileChooser.APPROVE_OPTION) { try { savePath(fc); String path = fc.getSelectedFile().getAbsolutePath(); if (!path.toLowerCase().endsWith(".varna")) { Collection<RNA> rnas = RNAFactory.loadSecStr(path); if (rnas.isEmpty()) { throw new ExceptionFileFormatOrSyntax("No RNA could be parsed from that source."); } RNA r = rnas.iterator().next(); _vp.drawRNAInterpolated(r); _vp.fireUINewStructure(); _vp.repaint(); } else { FullBackup bck = _vp.loadSession(path); _vp.fireUINewStructure(); } } catch (ExceptionExportFailed e1) { _vp.errorDialog(e1); } catch (ExceptionPermissionDenied e1) { _vp.errorDialog(e1); } catch (ExceptionLoadingFailed e1) { _vp.errorDialog(e1); } catch (ExceptionFileFormatOrSyntax e1) { _vp.errorDialog(e1); } catch (ExceptionUnmatchedClosingParentheses e1) { _vp.errorDialog(e1); } catch (FileNotFoundException e) { _vp.errorDialog(e); } } } } public void UISetColorMapStyle() { VueColorMapStyle cms = new VueColorMapStyle(_vp); if (JOptionPane.showConfirmDialog(_vp, cms, "Choose color map style", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { _vp.setColorMap(cms.getColorMap()); } else { cms.cancelChanges(); } } public void UILoadColorMapValues() { VueLoadColorMapValues cms = new VueLoadColorMapValues(_vp); if (JOptionPane.showConfirmDialog(_vp, cms, "Load base values", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { try { _vp.setColorMapVisible(true); _vp.readValues(cms.getReader()); } catch (IOException e) { _vp.errorDialog(e); } } } public void UISetColorMapValues() { VueBaseValues cms = new VueBaseValues(_vp); if (JOptionPane.showConfirmDialog(_vp, cms, "Choose base values", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { } else { cms.cancelChanges(); } } public void UIManualInput() throws ParseException, ExceptionNonEqualLength { if (_vp.isModifiable()) { VueManualInput manualInput = new VueManualInput(_vp); if (JOptionPane.showConfirmDialog(_vp, manualInput.getPanel(), "Input sequence/structure", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { if (_vp.getRNA().getSize() == 0) { } try { RNA r = new RNA(); VARNAConfig cfg = new VARNAConfig(); r.setRNA(manualInput.getTseq().getText(),manualInput.getTstr().getText()); r.drawRNA(_vp.getRNA().get_drawMode(), cfg); _vp.drawRNAInterpolated(r); _vp.fireUINewStructure(); _vp.repaint(); } catch (ExceptionFileFormatOrSyntax e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExceptionNAViewAlgorithm e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public void UISetTitle() { if (_vp.isModifiable()) { String res = JOptionPane.showInputDialog(_vp, "Input title", _vp .getTitle()); if (res != null) { _vp.setTitle(res); _vp.repaint(); } } } public void UISetColorMapCaption() { if (_vp.isModifiable()) { String res = JOptionPane.showInputDialog(_vp, "Input new color map caption", _vp.getColorMapCaption()); if (res != null) { _vp.setColorMapCaption(res); _vp.repaint(); } } } public void UISetBaseCharacter() { if (_vp.isModifiable()) { int i = _vp.getNearestBase(); if (_vp.isComparisonMode()) { String res = JOptionPane.showInputDialog(_vp, "Input base", ((ModeleBasesComparison) _vp.getRNA().get_listeBases() .get(i)).getBases()); if (res != null) { ModeleBasesComparison mb = (ModeleBasesComparison) _vp.getRNA().get_listeBases().get(i); String bck = mb.get_base1()+"|"+mb.get_base2(); mb.set_base1(((res.length()>0)?res.charAt(0):' ')); mb.set_base2(((res.length()>1)?res.charAt(1):' ')); _vp.repaint(); _vp.fireSequenceChanged(i, bck, res); } } else { String res = JOptionPane.showInputDialog(_vp, "Input base", ((ModeleBaseNucleotide) _vp.getRNA().get_listeBases() .get(i)).get_c()); if (res != null) { ModeleBaseNucleotide mb = (ModeleBaseNucleotide) _vp.getRNA().get_listeBases().get(i); String bck = mb.get_c(); mb.set_c(res); _vp.repaint(); _vp.fireSequenceChanged(i, bck, res); } } } } FileNameExtensionFilter _varnaFilter = new FileNameExtensionFilter( "VARNA Session File", "varna", "VARNA"); FileNameExtensionFilter _bpseqFilter = new FileNameExtensionFilter( "BPSeq (CRW) File", "bpseq", "BPSEQ"); FileNameExtensionFilter _ctFilter = new FileNameExtensionFilter( "Connect (MFold) File", "ct", "CT"); FileNameExtensionFilter _dbnFilter = new FileNameExtensionFilter( "Dot-bracket notation (Vienna) File", "dbn", "DBN", "faa", "FAA"); FileNameExtensionFilter _jpgFilter = new FileNameExtensionFilter( "JPEG Picture", "jpeg", "jpg", "JPG", "JPEG"); FileNameExtensionFilter _pngFilter = new FileNameExtensionFilter( "PNG Picture", "png", "PNG"); FileNameExtensionFilter _epsFilter = new FileNameExtensionFilter( "EPS File", "eps", "EPS"); FileNameExtensionFilter _svgFilter = new FileNameExtensionFilter( "SVG Picture", "svg", "SVG"); FileNameExtensionFilter _xfigFilter = new FileNameExtensionFilter( "XFig Diagram", "fig", "xfig", "FIG", "XFIG"); public void UIExport() throws ExceptionExportFailed, ExceptionPermissionDenied, ExceptionWritingForbidden, ExceptionJPEGEncoding { ArrayList<FileNameExtensionFilter> v = new ArrayList<FileNameExtensionFilter>(); v.add(_epsFilter); v.add(_svgFilter); v.add(_xfigFilter); v.add(_jpgFilter); v.add(_pngFilter); String dest = UIChooseOutputFile(v); if (dest != null) { String extLower = dest.substring(dest.lastIndexOf('.')) .toLowerCase(); // System.out.println(extLower); if (extLower.equals(".eps")) { _vp.getRNA().saveRNAEPS(dest, _vp.getConfig()); } else if (extLower.equals(".svg")) { _vp.getRNA().saveRNASVG(dest, _vp.getConfig()); } else if (extLower.equals(".fig") || extLower.equals("xfig")) { _vp.getRNA().saveRNAXFIG(dest, _vp.getConfig()); } else if (extLower.equals(".png")) { saveToPNG(dest); } else if (extLower.equals("jpg") || extLower.equals("jpeg")) { saveToJPEG(dest); } } } public void UIExportJPEG() throws ExceptionJPEGEncoding, ExceptionExportFailed { String dest = UIChooseOutputFile(_jpgFilter); if (dest != null) { saveToJPEG(dest); } } public void UIPrint() { VARNAPrinter.printComponent(_vp); } public void UIExportPNG() throws ExceptionExportFailed { String dest = UIChooseOutputFile(_pngFilter); if (dest != null) { saveToPNG(dest); } } public void UIExportXFIG() throws ExceptionExportFailed, ExceptionWritingForbidden { String dest = UIChooseOutputFile(_xfigFilter); if (dest != null) { _vp.getRNA().saveRNAXFIG(dest, _vp.getConfig()); } } public void UIExportEPS() throws ExceptionExportFailed, ExceptionWritingForbidden { String dest = UIChooseOutputFile(_epsFilter); if (dest != null) { _vp.getRNA().saveRNAEPS(dest, _vp.getConfig()); } } public void UIExportSVG() throws ExceptionExportFailed, ExceptionWritingForbidden { String dest = UIChooseOutputFile(_svgFilter); if (dest != null) { _vp.getRNA().saveRNASVG(dest, _vp.getConfig()); } } public void UISaveAsDBN() throws ExceptionExportFailed, ExceptionPermissionDenied { String name = _vp.getVARNAUI().UIChooseOutputFile(_dbnFilter); if (name != null) _vp.getRNA().saveAsDBN(name, _vp.getTitle()); } public void UISaveAsCT() throws ExceptionExportFailed, ExceptionPermissionDenied { String name = _vp.getVARNAUI().UIChooseOutputFile(_ctFilter); if (name != null) _vp.getRNA().saveAsCT(name, _vp.getTitle()); } public void UISaveAsBPSEQ() throws ExceptionExportFailed, ExceptionPermissionDenied { String name = _vp.getVARNAUI().UIChooseOutputFile(_bpseqFilter); if (name != null) _vp.getRNA().saveAsBPSEQ(name, _vp.getTitle()); } public void UISaveAs() throws ExceptionExportFailed, ExceptionPermissionDenied { ArrayList<FileNameExtensionFilter> v = new ArrayList<FileNameExtensionFilter>(); v.add(_bpseqFilter); v.add(_dbnFilter); v.add(_ctFilter); v.add(_varnaFilter); String dest = UIChooseOutputFile(v); if (dest != null) { String extLower = dest.substring(dest.lastIndexOf('.')) .toLowerCase(); if (extLower.endsWith("bpseq")) { _vp.getRNA().saveAsBPSEQ(dest, _vp.getTitle()); } else if (extLower.endsWith("ct")) { _vp.getRNA().saveAsCT(dest, _vp.getTitle()); } else if (extLower.endsWith("dbn") || extLower.endsWith("faa")) { _vp.getRNA().saveAsDBN(dest, _vp.getTitle()); } else if (extLower.endsWith("varna")) { _vp.saveSession(dest); } } } public String UIChooseOutputFile(FileNameExtensionFilter filtre) { ArrayList<FileNameExtensionFilter> v = new ArrayList<FileNameExtensionFilter>(); v.add(filtre); return UIChooseOutputFile(v); } /** * Opens a save dialog with right extensions and return the absolute path * * @param filtre * Allowed extensions * @return <code>null</code> if the user doesn't approve the save dialog,<br> * <code>absolutePath</code> if the user approve the save dialog */ public String UIChooseOutputFile(ArrayList<FileNameExtensionFilter> filtre) { JFileChooser fc = new JFileChooser(); loadPath(fc); String absolutePath = null; // applique le filtre for (int i = 0; i < filtre.size(); i++) { fc.addChoosableFileFilter(filtre.get(i)); } // en mode open dialog pour voir les autres fichiers avec la meme // extension fc.setFileSelectionMode(JFileChooser.OPEN_DIALOG); fc.setDialogTitle("Save..."); // Si l'utilisateur a valider if (fc.showSaveDialog(_vp) == JFileChooser.APPROVE_OPTION) { savePath(fc); absolutePath = fc.getSelectedFile().getAbsolutePath(); String extension = _vp.getPopupMenu().get_controleurMenu() .getExtension(fc.getSelectedFile()); FileFilter f = fc.getFileFilter(); if (f instanceof FileNameExtensionFilter) { ArrayList<String> listeExtension = new ArrayList<String>(); listeExtension.addAll(Arrays .asList(((FileNameExtensionFilter) f).getExtensions())); // si l'extension du fichier ne fait pas partie de la liste // d'extensions acceptées if (!listeExtension.contains(extension)) { absolutePath += "." + listeExtension.get(0); } } } return absolutePath; } public void UISetBorder() { VueBorder border = new VueBorder(_vp); Dimension oldBorder = _vp.getBorderSize(); _vp.drawBBox(true); _vp.drawBorder(true); _vp.repaint(); if (JOptionPane.showConfirmDialog(_vp, border.getPanel(), "Set new border size", JOptionPane.OK_CANCEL_OPTION) != JOptionPane.OK_OPTION) { _vp.setBorderSize(oldBorder); } _vp.drawBorder(false); _vp.drawBBox(false); _vp.repaint(); } public void UISetBackground() { Color c = JColorChooser.showDialog(_vp, "Choose new background color", _vp.getBackground()); if (c != null) { _vp.setBackground(c); _vp.repaint(); } } public void UIZoomIn() { double _actualZoom = _vp.getZoom(); double _actualAmount = _vp.getZoomIncrement(); Point _actualTranslation = _vp.getTranslation(); double newZoom = Math.min(VARNAConfig.MAX_ZOOM, _actualZoom * _actualAmount); double ratio = newZoom / _actualZoom; Point newTrans = new Point((int) (_actualTranslation.x * ratio), (int) (_actualTranslation.y * ratio)); _vp.setZoom(newZoom); _vp.setTranslation(newTrans); // verification que la translation ne pose pas de problemes _vp.checkTranslation(); //System.out.println("Zoom in"); _vp.repaint(); } public void UIZoomOut() { double _actualZoom = _vp.getZoom(); double _actualAmount = _vp.getZoomIncrement(); Point _actualTranslation = _vp.getTranslation(); double newZoom = Math.max(_actualZoom / _actualAmount, VARNAConfig.MIN_ZOOM); double ratio = newZoom / _actualZoom; Point newTrans = new Point((int) (_actualTranslation.x * ratio), (int) (_actualTranslation.y * ratio)); _vp.setTranslation(newTrans); _vp.setZoom(newZoom); // verification que la translation ne pose pas de problemes _vp.checkTranslation(); _vp.repaint(); } public void UICustomZoom() { VueZoom zoom = new VueZoom(_vp); double oldZoom = _vp.getZoom(); double oldZoomAmount = _vp.getZoomIncrement(); _vp.drawBBox(true); _vp.repaint(); if (JOptionPane.showConfirmDialog(_vp, zoom.getPanel(), "Set zoom", JOptionPane.OK_CANCEL_OPTION) != JOptionPane.OK_OPTION) { _vp.setZoom(oldZoom); _vp.setZoomIncrement(oldZoomAmount); } _vp.drawBBox(false); _vp.repaint(); } public void UIGlobalRotation() { if (_vp.getRNA().get_listeBases().size() > 0) { _vp.drawBBox(true); _vp.repaint(); VueGlobalRotation rotation = new VueGlobalRotation(_vp); if (JOptionPane.showConfirmDialog(_vp, rotation.getPanel(), "Rotates the whole RNA", JOptionPane.OK_CANCEL_OPTION) != JOptionPane.OK_OPTION) { UIGlobalRotation(-rotation.getAngle()); } _vp.drawBBox(false); _vp.repaint(); } } public void UIGlobalRotation(double d) { if (_vp.isModifiable()) { if (_vp.getRNA().get_listeBases().size() > 0) { _vp.globalRotation(d); _undoableEditSupport.postEdit(new VARNAEdits.RotateRNAEdit(d,_vp)); } } } public void UISetBPStyle() { if (_vp.getRNA().get_listeBases().size() > 0) { VueStyleBP bpstyle = new VueStyleBP(_vp); VARNAConfig.BP_STYLE bck = _vp.getBPStyle(); if (JOptionPane.showConfirmDialog(_vp, bpstyle.getPanel(), "Set main base pair style", JOptionPane.OK_CANCEL_OPTION) != JOptionPane.OK_OPTION) { _vp.setBPStyle(bck); _vp.repaint(); } } } public void UISetTitleColor() { if (_vp.isModifiable()) { Color c = JColorChooser.showDialog(_vp, "Choose new title color", _vp.getTitleColor()); if (c != null) { _vp.setTitleColor(c); _vp.repaint(); } } } public void UISetBackboneColor() { if (_vp.isModifiable()) { Color c = JColorChooser.showDialog(_vp, "Choose new backbone color", _vp.getBackboneColor()); if (c != null) { _vp.setBackboneColor(c); _vp.repaint(); } } } public void UISetTitleFont() { if (_vp.isModifiable()) { VueFont font = new VueFont(_vp); if (JOptionPane.showConfirmDialog(_vp, font.getPanel(), "New Title font", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { _vp.setTitleFont(font.getFont()); _vp.repaint(); } } } public void UISetSpaceBetweenBases() { if (_vp.isModifiable()) { VueSpaceBetweenBases vsbb = new VueSpaceBetweenBases(_vp); Double oldSpace = _vp.getRNA().get_spaceBetweenBases(); if (JOptionPane.showConfirmDialog(_vp, vsbb.getPanel(), "Set the space between each base", JOptionPane.OK_CANCEL_OPTION) != JOptionPane.OK_OPTION) { _vp.getRNA().set_spaceBetweenBases(oldSpace); _vp.drawRNA(_vp.getRNA()); _vp.repaint(); } } } public void UISetBPHeightIncrement() { if (_vp.isModifiable()) { VueBPHeightIncrement vsbb = new VueBPHeightIncrement(_vp); Double oldSpace = _vp.getBPHeightIncrement(); if (JOptionPane.showConfirmDialog(_vp, vsbb.getPanel(), "Set the vertical increment in linear mode", JOptionPane.OK_CANCEL_OPTION) != JOptionPane.OK_OPTION) { _vp.setBPHeightIncrement(oldSpace); _vp.drawRNA(_vp.getRNA()); _vp.repaint(); } } } public void UISetNumPeriod() { if (_vp.getRNA().get_listeBases().size() != 0) { int oldNumPeriod = _vp.getNumPeriod(); VueNumPeriod vnp = new VueNumPeriod(_vp); if (JOptionPane.showConfirmDialog(_vp, vnp.getPanel(), "Set new numbering period", JOptionPane.OK_CANCEL_OPTION) != JOptionPane.OK_OPTION) { _vp.setNumPeriod(oldNumPeriod); _vp.repaint(); } } } public void UIEditBasePair() { if (_vp.isModifiable()) { ModeleBase mb = _vp.getRNA().get_listeBases().get( _vp.getNearestBase()); if (mb.getElementStructure() != -1) { ModeleBP msbp = mb.getStyleBP(); ModeleBP.Edge bck5 = msbp.getEdgePartner5(); ModeleBP.Edge bck3 = msbp.getEdgePartner3(); ModeleBP.Stericity bcks = msbp.getStericity(); VueBPType vbpt = new VueBPType(_vp, msbp); if (JOptionPane.showConfirmDialog(_vp, vbpt.getPanel(), "Set base pair L/W type", JOptionPane.OK_CANCEL_OPTION) != JOptionPane.OK_OPTION) { msbp.setEdge5(bck5); msbp.setEdge3(bck3); msbp.setStericity(bcks); _vp.repaint(); } } } } public void UIColorBasePair() { if (_vp.isModifiable()) { ModeleBase mb = _vp.getRNA().get_listeBases().get( _vp.getNearestBase()); if (mb.getElementStructure() != -1) { ModeleBP msbp = mb.getStyleBP(); Color c = JColorChooser.showDialog(_vp, "Choose custom base pair color", msbp.getStyle().getColor(_vp .getConfig()._bondColor)); if (c != null) { msbp.getStyle().setCustomColor(c); _vp.repaint(); } } } } public void UIThicknessBasePair() { if (_vp.isModifiable()) { ModeleBase mb = _vp.getRNA().get_listeBases().get( _vp.getNearestBase()); if (mb.getElementStructure() != -1) { ModeleBP msbp = mb.getStyleBP(); ArrayList<ModeleBP> bases = new ArrayList<ModeleBP>(); bases.add(msbp); VueBPThickness vbpt = new VueBPThickness(_vp, bases); if (JOptionPane.showConfirmDialog(_vp, vbpt.getPanel(), "Set base pair(s) thickness", JOptionPane.OK_CANCEL_OPTION) != JOptionPane.OK_OPTION) { vbpt.restoreThicknesses(); _vp.repaint(); } } } } public void saveToPNG(String filename) throws ExceptionExportFailed { VueJPEG jpeg = new VueJPEG(true, false); if (JOptionPane.showConfirmDialog(_vp, jpeg.getPanel(), "Set resolution", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { Double scale = jpeg.getScaleSlider().getValue() / 100.0; BufferedImage myImage = new BufferedImage((int) Math.round(_vp .getWidth() * scale), (int) Math.round(_vp.getHeight() * scale), BufferedImage.TRANSLUCENT); Graphics2D g2 = myImage.createGraphics(); AffineTransform AF = new AffineTransform(); AF.setToScale(scale, scale); g2.setTransform(AF); _vp.paintComponent(g2,!_vp.getConfig()._drawBackground); g2.dispose(); try { ImageIO.write(myImage, "PNG", new File(filename)); } catch (IOException e) { e.printStackTrace(); } } } public void saveToJPEG(String filename) throws ExceptionJPEGEncoding, ExceptionExportFailed { VueJPEG jpeg = new VueJPEG(true, true); if (JOptionPane.showConfirmDialog(_vp, jpeg.getPanel(), "Set resolution/quality", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { Double scale; if (jpeg.getScaleSlider().getValue() == 0) scale = 1. / 100.; else scale = jpeg.getScaleSlider().getValue() / 100.; BufferedImage myImage = new BufferedImage((int) Math.round(_vp .getWidth() * scale), (int) Math.round(_vp.getHeight() * scale), BufferedImage.TYPE_INT_RGB); Graphics2D g2 = myImage.createGraphics(); AffineTransform AF = new AffineTransform(); AF.setToScale(scale, scale); g2.setTransform(AF); _vp.paintComponent(g2); try { FileImageOutputStream out = new FileImageOutputStream(new File(filename)); ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next(); ImageWriteParam params = writer.getDefaultWriteParam(); params.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); params.setCompressionQuality(jpeg.getQualitySlider().getValue() / 100.0f); writer.setOutput(out); IIOImage myIIOImage = new IIOImage(myImage, null, null); writer.write(null, myIIOImage, params); out.close(); } catch (IOException e) { throw new ExceptionExportFailed(e.getMessage(), filename); } } } public void UIToggleShowNCBP() { if (_vp.isModifiable()) { _vp.setShowNonCanonicalBP(!_vp.getShowNonCanonicalBP()); _vp.repaint(); } } public void UIToggleColorSpecialBases() { _vp.setColorNonStandardBases(!_vp.getColorSpecialBases()); _vp.repaint(); } public void UIToggleColorGapsBases() { _vp.setColorGapsBases(!_vp.getColorGapsBases()); _vp.repaint(); } public void UIToggleShowNonPlanar() { if (_vp.isModifiable()) { _vp.setShowNonPlanarBP(!_vp.getShowNonPlanarBP()); _vp.repaint(); } } public void UIToggleShowWarnings() { _vp.setShowWarnings(!_vp.getShowWarnings()); _vp.repaint(); } public void UIPickSpecialBasesColor() { Color c = JColorChooser.showDialog(_vp, "Choose new special bases color", _vp .getNonStandardBasesColor()); if (c != null) { _vp.setNonStandardBasesColor(c); _vp.setColorNonStandardBases(true); _vp.repaint(); } } public void UIPickGapsBasesColor() { Color c = JColorChooser.showDialog(_vp, "Choose new gaps bases color", _vp.getGapsBasesColor()); if (c != null) { _vp.setGapsBasesColor(c); _vp.setColorGapsBases(true); _vp.repaint(); } } public void UIBaseTypeColor() { if (_vp.isModifiable()) { new VueBases(_vp, VueBases.KIND_MODE); } } public void UIToggleModifiable() { _vp.setModifiable(!_vp.isModifiable()); } public void UIBasePairTypeColor() { if (_vp.isModifiable()) { new VueBases(_vp, VueBases.COUPLE_MODE); } } public void UIBaseAllColor() { if (_vp.isModifiable()) { new VueBases(_vp, VueBases.ALL_MODE); } } public void UIAbout() { VueAboutPanel about = new VueAboutPanel(); JOptionPane.showMessageDialog(_vp, about, "About VARNA " + VARNAConfig.MAJOR_VERSION + "." + VARNAConfig.MINOR_VERSION, JOptionPane.PLAIN_MESSAGE); about.gracefulStop(); } public void UIAutoAnnotateHelices() { if (_vp.isModifiable()) { _vp.getRNA().autoAnnotateHelices(); _vp.repaint(); } } public void UIAutoAnnotateStrandEnds() { if (_vp.isModifiable()) { _vp.getRNA().autoAnnotateStrandEnds(); _vp.repaint(); } } public void UIAutoAnnotateInteriorLoops() { if (_vp.isModifiable()) { _vp.getRNA().autoAnnotateInteriorLoops(); _vp.repaint(); } } public void UIAutoAnnotateTerminalLoops() { if (_vp.isModifiable()) { _vp.getRNA().autoAnnotateTerminalLoops(); _vp.repaint(); } } public void UIAnnotationRemoveFromAnnotation(TextAnnotation textAnnotation) { if (_vp.isModifiable()) { _vp.set_selectedAnnotation(null); _vp.getListeAnnotations().remove(textAnnotation); _vp.repaint(); } } public void UIAnnotationEditFromAnnotation(TextAnnotation textAnnotation) { VueAnnotation vue; if (textAnnotation.getType() == TextAnnotation.POSITION) vue = new VueAnnotation(_vp, textAnnotation, false); else vue = new VueAnnotation(_vp, textAnnotation, true, false); vue.show(); } public void UIAnnotationAddFromStructure(int type, ArrayList<Integer> listeIndex) throws Exception { TextAnnotation textAnnot; ArrayList<ModeleBase> listeBase; VueAnnotation vue; switch (type) { case TextAnnotation.BASE: textAnnot = new TextAnnotation("", _vp.getRNA().get_listeBases() .get(listeIndex.get(0))); vue = new VueAnnotation(_vp, textAnnot, true); vue.show(); break; case TextAnnotation.LOOP: listeBase = new ArrayList<ModeleBase>(); for (Integer i : listeIndex) { listeBase.add(_vp.getRNA().get_listeBases().get(i)); } textAnnot = new TextAnnotation("", listeBase, type); vue = new VueAnnotation(_vp, textAnnot, true); vue.show(); break; case TextAnnotation.HELIX: listeBase = new ArrayList<ModeleBase>(); for (Integer i : listeIndex) { listeBase.add(_vp.getRNA().get_listeBases().get(i)); } textAnnot = new TextAnnotation("", listeBase, type); vue = new VueAnnotation(_vp, textAnnot, true); vue.show(); break; default: _vp.errorDialog(new Exception("Unknown structure type")); break; } } public void UIAnnotationEditFromStructure(int type, ArrayList<Integer> listeIndex) { if (_vp.isModifiable()) { ModeleBase mb = _vp.getRNA().get_listeBases() .get(listeIndex.get(0)); TextAnnotation ta = _vp.getRNA().getAnnotation(type, mb); if (ta != null) UIAnnotationEditFromAnnotation(ta); } } public void UIAnnotationRemoveFromStructure(int type, ArrayList<Integer> listeIndex) { if (_vp.isModifiable()) { ModeleBase mb = _vp.getRNA().get_listeBases() .get(listeIndex.get(0)); TextAnnotation ta = _vp.getRNA().getAnnotation(type, mb); if (ta != null) UIAnnotationRemoveFromAnnotation(ta); } } public void UIAnnotationsAddPosition(int x, int y) { if (_vp.isModifiable()) { Point2D.Double p = _vp.panelToLogicPoint(new Point2D.Double(x, y)); VueAnnotation annotationAdd = new VueAnnotation(_vp, (int) p.x, (int) p.y); annotationAdd.show(); } } public void UIAnnotationsAddBase(int x, int y) { if (_vp.isModifiable()) { ModeleBase mb = _vp.getBaseAt(new Point2D.Double(x, y)); if(mb!=null) { _vp.highlightSelectedBase(mb); TextAnnotation textAnnot = new TextAnnotation("", mb); VueAnnotation annotationAdd = new VueAnnotation(_vp, textAnnot,true); annotationAdd.show(); } } } public void UIAnnotationsAddLoop(int x, int y) { if (_vp.isModifiable()) { try { ModeleBase mb = _vp.getBaseAt(new Point2D.Double(x, y)); if(mb!=null) { Vector<Integer> v = _vp.getRNA().getLoopBases(mb.getIndex()); ArrayList<ModeleBase> mbs = _vp.getRNA().getBasesAt(v); TextAnnotation textAnnot; textAnnot = new TextAnnotation("", mbs,TextAnnotation.LOOP); _vp.setSelection(mbs); VueAnnotation annotationAdd = new VueAnnotation(_vp, textAnnot,true); annotationAdd.show(); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } private ArrayList<ModeleBase> extractMaxContiguousPortion(ArrayList<ModeleBase> m) { ModeleBase[] tab = new ModeleBase[_vp.getRNA().getSize()]; for(int i=0;i<tab.length;i++) { tab[i] = null; } for(ModeleBase mb : m) { tab[mb.getIndex()] = mb; } ArrayList<ModeleBase> best = new ArrayList<ModeleBase>(); ArrayList<ModeleBase> current = new ArrayList<ModeleBase>(); for(int i=0;i<tab.length;i++) { if (tab[i] != null) { current.add(tab[i]); } else { if (current.size()>best.size()) best = current; current = new ArrayList<ModeleBase>(); } } if (current.size()>best.size()) { best = current; } return best; } public void UIAnnotationsAddRegion(int x, int y) { if (_vp.isModifiable()) { ArrayList<ModeleBase> mb = _vp.getSelection().getBases(); if (mb.size()==0) { ModeleBase m = _vp.getBaseAt(new Point2D.Double(x, y)); mb.add(m); } mb = extractMaxContiguousPortion(extractMaxContiguousPortion(mb)); _vp.setSelection(mb); HighlightRegionAnnotation regionAnnot = new HighlightRegionAnnotation(mb); _vp.addHighlightRegion(regionAnnot); VueHighlightRegionEdit annotationAdd = new VueHighlightRegionEdit(_vp,regionAnnot); if (!annotationAdd.show()) { _vp.removeHighlightRegion(regionAnnot); } _vp.clearSelection(); } } public void UIAnnotationsAddChemProb(int x, int y) { if (_vp.isModifiable() && _vp.getRNA().getSize()>1) { Point2D.Double p = _vp.panelToLogicPoint(new Point2D.Double(x, y)); ModeleBase m1 = _vp.getBaseAt(new Point2D.Double(x, y)); ModeleBase best = null; if (m1.getIndex()-1>=0) { best = _vp.getRNA().getBaseAt(m1.getIndex()-1);} if (m1.getIndex()+1<_vp.getRNA().getSize()) { ModeleBase m2 = _vp.getRNA().getBaseAt(m1.getIndex()+1); if (best==null) { best = m2; } else { if (best.getCoords().distance(p)>m2.getCoords().distance(p)) { best = m2; } } } ArrayList<ModeleBase> tab = new ArrayList<ModeleBase>(); tab.add(m1); tab.add(best); _vp.setSelection(tab); ChemProbAnnotation regionAnnot = new ChemProbAnnotation(m1,best); _vp.getRNA().addChemProbAnnotation(regionAnnot); VueChemProbAnnotation annotationAdd = new VueChemProbAnnotation(_vp,regionAnnot); if (!annotationAdd.show()) { _vp.getRNA().removeChemProbAnnotation(regionAnnot); } _vp.clearSelection(); } } public void UIAnnotationsAddHelix(int x, int y) { if (_vp.isModifiable()) { try { ModeleBase mb = _vp.getBaseAt(new Point2D.Double(x, y)); if(mb!=null) { ArrayList<Integer> v = _vp.getRNA().findHelix(mb.getIndex()); ArrayList<ModeleBase> mbs = _vp.getRNA().getBasesAt(v); TextAnnotation textAnnot; textAnnot = new TextAnnotation("", mbs,TextAnnotation.HELIX); _vp.setSelection(mbs); VueAnnotation annotationAdd = new VueAnnotation(_vp, textAnnot,true); annotationAdd.show(); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void UIToggleGaspinMode() { if (_vp.isModifiable()) { _vp.toggleDrawOutlineBase(); _vp.toggleFillBase(); _vp.repaint(); } } public void UIAnnotationsAdd() { if (_vp.isModifiable()) { VueAnnotation annotationAdd = new VueAnnotation(_vp); annotationAdd.show(); } } public void UIEditAllBasePairs() { if (_vp.isModifiable()) { new VueBPList(_vp); } } public void UIEditAllBases() { if (_vp.isModifiable()) { new VueBases(_vp,VueBases.ALL_MODE); } } public void UIAnnotationsRemove() { if (_vp.isModifiable()) { new VueListeAnnotations(_vp, VueListeAnnotations.REMOVE); } } public void UIAnnotationsEdit() { if (_vp.isModifiable()) { new VueListeAnnotations(_vp, VueListeAnnotations.EDIT); } } public void UIAddBP(int i, int j, ModeleBP ms) { if (_vp.isModifiable()) { _vp.getRNA().addBPToStructure(i, j, ms); _undoableEditSupport.postEdit(new VARNAEdits.AddBPEdit(i,j,ms,_vp)); _vp.repaint(); HashSet<ModeleBP> tmp = new HashSet<ModeleBP>(); tmp.add(ms); _vp.fireStructureChanged(new HashSet<ModeleBP>(_vp.getRNA().getAllBPs()), tmp, new HashSet<ModeleBP>()); } } public void UIRemoveBP(ModeleBP ms) { if (_vp.isModifiable()) { _undoableEditSupport.postEdit(new VARNAEdits.RemoveBPEdit(ms.getIndex5(),ms.getIndex3(),ms,_vp)); _vp.getRNA().removeBP(ms); _vp.repaint(); HashSet<ModeleBP> tmp = new HashSet<ModeleBP>(); tmp.add(ms); _vp.fireStructureChanged(new HashSet<ModeleBP>(_vp.getRNA().getAllBPs()), new HashSet<ModeleBP>(), tmp); } } public void UIShiftBaseCoord(ArrayList<Integer> indices, double dx, double dy) { if (_vp.isModifiable()) { Hashtable<Integer,Point2D.Double> backupPos = new Hashtable<Integer,Point2D.Double>(); for (int index:indices) { ModeleBase mb = _vp.getRNA().getBaseAt(index); Point2D.Double d = mb.getCoords(); backupPos.put(index,d); _vp.getRNA().setCoord(index, d.x+dx,d.y+dy); _vp.getRNA().setCenter(index, mb.getCenter().x+dx,mb.getCenter().y+dy); } _undoableEditSupport.postEdit(new VARNAEdits.BasesShiftEdit(indices,dx,dy,_vp)); _vp.repaint(); _vp.fireLayoutChanged(backupPos); } } public void UIShiftBaseCoord(ArrayList<Integer> indices, Point2D.Double dv) { UIShiftBaseCoord(indices, dv.x,dv.y); } public void UIMoveSingleBase(int index, double nx, double ny) { if (_vp.isModifiable()) { ModeleBase mb = _vp.getRNA().getBaseAt(index); Point2D.Double d = mb.getCoords(); Hashtable<Integer,Point2D.Double> backupPos = new Hashtable<Integer,Point2D.Double>(); backupPos.put(index,d); _undoableEditSupport.postEdit(new VARNAEdits.SingleBaseMoveEdit(index,nx,ny,_vp)); _vp.getRNA().setCoord(index, nx,ny); _vp.repaint(); _vp.fireLayoutChanged(backupPos); } } public void UIMoveSingleBase(int index, Point2D.Double dv) { UIMoveSingleBase(index, dv.x,dv.y); } public void UISetBaseCenter(int index, double x, double y) { UISetBaseCenter(index, new Point2D.Double(x, y)); } public void UISetBaseCenter(int index, Point2D.Double p) { if (_vp.isModifiable()) { _vp.getRNA().setCenter(index, p); } } public void UIUndo() { _vp.undo(); } public void UIRedo() { _vp.redo(); } /** * Move a helix of the rna * * @param index * :the index of the selected base * @param newPos * :the new xy coordinate, within the logical system of coordinates */ public void UIMoveHelixAtom(int index, Point2D.Double newPos) { if (_vp.isModifiable() && (index >= 0) && (index < _vp.getRNA().get_listeBases().size())) { int indexTo = _vp.getRNA().get_listeBases().get(index) .getElementStructure(); Point h = _vp.getRNA().getHelixInterval(index); Point ml = _vp.getRNA().getMultiLoop(h.x); int i = ml.x; if (indexTo != -1) { if (i == 0) { if (shouldFlip(index, newPos)) { UIFlipHelix(h); _undoableEditSupport.postEdit(new VARNAEdits.HelixFlipEdit(h,_vp)); } } else { UIRotateHelixAtom(index, newPos); } } _vp.fireLayoutChanged(); } } private Point2D.Double project(Point2D.Double O, Point2D.Double Ox, Point2D.Double C) { Point2D.Double OC = new Point2D.Double(C.x - O.x, C.y - O.y); // Projection of OC on OI => OX double normOX = (Ox.x * OC.x + Ox.y * OC.y); Point2D.Double OX = new Point2D.Double((normOX * Ox.x), (normOX * Ox.y)); // Portion of OC orthogonal to Ox => XC Point2D.Double XC = new Point2D.Double(OC.x - OX.x, OC.y - OX.y); // Reflexive image of C with respect to Ox => CP Point2D.Double OCP = new Point2D.Double(OX.x - XC.x, OX.y - XC.y); Point2D.Double CP = new Point2D.Double(O.x + OCP.x, O.y + OCP.y); return CP; } /** * Flip an helix around its supporting base */ public void UIFlipHelix(Point h) { int hBeg=h.x; int hEnd=h.y; Point2D.Double A = _vp.getRNA().getCoords(hBeg); Point2D.Double B = _vp.getRNA().getCoords(hEnd); Point2D.Double AB = new Point2D.Double(B.x - A.x, B.y - A.y); double normAB = Math.sqrt(AB.x * AB.x + AB.y * AB.y); // Creating a coordinate system centered on A and having // unit x-vector Ox. Point2D.Double O = A; Point2D.Double Ox = new Point2D.Double(AB.x / normAB, AB.y / normAB); Hashtable<Integer,Point2D.Double> old = new Hashtable<Integer,Point2D.Double>(); for (int i = hBeg + 1; i < hEnd; i++) { Point2D.Double P = _vp.getRNA().getCoords(i); Point2D.Double nP = project(O, Ox, P); old.put(i, nP); _vp.getRNA().setCoord(i, nP); Point2D.Double Center = _vp.getRNA().getCenter(i); _vp.getVARNAUI().UISetBaseCenter(i, project(O, Ox, Center)); } _vp.fireLayoutChanged(old); } /** * Tests if an helix needs to be flipped. */ boolean shouldFlip(int index, Point2D.Double P) { Point h = _vp.getRNA().getHelixInterval(index); Point2D.Double A = _vp.getRNA().getCoords(h.x); Point2D.Double B = _vp.getRNA().getCoords(h.y); Point2D.Double C = _vp.getRNA().getCoords(h.x + 1); // Creating a vector that is orthogonal to AB Point2D.Double hAB = new Point2D.Double(B.y - A.y, -(B.x - A.x)); Point2D.Double AC = new Point2D.Double(C.x - A.x, C.y - A.y); Point2D.Double AP = new Point2D.Double(P.x - A.x, P.y - A.y); double signC = (hAB.x * AC.x + hAB.y * AC.y); double signP = (hAB.x * AP.x + hAB.y * AP.y); // Now, the product signC*signP is negative iff the mouse and the first // base inside // the helix are on different sides of the end of the helix => Flip the // helix! return (signC * signP < 0.0); } public void UIRotateHelixAtom(int index, Point2D.Double newPos) { Point h = _vp.getRNA().getHelixInterval(index); Point ml = _vp.getRNA().getMultiLoop(h.x); int i = ml.x; int prevIndex = h.x; int nextIndex = h.y; while (i <= ml.y) { int j = _vp.getRNA().get_listeBases().get(i) .getElementStructure(); if ((j != -1) && (i < h.x)) { prevIndex = i; } if ((j != -1) && (i > h.y) && (nextIndex == h.y)) { nextIndex = i; } if ((j > i) && (j < ml.y)) { i = _vp.getRNA().get_listeBases().get(i) .getElementStructure(); } else { i++; } } Point2D.Double oldPos = _vp.getRNA().getCoords(index); Point2D.Double limitLoopLeft, limitLoopRight, limitLeft, limitRight, helixStart, helixStop; boolean isDirect = _vp.getRNA().testDirectionality(ml.x, ml.y, h.x); if (isDirect) { limitLoopLeft = _vp.getRNA().getCoords(ml.y); limitLoopRight = _vp.getRNA().getCoords(ml.x); limitLeft = _vp.getRNA().getCoords(prevIndex); limitRight = _vp.getRNA().getCoords(nextIndex); helixStart = _vp.getRNA().getCoords(h.x); helixStop = _vp.getRNA().getCoords(h.y); } else { limitLoopLeft = _vp.getRNA().getCoords(ml.x); limitLoopRight = _vp.getRNA().getCoords(ml.y); limitLeft = _vp.getRNA().getCoords(nextIndex); limitRight = _vp.getRNA().getCoords(prevIndex); helixStart = _vp.getRNA().getCoords(h.y); helixStop = _vp.getRNA().getCoords(h.x); } Point2D.Double center = _vp.getRNA().get_listeBases().get( h.x).getCenter(); double base = (computeAngle(center, limitLoopRight) + computeAngle( center, limitLoopLeft)) / 2.0; double pLimR = computeAngle(center, limitLeft) - base; double pHelR = computeAngle(center, helixStart) - base; double pNew = computeAngle(center, newPos) - base; double pOld = computeAngle(center, oldPos) - base; double pHelL = computeAngle(center, helixStop) - base; double pLimL = computeAngle(center, limitRight) - base; while (pLimR < 0.0) pLimR += 2.0 * Math.PI; while (pHelR < pLimR) pHelR += 2.0 * Math.PI; while ((pNew < pHelR)) pNew += 2.0 * Math.PI; while ((pOld < pHelR)) pOld += 2.0 * Math.PI; while ((pHelL < pOld)) pHelL += 2.0 * Math.PI; while ((pLimL < pHelL)) pLimL += 2.0 * Math.PI; double minDelta = normalizeAngle((pLimR - pHelR) + 0.25); double maxDelta = normalizeAngle((pLimL - pHelL) - 0.25); while (maxDelta < minDelta) maxDelta += 2.0 * Math.PI; double delta = normalizeAngle(pNew - pOld); while (delta < minDelta) delta += 2.0 * Math.PI; if (delta > maxDelta) { double distanceMax = delta - maxDelta; double distanceMin = minDelta - (delta - 2.0 * Math.PI); if (distanceMin < distanceMax) { delta = minDelta; } else { delta = maxDelta; } } _undoableEditSupport.postEdit(new VARNAEdits.HelixRotateEdit(delta,base,pLimL,pLimR,h,ml,_vp)); UIRotateEverything(delta, base, pLimL, pLimR, h, ml); } public void UIRotateEverything(double delta, double base, double pLimL, double pLimR, Point h, Point ml) { Hashtable<Integer,Point2D.Double> backupPos = new Hashtable<Integer,Point2D.Double>(); boolean isDirect = _vp.getRNA().testDirectionality(ml.x, ml.y, h.x); Point2D.Double center = _vp.getRNA().get_listeBases().get( h.x).getCenter(); for(int k=h.x;k<=h.y;k++) {backupPos.put(k, _vp.getRNA().getBaseAt(k).getCoords());} rotateHelix(center, h.x, h.y, delta); // Re-assigns unpaired atoms boolean over = false; Point2D.Double helixStart = _vp.getRNA().getCoords(h.x); Point2D.Double helixStop = _vp.getRNA().getCoords(h.y); double pHelR,pHelL; if (isDirect) { pHelR = computeAngle(center, helixStop) - base; pHelL = computeAngle(center, helixStart) - base; } else { pHelL = computeAngle(center, helixStop) - base; pHelR = computeAngle(center, helixStart) - base; } int i = h.x - 1; Vector<Integer> nextBases = new Vector<Integer>(); while (!over) { if (i < 0) { over = true; } else { if (_vp.getRNA().get_listeBases().get(i) .getElementStructure() == -1) { nextBases.add(new Integer(i)); } else { over = true; } } i--; } Vector<Integer> prevBases = new Vector<Integer>(); over = false; i = h.y + 1; while (!over) { if (i >= _vp.getRNA().get_listeBases().size()) { over = true; } else { if (_vp.getRNA().get_listeBases().get(i) .getElementStructure() == -1) { prevBases.add(new Integer(i)); } else { over = true; } } i++; } double radius = center.distance(helixStart); double anglePrev, angleNext; double newAngle; if (isDirect) { anglePrev = normalizeAngle(pLimL - pHelR); angleNext = normalizeAngle(pHelL - pLimR); } else { anglePrev = normalizeAngle(pHelL - pLimR); angleNext = normalizeAngle(pLimL - pHelR); } for (i = 0; i < prevBases.size(); i++) { int k = prevBases.get(i).intValue(); if (isDirect) { newAngle = base + pHelR + ((i + 1) * anglePrev) / (prevBases.size() + 1); } else { newAngle = base + pHelL - ((i + 1) * anglePrev) / (prevBases.size() + 1); } double newX = center.x + radius * Math.cos(newAngle); double newY = center.y + radius * Math.sin(newAngle); backupPos.put(k, _vp.getRNA().getCoords(k)); _vp.getRNA().setCoord(k, newX, newY); } for (i = 0; i < nextBases.size(); i++) { int k = nextBases.get(i).intValue(); if (isDirect) { newAngle = base + pHelL - ((i + 1) * angleNext) / (nextBases.size() + 1); } else { newAngle = base + pHelR + ((i + 1) * angleNext) / (nextBases.size() + 1); } double newX = center.x + radius * Math.cos(newAngle); double newY = center.y + radius * Math.sin(newAngle); backupPos.put(k, _vp.getRNA().getCoords(k)); _vp.getRNA().setCoord(k, newX, newY); } _vp.fireLayoutChanged(backupPos); } private double normalizeAngle(double angle) { return normalizeAngle(angle, 0.0); } private double normalizeAngle(double angle, double base) { while (angle < base) { angle += 2.0 * Math.PI; } while (angle >= (2.0 * Math.PI) - base) { angle -= 2.0 * Math.PI; } return angle; } public static double computeAngle(Point2D.Double center, Point2D.Double p) { double dist = center.distance(p); double angle = Math.asin((p.y - center.y) / dist); if (p.x - center.x < 0) { angle = Math.PI - angle; } return angle; } private void rotateHelix(Point2D.Double center, int i, int j, double angle) { for (int k = i; k <= j; k++) { Point2D.Double oldp = _vp.getRNA().getCoords(k); Point2D.Double newp = rotatePoint(center, oldp, angle); _vp.getRNA().setCoord(k, newp); if ((k != i) && (k != j)) { Point2D.Double oldc = _vp.getRNA().get_listeBases().get(k) .getCenter(); Point2D.Double newc = rotatePoint(center, oldc, angle); UISetBaseCenter(k,newc); } } } private Point2D.Double rotatePoint(Point2D.Double center, Point2D.Double p, double angle) { double dist = p.distance(center); double oldAngle = Math.asin((p.y - center.y) / dist); if (p.x - center.x < 0) { oldAngle = Math.PI - oldAngle; } double newX = (center.x + dist * Math.cos(oldAngle + angle)); double newY = (center.y + dist * Math.sin(oldAngle + angle)); return new Point2D.Double(newX, newY); } }