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

src/representation/ballandstick-representation.js

/**
 * @file Ball And Stick Representation
 * @author Alexander Rose <alexander.rose@weirdbyte.de>
 * @private
 */

import { defaults } from '../utils.js'
import { ExtensionFragDepth, RepresentationRegistry } from '../globals.js'
import StructureRepresentation from './structure-representation.js'
import SphereBuffer from '../buffer/sphere-buffer.js'
import CylinderBuffer from '../buffer/cylinder-buffer.js'
import WideLineBuffer from '../buffer/wideline-buffer.js'

/**
 * Ball And Stick representation parameter object. Extends {@link RepresentationParameters} and
 * {@link StructureRepresentationParameters}.
 *
 * @typedef {Object} BallAndStickRepresentationParameters - ball and stick representation parameters
 *
 * @property {Integer} sphereDetail - sphere quality (icosahedron subdivisions)
 * @property {Integer} radialSegments - cylinder quality (number of segments)
 * @property {Boolean} openEnded - capped or not
 * @property {Boolean} disableImpostor - disable use of raycasted impostors for rendering
 * @property {Float} aspectRatio - size difference between atom and bond radii
 * @property {Boolean} lineOnly - render only bonds, and only as lines
 * @property {Integer} linewidth - width of lines
 * @property {Boolean} cylinderOnly - render only bonds (no atoms)
 * @property {String} multipleBond - one off "off", "symmetric", "offset"
 * @property {Float} bondSpacing - spacing for multiple bond rendering
 * @property {Float} bondScale - scale/radius for multiple bond rendering
 */

/**
 * Ball And Stick representation. Show atoms as spheres and bonds as cylinders.
 *
 * __Name:__ _ball+stick_
 *
 * @example
 * stage.loadFile( "rcsb://1crn" ).then( function( o ){
 *     o.addRepresentation( "ball+stick" );
 *     o.autoView();
 * } );
 */
class BallAndStickRepresentation extends StructureRepresentation {
  /**
   * Create Ball And Stick representation object
   * @param {Structure} structure - the structure to be represented
   * @param {Viewer} viewer - a viewer object
   * @param {BallAndStickRepresentationParameters} params - ball and stick representation parameters
   */
  constructor (structure, viewer, params) {
    super(structure, viewer, params)

    this.type = 'ball+stick'

    this.parameters = Object.assign({

      sphereDetail: true,
      radialSegments: true,
      openEnded: true,
      disableImpostor: true,
      aspectRatio: {
        type: 'number', precision: 1, max: 10.0, min: 1.0
      },
      lineOnly: {
        type: 'boolean', rebuild: true
      },
      cylinderOnly: {
        type: 'boolean', rebuild: true
      },
      multipleBond: {
        type: 'select',
        rebuild: true,
        options: {
          'off': 'off',
          'symmetric': 'symmetric',
          'offset': 'offset'
        }
      },
      bondScale: {
        type: 'number', precision: 2, max: 1.0, min: 0.01
      },
      bondSpacing: {
        type: 'number', precision: 2, max: 2.0, min: 0.5
      },
      linewidth: {
        type: 'integer', max: 50, min: 1, buffer: true
      }

    }, this.parameters)

    this.init(params)
  }

  init (params) {
    var p = params || {}
    p.radius = defaults(p.radius, 0.15)

    this.aspectRatio = defaults(p.aspectRatio, 2.0)
    this.lineOnly = defaults(p.lineOnly, false)
    this.cylinderOnly = defaults(p.cylinderOnly, false)
    this.multipleBond = defaults(p.multipleBond, 'off')
    this.bondSpacing = defaults(p.bondSpacing, 1.0)
    this.bondScale = defaults(p.bondScale, 0.4)
    this.linewidth = defaults(p.linewidth, 2)

    super.init(p)
  }

  getAtomParams (what, params) {
    params = Object.assign({
      radiusParams: { 'radius': this.radius, 'scale': this.scale * this.aspectRatio }
    }, params)

    return super.getAtomParams(what, params)
  }

  getAtomData (sview, what, params) {
    return sview.getAtomData(this.getAtomParams(what, params))
  }

  getBondParams (what, params) {
    params = Object.assign({
      multipleBond: this.multipleBond,
      bondSpacing: this.bondSpacing,
      bondScale: this.bondScale
    }, params)

    return super.getBondParams(what, params)
  }

  getBondData (sview, what, params) {
    return sview.getBondData(this.getBondParams(what, params))
  }

  createData (sview) {
    var bufferList = []

    if (this.lineOnly) {
      this.lineBuffer = new WideLineBuffer(
        this.getBondData(sview, { position: true, color: true, picking: true }),
        this.getBufferParams({ linewidth: this.linewidth })
      )

      bufferList.push(this.lineBuffer)
    } else {
      var cylinderBuffer = new CylinderBuffer(
        this.getBondData(sview),
        this.getBufferParams({
          openEnded: this.openEnded,
          radialSegments: this.radialSegments,
          disableImpostor: this.disableImpostor,
          dullInterior: true
        })
      )

      bufferList.push(cylinderBuffer)

      if (!this.cylinderOnly) {
        var sphereBuffer = new SphereBuffer(
          this.getAtomData(sview),
          this.getBufferParams({
            sphereDetail: this.sphereDetail,
            disableImpostor: this.disableImpostor,
            dullInterior: true
          })
        )

        bufferList.push(sphereBuffer)
      }
    }

    return {
      bufferList: bufferList
    }
  }

  updateData (what, data) {
    if (this.multipleBond !== 'off' && what && what.radius) {
      what.position = true
    }

    var bondData = this.getBondData(data.sview, what)

    if (this.lineOnly) {
      var lineData = {}

      if (!what || what.position) {
        lineData.position1 = bondData.position1
        lineData.position2 = bondData.position2
      }

      if (!what || what.color) {
        lineData.color = bondData.color
        lineData.color2 = bondData.color2
      }

      data.bufferList[ 0 ].setAttributes(lineData)
    } else {
      var cylinderData = {}

      if (!what || what.position) {
        cylinderData.position1 = bondData.position1
        cylinderData.position2 = bondData.position2
      }

      if (!what || what.color) {
        cylinderData.color = bondData.color
        cylinderData.color2 = bondData.color2
      }

      if (!what || what.radius) {
        cylinderData.radius = bondData.radius
      }

      data.bufferList[ 0 ].setAttributes(cylinderData)

      if (!this.cylinderOnly) {
        var atomData = this.getAtomData(data.sview, what)

        var sphereData = {}

        if (!what || what.position) {
          sphereData.position = atomData.position
        }

        if (!what || what.color) {
          sphereData.color = atomData.color
        }

        if (!what || what.radius) {
          sphereData.radius = atomData.radius
        }

        data.bufferList[ 1 ].setAttributes(sphereData)
      }
    }
  }

  setParameters (params) {
    var rebuild = false
    var what = {}

    if (params && (params.aspectRatio || params.bondSpacing || params.bondScale)) {
      what.radius = true
      if (!ExtensionFragDepth || this.disableImpostor) {
        rebuild = true
      }
    }

    super.setParameters(params, what, rebuild)

    return this
  }
}

RepresentationRegistry.add('ball+stick', BallAndStickRepresentation)

export default BallAndStickRepresentation