@babylonjs/core
Version: 
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
336 lines • 12.3 kB
JavaScript
import { Logger } from "../Misc/logger.js";
import { Vector3, Vector2 } from "../Maths/math.vector.js";
import { VertexBuffer } from "../Buffers/buffer.js";
import { Mesh } from "../Meshes/mesh.js";
import { VertexData } from "../Meshes/mesh.vertexData.js";
import { Path2 } from "../Maths/math.path.js";
import { Epsilon } from "../Maths/math.constants.js";
import { EngineStore } from "../Engines/engineStore.js";
/**
 * Vector2 wth index property
 */
class IndexedVector2 extends Vector2 {
    constructor(original, 
    /** Index of the vector2 */
    index) {
        super(original.x, original.y);
        this.index = index;
    }
}
/**
 * Defines points to create a polygon
 */
class PolygonPoints {
    constructor() {
        this.elements = [];
    }
    add(originalPoints) {
        const result = [];
        for (const point of originalPoints) {
            const newPoint = new IndexedVector2(point, this.elements.length);
            result.push(newPoint);
            this.elements.push(newPoint);
        }
        return result;
    }
    computeBounds() {
        const lmin = new Vector2(this.elements[0].x, this.elements[0].y);
        const lmax = new Vector2(this.elements[0].x, this.elements[0].y);
        for (const point of this.elements) {
            // x
            if (point.x < lmin.x) {
                lmin.x = point.x;
            }
            else if (point.x > lmax.x) {
                lmax.x = point.x;
            }
            // y
            if (point.y < lmin.y) {
                lmin.y = point.y;
            }
            else if (point.y > lmax.y) {
                lmax.y = point.y;
            }
        }
        return {
            min: lmin,
            max: lmax,
            width: lmax.x - lmin.x,
            height: lmax.y - lmin.y,
        };
    }
}
/**
 * Polygon
 * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/creation/param#non-regular-polygon
 */
export class Polygon {
    /**
     * Creates a rectangle
     * @param xmin bottom X coord
     * @param ymin bottom Y coord
     * @param xmax top X coord
     * @param ymax top Y coord
     * @returns points that make the resulting rectangle
     */
    static Rectangle(xmin, ymin, xmax, ymax) {
        return [new Vector2(xmin, ymin), new Vector2(xmax, ymin), new Vector2(xmax, ymax), new Vector2(xmin, ymax)];
    }
    /**
     * Creates a circle
     * @param radius radius of circle
     * @param cx scale in x
     * @param cy scale in y
     * @param numberOfSides number of sides that make up the circle
     * @returns points that make the resulting circle
     */
    static Circle(radius, cx = 0, cy = 0, numberOfSides = 32) {
        const result = [];
        let angle = 0;
        const increment = (Math.PI * 2) / numberOfSides;
        for (let i = 0; i < numberOfSides; i++) {
            result.push(new Vector2(cx + Math.cos(angle) * radius, cy + Math.sin(angle) * radius));
            angle -= increment;
        }
        return result;
    }
    /**
     * Creates a polygon from input string
     * @param input Input polygon data
     * @returns the parsed points
     */
    static Parse(input) {
        const floats = input
            .split(/[^-+eE.\d]+/)
            .map(parseFloat)
            .filter((val) => !isNaN(val));
        let i;
        const result = [];
        for (i = 0; i < (floats.length & 0x7ffffffe); i += 2) {
            result.push(new Vector2(floats[i], floats[i + 1]));
        }
        return result;
    }
    /**
     * Starts building a polygon from x and y coordinates
     * @param x x coordinate
     * @param y y coordinate
     * @returns the started path2
     */
    static StartingAt(x, y) {
        return Path2.StartingAt(x, y);
    }
}
/**
 * Builds a polygon
 * @see https://doc.babylonjs.com/features/featuresDeepDive/mesh/creation/param/polyMeshBuilder
 */
export class PolygonMeshBuilder {
    _addToepoint(points) {
        for (const p of points) {
            this._epoints.push(p.x, p.y);
        }
    }
    /**
     * Creates a PolygonMeshBuilder
     * @param name name of the builder
     * @param contours Path of the polygon
     * @param scene scene to add to when creating the mesh
     * @param earcutInjection can be used to inject your own earcut reference
     */
    constructor(name, contours, scene, earcutInjection = earcut) {
        this._points = new PolygonPoints();
        this._outlinepoints = new PolygonPoints();
        this._holes = new Array();
        this._epoints = new Array();
        this._eholes = new Array();
        this.bjsEarcut = earcutInjection;
        this._name = name;
        this._scene = scene || EngineStore.LastCreatedScene;
        let points;
        if (contours instanceof Path2) {
            points = contours.getPoints();
        }
        else {
            points = contours;
        }
        this._addToepoint(points);
        this._points.add(points);
        this._outlinepoints.add(points);
        if (typeof this.bjsEarcut === "undefined") {
            Logger.Warn("Earcut was not found, the polygon will not be built.");
        }
    }
    /**
     * Adds a hole within the polygon
     * @param hole Array of points defining the hole
     * @returns this
     */
    addHole(hole) {
        this._points.add(hole);
        const holepoints = new PolygonPoints();
        holepoints.add(hole);
        this._holes.push(holepoints);
        this._eholes.push(this._epoints.length / 2);
        this._addToepoint(hole);
        return this;
    }
    /**
     * Creates the polygon
     * @param updatable If the mesh should be updatable
     * @param depth The depth of the mesh created
     * @param smoothingThreshold Dot product threshold for smoothed normals
     * @returns the created mesh
     */
    build(updatable = false, depth = 0, smoothingThreshold = 2) {
        const result = new Mesh(this._name, this._scene);
        const vertexData = this.buildVertexData(depth, smoothingThreshold);
        result.setVerticesData(VertexBuffer.PositionKind, vertexData.positions, updatable);
        result.setVerticesData(VertexBuffer.NormalKind, vertexData.normals, updatable);
        result.setVerticesData(VertexBuffer.UVKind, vertexData.uvs, updatable);
        result.setIndices(vertexData.indices);
        return result;
    }
    /**
     * Creates the polygon
     * @param depth The depth of the mesh created
     * @param smoothingThreshold Dot product threshold for smoothed normals
     * @returns the created VertexData
     */
    buildVertexData(depth = 0, smoothingThreshold = 2) {
        const result = new VertexData();
        const normals = [];
        const positions = [];
        const uvs = [];
        const bounds = this._points.computeBounds();
        for (const p of this._points.elements) {
            normals.push(0, 1.0, 0);
            positions.push(p.x, 0, p.y);
            uvs.push((p.x - bounds.min.x) / bounds.width, (p.y - bounds.min.y) / bounds.height);
        }
        const indices = [];
        const res = this.bjsEarcut(this._epoints, this._eholes, 2);
        for (let i = 0; i < res.length; i++) {
            indices.push(res[i]);
        }
        if (depth > 0) {
            const positionscount = positions.length / 3; //get the current pointcount
            for (const p of this._points.elements) {
                //add the elements at the depth
                normals.push(0, -1.0, 0);
                positions.push(p.x, -depth, p.y);
                uvs.push(1 - (p.x - bounds.min.x) / bounds.width, 1 - (p.y - bounds.min.y) / bounds.height);
            }
            const totalCount = indices.length;
            for (let i = 0; i < totalCount; i += 3) {
                const i0 = indices[i + 0];
                const i1 = indices[i + 1];
                const i2 = indices[i + 2];
                indices.push(i2 + positionscount);
                indices.push(i1 + positionscount);
                indices.push(i0 + positionscount);
            }
            //Add the sides
            this._addSide(positions, normals, uvs, indices, bounds, this._outlinepoints, depth, false, smoothingThreshold);
            for (const hole of this._holes) {
                this._addSide(positions, normals, uvs, indices, bounds, hole, depth, true, smoothingThreshold);
            }
        }
        result.indices = indices;
        result.positions = positions;
        result.normals = normals;
        result.uvs = uvs;
        return result;
    }
    /**
     * Adds a side to the polygon
     * @param positions points that make the polygon
     * @param normals normals of the polygon
     * @param uvs uvs of the polygon
     * @param indices indices of the polygon
     * @param bounds bounds of the polygon
     * @param points points of the polygon
     * @param depth depth of the polygon
     * @param flip flip of the polygon
     * @param smoothingThreshold
     */
    _addSide(positions, normals, uvs, indices, bounds, points, depth, flip, smoothingThreshold) {
        let startIndex = positions.length / 3;
        let ulength = 0;
        for (let i = 0; i < points.elements.length; i++) {
            const p = points.elements[i];
            const p1 = points.elements[(i + 1) % points.elements.length];
            positions.push(p.x, 0, p.y);
            positions.push(p.x, -depth, p.y);
            positions.push(p1.x, 0, p1.y);
            positions.push(p1.x, -depth, p1.y);
            const p0 = points.elements[(i + points.elements.length - 1) % points.elements.length];
            const p2 = points.elements[(i + 2) % points.elements.length];
            let vc = new Vector3(-(p1.y - p.y), 0, p1.x - p.x);
            let vp = new Vector3(-(p.y - p0.y), 0, p.x - p0.x);
            let vn = new Vector3(-(p2.y - p1.y), 0, p2.x - p1.x);
            if (!flip) {
                vc = vc.scale(-1);
                vp = vp.scale(-1);
                vn = vn.scale(-1);
            }
            const vcNorm = vc.normalizeToNew();
            let vpNorm = vp.normalizeToNew();
            let vnNorm = vn.normalizeToNew();
            const dotp = Vector3.Dot(vpNorm, vcNorm);
            if (dotp > smoothingThreshold) {
                if (dotp < Epsilon - 1) {
                    vpNorm = new Vector3(p.x, 0, p.y).subtract(new Vector3(p1.x, 0, p1.y)).normalize();
                }
                else {
                    // cheap average weighed by side length
                    vpNorm = vp.add(vc).normalize();
                }
            }
            else {
                vpNorm = vcNorm;
            }
            const dotn = Vector3.Dot(vn, vc);
            if (dotn > smoothingThreshold) {
                if (dotn < Epsilon - 1) {
                    // back to back
                    vnNorm = new Vector3(p1.x, 0, p1.y).subtract(new Vector3(p.x, 0, p.y)).normalize();
                }
                else {
                    // cheap average weighed by side length
                    vnNorm = vn.add(vc).normalize();
                }
            }
            else {
                vnNorm = vcNorm;
            }
            uvs.push(ulength / bounds.width, 0);
            uvs.push(ulength / bounds.width, 1);
            ulength += vc.length();
            uvs.push(ulength / bounds.width, 0);
            uvs.push(ulength / bounds.width, 1);
            normals.push(vpNorm.x, vpNorm.y, vpNorm.z);
            normals.push(vpNorm.x, vpNorm.y, vpNorm.z);
            normals.push(vnNorm.x, vnNorm.y, vnNorm.z);
            normals.push(vnNorm.x, vnNorm.y, vnNorm.z);
            if (!flip) {
                indices.push(startIndex);
                indices.push(startIndex + 1);
                indices.push(startIndex + 2);
                indices.push(startIndex + 1);
                indices.push(startIndex + 3);
                indices.push(startIndex + 2);
            }
            else {
                indices.push(startIndex);
                indices.push(startIndex + 2);
                indices.push(startIndex + 1);
                indices.push(startIndex + 1);
                indices.push(startIndex + 2);
                indices.push(startIndex + 3);
            }
            startIndex += 4;
        }
    }
}
//# sourceMappingURL=polygonMesh.js.map