Repository

js/Mobilizing/renderer/3D/three/shape/3D/composite/TextOpentype.js

import * as THREE from 'three';
import Mesh from "../../Mesh";
import Transform from '../../../scene/Transform';
import Font from "../../../../../../text/Font";
import Material from '../../../scene/Material';
/**
* 3D Text is created from a standard font file compatible with opentype.js.
*/
export default class TextOpentype extends Mesh {
    /**
    * @param {Object} params Parameters object, given by the constructor.
    * @param {Number} [params.text="text"] the text string
    * @param {Number} [params.fontSize=1] the text font height
    * @param {Number} [params.depth=1] the glyph extrusion height. 0 to have flat letters.
    * @param {Number} [params.bevelSegments=5] the text bevel details and tesselation
    * @param {Boolean} [params.bevelEnabled=false] the text geometry uses curves
    * @param {Number} [params.bevelSize=1] the text geometry bevel
    * @param {Number} params.font the text font to use.
    */
    constructor({
        text = "text",
        fontSize = 1,
        depth = 1,
        steps = 1,
        bevelEnabled = false,
        bevelSegments = 1,
        bevelThickness = 1,
        bevelSize = 1,
        fontFile = undefined,
        lineMode = undefined,
        lineModeDivision = 5
    } = {}) {
        super(...arguments);

        this.text = text;
        this.fontSize = fontSize;
        this.depth = depth;
        this.steps = steps;
        this.bevelEnabled = bevelEnabled;
        this.bevelSegments = bevelSegments;
        this.bevelThickness = bevelThickness;
        this.bevelSize = bevelSize;
        this.fontFile = fontFile;
        this.lineMode = lineMode;
        this.lineModeDivision = lineModeDivision;

        this._extrudeSettings = {
            steps: this.steps,
            depth: this.depth,
            bevelEnabled: this.bevelEnabled,
            bevelThickness: this.bevelThickness,
            bevelSize: this.bevelSize,
            bevelSegments: this.bevelSegments
        };

        this._font = new Font({ "fontFile": this.fontFile }).getFont();

        const shapes = this.makeShapes(this.text);

        //manage the material according to the passed params, see the attachMaterial method below
        this.attachMaterial(this.material);

        if (!this.lineMode) {
            this.createTextGeometry(shapes);
        }
        else {
            this.createTextLines(shapes);
        }

        this.geometryVerticesAttributes = this.geometry.attributes.position;
        this.geometryNormalAttributes = this.geometry.attributes.normal;
        this.geometryUVAttributes = this.geometry.attributes.uv;

        this.transform = new Transform(this);
    }

    setText(text) {
        const shapes = this.makeShapes(text);
        if (!this.lineMode) {
            this.createTextGeometry(shapes);
        }
        else {
            this.createTextLines(shapes);
        }
    }

    makeShapes(text) {
        const paths = this._font.getPath(text, 0, 0, this.fontSize);
        //console.log("paths", paths);

        const allCommands = paths.commands;

        let shapes = []
        const path = new THREE.ShapePath();

        for (let i = 0; i < allCommands.length; i++) {
            const command = allCommands[i];

            switch (command.type) {
                case "M":
                    path.moveTo(command.x, command.y);
                    break;

                case "L":
                    path.lineTo(command.x, command.y);
                    break;

                case "C":
                    path.bezierCurveTo(command.x1, command.y1, command.x2, command.y2, command.x, command.y);
                    break;

                case "Q":
                    path.quadraticCurveTo(command.x1, command.y1, command.x, command.y);
                    break;

                case "Z":
                    path.currentPath = new THREE.Path();
                    path.subPaths.push(path.currentPath);
                    break;

                default:
                    console.error("error");
            }
        }

        shapes = shapes.concat(path.toShapes(true, false));
        return shapes;
    }

    createTextGeometry(shapes) {
        if (this.geometry) {
            this.erase();
        }

        this.geometry = new THREE.ExtrudeGeometry(shapes, this._extrudeSettings);

        this.geometry.computeBoundingBox();
        //this.geometry.computeVertexNormals();

        if (this._mesh) {
            this._mesh.geometry = this.geometry;
            this.setScale(1, -1, 1);
            //console.log("textMesh", this._mesh);
        }
        else {
            const textMesh = new THREE.Mesh(this.geometry, this.material._material);
            this._mesh = textMesh;

            const centerOffset = -0.5 * (this.geometry.boundingBox.max.x - this.geometry.boundingBox.min.x);

            textMesh.position.x = centerOffset;
            textMesh.position.y = 0;
            textMesh.position.z = 0;

            textMesh.rotation.x = 0;
            textMesh.rotation.y = Math.PI * 2;
            textMesh.scale.y *= -1;
            //console.log("textMesh", this._mesh);
        }

        //console.log(this._mesh);
    }

    createTextLines(shapes) {

        //console.log(shapes);
        let points = [];
        this.indices = [];
        this.index = 0;
        shapes.forEach((shape) => {
            const spacedPoints = shape.extractPoints(this.lineModeDivision);
            //console.log("spacedPoints", spacedPoints);
            if (spacedPoints.shape.length > 0) {
                points = points.concat(spacedPoints.shape);
            }
            if (spacedPoints.holes.length > 0) {
                spacedPoints.holes.forEach((hole) => {
                    points = points.concat(hole);
                });
            }
        });


        this.geometry = new THREE.BufferGeometry().setFromPoints(points);

        this.geometryVerticesAttributes = this.geometry.attributes.position;

        for (let i = 0; i < this.geometryVerticesAttributes.count; i++) {
            this.indices.push(i);
        }
        this.geometry.setIndex(this.indices);
        //console.log("this.geometry", this.geometry);

        this.material = new Material({ type: "line" });
        const textMesh = new THREE.Line(this.geometry, this.material._material);
        this._mesh = textMesh;

        this.geometry.computeBoundingBox();

        const centerOffset = -0.5 * (this.geometry.boundingBox.max.x - this.geometry.boundingBox.min.x);

        textMesh.position.x = centerOffset;
        textMesh.position.y = 0;
        textMesh.position.z = 0;

        textMesh.rotation.x = 0;
        textMesh.rotation.y = Math.PI * 2;
        textMesh.scale.y *= -1;
        //console.log("textMesh", this._mesh);
    }

    //console.log(this._mesh);
}