001package org.biojava.nbio.structure.contact;
002
003import org.biojava.nbio.structure.Atom;
004import org.biojava.nbio.structure.Calc;
005import org.biojava.nbio.structure.Chain;
006import org.biojava.nbio.structure.Group;
007import org.biojava.nbio.structure.Structure;
008import org.biojava.nbio.structure.StructureTools;
009import org.biojava.nbio.structure.xtal.CrystalTransform;
010import org.biojava.nbio.structure.xtal.SpaceGroup;
011
012import javax.vecmath.Point3d;
013import java.util.ArrayList;
014import java.util.Collection;
015import java.util.List;
016
017/**
018 * A class containing methods to find interfaces in a given structure.
019 * @author Jose Duarte
020 * @since 5.4.0
021 */
022public class InterfaceFinder {
023
024    public static final double DEFAULT_CONTACT_CUTOFF = 6;
025
026    private static final CrystalTransform IDENTITY_TRANSFORM = new CrystalTransform((SpaceGroup) null);
027    private static final boolean INCLUDE_HETATOMS = true;
028
029    private List<Chain> polyChains;
030    private double cutoff;
031
032    private BoundingBox[] boundingBoxes;
033
034    public InterfaceFinder(Structure structure) {
035        this.polyChains = new ArrayList<>(structure.getPolyChains());
036        trimPolyChains();
037        this.cutoff = DEFAULT_CONTACT_CUTOFF;
038    }
039
040    /**
041     * Remove polymer chains with 0 atoms.
042     */
043    private void trimPolyChains() {
044        polyChains.removeIf(chain -> {
045            int count = chain.getAtomGroups().stream().map(Group::getAtoms).mapToInt(Collection::size).sum();
046            return count == 0;
047        });
048    }
049
050    /**
051     * Set the contact distance cutoff.
052     * @param cutoff the distance value in Angstroms
053     */
054    public void setCutoff(double cutoff) {
055        this.cutoff = cutoff;
056    }
057
058    /**
059     * Find all inter polymer-chain interfaces in the structure.
060     * Two chains will be considered in contact if at least a pair of atoms (one from each chain) is within the
061     * contact cutoff.
062     * @return the list of all interfaces
063     */
064    public StructureInterfaceList getAllInterfaces() {
065        initBoundingBoxes();
066
067        StructureInterfaceList list = new StructureInterfaceList();
068
069        for (int i = 0; i<polyChains.size(); i++) {
070            for (int j = i + 1; j<polyChains.size(); j++) {
071                if (! boundingBoxes[i].overlaps(boundingBoxes[j], cutoff)) {
072                    continue;
073                }
074                StructureInterface interf = calcInterface(polyChains.get(i), polyChains.get(j));
075                if (interf!=null) {
076                    list.add(interf);
077                }
078            }
079        }
080        return list;
081    }
082
083    private void initBoundingBoxes() {
084        boundingBoxes = new BoundingBox[polyChains.size()];
085        for (int i = 0; i<polyChains.size(); i++) {
086            Atom[] atoms = StructureTools.getAllNonHAtomArray(polyChains.get(i), INCLUDE_HETATOMS);
087            Point3d[] points = Calc.atomsToPoints(atoms);
088            BoundingBox bb = new BoundingBox(points);
089            boundingBoxes[i] = bb;
090        }
091    }
092
093    private StructureInterface calcInterface(Chain chain1, Chain chain2) {
094        AtomContactSet graph = StructureTools.getAtomsInContact(chain1, chain2, cutoff, INCLUDE_HETATOMS);
095
096        StructureInterface interf = null;
097        if (graph.size()>0) {
098            interf = new StructureInterface(
099                    StructureTools.getAllNonHAtomArray(chain1, INCLUDE_HETATOMS), StructureTools.getAllNonHAtomArray(chain2, INCLUDE_HETATOMS),
100                    chain1.getName(), chain2.getName(),
101                    graph,
102                    IDENTITY_TRANSFORM, IDENTITY_TRANSFORM);
103        }
104
105        return interf;
106    }
107}