packages/miew/src/chem/Chain.js

Summary

Maintainability
B
5 hrs
Test Coverage
import Residue from './Residue'
import ResidueType from './ResidueType'
import { Vector3 } from 'three'

/**
 * Residues in chain are either amino acid either nucleic acid (and water)
 * There might be some modified/mutated residues, which type could not be determined by their name (nucleic or amino); In this
 * case firstly program definites the chain type (by well-known residues) and then definites modified/mutated residues
 */
const ChainType = {
  UNKNOWN: 0,
  PROTEIN: 1,
  NUCLEIC: 2
}

/**
 * Residue chain.
 *
 * @param {Complex} complex - Molecular complex this chain belongs to.
 * @param {string} name - One character identifier (usually space, A-Z, 0-9, or a-z).
 *
 * @exports Chain
 * @constructor
 */
class Chain {
  constructor(complex, name) {
    this._complex = complex
    this._name = name
    this._mask = 1 | 0
    this._index = -1
    this._residues = []

    this.minSequence = Number.POSITIVE_INFINITY
    this.maxSequence = Number.NEGATIVE_INFINITY
  }

  getComplex() {
    return this._complex
  }

  getName() {
    return this._name
  }

  getResidues() {
    return this._residues
  }

  _determineType() {
    const residues = this._residues

    const { PROTEIN, NUCLEIC } = ResidueType.Flags

    this.type = ChainType.UNKNOWN

    for (let i = 0, n = residues.length; i < n; ++i) {
      const { flags } = residues[i]._type

      if ((flags & NUCLEIC) !== 0) {
        this.type = ChainType.NUCLEIC
        break
      } else if ((flags & PROTEIN) !== 0) {
        this.type = ChainType.PROTEIN
        break
      }
    }
  }

  /**
   * Finds thre residue with specified sequence number and inserion code
   * @param {Number} seqNum sequence number
   * @param {string} iCode insertion code
   * @returns {*} Residue or null if not found
   */
  findResidue(seqNum, iCode) {
    const residues = this._residues

    for (let i = 0, n = residues.length; i < n; ++i) {
      const res = residues[i]
      if (res._sequence === seqNum && res._icode === iCode) {
        return [res, i]
      }
    }

    return null
  }

  _finalize() {
    this._determineType()

    const residues = this._residues

    let prev = null
    for (let i = 0, n = residues.length; i < n; ++i) {
      const next = i + 1 < n ? residues[i + 1] : null
      const curr = residues[i]
      // TODO: skip invalid residues
      // eslint-disable-next-line no-constant-condition
      if (1 /* curr._isValid */) {
        curr._finalize2(prev, next, this.type === ChainType.NUCLEIC)
        prev = curr
      }
    }

    // fix very first wing
    if (residues.length > 1 && residues[1]._wingVector) {
      const p = residues[1]._wingVector
      residues[0]._wingVector = new Vector3(p.x, p.y, p.z)
    } else if (residues.length > 0) {
      residues[0]._wingVector = new Vector3(1, 0, 0)
    }
  }

  updateToFrame(frameData) {
    const residues = this._residues
    let prev = null
    let prevData = null
    const frameRes = frameData._residues
    const n = residues.length
    function getAtomPos(atom) {
      return frameData.getAtomPos(atom.index)
    }

    for (let i = 0; i < n; ++i) {
      const curr = residues[i]
      const currData = frameRes[curr._index]
      const nextRes = i + 1 < n ? residues[i + 1] : null
      curr._innerFinalize(
        prev,
        prevData,
        nextRes,
        currData,
        this.type === ChainType.NUCLEIC,
        getAtomPos
      )
      prev = curr
      prevData = currData
    }

    frameRes[residues[0]._index]._wingVector =
      n > 1 ? frameRes[residues[1]._index]._wingVector : new Vector3(1, 0, 0)
  }

  /**
   * Create a new residue.
   *
   * @param {string} name - Residue name.
   * @param {number} sequence - Residue sequence number.
   * @param {string} iCode - Insertion code.
   * @returns {Residue} - Newly created residue instance.
   */
  addResidue(name, sequence, iCode) {
    let type = this._complex.getResidueType(name)
    if (type === null) {
      type = this._complex.addResidueType(name)
    }
    const residue = new Residue(this, type, sequence, iCode)
    this._complex.addResidue(residue)
    this._residues.push(residue)

    if (type.flags & (ResidueType.Flags.NUCLEIC | ResidueType.Flags.PROTEIN)) {
      if (this.maxSequence < sequence) {
        this.maxSequence = sequence
      }
      if (this.minSequence > sequence) {
        this.minSequence = sequence
      }
    }

    return residue
  }

  getResidueCount() {
    return this._residues.length
  }

  forEachResidue(process) {
    const residues = this._residues
    for (let i = 0, n = residues.length; i < n; ++i) {
      process(residues[i])
    }
  }

  collectMask() {
    let mask = 0xffffffff
    const residues = this._residues
    for (let i = 0, n = residues.length; i < n; ++i) {
      mask &= residues[i]._mask
    }
    this._mask = mask
  }
}

export default Chain