Reference Source

src/viewer/scene/geometry/builders/buildLineGeometry.js

import {utils} from '../../utils.js';

/**
 * @desc Creates a 3D line {@link Geometry}.
 *
 * ## Usage
 *
 * In the example below we'll create a {@link Mesh} with a line {@link ReadableGeometry}.
 *
 * [[Run this example](https://xeokit.github.io/xeokit-sdk/examples/scenegraph/#buildLineGeometry)]
 *
 * ````javascript
 * //------------------------------------------------------------------------------------------------------------------
 * // Import the modules we need for this example
 * //------------------------------------------------------------------------------------------------------------------
 *
 * import {buildLineGeometry, Viewer, Mesh, ReadableGeometry, PhongMaterial} from "../../dist/xeokit-sdk.min.es.js";
 *
 * //------------------------------------------------------------------------------------------------------------------
 * // Create a Viewer and arrange the camera
 * //------------------------------------------------------------------------------------------------------------------
 *
 * const viewer = new Viewer({
 *     canvasId: "myCanvas"
 * });
 *
 * viewer.camera.eye = [0, 0, 8];
 * viewer.camera.look = [0, 0, 0];
 * viewer.camera.up = [0, 1, 0];
 *
 * //------------------------------------------------------------------------------------------------------------------
 * // Create a mesh with simple 2d line shape
 * //------------------------------------------------------------------------------------------------------------------
 *
 * new Mesh(viewer.scene, {
 *     geometry: new ReadableGeometry(viewer.scene, buildLineGeometry({
 *         startPoint: [-5,-2,0],
 *         endPoint: [-5,2,0],
 *     })),
 *     material: new PhongMaterial(viewer.scene, {
 *         emissive: [0, 1,]
 *     })
 * });
 *
 * //------------------------------------------------------------------------------------------------------------------
 * // Create a mesh with simple 2d line shape with black color
 * //------------------------------------------------------------------------------------------------------------------
 *
 * new Mesh(viewer.scene, {
 *     geometry: new ReadableGeometry(viewer.scene, buildLineGeometry({
 *         startPoint: [-4,-2,0],
 *         endPoint: [-4,2,0],
 *     })),
 *     material: new PhongMaterial(viewer.scene, {
 *         emissive: [0, 0, 0]
 *     })
 * });
 *
 * //------------------------------------------------------------------------------------------------------------------
 * // Create a mesh with simple 2d line shape with black color and simple pattern
 * //------------------------------------------------------------------------------------------------------------------
 *
 * new Mesh(viewer.scene, {
 *     geometry: new ReadableGeometry(viewer.scene, buildLineGeometry({
 *         startPoint: [-3,-2,0],
 *         endPoint: [-3,2,0],
 *         pattern: [0.10],
 *     })),
 *     material: new PhongMaterial(viewer.scene, {
 *         emissive: [0, 0, 0]
 *     })
 * });
 *
 * //------------------------------------------------------------------------------------------------------------------
 * // Create a mesh with simple 2d line shape with blue color and simple pattern extended to end
 * //------------------------------------------------------------------------------------------------------------------
 *
 * new Mesh(viewer.scene, {
 *     geometry: new ReadableGeometry(viewer.scene, buildLineGeometry({
 *         startPoint: [-2,-2,0],
 *         endPoint: [-2,2,0],
 *         pattern: [0.10],
 *         extendToEnd: true,
 *     })),
 *     material: new PhongMaterial(viewer.scene, {
 *         emissive: [0, 0, 1]
 *     })
 * });
 *
 * //------------------------------------------------------------------------------------------------------------------
 * // Create a mesh with simple 2d line shape with black color and more complex pattern
 * //------------------------------------------------------------------------------------------------------------------
 *
 * new Mesh(viewer.scene, {
 *     geometry: new ReadableGeometry(viewer.scene, buildLineGeometry({
 *         startPoint: [-1,-2,0],
 *         endPoint: [-1,2,0],
 *         pattern: [0.15, 0.05],
 *     })),
 *     material: new PhongMaterial(viewer.scene, {
 *         emissive: [0, 0, 0]
 *     })
 * });
 *
 * //------------------------------------------------------------------------------------------------------------------
 * // Create a mesh with simple 2d line shape with blue color and more complex pattern extended to end
 * //------------------------------------------------------------------------------------------------------------------
 *
 * new Mesh(viewer.scene, {
 *     geometry: new ReadableGeometry(viewer.scene, buildLineGeometry({
 *         startPoint: [0,-2,0],
 *         endPoint: [0,2,0],
 *         pattern: [0.15, 0.05],
 *         extendToEnd: true,
 *     })),
 *     material: new PhongMaterial(viewer.scene, {
 *         emissive: [0, 0, 1]
 *     })
 * });
 *
 * //------------------------------------------------------------------------------------------------------------------
 * // Create a mesh with simple 2d line shape with black color and complex pattern
 * //------------------------------------------------------------------------------------------------------------------
 *
 * new Mesh(viewer.scene, {
 *     geometry: new ReadableGeometry(viewer.scene, buildLineGeometry({
 *         startPoint: [1,-2,0],
 *         endPoint: [1,2,0],
 *         pattern: [0.15, 0.05, 0.50],
 *     })),
 *     material: new PhongMaterial(viewer.scene, {
 *         emissive: [0, 0, 0]
 *     })
 * });
 *
 * //------------------------------------------------------------------------------------------------------------------
 * // Create a mesh with simple 2d line shape with blue color and complex pattern extended to end
 * //------------------------------------------------------------------------------------------------------------------
 *
 * new Mesh(viewer.scene, {
 *     geometry: new ReadableGeometry(viewer.scene, buildLineGeometry({
 *         startPoint: [2,-2,0],
 *         endPoint: [2,2,0],
 *         pattern: [0.15, 0.05, 0.50],
 *         extendToEnd: true,
 *     })),
 *     material: new PhongMaterial(viewer.scene, {
 *         emissive: [0, 0, 1]
 *     })
 * });
 *
 * //------------------------------------------------------------------------------------------------------------------
 * // Create a mesh with simple 3d line shape with white color and simple pattern
 * //------------------------------------------------------------------------------------------------------------------
 *
 * new Mesh(viewer.scene, {
 *     geometry: new ReadableGeometry(viewer.scene, buildLineGeometry({
 *         startPoint: [3,-2,-1],
 *         endPoint: [5,2,1],
 *         pattern: [0.10],
 *     })),
 *     material: new PhongMaterial(viewer.scene, {
 *         emissive: [1, 1, 1]
 *     })
 * });
 *
 * //------------------------------------------------------------------------------------------------------------------
 * // Create a mesh with simple 3d line shape with black color and simple dot pattern
 * //------------------------------------------------------------------------------------------------------------------
 *
 * new Mesh(viewer.scene, {
 *     geometry: new ReadableGeometry(viewer.scene, buildLineGeometry({
 *         startPoint: [5,-2,-1],
 *         endPoint: [7,2,1],
 *         pattern: [0.03],
 *     })),
 *     material: new PhongMaterial(viewer.scene, {
 *         emissive: [0, 0, 0]
 *     })
 * });
 * ````
 *
 * @function buildLineGeometry
 * @param {*} [cfg] Configs
 * @param {String} [cfg.id] Optional ID, unique among all components in the parent {@link Scene}, generated automatically when omitted.
 * @param {Number[]} [cfg.startPoint]  3D start point (x0, y0, z0).
 * @param {Number[]} [cfg.endPoint]  3D end point (x1, y1, z1).
 * @param {Number[]} [cfg.pattern] Lengths of segments that describe a pattern.
 * @param {Bool} [cfg.extendToEnd] If true: it will try to make sure the line doesn't end up with a gap, as it will
 * extend the last segment.
 * @returns {Object} Configuration for a {@link Geometry} subtype.
 */
function buildLineGeometry(cfg = {}) {

    if (cfg.startPoint.length !== 3) {
        throw "Start point should contain 3 elements in array: x, y and z";
    }
    if (cfg.endPoint.length !== 3) {
        throw "End point should contain 3 elements in array: x, y and z";
    }
    let indices = [];
    let points = [];
    let x0 = cfg.startPoint[0]; let y0 = cfg.startPoint[1]; let z0 = cfg.startPoint[2];
    let x1 = cfg.endPoint[0]; let y1 = cfg.endPoint[1]; let z1 = cfg.endPoint[2];
    let lineLength = Math.sqrt((x1- x0)**2 + (y1 - y0)**2 + (z1 - z0)**2);
    let normalizedDirectionVectorOfLine = [(x1-x0)/lineLength, (y1-y0)/lineLength, (z1-z0)/lineLength];

    if (!cfg.pattern) {
        indices.push(0);
        indices.push(1);
        points.push(x0, y0, z0, x1, y1, z1);
    }
    else {
        let patternsNumber = cfg.pattern.length;
        let gap = false;
        let segmentFilled = 0.0;
        let idOfCurrentPatternLength = 0;
        let pointIndicesCounter = 0;
        let currentStartPoint = [x0, y0, z0];
        let currentPatternLength = cfg.pattern[idOfCurrentPatternLength];
        points.push(currentStartPoint[0], currentStartPoint[1], currentStartPoint[2]);

        while (currentPatternLength <= (lineLength - segmentFilled)) {
            let vectorFromCurrentStartPointToCurrentEndPoint = [
                normalizedDirectionVectorOfLine[0] * currentPatternLength,
                normalizedDirectionVectorOfLine[1] * currentPatternLength,
                normalizedDirectionVectorOfLine[2] * currentPatternLength,
            ];
            let currentEndPoint = [
                currentStartPoint[0] + vectorFromCurrentStartPointToCurrentEndPoint[0],
                currentStartPoint[1] + vectorFromCurrentStartPointToCurrentEndPoint[1],
                currentStartPoint[2] + vectorFromCurrentStartPointToCurrentEndPoint[2],
            ];

            points.push(currentEndPoint[0], currentEndPoint[1], currentEndPoint[2]);

            if (!gap) {
                indices.push(pointIndicesCounter);
                indices.push(pointIndicesCounter + 1);
            }
            gap = !gap;

            pointIndicesCounter += 1;
            currentStartPoint = currentEndPoint;
            idOfCurrentPatternLength += 1;
            if (idOfCurrentPatternLength >= patternsNumber) {
                idOfCurrentPatternLength = 0;
            }
            segmentFilled += currentPatternLength;
            currentPatternLength = cfg.pattern[idOfCurrentPatternLength];
        }

        if (cfg.extendToEnd) {
            points.push(x1, y1, z1);
            indices.push(indices.length - 2);
            indices.push(indices.length - 1);
        }
    }

    return utils.apply(cfg, {
        primitive: "lines",
        positions: points,
        indices: indices,
    });
}

export {buildLineGeometry};