NGL@1.0.0-beta.7 Home Manual Reference Source Gallery

src/proxy/residue-proxy.js

/**
 * @file Residue Proxy
 * @author Alexander Rose <alexander.rose@weirdbyte.de>
 * @private
 */

import {
    SecStrucHelix, SecStrucSheet, SecStrucTurn,
    ProteinType, RnaType, DnaType, WaterType, IonType, SaccharideType,
    CgProteinBackboneType, CgRnaBackboneType, CgDnaBackboneType,
    AA1
} from '../structure/structure-constants.js'

/**
 * Residue proxy
 */
class ResidueProxy {
  /**
   * @param {Structure} structure - the structure
   * @param {Integer} index - the index
   */
  constructor (structure, index) {
    /**
     * @type {Structure}
     */
    this.structure = structure
    /**
     * @type {ChainStore}
     */
    this.chainStore = structure.chainStore
    /**
     * @type {ResidueStore}
     */
    this.residueStore = structure.residueStore
    /**
     * @type {AtomStore}
     */
    this.atomStore = structure.atomStore
    /**
     * @type {ResidueMap}
     */
    this.residueMap = structure.residueMap
    /**
     * @type {AtomMap}
     */
    this.atomMap = structure.atomMap
    /**
     * @type {Integer}
     */
    this.index = index
  }

  /**
   * Entity
   * @type {Entity}
   */
  get entity () {
    return this.structure.entityList[ this.entityIndex ]
  }
  get entityIndex () {
    return this.chainStore.entityIndex[ this.chainIndex ]
  }
  /**
   * Chain
   * @type {ChainProxy}
   */
  get chain () {
    return this.structure.getChainProxy(this.chainIndex)
  }

  get chainIndex () {
    return this.residueStore.chainIndex[ this.index ]
  }
  set chainIndex (value) {
    this.residueStore.chainIndex[ this.index ] = value
  }

  get atomOffset () {
    return this.residueStore.atomOffset[ this.index ]
  }
  set atomOffset (value) {
    this.residueStore.atomOffset[ this.index ] = value
  }

  /**
   * Atom count
   * @type {Integer}
   */
  get atomCount () {
    return this.residueStore.atomCount[ this.index ]
  }
  set atomCount (value) {
    this.residueStore.atomCount[ this.index ] = value
  }

  get atomEnd () {
    return this.atomOffset + this.atomCount - 1
  }

  //

  get modelIndex () {
    return this.chainStore.modelIndex[ this.chainIndex ]
  }
  /**
   * Chain name
   * @type {String}
   */
  get chainname () {
    return this.chainStore.getChainname(this.chainIndex)
  }
  /**
   * Chain id
   * @type {String}
   */
  get chainid () {
    return this.chainStore.getChainid(this.chainIndex)
  }

  //

  /**
   * Residue number/label
   * @type {Integer}
   */
  get resno () {
    return this.residueStore.resno[ this.index ]
  }
  set resno (value) {
    this.residueStore.resno[ this.index ] = value
  }

  /**
   * Secondary structure code
   * @type {String}
   */
  get sstruc () {
    return this.residueStore.getSstruc(this.index)
  }
  set sstruc (value) {
    this.residueStore.setSstruc(this.index, value)
  }

  /**
   * Insertion code
   * @type {String}
   */
  get inscode () {
    return this.residueStore.getInscode(this.index)
  }
  set inscode (value) {
    this.residueStore.getInscode(this.index, value)
  }

  //

  get residueType () {
    return this.residueMap.get(this.residueStore.residueTypeId[ this.index ])
  }

  /**
   * Residue name
   * @type {String}
   */
  get resname () {
    return this.residueType.resname
  }
  /**
   * Hetero flag
   * @type {Boolean}
   */
  get hetero () {
    return this.residueType.hetero
  }
  get moleculeType () {
    return this.residueType.moleculeType
  }
  get backboneType () {
    return this.residueType.backboneType
  }
  get backboneStartType () {
    return this.residueType.backboneStartType
  }
  get backboneEndType () {
    return this.residueType.backboneEndType
  }
  get traceAtomIndex () {
    return this.residueType.traceAtomIndex + this.atomOffset
  }
  get direction1AtomIndex () {
    return this.residueType.direction1AtomIndex + this.atomOffset
  }
  get direction2AtomIndex () {
    return this.residueType.direction2AtomIndex + this.atomOffset
  }
  get backboneStartAtomIndex () {
    return this.residueType.backboneStartAtomIndex + this.atomOffset
  }
  get backboneEndAtomIndex () {
    return this.residueType.backboneEndAtomIndex + this.atomOffset
  }
  get rungEndAtomIndex () {
    return this.residueType.rungEndAtomIndex + this.atomOffset
  }

  //

  /**
   * Atom iterator
   * @param  {function(atom: AtomProxy)} callback - the callback
   * @param  {Selection} [selection] - the selection
   * @return {undefined}
   */
  eachAtom (callback, selection) {
    var i
    var count = this.atomCount
    var offset = this.atomOffset
    var ap = this.structure._ap
    var end = offset + count

    if (selection && selection.atomOnlyTest) {
      var atomOnlyTest = selection.atomOnlyTest
      for (i = offset; i < end; ++i) {
        ap.index = i
        if (atomOnlyTest(ap)) callback(ap)
      }
    } else {
      for (i = offset; i < end; ++i) {
        ap.index = i
        callback(ap)
      }
    }
  }

  //

  /**
   * If residue is from a protein
   * @return {Boolean} flag
   */
  isProtein () {
    return this.residueType.moleculeType === ProteinType
  }

  /**
   * If residue is nucleic
   * @return {Boolean} flag
   */
  isNucleic () {
    var moleculeType = this.residueType.moleculeType
    return (
            moleculeType === RnaType ||
            moleculeType === DnaType
    )
  }

  /**
   * If residue is rna
   * @return {Boolean} flag
   */
  isRna () {
    return this.residueType.moleculeType === RnaType
  }

  /**
   * If residue is dna
   * @return {Boolean} flag
   */
  isDna () {
    return this.residueType.moleculeType === DnaType
  }

  /**
   * If residue is coarse-grain
   * @return {Boolean} flag
   */
  isCg () {
    var backboneType = this.residueType.backboneType
    return (
      backboneType === CgProteinBackboneType ||
      backboneType === CgRnaBackboneType ||
      backboneType === CgDnaBackboneType
    )
  }

  /**
   * If residue is from a polymer
   * @return {Boolean} flag
   */
  isPolymer () {
    if (this.structure.entityList.length > 0) {
      return this.entity.isPolymer()
    } else {
      var moleculeType = this.residueType.moleculeType
      return (
        moleculeType === ProteinType ||
        moleculeType === RnaType ||
        moleculeType === DnaType
      )
    }
  }

  /**
   * If residue is hetero
   * @return {Boolean} flag
   */
  isHetero () {
    return this.residueType.hetero === 1
  }

  /**
   * If residue is a water molecule
   * @return {Boolean} flag
   */
  isWater () {
    return this.residueType.moleculeType === WaterType
  }

  /**
   * If residue is an ion
   * @return {Boolean} flag
   */
  isIon () {
    return this.residueType.moleculeType === IonType
  }

  /**
   * If residue is a saccharide
   * @return {Boolean} flag
   */
  isSaccharide () {
    return this.residueType.moleculeType === SaccharideType
  }

  /**
   * If residue is part of a helix
   * @return {Boolean} flag
   */
  isHelix () {
    return SecStrucHelix.includes(this.sstruc)
  }

  /**
   * If residue is part of a sheet
   * @return {Boolean} flag
   */
  isSheet () {
    return SecStrucSheet.includes(this.sstruc)
  }

  /**
   * If residue is part of a turn
   * @return {Boolean} flag
   */
  isTurn () {
    return SecStrucTurn.includes(this.sstruc) && this.isProtein()
  }

  getAtomType (index) {
    return this.atomMap.get(this.atomStore.atomTypeId[ index ])
  }

  getResname1 () {
    // FIXME nucleic support
    return AA1[ this.resname.toUpperCase() ] || 'X'
  }

  getBackboneType (position) {
    switch (position) {
      case -1:
        return this.residueType.backboneStartType
      case 1:
        return this.residueType.backboneEndType
      default:
        return this.residueType.backboneType
    }
  }

  getAtomIndexByName (atomname) {
    var index = this.residueType.getAtomIndexByName(atomname)
    if (index !== undefined) {
      index += this.atomOffset
    }
    return index
  }

  getAtomByName (atomname) {
    return this.residueType.getAtomByName(atomname)
  }

  hasAtomWithName (atomname) {
    return this.residueType.hasAtomWithName(atomname)
  }

  getAtomnameList () {
    console.warn('getAtomnameList - might be expensive')

    var n = this.atomCount
    var offset = this.atomOffset
    var list = new Array(n)
    for (var i = 0; i < n; ++i) {
      list[ i ] = this.getAtomType(offset + i).atomname
    }
    return list
  }

  /**
   * If residue is connected to another
   * @param  {ResidueProxy} rNext - the other residue
   * @return {Boolean} - flag
   */
  connectedTo (rNext) {
    var bbAtomEnd = this.structure.getAtomProxy(this.backboneEndAtomIndex)
    var bbAtomStart = this.structure.getAtomProxy(rNext.backboneStartAtomIndex)
    if (bbAtomEnd && bbAtomStart) {
      return bbAtomEnd.connectedTo(bbAtomStart)
    } else {
      return false
    }
  }

  getNextConnectedResidue () {
    var rOffset = this.chainStore.residueOffset[ this.chainIndex ]
    var rCount = this.chainStore.residueCount[ this.chainIndex ]
    var nextIndex = this.index + 1
    if (nextIndex < rOffset + rCount) {
      var rpNext = this.structure.getResidueProxy(nextIndex)
      if (this.connectedTo(rpNext)) {
        return rpNext
      }
    } else if (nextIndex === rOffset + rCount) {  // cyclic
      var rpFirst = this.structure.getResidueProxy(rOffset)
      if (this.connectedTo(rpFirst)) {
        return rpFirst
      }
    }
    return undefined
  }

  getPreviousConnectedResidue () {
    var rOffset = this.chainStore.residueOffset[ this.chainIndex ]
    var prevIndex = this.index - 1
    if (prevIndex >= rOffset) {
      var rpPrev = this.structure.getResidueProxy(prevIndex)
      if (rpPrev.connectedTo(this)) {
        return rpPrev
      }
    } else if (prevIndex === rOffset - 1) {  // cyclic
      var rCount = this.chainStore.residueCount[ this.chainIndex ]
      var rpLast = this.structure.getResidueProxy(rOffset + rCount - 1)
      if (rpLast.connectedTo(this)) {
        return rpLast
      }
    }
    return undefined
  }

  getBonds () {
    return this.residueType.getBonds(this)
  }

  getRings () {
    return this.residueType.getRings()
  }

  qualifiedName (noResname) {
    var name = ''
    if (this.resname && !noResname) name += '[' + this.resname + ']'
    if (this.resno !== undefined) name += this.resno
    if (this.inscode) name += '^' + this.inscode
    if (this.chain) name += ':' + this.chainname
    name += '/' + this.modelIndex
    return name
  }

  /**
   * Clone object
   * @return {ResidueProxy} cloned residue
   */
  clone () {
    return new this.constructor(this.structure, this.index)
  }

  toObject () {
    return {
      index: this.index,
      chainIndex: this.chainIndex,
      atomOffset: this.atomOffset,
      atomCount: this.atomCount,

      resno: this.resno,
      resname: this.resname,
      sstruc: this.sstruc
    }
  }
}

export default ResidueProxy