Source: primitives/tubeLines.js

const THREE = require('three');
const mergeGeometries = require('../utilities').mergeGeometries;

/**
 * Provides an object which stores lines.
 * This is created when a valid json file containing lines is read into a {@link Zinc.Scene}
 * object.
 * 
 * @class
 * @author Alan Wu
 * @return {TubeLines}
 */
const TubeLines = function () {
    (require('./zincObject').ZincObject).call(this);
    this.isTubeLines = true;
    let dataIn = {};
    let geometryConfig = { radius: 1, radialSegments: 8, smooth: false };

    /**
     * Create the line segements using geometry and material.
     * 
     * @param {THREE.Geomtry} geometryIn - Geometry of lines to be rendered.
     * @param {THREE.Material} materialIn - Material to be set for the lines.
     * @param {Object} options - Provide various options
     * @param {Boolean} options.localTimeEnabled - A flag to indicate either the lines is
     * time dependent.
     * @param {Boolean} options.localMorphColour - A flag to indicate either the colour is
     * time dependent.
     */
    this.createLineSegment = (geometryIn, materialIn, options) => {
        if (geometryIn && materialIn) {
            dataIn = { geometryIn, materialIn, options };
            const geometry = getTubeLinesGeometry(geometryIn.vertices);
            const material = new THREE.MeshStandardMaterial({ color: materialIn.color });
            const mesh = new THREE.Mesh(geometry, material);
            this.setMesh(mesh, options.localTimeEnabled, options.localMorphColour);
        }
    }

    /**
   * Set the width for the lines.
   * 
   * @param {Number} width - Width of the lines.
   */
	this.setWidth = width => {
		if (this.morph && this.morph.material) {
			this.morph.material.linewidth = width;
			this.morph.material.needsUpdate = true;
		}
	}

    /**
     * Set the opacity of this Geometry. This function will also set the transparent
     * according to the provided alpha value.
     * 
     * @param {Number} alpah - Alpha value to set for this geometry, 
     * can be any value between from 0 to 1.0.
     */
    this.setAlpha = function (alpha) {
        let mesh = this.getMorph();
        mesh.material.opacity = alpha;
        mesh.material.transparent = alpha < 1.0;
        mesh.material.depthWrite = alpha > 0.5;
    }

    /**
     * Set the wireframe mode for this geometry.
     * @param {Boolean} wireframe 
     */
    this.setWireframe = (wireframe) => {
        let mesh = this.getMorph();
        mesh.material.wireframe = wireframe;
    }

    /**
     * Update tube radius/radialSegments value
     * 
     * @param {Float} radius The radius of the tube.
     * @param {Integer} radialSegments The number of segments that make up the cross-section.
     */
    this.setTubeLines = (radius, radialSegments) => {
        if (radius && radialSegments) {
            const { geometryIn } = dataIn;
            let mesh = this.getMorph();
            mesh.geometry.dispose();

            geometryConfig = Object.assign(geometryConfig, { radius, radialSegments });
            mesh.geometry = getTubeLinesGeometry(geometryIn.vertices);
        }
    }

    /**
     * Get merged geometry from list of geometry vertices
     * 
     * @param {Array} vertices - An array of THREE.Vector3 vertices.
     * @returns {Object}
     */
    const getTubeLinesGeometry = (vertices) => {
        const { radius, radialSegments, smooth } = geometryConfig;
        let finalGeometry;
        if (smooth) {
            const curve = new THREE.CatmullRomCurve3(vertices);
            finalGeometry = new THREE.TubeGeometry(curve, vertices.length, radius, radialSegments, false);
        } else {
            const geometries = vertices.slice(0, -1).map((start, i) => {
                const end = vertices[i + 1];
                const curve = new THREE.LineCurve3(start, end);
                const tubeGeometry = new THREE.TubeGeometry(curve, 1, radius, radialSegments, false);
                return tubeGeometry;
            });
            finalGeometry = mergeGeometries(geometries, true);
            geometries.forEach(g => g.dispose());
        }
        return finalGeometry;
    }
}

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