001/*
002 *                    PDB web development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 *
015 * Created on Jun 13, 2009
016 * Created by Andreas Prlic
017 *
018 */
019
020package org.biojava.nbio.structure.align.gui;
021
022import java.awt.Dimension;
023import java.util.ArrayList;
024import java.util.Collections;
025import java.util.List;
026
027import javax.swing.Box;
028import javax.swing.JFrame;
029import javax.swing.JMenuBar;
030import javax.swing.JScrollPane;
031
032import org.biojava.nbio.structure.Atom;
033import org.biojava.nbio.structure.Group;
034import org.biojava.nbio.structure.Structure;
035import org.biojava.nbio.structure.StructureException;
036import org.biojava.nbio.structure.StructureTools;
037import org.biojava.nbio.structure.align.gui.aligpanel.AligPanel;
038import org.biojava.nbio.structure.align.gui.aligpanel.StatusDisplay;
039import org.biojava.nbio.structure.align.gui.jmol.AbstractAlignmentJmol;
040import org.biojava.nbio.structure.align.gui.jmol.JmolTools;
041import org.biojava.nbio.structure.align.gui.jmol.StructureAlignmentJmol;
042import org.biojava.nbio.structure.align.model.AFPChain;
043import org.biojava.nbio.structure.align.util.AFPAlignmentDisplay;
044import org.biojava.nbio.structure.align.util.AlignmentTools;
045import org.slf4j.Logger;
046import org.slf4j.LoggerFactory;
047
048/** A utility class for visualistion of structure alignments
049 *
050 * @author Andreas Prlic
051 *
052 */
053public class DisplayAFP {
054
055        private static final Logger logger = LoggerFactory.getLogger(DisplayAFP.class);
056
057        //TODO: same as getEqrPos??? !!!
058        public static final List<Integer> getEQRAlignmentPos(AFPChain afpChain){
059                List<Integer> lst = new ArrayList<Integer>();
060
061                char[] s1 = afpChain.getAlnseq1();
062                char[] s2 = afpChain.getAlnseq2();
063                char[] symb = afpChain.getAlnsymb();
064                boolean isFatCat = afpChain.getAlgorithmName().startsWith("jFatCat");
065
066                for ( int i =0 ; i< s1.length; i++){
067                        char c1 = s1[i];
068                        char c2 = s2[i];
069
070                        if ( isAlignedPosition(i,c1,c2,isFatCat, symb)) {
071                                lst.add(i);
072                        }
073
074                }
075                return lst;
076
077        }
078
079
080
081
082
083        private static boolean isAlignedPosition(int i, char c1, char c2, boolean isFatCat,char[]symb)
084        {
085//              if ( isFatCat){
086                        char s = symb[i];
087                        if ( c1 != '-' && c2 != '-' && s != ' '){
088                                return true;
089                        }
090//              } else {
091//
092//                      if ( c1 != '-' && c2 != '-')
093//                              return true;
094//              }
095
096                return false;
097
098
099        }
100
101        /**
102         * Return a list of pdb Strings corresponding to the aligned positions of the molecule.
103         * Only supports a pairwise alignment with the AFPChain DS.
104         *
105         * @param aligPos
106         * @param afpChain
107         * @param ca
108         */
109        public static final List<String> getPDBresnum(int aligPos, AFPChain afpChain, Atom[] ca){
110                List<String> lst = new ArrayList<String>();
111                if ( aligPos > 1) {
112                        System.err.println("multiple alignments not supported yet!");
113                        return lst;
114                }
115
116                int blockNum = afpChain.getBlockNum();
117                int[] optLen = afpChain.getOptLen();
118                int[][][] optAln = afpChain.getOptAln();
119
120                if ( optLen == null)
121                        return lst;
122
123                for(int bk = 0; bk < blockNum; bk ++)       {
124
125                        for ( int i=0;i< optLen[bk];i++){
126
127                                int pos = optAln[bk][aligPos][i];
128                                if ( pos < ca.length) {
129                                        String pdbInfo = JmolTools.getPdbInfo(ca[pos]);
130                                        //lst.add(ca1[pos].getParent().getPDBCode());
131                                        lst.add(pdbInfo);
132                                }
133                        }
134
135                }
136                return lst;
137        }
138
139
140        /**
141         * Return the atom at alignment position aligPos. at the present only works with block 0
142         * @param chainNr the number of the aligned pair. 0... first chain, 1... second chain.
143         * @param afpChain an afpChain object
144         * @param aligPos position on the alignment
145         * @param getPrevious gives the previous position if false, gives the next posible atom
146         * @return a CA atom that is at a particular position of the alignment
147         */
148        public static final Atom getAtomForAligPos(AFPChain afpChain,int chainNr, int aligPos, Atom[] ca , boolean getPrevious ) throws StructureException{
149                int[] optLen = afpChain.getOptLen();
150                // int[][][] optAln = afpChain.getOptAln();
151
152                if ( optLen == null)
153                        return null;
154
155                if (chainNr < 0 || chainNr > 1){
156                        throw new StructureException("So far only pairwise alignments are supported, but you requested results for alinged chain nr " + chainNr);
157                }
158
159                //if (  afpChain.getAlgorithmName().startsWith("jFatCat")){
160
161                /// for FatCat algorithms...
162                int capos = getUngappedFatCatPos(afpChain, chainNr, aligPos);
163                if ( capos < 0) {
164
165                        capos = getNextFatCatPos(afpChain, chainNr, aligPos,getPrevious);
166
167                        //System.out.println(" got next" + capos + " for " + chainNr + " alignedPos: " + aligPos);
168                } else {
169                        //System.out.println("got aligned fatcat position: " + capos + " " + chainNr + " for alig pos: " + aligPos);
170                }
171
172                if ( capos < 0) {
173                        System.err.println("could not match position " + aligPos + " in chain " + chainNr +". Returing null...");
174                        return null;
175                }
176                if ( capos > ca.length){
177                        System.err.println("Atom array "+ chainNr + " does not have " + capos +" atoms. Returning null.");
178                        return null;
179                }
180                return ca[capos];
181                //}
182
183                //
184                //
185                //      int ungappedPos = getUngappedPos(afpChain, aligPos);
186                //      System.out.println("getAtomForAligPOs " + aligPos  + " " + ungappedPos );
187                //      return ca[ungappedPos];
188                //
189                //      if ( ungappedPos >= optAln[bk][chainNr].length)
190                //         return null;
191                //      int pos = optAln[bk][chainNr][ungappedPos];
192                //      if ( pos > ca.length)
193                //         return null;
194                //      return ca[pos];
195        }
196
197
198        private static int getNextFatCatPos(AFPChain afpChain, int chainNr,
199                        int aligPos, boolean getPrevious) {
200
201                char[] aseq;
202                if ( chainNr == 0 )
203                        aseq = afpChain.getAlnseq1();
204                else
205                        aseq = afpChain.getAlnseq2();
206
207                if ( aligPos > aseq.length)
208                        return -1;
209                if ( aligPos < 0)
210                        return -1;
211
212                int blockNum = afpChain.getBlockNum();
213                int[] optLen = afpChain.getOptLen();
214                int[][][] optAln = afpChain.getOptAln();
215
216                int p1, p2;
217                int p1b = 0;
218                int p2b = 0;
219                int len = 0;
220
221
222
223                boolean terminateNextMatch = false;
224                for(int i = 0; i < blockNum; i ++)  {
225                        for(int j = 0; j < optLen[i]; j ++) {
226
227                                p1 = optAln[i][0][j];
228                                p2 = optAln[i][1][j];
229
230
231                                if(len > 0)     {
232
233                                        int lmax = (p1 - p1b - 1)>(p2 - p2b - 1)?(p1 - p1b - 1):(p2 - p2b - 1);
234
235                                        // lmax gives the length of an alignment gap
236
237                                        //System.out.println("  pos "+ len+" p1-p2: " + p1 + " - " + p2 + " lmax: " + lmax + " p1b-p2b:"+p1b + " " + p2b + " terminate? "+ terminateNextMatch);
238                                        for(int k = 0; k < lmax; k ++)      {
239
240                                                if(k >= (p1 - p1b - 1)) {
241                                                        // a gap position in chain 0
242                                                        if ( aligPos == len && chainNr == 0 ){
243                                                                if ( getPrevious)
244                                                                        return p1b;
245                                                                else
246                                                                        terminateNextMatch = true;
247                                                        }
248                                                }
249                                                else {
250                                                        if ( aligPos == len && chainNr == 0)
251                                                                return p1b+1+k;
252
253
254                                                }
255                                                if(k >= (p2 - p2b - 1)) {
256                                                        // a gap position in chain 1
257                                                        if ( aligPos == len && chainNr == 1){
258                                                                if ( getPrevious)
259                                                                        return p2b;
260                                                                else
261                                                                        terminateNextMatch = true;
262                                                        }
263                                                }
264                                                else  {
265                                                        if ( aligPos == len && chainNr == 1) {
266                                                                return p2b+1+k;
267                                                        }
268
269
270                                                }
271                                                len++;
272
273                                        }
274                                }
275
276                                if ( aligPos == len && chainNr == 0)
277                                        return p1;
278                                if ( aligPos == len && chainNr == 1)
279                                        return p2;
280
281
282
283                                if ( terminateNextMatch)
284                                        if ( chainNr == 0)
285                                                return p1;
286                                        else
287                                                return p2;
288                                if ( len > aligPos) {
289                                        if ( getPrevious) {
290                                                if ( chainNr == 0)
291                                                        return p1b;
292                                                else
293                                                        return p2b;
294                                        } else {
295                                                terminateNextMatch = true;
296                                        }
297                                }
298
299                                len++;
300                                p1b = p1;
301                                p2b = p2;
302
303
304
305
306                        }
307                }
308
309
310                // we did not find an aligned position
311                return -1;
312
313        }
314
315        private static final int getUngappedFatCatPos(AFPChain afpChain, int chainNr, int aligPos){
316                char[] aseq;
317                if ( chainNr == 0 )
318                        aseq = afpChain.getAlnseq1();
319                else
320                        aseq = afpChain.getAlnseq2();
321
322                if ( aligPos > aseq.length)
323                        return -1;
324                if ( aligPos < 0)
325                        return -1;
326
327                int blockNum = afpChain.getBlockNum();
328                int[] optLen = afpChain.getOptLen();
329                int[][][] optAln = afpChain.getOptAln();
330
331                int p1, p2;
332                int p1b = 0;
333                int p2b = 0;
334                int len = 0;
335
336
337                for(int i = 0; i < blockNum; i ++)  {
338                        for(int j = 0; j < optLen[i]; j ++) {
339
340                                p1 = optAln[i][0][j];
341                                p2 = optAln[i][1][j];
342
343
344                                if(len > 0)     {
345
346                                        int lmax = (p1 - p1b - 1)>(p2 - p2b - 1)?(p1 - p1b - 1):(p2 - p2b - 1);
347
348                                        // lmax gives the length of an alignment gap
349
350                                        //System.out.println("   p1-p2: " + p1 + " - " + p2 + " lmax: " + lmax + " p1b-p2b:"+p1b + " " + p2b);
351                                        for(int k = 0; k < lmax; k ++)      {
352
353                                                if(k >= (p1 - p1b - 1)) {
354                                                        // a gap position in chain 0
355                                                        if ( aligPos == len && chainNr == 0){
356                                                                return -1;
357                                                        }
358                                                }
359                                                else {
360                                                        if ( aligPos == len && chainNr == 0)
361                                                                return p1b+1+k;
362
363
364                                                }
365                                                if(k >= (p2 - p2b - 1)) {
366                                                        // a gap position in chain 1
367                                                        if ( aligPos == len && chainNr == 1){
368                                                                return -1;
369                                                        }
370                                                }
371                                                else  {
372                                                        if ( aligPos == len && chainNr == 1) {
373                                                                return p2b+1+k;
374                                                        }
375
376
377                                                }
378                                                len++;
379
380                                        }
381                                }
382
383                                if ( aligPos == len && chainNr == 0)
384                                        return p1;
385                                if ( aligPos == len && chainNr == 1)
386                                        return p2;
387
388                                len++;
389                                p1b = p1;
390                                p2b = p2;
391
392
393                        }
394                }
395
396
397                // we did not find an aligned position
398                return -1;
399        }
400
401
402        /**
403         * Returns the first atom for each group
404         * @param ca
405         * @param hetatms
406         * @return
407         * @throws StructureException
408         */
409        public static final Atom[] getAtomArray(Atom[] ca,List<Group> hetatms ) throws StructureException{
410                List<Atom> atoms = new ArrayList<Atom>();
411                Collections.addAll(atoms, ca);
412
413                logger.debug("got {} hetatoms", hetatms.size());
414
415                // we only add atom nr 1, since the getAlignedStructure method actually adds the parent group, and not the atoms...
416                for (Group g : hetatms){
417                        if (g.size() < 1)
418                                continue;
419                        //if (debug)
420                        //   System.out.println("adding group " + g);
421                        Atom a = g.getAtom(0);
422                        //if (debug)
423                        //  System.out.println(a);
424                        a.setGroup(g);
425                        atoms.add(a);
426                }
427
428                Atom[] arr = atoms.toArray(new Atom[atoms.size()]);
429
430                return arr;
431        }
432
433
434        /** Note: ca2, hetatoms2 and nucleotides2 should not be rotated. This will be done here...
435         * */
436
437        public static final StructureAlignmentJmol display(AFPChain afpChain,Group[] twistedGroups, Atom[] ca1, Atom[] ca2,List<Group> hetatms1, List<Group> hetatms2 ) throws StructureException {
438
439                List<Atom> twistedAs = new ArrayList<Atom>();
440
441                for ( Group g: twistedGroups){
442                        if ( g == null )
443                                continue;
444                        if ( g.size() < 1)
445                                continue;
446                        Atom a = g.getAtom(0);
447                        twistedAs.add(a);
448                }
449                Atom[] twistedAtoms = twistedAs.toArray(new Atom[twistedAs.size()]);
450                twistedAtoms = StructureTools.cloneAtomArray(twistedAtoms);
451
452                Atom[] arr1 = getAtomArray(ca1, hetatms1);
453                Atom[] arr2 = getAtomArray(twistedAtoms, hetatms2);
454
455                //
456
457                //if ( hetatms2.size() > 0)
458                        //      System.out.println("atom after:" + hetatms2.get(0).getAtom(0));
459
460                //if ( hetatms2.size() > 0)
461                //      System.out.println("atom after:" + hetatms2.get(0).getAtom(0));
462
463                String title =  afpChain.getAlgorithmName() + " V." +afpChain.getVersion() + " : " + afpChain.getName1() + " vs. " + afpChain.getName2();
464
465                //System.out.println(artificial.toPDB());
466
467
468
469                StructureAlignmentJmol jmol = new StructureAlignmentJmol(afpChain,arr1,arr2);
470                //jmol.setStructure(artificial);
471
472                System.out.format("CA2[0]=(%.2f,%.2f,%.2f)%n", arr2[0].getX(), arr2[0].getY(), arr2[0].getZ());
473
474                //jmol.setTitle("Structure Alignment: " + afpChain.getName1() + " vs. " + afpChain.getName2());
475                jmol.setTitle(title);
476                return jmol;
477        }
478
479        public static void showAlignmentPanel(AFPChain afpChain, Atom[] ca1, Atom[] ca2, AbstractAlignmentJmol jmol) throws StructureException {
480
481                AligPanel me = new AligPanel();
482                me.setAlignmentJmol(jmol);
483                me.setAFPChain(afpChain);
484                me.setCa1(ca1);
485                me.setCa2(ca2);
486
487                JFrame frame = new JFrame();
488
489                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
490                frame.setTitle(afpChain.getName1() + " vs. " + afpChain.getName2() + " | " + afpChain.getAlgorithmName() + " V. " + afpChain.getVersion());
491                me.setPreferredSize(new Dimension(me.getCoordManager().getPreferredWidth() , me.getCoordManager().getPreferredHeight()));
492
493                JMenuBar menu = MenuCreator.getAlignmentPanelMenu(frame,me,afpChain,null);
494                frame.setJMenuBar(menu);
495
496                JScrollPane scroll = new JScrollPane(me);
497                scroll.setAutoscrolls(true);
498
499                StatusDisplay status = new StatusDisplay();
500                status.setAfpChain(afpChain);
501                status.setCa1(ca1);
502                status.setCa2(ca2);
503                me.addAlignmentPositionListener(status);
504
505                Box vBox = Box.createVerticalBox();
506                vBox.add(scroll);
507                vBox.add(status);
508
509                frame.getContentPane().add(vBox);
510
511                frame.pack();
512                frame.setVisible(true);
513                // make sure they get cleaned up correctly:
514                frame.addWindowListener(me);
515                frame.addWindowListener(status);
516        }
517
518        public static void showAlignmentImage(AFPChain afpChain, String result) {
519
520                JFrame frame = new JFrame();
521
522                String title = afpChain.getAlgorithmName() + " V."+afpChain.getVersion() + " : " + afpChain.getName1()  + " vs. " + afpChain.getName2() ;
523                frame.setTitle(title);
524                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
525
526                AlignmentTextPanel txtPanel = new AlignmentTextPanel();
527                txtPanel.setText(result);
528
529                JMenuBar menu = MenuCreator.getAlignmentTextMenu(frame,txtPanel,afpChain,null);
530
531                frame.setJMenuBar(menu);
532                JScrollPane js = new JScrollPane();
533                js.getViewport().add(txtPanel);
534                js.getViewport().setBorder(null);
535                //js.setViewportBorder(null);
536                //js.setBorder(null);
537                //js.setBackground(Color.white);
538
539                frame.getContentPane().add(js);
540                frame.pack();
541                frame.setVisible(true);
542
543        }
544
545        /** Create a "fake" Structure objects that contains the two sets of atoms aligned on top of each other.
546         *
547         * @param afpChain the container of the alignment
548         * @param ca1 atoms for protein 1
549         * @param ca2 atoms for protein 2
550         * @return a protein structure with 2 models.
551         * @throws StructureException
552         */
553        public static Structure createArtificalStructure(AFPChain afpChain, Atom[] ca1,
554                        Atom[] ca2) throws StructureException{
555
556
557                if ( afpChain.getNrEQR() < 1){
558                        return AlignmentTools.getAlignedStructure(ca1, ca2);
559                }
560
561                Group[] twistedGroups = AlignmentTools.prepareGroupsForDisplay(afpChain,ca1, ca2);
562
563                List<Atom> twistedAs = new ArrayList<Atom>();
564
565                for ( Group g: twistedGroups){
566                        if ( g == null )
567                                continue;
568                        if ( g.size() < 1)
569                                continue;
570                        Atom a = g.getAtom(0);
571                        twistedAs.add(a);
572                }
573                Atom[] twistedAtoms = twistedAs.toArray(new Atom[twistedAs.size()]);
574
575                List<Group> hetatms  = StructureTools.getUnalignedGroups(ca1);
576                List<Group> hetatms2 = StructureTools.getUnalignedGroups(ca2);
577
578                Atom[] arr1 = DisplayAFP.getAtomArray(ca1, hetatms);
579                Atom[] arr2 = DisplayAFP.getAtomArray(twistedAtoms, hetatms2);
580
581                Structure artificial = AlignmentTools.getAlignedStructure(arr1,arr2);
582                return artificial;
583        }
584}