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