Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TextGeometry changes and support in editor #27931

Open
wants to merge 11 commits into
base: dev
Choose a base branch
from
24 changes: 21 additions & 3 deletions build/three.cjs

Large diffs are not rendered by default.

24 changes: 21 additions & 3 deletions build/three.module.js

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion build/three.module.min.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/examples/en/geometries/TextGeometry.html
Expand Up @@ -38,7 +38,7 @@ <h2>Code Example</h2>
const geometry = new TextGeometry( 'Hello three.js!', {
font: font,
size: 80,
height: 5,
depth: 5,
curveSegments: 12,
bevelEnabled: true,
bevelThickness: 10,
Expand All @@ -64,7 +64,7 @@ <h3>[name]([param:String text], [param:Object parameters])</h3>
<ul>
<li>font — an instance of THREE.Font.</li>
<li>size — Float. Size of the text. Default is 100.</li>
<li>height — Float. Thickness to extrude text. Default is 50.</li>
<li>depth — Float. Thickness to extrude text. Default is 50.</li>
<li>curveSegments — Integer. Number of points on the curves. Default is 12.</li>
<li>bevelEnabled — Boolean. Turn on bevel. Default is False.</li>
<li>bevelThickness — Float. How deep into text bevel goes. Default is 10.</li>
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/zh/geometries/TextGeometry.html
Expand Up @@ -38,7 +38,7 @@ <h2>代码示例</h2>
const geometry = new TextGeometry( 'Hello three.js!', {
font: font,
size: 80,
height: 5,
depth: 5,
curveSegments: 12,
bevelEnabled: true,
bevelThickness: 10,
Expand All @@ -63,7 +63,7 @@ <h3>[name]([param:String text], [param:Object parameters])</h3>
<ul>
<li>font — THREE.Font的实例。</li>
<li>size — Float。字体大小,默认值为100。</li>
<li>height — Float。挤出文本的厚度。默认值为50。</li>
<li>depth — Float。挤出文本的厚度。默认值为50。</li>
<li>curveSegments — Integer。(表示文本的)曲线上点的数量。默认值为12。</li>
<li>bevelEnabled — Boolean。是否开启斜角,默认为false。</li>
<li>bevelThickness — Float。文本上斜角的深度,默认值为20。</li>
Expand Down
4 changes: 4 additions & 0 deletions editor/js/Editor.js
@@ -1,5 +1,7 @@
import * as THREE from 'three';

import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';

import { Config } from './Config.js';
import { Loader } from './Loader.js';
import { History as _History } from './History.js';
Expand Down Expand Up @@ -657,6 +659,8 @@ Editor.prototype = {
fromJSON: async function ( json ) {

var loader = new THREE.ObjectLoader();
loader.registerGeometry( 'TextGeometry', TextGeometry );

var camera = await loader.parseAsync( json.camera );

this.camera.copy( camera );
Expand Down
41 changes: 41 additions & 0 deletions editor/js/Menubar.Add.js
@@ -1,5 +1,8 @@
import * as THREE from 'three';

import { FontLoader } from 'three/addons/loaders/FontLoader.js';
import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';

import { UIPanel, UIRow, UIHorizontalRule } from './libs/ui.js';

import { AddObjectCommand } from './commands/AddObjectCommand.js';
Expand Down Expand Up @@ -248,6 +251,44 @@ function MenubarAdd( editor ) {
} );
options.add( option );

// Text

option = new UIRow();
option.setClass( 'option' );
option.setTextContent( strings.getKey( 'menubar/add/text' ) );
option.onClick( function () {

const loader = new FontLoader();
loader.load( '../examples/fonts/helvetiker_bold.typeface.json', function ( font ) {

const text = 'THREE.JS';

const geometry = new TextGeometry( text, {
text: text,
font,
size: 70,
depth: 20,
curveSegments: 4,

bevelEnabled: false,
bevelThickness: 10,
bevelSize: 8,
bevelOffset: 0,
bevelSegments: 3,
scale: 0.01

} );

const mesh = new THREE.Mesh( geometry, new THREE.MeshStandardMaterial() );
mesh.name = 'Text';

editor.execute( new AddObjectCommand( editor, mesh ) );

} );

} );
options.add( option );

// Torus

option = new UIRow();
Expand Down
150 changes: 150 additions & 0 deletions editor/js/Sidebar.Geometry.TextGeometry.js
@@ -0,0 +1,150 @@
import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';

import { UIDiv, UIRow, UIText, UINumber, UIInteger, UIInput, UICheckbox } from './libs/ui.js';

import { SetGeometryCommand } from './commands/SetGeometryCommand.js';

function GeometryParametersPanel( editor, object ) {

const strings = editor.strings;

const container = new UIDiv();

const geometry = object.geometry;
const parameters = geometry.parameters.options;

// text

const textRow = new UIRow();
const text = new UIInput().setValue( parameters.text ).onChange( update );

textRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/text' ) ).setClass( 'Label' ) );
textRow.add( text );

container.add( textRow );

// size

const sizeRow = new UIRow();
const size = new UINumber().setPrecision( 3 ).setValue( parameters.size ).onChange( update );

sizeRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/size' ) ).setClass( 'Label' ) );
sizeRow.add( size );

container.add( sizeRow );

// depth

const depthRow = new UIRow();
const depth = new UINumber().setPrecision( 3 ).setValue( parameters.depth ).onChange( update );

depthRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/depth' ) ).setClass( 'Label' ) );
depthRow.add( depth );

container.add( depthRow );

// curveSegments

const curveSegmentsRow = new UIRow();
const curveSegments = new UIInteger( parameters.curveSegments ).setRange( 1, Infinity ).onChange( update );

curveSegmentsRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/curveseg' ) ).setClass( 'Label' ) );
curveSegmentsRow.add( curveSegments );

container.add( curveSegmentsRow );


// scale

const scaleRow = new UIRow();
const scale = new UINumber().setPrecision( 4 ).setValue( parameters.scale ).onChange( update );

scaleRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/scale' ) ).setClass( 'Label' ) );
scaleRow.add( scale );

container.add( scaleRow );

// bevelEnabled

const bevelEnabledRow = new UIRow();
const bevelEnabled = new UICheckbox( parameters.bevelEnabled ).onChange( update );

bevelEnabledRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/bevelenabled' ) ).setClass( 'Label' ) );
bevelEnabledRow.add( bevelEnabled );

container.add( bevelEnabledRow );

// bevelThickness

const bevelThicknessRow = new UIRow();
const bevelThickness = new UINumber( parameters.bevelThickness ).setPrecision( 3 ).setRange( 0, Infinity ).onChange( update );

bevelThicknessRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/bevelthickness' ) ).setClass( 'Label' ) );
bevelThicknessRow.add( bevelThickness );

container.add( bevelThicknessRow );

// bevelSize

const bevelSizeRow = new UIRow();
const bevelSize = new UINumber( parameters.bevelSize ).setRange( 0, Infinity ).onChange( update );

bevelSizeRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/bevelsize' ) ).setClass( 'Label' ) );
bevelSizeRow.add( bevelSize );

container.add( bevelSizeRow );

// bevelOffset

const bevelOffsetRow = new UIRow();
const bevelOffset = new UINumber( parameters.bevelOffset ).setRange( 0, Infinity ).onChange( update );

bevelOffsetRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/bevelOffset' ) ).setClass( 'Label' ) );
bevelOffsetRow.add( bevelOffset );

container.add( bevelOffsetRow );


// bevelSegments

const bevelSegmentsRow = new UIRow();
const bevelSegments = new UIInteger( parameters.bevelSegments ).setRange( 0, Infinity ).onChange( update );

bevelSegmentsRow.add( new UIText( strings.getKey( 'sidebar/geometry/text_geometry/bevelseg' ) ).setClass( 'Label' ) );
bevelSegmentsRow.add( bevelSegments );

container.add( bevelSegmentsRow );

function update() {

const options = {

text: text.getValue(),
font: parameters.font,

size: size.getValue(),
depth: depth.getValue(),
curveSegments: curveSegments.getValue(),

bevelEnabled: bevelEnabled.getValue(),
bevelThickness: bevelThickness.getValue(),
bevelSize: bevelSize.getValue(),
bevelOffset: bevelOffset.getValue(),
bevelSegments: bevelSegments.getValue(),

scale: scale.getValue(),

};

const geometry = new TextGeometry( options.text, options );

editor.execute( new SetGeometryCommand( editor, object, geometry ) );

}

return container;

}

export { GeometryParametersPanel };

14 changes: 13 additions & 1 deletion editor/js/Strings.js
Expand Up @@ -39,6 +39,7 @@ function Strings( config ) {
'menubar/add/icosahedron': 'Icosahedron',
'menubar/add/octahedron': 'Octahedron',
'menubar/add/tetrahedron': 'Tetrahedron',
'menubar/add/text': 'Text',
'menubar/add/torus': 'Torus',
'menubar/add/tube': 'Tube',
'menubar/add/torusknot': 'TorusKnot',
Expand Down Expand Up @@ -208,6 +209,17 @@ function Strings( config ) {
'sidebar/geometry/sphere_geometry/thetastart': 'Theta start',
'sidebar/geometry/sphere_geometry/thetalength': 'Theta length',

'sidebar/geometry/text_geometry/text': 'Text',
'sidebar/geometry/text_geometry/size': 'Font size',
'sidebar/geometry/text_geometry/depth': 'Extrude depth',
Fixed Show fixed Hide fixed
'sidebar/geometry/text_geometry/scale': 'Scale',
'sidebar/geometry/text_geometry/curveseg': 'Curve segments',
'sidebar/geometry/text_geometry/bevelenabled': 'Bevel enabled',
'sidebar/geometry/text_geometry/bevelthickness': 'Bevel thickness',
'sidebar/geometry/text_geometry/bevelsize': 'Bevel size',
'sidebar/geometry/text_geometry/bevelOffset': 'Bevel offset',
'sidebar/geometry/text_geometry/bevelseg': 'Bevel segments',

'sidebar/geometry/torus_geometry/radius': 'Radius',
'sidebar/geometry/torus_geometry/tube': 'Tube',
'sidebar/geometry/torus_geometry/radialsegments': 'Radial segments',
Expand Down Expand Up @@ -1059,7 +1071,7 @@ function Strings( config ) {

getKey: function ( key ) {

return values[ language ][ key ] || '???';
return values[ language ][ key ] || `???${key.split('/').pop().substring(0, 8)}`;

}

Expand Down
38 changes: 37 additions & 1 deletion examples/jsm/geometries/TextGeometry.js
Expand Up @@ -15,6 +15,7 @@
* }
*/

import { Font } from '../loaders/FontLoader.js';
import {
ExtrudeGeometry
} from 'three';
Expand All @@ -35,7 +36,15 @@ class TextGeometry extends ExtrudeGeometry {

// translate parameters to ExtrudeGeometry API

parameters.depth = parameters.height !== undefined ? parameters.height : 50;
if ( parameters.depth === undefined && parameters.height !== undefined ) {
Copy link
Collaborator

@Mugen87 Mugen87 Mar 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR does two things in one go. It adds support to the editor and renames the height parameter to depth.

Ideally, there is a single PR for each change since both changes are independent of each other.

Besides, if height is renamed, the example code (meaning webgl_geometry_text.html and others) should be updated in order to avoid deprecation warnings.

That said, I also favor depth since height was a confusing name for describing the thickness (or depth) of the text.

Would you be okay with moving the renaming change to a separate PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure I can do that. I'll wait on the confirmation of the loader.registerGeometry() api before making the changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually the change to extract is easier to do, so it's in #27949


console.warn( 'THREE.TextGeometry: .height is now depreciated. Please use .depth instead' ); // @deprecated, r163

}

parameters.depth = parameters.depth !== undefined ?
parameters.depth : parameters.height !== undefined ?
parameters.height : 50;

// defaults

Expand All @@ -45,12 +54,39 @@ class TextGeometry extends ExtrudeGeometry {

super( shapes, parameters );

// for conversion of font to object units (ie. px -> m)

const scale = parameters.scale;

if ( scale !== undefined ) {

this.computeBoundingBox();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the bounding box computed at this point? It will be automatically recomputed when calling BufferGeometry.scale()?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW: Is the introduction of a scale parameter really necessary? Couldn't apps just use Object3D.scale instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the reason to do it here is getting a scaled down (0.01) geometry mostly for connivence to be used in Editor.

the object.scale could be used, but I wasn't sure if it was a good idea to insert a TextGeometry at a custom object scale, hence having it in the geometry seems like a more convenient way.

Copy link
Collaborator

@Mugen87 Mugen87 Apr 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to keep the parameters of TextGeometry to a minimum, it would be better to use Object3D.scale and not introduce an additional way for transforming geometry, imo.

this.scale( scale, scale, scale );

}

}

this.type = 'TextGeometry';

}

toJSON() {

const data = super.toJSON();
return data;

}

static fromJSON( data ) {

const options = data.options;

options.font = new Font( options.font.data );
return new TextGeometry( options.text, options );

}

}


Expand Down
12 changes: 10 additions & 2 deletions src/loaders/ObjectLoader.js
Expand Up @@ -69,6 +69,8 @@ class ObjectLoader extends Loader {

super( manager );

this.Geometries = Object.assign( {}, Geometries );

}

load( url, onLoad, onProgress, onError ) {
Expand Down Expand Up @@ -261,6 +263,12 @@ class ObjectLoader extends Loader {

}

registerGeometry( type, klass ) {
Copy link
Collaborator

@Mugen87 Mugen87 Mar 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrdoob Are you okay with this kind of new interface?

This approach is in general interesting since it could be used in context of other classes like materials as well. Meaning custom/addon materials implement a static fromJSON() (factory) method that ObjectLoader/MaterialLoader can use to create an instance. The internal material lib can be enhanced via:

loader.registerMaterial( 'MeshGouraudMaterial', MeshGouraudMaterial );

Related #21265, #11266.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's interesting indeed...

I'd rename this.Geometries to this.geometryTypes though.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated ObjectLoader manually but there still seems to be a diff in build/three.cjs.

Apart from that, the PR looks good to me!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated a merge from mrdoob/dev so there should be no conflicts now


this.Geometries[ type ] = klass;

}

parseGeometries( json, shapes ) {

const geometries = {};
Expand All @@ -284,9 +292,9 @@ class ObjectLoader extends Loader {

default:

if ( data.type in Geometries ) {
if ( data.type in this.Geometries ) {

geometry = Geometries[ data.type ].fromJSON( data, shapes );
geometry = this.Geometries[ data.type ].fromJSON( data, shapes );

} else {

Expand Down