Source: primitives/glyphset.js

const THREE = require('three');
const JSONLoader = require('../loaders/JSONLoader').JSONLoader;

/**
 * This is a container of {@link Glyph} and their graphical properties 
 * including transformations, colors, number of time steps, duration of animations
 * and group name. Please note that all glyphs in the glyphset share the same geometry
 * however they may have different transformations.
 * 
 * @class
 * @author Alan Wu
 * @return {Glyphset}
 */
const Glyphset = function () {
  (require('./zincObject').ZincObject).call(this);
  const glyphList = [];
  let axis1s = undefined;
  let axis2s = undefined;
  let axis3s = undefined;
  let positions = undefined;
  let scales = undefined;
  let colors = undefined;
  let labels = undefined;
  let numberOfTimeSteps = 0;
  let numberOfVertices = 0;
  let baseSize = [0, 0, 0];
  let offset = [0, 0, 0];
  let scaleFactors = [0, 0, 0];
  let repeat_mode = "NONE";
  this.ready = false;
  let morphColours = false;
  let morphVertices = false;
  this.isGlyphset = true;
  let _transformMatrix = new THREE.Matrix4();
  const _bot_colour = new THREE.Color();
  const _top_colour = new THREE.Color();
  const _boundingBox1 = new THREE.Box3();
  const _boundingBox2 = new THREE.Box3();
  const _boundingBox3 = new THREE.Box3();
  const _points = [];
  const _current_positions = [];
  const _current_axis1s = [];
  const _current_axis2s = [];
  const _current_axis3s = [];
  const _current_scales = [];
  const _current_colors = [];
  const _glyph_axis_array = [];
  for (let i = 0; i < 8; i++) {
    _points[i] = new THREE.Vector3();
  }

/**
   * Copy glyphset data into this glyphset then load the glyph's geoemtry 
   * with the provided glyphURL. FinishCallback will be called once
   * glyph is loaded.
   * 
   * @param {Array} glyphsetData - contains the informations about the glyphs.
   * @param {String} glyphURL - URL to the geometry which will be applied to all
   * all the glyphs in the glyphset once loaded.
   * @param {Function} finishCallback - User's function to be called once glyph's
   * geometry is loaded.
   */
  this.load = (glyphsetData, glyphURL, finishCallback, isInline, displayLabels) => {
    axis1s = glyphsetData.axis1;
    axis2s = glyphsetData.axis2;
    axis3s = glyphsetData.axis3;
    positions = glyphsetData.positions;
    scales = glyphsetData.scale;
    colors = glyphsetData.colors;
    labels = glyphsetData.label;
    morphColours = glyphsetData.metadata.MorphColours;
    morphVertices = glyphsetData.metadata.MorphVertices;
    numberOfTimeSteps = glyphsetData.metadata.number_of_time_steps;
    repeat_mode = glyphsetData.metadata.repeat_mode;
    numberOfVertices = glyphsetData.metadata.number_of_vertices;
    if (repeat_mode == "AXES_2D" || repeat_mode == "MIRROR")
      numberOfVertices = numberOfVertices * 2;
    else if (repeat_mode == "AXES_3D")
      numberOfVertices = numberOfVertices * 3;
    baseSize = glyphsetData.metadata.base_size;
    offset = glyphsetData.metadata.offset;
    scaleFactors = glyphsetData.metadata.scale_factors;
    const loader = new JSONLoader();
    this.geometry = new THREE.BufferGeometry();
    const instancedMesh = new THREE.InstancedMesh(this.geometry, undefined, numberOfVertices);
    this.setMorph(instancedMesh);
    if (isInline) {
      var object = loader.parse(glyphURL);
      (meshloader(finishCallback, displayLabels))(object.geometry, object.materials);
      object.geometry.dispose();
    } else {
      loader.crossOrigin = "Anonymous";
      loader.load(glyphURL, meshloader(finishCallback, displayLabels));
    }
  }

  /**
   * Calculate the actual transformation value that can be applied 
   * to the transformation matrix.
   * 
   * @returns {Array}
   */
  const resolve_glyph_axes = (point, axis1, axis2, axis3, scale, return_arrays) => {
    if (repeat_mode == "NONE" || repeat_mode == "MIRROR") {
      let axis_scale = [0.0, 0.0, 0.0];
      let final_axis1 = [0.0, 0.0, 0.0];
      let final_axis2 = [0.0, 0.0, 0.0];
      let final_axis3 = [0.0, 0.0, 0.0];
      let final_point = [0.0, 0.0, 0.0];
      const mirrored_axis1 = [0.0, 0.0, 0.0];
      const mirrored_axis2 = [0.0, 0.0, 0.0];
      const mirrored_axis3 = [0.0, 0.0, 0.0];
      const mirrored_point = [0.0, 0.0, 0.0];
      for (var j = 0; j < 3; j++) {
        var sign = (scale[j] < 0.0) ? -1.0 : 1.0;
        axis_scale[j] = sign * baseSize[j] + scale[j] * scaleFactors[j];
      }
      for (var j = 0; j < 3; j++) {
        final_axis1[j] = axis1[j] * axis_scale[0];
        final_axis2[j] = axis2[j] * axis_scale[1];
        final_axis3[j] = axis3[j] * axis_scale[2];
        final_point[j] = point[j]
          + offset[0] * final_axis1[j]
          + offset[1] * final_axis2[j]
          + offset[2] * final_axis3[j];
        if (repeat_mode == "MIRROR") {
          mirrored_axis1[j] = -final_axis1[j];
          mirrored_axis2[j] = -final_axis2[j];
          mirrored_axis3[j] = -final_axis3[j];
          mirrored_point[j] = final_point[j];
          if (scale[0] < 0.0) {
            // shift glyph origin to end of axis1 
            final_point[j] -= final_axis1[j];
            mirrored_point[j] -= mirrored_axis1[j];
          }
        }
      }
      /* if required, reverse axis3 to maintain right-handed coordinate system */
      if (0.0 > (
        final_axis3[0] * (final_axis1[1] * final_axis2[2] -
          final_axis1[2] * final_axis2[1]) +
        final_axis3[1] * (final_axis1[2] * final_axis2[0] -
          final_axis1[0] * final_axis2[2]) +
        final_axis3[2] * (final_axis1[0] * final_axis2[1] -
          final_axis1[1] * final_axis2[0]))) {
        final_axis3[0] = -final_axis3[0];
        final_axis3[1] = -final_axis3[1];
        final_axis3[2] = -final_axis3[2];
      }
      return_arrays[0] = [final_point, final_axis1, final_axis2, final_axis3];
      if (repeat_mode == "MIRROR") {
        if (0.0 > (
          mirrored_axis3[0] * (mirrored_axis1[1] * mirrored_axis2[2] -
            mirrored_axis1[2] * mirrored_axis2[1]) +
          mirrored_axis3[1] * (mirrored_axis1[2] * mirrored_axis2[0] -
            mirrored_axis1[0] * mirrored_axis2[2]) +
          mirrored_axis3[2] * (mirrored_axis1[0] * mirrored_axis2[1] -
            mirrored_axis1[1] * mirrored_axis2[0]))) {
          mirrored_axis3[0] = -mirrored_axis3[0];
          mirrored_axis3[1] = -mirrored_axis3[1];
          mirrored_axis3[2] = -mirrored_axis3[2];
        }
        return_arrays[1] = [mirrored_point, mirrored_axis1, mirrored_axis2, mirrored_axis3];
      }
    }
    else if (repeat_mode == "AXES_2D" || repeat_mode == "AXES_3D") {
      let axis_scale = [0.0, 0.0, 0.0];
      let final_point = [0.0, 0.0, 0.0];
      for (var j = 0; j < 3; j++) {
        var sign = (scale[j] < 0.0) ? -1.0 : 1.0;
        axis_scale[j] = sign * baseSize[0] + scale[j] * scaleFactors[0];
      }
      for (var j = 0; j < 3; j++) {
        final_point[j] = point[j]
          + offset[0] * axis_scale[0] * axis1[j]
          + offset[1] * axis_scale[1] * axis2[j]
          + offset[2] * axis_scale[2] * axis3[j];
      }
      const number_of_glyphs = (glyph_repeat_mode == "AXES_2D") ? 2 : 3;
      for (let k = 0; k < number_of_glyphs; k++) {
        let use_axis1, use_axis2;
        const use_scale = scale[k];
        let final_axis1 = [0.0, 0.0, 0.0];
        let final_axis2 = [0.0, 0.0, 0.0];
        let final_axis3 = [0.0, 0.0, 0.0];
        if (k == 0) {
          use_axis1 = axis1;
          use_axis2 = axis2;
        }
        else if (k == 1) {
          use_axis1 = axis2;
          use_axis2 = (glyph_repeat_mode == "AXES_2D") ? axis1 : axis3;
        }
        else // if (k == 2)
        {
          use_axis1 = axis3;
          use_axis2 = axis1;
        }
        const final_scale1 = baseSize[0] + use_scale * scaleFactors[0];
        final_axis1[0] = use_axis1[0] * final_scale1;
        final_axis1[1] = use_axis1[1] * final_scale1;
        final_axis1[2] = use_axis1[2] * final_scale1;
        final_axis3[0] = final_axis1[1] * use_axis2[2] - use_axis2[1] * final_axis1[2];
        final_axis3[1] = final_axis1[2] * use_axis2[0] - use_axis2[2] * final_axis1[0];
        final_axis3[2] = final_axis1[0] * use_axis2[1] - final_axis1[1] * use_axis2[0];
        let magnitude = Math.sqrt(final_axis3[0] * final_axis3[0] + final_axis3[1] * final_axis3[1] + final_axis3[2] * final_axis3[2]);
        if (0.0 < magnitude) {
          let scaling = (baseSize[2] + use_scale * scaleFactors[2]) / magnitude;
          if ((repeat_mode == "AXES_2D") && (k > 0)) {
            scaling *= -1.0;
          }
          final_axis3[0] *= scaling;
          final_axis3[1] *= scaling;
          final_axis3[2] *= scaling;
        }

        final_axis2[0] = final_axis3[1] * final_axis1[2] - final_axis1[1] * final_axis3[2];
        final_axis2[1] = final_axis3[2] * final_axis1[0] - final_axis1[2] * final_axis3[0];
        final_axis2[2] = final_axis3[0] * final_axis1[1] - final_axis3[1] * final_axis1[0];
        magnitude = Math.sqrt(final_axis2[0] * final_axis2[0] + final_axis2[1] * final_axis2[1] + final_axis2[2] * final_axis2[2]);
        if (0.0 < magnitude) {
          var scaling = (baseSize[1] + use_scale * scaleFactors[1]) / magnitude;
          final_axis2[0] *= scaling;
          final_axis2[1] *= scaling;
          final_axis2[2] *= scaling;
        }
        return_arrays[k] = [final_point, final_axis1, final_axis2, final_axis3];
      }
    }
    return return_arrays;
  };

  /**
   * Update transformation for each of the glyph in this glyphset.
   */
  const updateGlyphsetTransformation = (
    current_positions,
    current_axis1s,
    current_axis2s,
    current_axis3s,
    current_scales
  ) => {
    let numberOfGlyphs = 1;
    if (repeat_mode == "AXES_2D" || repeat_mode == "MIRROR")
      numberOfGlyphs = 2;
    else if (repeat_mode == "AXES_3D")
      numberOfGlyphs = 3;
    const numberOfPositions = current_positions.length / 3;
    let current_glyph_index = 0;
    _glyph_axis_array.length = numberOfGlyphs;
    for (let i = 0; i < numberOfPositions; i++) {
      const current_index = i * 3;
      const current_position = [current_positions[current_index], current_positions[current_index + 1],
      current_positions[current_index + 2]];
      const current_axis1 = [current_axis1s[current_index], current_axis1s[current_index + 1],
      current_axis1s[current_index + 2]];
      const current_axis2 = [current_axis2s[current_index], current_axis2s[current_index + 1],
      current_axis2s[current_index + 2]];
      const current_axis3 = [current_axis3s[current_index], current_axis3s[current_index + 1],
      current_axis3s[current_index + 2]];
      const current_scale = [current_scales[current_index], current_scales[current_index + 1],
      current_scales[current_index + 2]];
      const arrays = resolve_glyph_axes(current_position, current_axis1, current_axis2,
        current_axis3, current_scale, _glyph_axis_array);
      if (arrays.length == numberOfGlyphs) {
        for (let j = 0; j < numberOfGlyphs; j++) {
          _transformMatrix.elements[0] = arrays[j][1][0];
          _transformMatrix.elements[1] = arrays[j][1][1];
          _transformMatrix.elements[2] = arrays[j][1][2];
          _transformMatrix.elements[3] = 0.0;
          _transformMatrix.elements[4] = arrays[j][2][0];
          _transformMatrix.elements[5] = arrays[j][2][1];
          _transformMatrix.elements[6] = arrays[j][2][2];
          _transformMatrix.elements[7] = 0.0;
          _transformMatrix.elements[8] = arrays[j][3][0];
          _transformMatrix.elements[9] = arrays[j][3][1];
          _transformMatrix.elements[10] = arrays[j][3][2];
          _transformMatrix.elements[11] = 0.0;
          _transformMatrix.elements[12] = arrays[j][0][0];
          _transformMatrix.elements[13] = arrays[j][0][1];
          _transformMatrix.elements[14] = arrays[j][0][2];
          _transformMatrix.elements[15] = 1.0;
          this.morph.setMatrixAt(current_glyph_index, _transformMatrix);
          const glyph = glyphList[current_glyph_index];
          if (glyph)
            glyph.setTransformation(arrays[j][0], arrays[j][1],
              arrays[j][2], arrays[j][3]);
          current_glyph_index++;
        }
      }
    }
    this.morph.instanceMatrix.needsUpdate = true;
  };

  /**
   * Update colour for each of the glyph in this glyphset.
   */
  const updateGlyphsetHexColors = current_colors => {
    let numberOfGlyphs = 1;
    if (repeat_mode == "AXES_2D" || repeat_mode == "MIRROR")
      numberOfGlyphs = 2;
    else if (repeat_mode == "AXES_3D")
      numberOfGlyphs = 3;
    const numberOfColours = current_colors.length;
    let current_glyph_index = 0;
    for (let i = 0; i < numberOfColours; i++) {
      const hex_values = current_colors[i];
      for (let j = 0; j < numberOfGlyphs; j++) {
        _bot_colour.setHex(hex_values)
        this.morph.setColorAt(current_glyph_index, _bot_colour);
        const glyph = glyphList[current_glyph_index];
        if (glyph)
          glyph.setColour(_bot_colour);
        current_glyph_index++;
      }
    }
    this.morph.instanceColor.needsUpdate = true;
  };

  /**
   * Update the current states of the glyphs in this glyphset, this includes transformation and
   * colour for each of them. This is called when glyphset and glyphs are initialised and whenever
   * the internal time has been updated.
   */
  const updateMorphGlyphsets = () => {
    const current_positions = _current_positions;
    const current_axis1s = _current_axis1s;
    const current_axis2s = _current_axis2s;
    const current_axis3s = _current_axis3s;
    const current_scales = _current_scales;
    const current_colors = _current_colors;

    const current_time = this.inbuildTime / this.duration * (numberOfTimeSteps - 1);
    const bottom_frame = Math.floor(current_time);
    const proportion = 1 - (current_time - bottom_frame);
    const top_frame = Math.ceil(current_time);
    if (morphVertices) {
      const bottom_positions = positions[bottom_frame.toString()];
      const top_positions = positions[top_frame.toString()];
      const bottom_axis1 = axis1s[bottom_frame.toString()];
      const top_axis1 = axis1s[top_frame.toString()];
      const bottom_axis2 = axis2s[bottom_frame.toString()];
      const top_axis2 = axis2s[top_frame.toString()];
      const bottom_axis3 = axis3s[bottom_frame.toString()];
      const top_axis3 = axis3s[top_frame.toString()];
      const bottom_scale = scales[bottom_frame.toString()];
      const top_scale = scales[top_frame.toString()];
      _current_positions.length = bottom_positions.length;
      _current_axis1s.length = bottom_positions.length;
      _current_axis2s.length = bottom_positions.length;
      _current_axis3s.length = bottom_positions.length;
      _current_scales.length = bottom_positions.length;

      for (let i = 0; i < bottom_positions.length; i++) {
        current_positions[i] = proportion * bottom_positions[i] + (1.0 - proportion) * top_positions[i];
        current_axis1s[i] = proportion * bottom_axis1[i] + (1.0 - proportion) * top_axis1[i];
        current_axis2s[i] = proportion * bottom_axis2[i] + (1.0 - proportion) * top_axis2[i];
        current_axis3s[i] = proportion * bottom_axis3[i] + (1.0 - proportion) * top_axis3[i];
        current_scales[i] = proportion * bottom_scale[i] + (1.0 - proportion) * top_scale[i];
      }
    } else {
      current_positions = positions["0"];
      current_axis1s = axis1s["0"];
      current_axis2s = axis2s["0"];
      current_axis3s = axis3s["0"];
      current_scales = scales["0"];
    }
    updateGlyphsetTransformation(current_positions, current_axis1s, current_axis2s, current_axis3s,
      current_scales);
    this.boundingBoxUpdateRequired = true;
    if (colors != undefined) {
      if (morphColours) {
        const bottom_colors = colors[bottom_frame.toString()];
        const top_colors = colors[top_frame.toString()];
        current_colors.length = bottom_colors.length;
        for (let i = 0; i < bottom_colors.length; i++) {
          _bot_colour.setHex(bottom_colors[i]);
          _top_colour.setHex(top_colors[i]);
          _bot_colour.setRGB(_bot_colour.r * proportion + _top_colour.r * (1 - proportion),
            _bot_colour.g * proportion + _top_colour.g * (1 - proportion),
            _bot_colour.b * proportion + _top_colour.b * (1 - proportion));
          current_colors[i] = _bot_colour.getHex();
        }
        /*
        for (var i = 0; i < bottom_colors.length; i++) {
          current_colors.push(proportion * bottom_colors[i] + (1.0 - proportion) * top_colors[i]);
        }
        */
      } else {
        current_colors = colors["0"];
      }
      updateGlyphsetHexColors(current_colors);
    }
  };

  /**
   * Display the label of the glyphs in the glyphset.
   */
  this.showLabel = () => {
    for (let i = 0; i < glyphList.length; i++) {
      glyphList[i].showLabel(this.morph.material ? this.morph.material.color : undefined);
    }
  }

  /**
   * Create the glyphs in the glyphset.
   * 
   * @param {Boolean} displayLabels -Flag to determine either the labels should be display or not.
   */
  const createGlyphs = (displayLabels) => {
    if ((labels != undefined) && displayLabels) {
      for (let i = 0; i < numberOfVertices; i++) {
        const glyph = new (require('./glyph').Glyph)(undefined, undefined, i, this);
        if (labels != undefined && labels[i] != undefined) {
          glyph.setLabel(labels[i]);
        }
        if (numberOfTimeSteps > 0) {
          glyph.setFrustumCulled(false);
        }
        glyphList[i] = glyph;
        this.morph.add(glyph.getGroup());
      }
    }
    if ((labels != undefined) && displayLabels) {
      this.showLabel(this.morph.material ? this.morph.material.color : undefined);
    }
    //Update the transformation of the glyphs.
    updateGlyphsetTransformation(positions["0"], axis1s["0"],
      axis2s["0"], axis3s["0"], scales["0"]);
    //Update the color of the glyphs.
    if (colors != undefined) {
      updateGlyphsetHexColors(colors["0"]);
    }
    this.ready = true;
    this.boundingBoxUpdateRequired = true;
  };

  /**
   * Add a custom {@link Glyph} to this {@link Glyphset}.
   * 
   * @param {Glyph} Glyph to be added.
   */
  this.addCustomGlyph = glyph => {
    if (glyph.isGlyph)
      glyphList.push(glyph);
    this.ready = true;
    this.boundingBoxUpdateRequired = true;
  }

  /**
   * Add a THREE.Mesh object to be displayed as glyph in this {@link Glyphset}.
   * 
   * @param {THREE.Mesh} Mesh to be added.
   * @param {Number} id of the mesh.
   */
  this.addMeshAsGlyph = (mesh, id) => {
    if (mesh.isMesh) {
      const glyph = new (require('./glyph').Glyph)(undefined, undefined, id, this);
      glyph.fromMesh(mesh);
      glyphList.push(glyph);
      this.morph.add(glyph.getGroup())
      this.ready = true;
      this.boundingBoxUpdateRequired = true;
      return glyph;
    }
    return undefined;
  }

  /**
   * A function which iterates through the list of glyphs and call the callback
   * function with the glyph as the argument.
   * 
   * @param {Function} callbackFunction - Callback function with the glyph
   * as an argument.
   */
  this.forEachGlyph = callbackFunction => {
    for (let i = 0; i < glyphList.length; i++) {
      callbackFunction(glyphList[i]);
    }
  }

  var meshloader = (finishCallback, displayLabels) => {
    return (geometry, materials) => {
      const tempGeometry = geometry.toBufferGeometry();
      this.geometry.copy(tempGeometry);
      this.geometry.computeBoundingSphere();
      this.geometry.computeBoundingBox();
      tempGeometry.dispose();
      if (materials && materials[0])
        this.morph.material = materials[0];
      createGlyphs(displayLabels);
      this.morph.name = this.groupName;
      this.morph.userData = this;
      this.setMorph(this.morph);
      geometry.dispose();
      if (finishCallback != undefined && (typeof finishCallback == 'function'))
        finishCallback(this);
    };
  }

  /**
 * Get the index of the closest vertex to centroid.
 */
  this.getClosestVertexIndex = function () {
    let closestIndex = -1;
    if (this.morph && this.ready) {
      this.getBoundingBox().getCenter(this._v1);
      let current_positions = positions["0"];
      const numberOfPositions = current_positions.length / 3;
      let distance = -1;
      let currentDistance = 0;
      for (let i = 0; i < numberOfPositions; i++) {
        const current_index = i * 3;
        this._v2.set(current_positions[current_index],
          current_positions[current_index + 1],
          current_positions[current_index + 2]);
        currentDistance = this._v1.distanceTo(this._v2);
        if (distance == -1) {
          distance = currentDistance;
          closestIndex = i;
        } else if (distance > currentDistance) {
          distance = currentDistance;
          closestIndex = i;
        }
      }
    }
    return closestIndex;
  }

  /**
   * Get the  closest vertex to centroid.
   */
  this.getClosestVertex = function () {

    if (this.closestVertexIndex == -1) {
      this.closestVertexIndex = this.getClosestVertexIndex();
    }
    if (this.closestVertexIndex >= 0) {
      /*
      if (glyphList && glyphList[this.closestVertexIndex]) {
        glyphList[this.closestVertexIndex].getBoundingBox().getCenter(position);
      }
      */
      if (this.morph) {
        let position = new THREE.Vector3();
        this.morph.getMatrixAt(this.closestVertexIndex, _transformMatrix);
        position.setFromMatrixPosition(_transformMatrix);
        return position;
      }
    }

    return undefined;
  }

  /**
   * Get the bounding box for the whole set of glyphs.
   * 
   * @return {Three.Box3};
   */
  this.getBoundingBox = () => {
    if (this.morph && this.ready && this.morph.visible) {
      if (this.boundingBoxUpdateRequired) {
        _boundingBox1.setFromBufferAttribute(
          this.morph.geometry.attributes.position);
        for (let i = 0; i < numberOfVertices; i++) {
          this.morph.getMatrixAt(i, _transformMatrix);
          _boundingBox2.copy(_boundingBox1).applyMatrix4(_transformMatrix);
          if (i == 0) {
            _boundingBox3.copy(_boundingBox2);
          } else {
            _boundingBox3.union(_boundingBox2);
          }
        }
        if (_boundingBox3) {
          this.cachedBoundingBox.copy(_boundingBox3);
          this.morph.updateWorldMatrix(true, true);
          this.cachedBoundingBox.applyMatrix4(this.morph.matrixWorld);
          this.boundingBoxUpdateRequired = false;
        } else
          return undefined;
      }
      return this.cachedBoundingBox;
    }
    return undefined;
  }

  /**
   * Set the local time of this glyphset.
   * 
   * @param {Number} time - Can be any value between 0 to duration.
   */
  this.setMorphTime = time => {
    if (time > this.duration)
      this.inbuildTime = this.duration;
    else if (0 > time)
      this.inbuildTime = 0;
    else
      this.inbuildTime = time;
    if (morphColours || morphVertices) {
      updateMorphGlyphsets();
      if (morphVertices)
        this.markerUpdateRequired = true;
    }
  }

  /**
   * Check if the glyphset is time varying.
   * 
   * @return {Boolean}
   */
  this.isTimeVarying = () => {
    if ((numberOfTimeSteps > 0) && (morphColours || morphVertices))
      return true;
    return false;
  }

  /**
   * Get the current inbuild time of the 
   * 
   * @return {Number}
   */
  this.getCurrentTime = () => {
    return this.inbuildTime;
  }


  /**
   * Clear this glyphset and its list of glyphs which will release them from the memory.
   */
  this.dispose = () => {
    for (let i = glyphList.length - 1; i >= 0; i--) {
      glyphList[i].dispose();
    }
    if (this.geometry)
      this.geometry.dispose();
    if (this.morph)
      this.morph.material.dispose();
    axis1s = undefined;
    axis2s = undefined;
    axis3s = undefined;
    positions = undefined;
    scales = undefined;
    colors = undefined;
    this.ready = false;
    this.groupName = undefined;
  }

  /**
   * Update the glyphsets if required the render.
   */
  this.render = (delta, playAnimation, options) => {
    if (playAnimation == true) {
      let targetTime = this.inbuildTime + delta;
      if (targetTime > this.duration)
        targetTime = targetTime - this.duration;
      this.inbuildTime = targetTime;
      if (morphColours || morphVertices) {
        updateMorphGlyphsets();
      }
    }
    this.updateMarker(playAnimation, options);
  }
}

Glyphset.prototype = Object.create((require('./zincObject').ZincObject).prototype);
exports.Glyphset = Glyphset;