From d8b53baa9b699caf3e87e3404f1bd9d1b0d2d5cb Mon Sep 17 00:00:00 2001 From: Mario Carneiro Date: Sun, 3 Mar 2024 14:12:29 -0500 Subject: [PATCH] copy TextGeometry from super-three --- src/components/text-geometry.js | 9 +- src/lib/FontLoader.js | 196 ++++++++++++++++++++++++++++++++ src/lib/TextGeometry.js | 55 +++++++++ 3 files changed, 257 insertions(+), 3 deletions(-) create mode 100644 src/lib/FontLoader.js create mode 100644 src/lib/TextGeometry.js diff --git a/src/components/text-geometry.js b/src/components/text-geometry.js index c7a7cd22a..80010209c 100644 --- a/src/components/text-geometry.js +++ b/src/components/text-geometry.js @@ -1,11 +1,14 @@ /** * TextGeometry component for A-Frame. */ +import { TextGeometry } from '../lib/TextGeometry.js'; +import { FontLoader } from '../lib/FontLoader.js'; + var debug = AFRAME.utils.debug; var error = debug('aframe-text-component:error'); -var fontLoader = new THREE.FontLoader(); +var fontLoader = new FontLoader(); AFRAME.registerComponent('text-geometry', { schema: { @@ -42,12 +45,12 @@ AFRAME.registerComponent('text-geometry', { fontLoader.load(data.font, function (response) { const textData = AFRAME.utils.clone(data); textData.font = response; - mesh.geometry = new THREE.TextGeometry(data.value, textData); + mesh.geometry = new TextGeometry(data.value, textData); mesh.geometry.translate(-0.18, 0, -0.07); }); } else if (data.font.constructor === Object) { // Set font if already have a typeface.json through setAttribute. - mesh.geometry = new THREE.TextGeometry(data.value, data); + mesh.geometry = new TextGeometry(data.value, data); mesh.geometry.translate(-0.18, 0, -0.07); } else { error('Must provide `font` (typeface.json) or `fontPath` (string) to text component.'); diff --git a/src/lib/FontLoader.js b/src/lib/FontLoader.js new file mode 100644 index 000000000..88c8eb99d --- /dev/null +++ b/src/lib/FontLoader.js @@ -0,0 +1,196 @@ +import { + FileLoader, + Loader, + ShapePath +} from 'three'; + +class FontLoader extends Loader { + + constructor(manager) { + + super(manager); + + } + + load(url, onLoad, onProgress, onError) { + + const scope = this; + + const loader = new FileLoader(this.manager); + loader.setPath(this.path); + loader.setRequestHeader(this.requestHeader); + loader.setWithCredentials(scope.withCredentials); + loader.load(url, function (text) { + + let json; + + try { + + json = JSON.parse(text); + + } catch (e) { + + console.warn('THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.'); + json = JSON.parse(text.substring(65, text.length - 2)); + + } + + const font = scope.parse(json); + + if (onLoad) onLoad(font); + + }, onProgress, onError); + + } + + parse(json) { + + return new Font(json); + + } + +} + +// + +class Font { + + constructor(data) { + + this.type = 'Font'; + + this.data = data; + + } + + generateShapes(text, size = 100) { + + const shapes = []; + const paths = createPaths(text, size, this.data); + + for (let p = 0, pl = paths.length; p < pl; p++) { + + Array.prototype.push.apply(shapes, paths[p].toShapes()); + + } + + return shapes; + + } + +} + +function createPaths(text, size, data) { + + const chars = Array.from(text); + const scale = size / data.resolution; + const line_height = (data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness) * scale; + + const paths = []; + + let offsetX = 0, offsetY = 0; + + for (let i = 0; i < chars.length; i++) { + + const char = chars[i]; + + if (char === '\n') { + + offsetX = 0; + offsetY -= line_height; + + } else { + + const ret = createPath(char, scale, offsetX, offsetY, data); + offsetX += ret.offsetX; + paths.push(ret.path); + + } + + } + + return paths; + +} + +function createPath(char, scale, offsetX, offsetY, data) { + + const glyph = data.glyphs[char] || data.glyphs['?']; + + if (!glyph) { + + console.error('THREE.Font: character "' + char + '" does not exists in font family ' + data.familyName + '.'); + + return; + + } + + const path = new ShapePath(); + + let x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2; + + if (glyph.o) { + + const outline = glyph._cachedOutline || (glyph._cachedOutline = glyph.o.split(' ')); + + for (let i = 0, l = outline.length; i < l;) { + + const action = outline[i++]; + + switch (action) { + + case 'm': // moveTo + + x = outline[i++] * scale + offsetX; + y = outline[i++] * scale + offsetY; + + path.moveTo(x, y); + + break; + + case 'l': // lineTo + + x = outline[i++] * scale + offsetX; + y = outline[i++] * scale + offsetY; + + path.lineTo(x, y); + + break; + + case 'q': // quadraticCurveTo + + cpx = outline[i++] * scale + offsetX; + cpy = outline[i++] * scale + offsetY; + cpx1 = outline[i++] * scale + offsetX; + cpy1 = outline[i++] * scale + offsetY; + + path.quadraticCurveTo(cpx1, cpy1, cpx, cpy); + + break; + + case 'b': // bezierCurveTo + + cpx = outline[i++] * scale + offsetX; + cpy = outline[i++] * scale + offsetY; + cpx1 = outline[i++] * scale + offsetX; + cpy1 = outline[i++] * scale + offsetY; + cpx2 = outline[i++] * scale + offsetX; + cpy2 = outline[i++] * scale + offsetY; + + path.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, cpx, cpy); + + break; + + } + + } + + } + + return { offsetX: glyph.ha * scale, path: path }; + +} + +Font.prototype.isFont = true; + +export { FontLoader, Font }; diff --git a/src/lib/TextGeometry.js b/src/lib/TextGeometry.js new file mode 100644 index 000000000..325efc9b8 --- /dev/null +++ b/src/lib/TextGeometry.js @@ -0,0 +1,55 @@ +/** + * Text = 3D Text + * + * parameters = { + * font: , // font + * + * size: , // size of the text + * height: , // thickness to extrude text + * curveSegments: , // number of points on the curves + * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into text bevel goes + * bevelSize: , // how far from text outline (including bevelOffset) is bevel + * bevelOffset: // how far from text outline does bevel start + * } + */ + +var ExtrudeGeometry = THREE.ExtrudeGeometry; + +class TextGeometry extends ExtrudeGeometry { + + constructor(text, parameters = {}) { + + const font = parameters.font; + + if (font === undefined) { + + super(); // generate default extrude geometry + + } else { + + const shapes = font.generateShapes(text, parameters.size); + + // translate parameters to ExtrudeGeometry API + + parameters.depth = parameters.height !== undefined ? parameters.height : 50; + + // defaults + + if (parameters.bevelThickness === undefined) parameters.bevelThickness = 10; + if (parameters.bevelSize === undefined) parameters.bevelSize = 8; + if (parameters.bevelEnabled === undefined) parameters.bevelEnabled = false; + + super(shapes, parameters); + + } + + this.type = 'TextGeometry'; + + } + +} + + +export { TextGeometry };