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

src/color/colormaker-registry.js

/**
 * @file Colormaker Registry
 * @author Alexander Rose <alexander.rose@weirdbyte.de>
 * @private
 */

import { generateUUID } from '../math/math-utils.js'
import Colormaker from './colormaker.js'
import SelectionColormaker from './selection-colormaker.js'

const ColormakerScales = {

  '': '',

    // Sequential
  OrRd: '[S] Orange-Red',
  PuBu: '[S] Purple-Blue',
  BuPu: '[S] Blue-Purple',
  Oranges: '[S] Oranges',
  BuGn: '[S] Blue-Green',
  YlOrBr: '[S] Yellow-Orange-Brown',
  YlGn: '[S] Yellow-Green',
  Reds: '[S] Reds',
  RdPu: '[S] Red-Purple',
  Greens: '[S] Greens',
  YlGnBu: '[S] Yellow-Green-Blue',
  Purples: '[S] Purples',
  GnBu: '[S] Green-Blue',
  Greys: '[S] Greys',
  YlOrRd: '[S] Yellow-Orange-Red',
  PuRd: '[S] Purple-Red',
  Blues: '[S] Blues',
  PuBuGn: '[S] Purple-Blue-Green',

    // Diverging
  Viridis: '[D] Viridis',
  Spectral: '[D] Spectral',
  RdYlGn: '[D] Red-Yellow-Green',
  RdBu: '[D] Red-Blue',
  PiYG: '[D] Pink-Yellowgreen',
  PRGn: '[D] Purplered-Green',
  RdYlBu: '[D] Red-Yellow-Blue',
  BrBG: '[D] Brown-Bluegreen',
  RdGy: '[D] Red-Grey',
  PuOr: '[D] Purple-Orange',

    // Qualitative
  Set1: '[Q] Set1',
  Set2: '[Q] Set2',
  Set3: '[Q] Set3',
  Dark2: '[Q] Dark2',
  Paired: '[Q] Paired',
  Pastel1: '[Q] Pastel1',
  Pastel2: '[Q] Pastel2',
  Accent: '[Q] Accent',

    // Other
  rainbow: '[?] Rainbow',
  rwb: '[?] Red-White-Blue'

}

const ColormakerModes = {

  '': '',

  rgb: 'Red Green Blue',
  hsv: 'Hue Saturation Value',
  hsl: 'Hue Saturation Lightness',
  hsi: 'Hue Saturation Intensity',
  lab: 'CIE L*a*b*',
  hcl: 'Hue Chroma Lightness'

}

/**
 * Class for registering {@link Colormaker}s. Generally use the
 * global {@link src/globals.js~ColormakerRegistry} instance.
 */
class ColormakerRegistry {
  constructor () {
    this.schemes = {}
    this.userSchemes = {}
  }

  getScheme (params) {
    const p = params || {}
    const id = (p.scheme || '').toLowerCase()

    let SchemeClass

    if (id in this.schemes) {
      SchemeClass = this.schemes[ id ]
    } else if (id in this.userSchemes) {
      SchemeClass = this.userSchemes[ id ]
    } else {
      SchemeClass = Colormaker
    }

    return new SchemeClass(params)
  }

  /**
   * Get an description of available schemes as an
   * object with id-label as key-value pairs
   * @return {Object} available schemes
   */
  getSchemes () {
    const types = {}

    Object.keys(this.schemes).forEach(function (k) {
      types[ k ] = k
    })

    Object.keys(this.userSchemes).forEach(function (k) {
      types[ k ] = k.split('|')[ 1 ]
    })

    return types
  }

  /**
   * Get an description of available scales as an
   * object with id-label as key-value pairs
   * @return {Object} available scales
   */
  getScales () {
    return ColormakerScales
  }

  getModes () {
    return ColormakerModes
  }

  /**
   * Add a scheme with a hardcoded id
   * @param {String} id - the id
   * @param {Colormaker} scheme - the colormaker
   * @return {undefined}
   */
  add (id, scheme) {
    id = id.toLowerCase()
    this.schemes[ id ] = scheme
  }

  /**
   * Register a custom scheme
   *
   * @example
   * // Create a class with a `atomColor` method that returns a hex color.
   * var schemeId = NGL.ColormakerRegistry.addScheme( function( params ){
   *     this.atomColor = function( atom ){
   *         if( atom.serial < 1000 ){
   *             return 0x0000FF;  // blue
   *         }else if( atom.serial > 2000 ){
   *             return 0xFF0000;  // red
   *         }else{
   *             return 0x00FF00;  // green
   *         }
   *     };
   * } );
   *
   * stage.loadFile( "rcsb://3dqb.pdb" ).then( function( o ){
   *     o.addRepresentation( "cartoon", { color: schemeId } );  // pass schemeId here
   *     o.autoView();
   * } );
   *
   * @param {Function|Colormaker} scheme - constructor or {@link Colormaker} instance
   * @param {String} label - scheme label
   * @return {String} id to refer to the registered scheme
   */
  addScheme (scheme, label) {
    if (!(scheme instanceof Colormaker)) {
      scheme = this._createScheme(scheme)
    }

    return this._addUserScheme(scheme, label)
  }

  /**
   * Add a user-defined scheme
   * @param {Colormaker} scheme - the user-defined scheme
   * @param {String} [label] - scheme label
   * @return {String} id to refer to the registered scheme
   */
  _addUserScheme (scheme, label) {
    label = label || ''
    const id = `${generateUUID()}|${label}`.toLowerCase()
    this.userSchemes[ id ] = scheme

    return id
  }

  /**
   * Remove the scheme with the given id
   * @param  {String} id - scheme to remove
   * @return {undefined}
   */
  removeScheme (id) {
    id = id.toLowerCase()
    delete this.userSchemes[ id ]
  }

  _createScheme (constructor) {
    const _Colormaker = function (params) {
      Colormaker.call(this, params)
      constructor.call(this, params)
    }

    _Colormaker.prototype = Colormaker.prototype
    _Colormaker.prototype.constructor = Colormaker

    return _Colormaker
  }

  /**
   * Create and a selection-based coloring scheme. Supply a list with pairs
   * of colorname and selection for coloring by selections. Use the last
   * entry as a default (catch all) coloring definition.
   *
   * @example
   * var schemeId = NGL.ColormakerRegistry.addSelectionScheme( [
   *     [ "red", "64-74 or 134-154 or 222-254 or 310-310 or 322-326" ],
   *     [ "green", "311-322" ],
   *     [ "yellow", "40-63 or 75-95 or 112-133 or 155-173 or 202-221 or 255-277 or 289-309" ],
   *     [ "blue", "1-39 or 96-112 or 174-201 or 278-288" ],
   *     [ "white", "*" ]
   * ], "Transmembrane 3dqb" );
   *
   * stage.loadFile( "rcsb://3dqb.pdb" ).then( function( o ){
   *     o.addRepresentation( "cartoon", { color: schemeId } );  // pass schemeId here
   *     o.autoView();
   * } );
   *
   * @param {Array} dataList - cloror-selection pairs
   * @param {String} label - scheme name
   * @return {String} id to refer to the registered scheme
   */
  addSelectionScheme (dataList, label) {
    class MySelectionColormaker extends SelectionColormaker {
      constructor (params) {
        super(Object.assign({ dataList: dataList }, params))
      }
    }

    return this._addUserScheme(MySelectionColormaker, label)
  }

  /**
   * Check if a scheme with the given id exists
   * @param  {String}  id - the id to check
   * @return {Boolean} flag indicating if the scheme exists
   */
  hasScheme (id) {
    id = id.toLowerCase()
    return id in this.schemes || id in this.userSchemes
  }
}

export default ColormakerRegistry