TextGeometry class
class TextGeometry extends ExtrudeGeometry { factory TextGeometry( text, [ height = 50, // height <=> amount, bend = false, // ExtrudeGeometry parameters bevelThickness = 10, bevelSize = 8, bevelSegments = 3, bevelEnabled = false, curveSegments = 12, steps = 1, bendPath, extrudePath, material, extrudeMaterial, // FontUtils.generateShapes parameters size = 100, font = "helvetiker", weight = "normal", style = "normal" ] ) { var textShapes = FontUtils.generateShapes( text, size, curveSegments, font, weight, style); var amount = height; var bendPath = null; if (bend) { var b = textShapes[ textShapes.length - 1 ].getBoundingBox(); var max = b.maxX; bendPath = new QuadraticBezierCurve( new Vector2.zero(), new Vector2( max / 2, 120.0 ), new Vector2( max, 0.0 ) ); } return new TextGeometry._internal( textShapes, amount, bevelThickness, bevelSize, bevelSegments, bevelEnabled, curveSegments, steps, bendPath, extrudePath, material, extrudeMaterial); } TextGeometry._internal( shapes, amount, bevelThickness, bevelSize, bevelSegments, bevelEnabled, curveSegments, steps, bendPath, extrudePath, material, extrudeMaterial) : super( shapes, amount: amount, bevelThickness: bevelThickness, bevelSize: bevelSize, bevelSegments: bevelSegments, bevelEnabled: bevelEnabled, curveSegments: curveSegments, steps: steps, bendPath: bendPath, extrudePath: extrudePath, material: material, extrudeMaterial: extrudeMaterial); }
Extends
Geometry > ExtrudeGeometry > TextGeometry
Constructors
factory TextGeometry(text, [height = 50, bend = false, bevelThickness = 10, bevelSize = 8, bevelSegments = 3, bevelEnabled = false, curveSegments = 12, steps = 1, bendPath, extrudePath, material, extrudeMaterial, size = 100, font = "helvetiker", weight = "normal", style = "normal"]) #
Creates a new Object instance.
Object instances have no meaningful state, and are only useful through their identity. An Object instance is equal to itself only.
docs inherited from Object
factory TextGeometry( text, [ height = 50, // height <=> amount, bend = false, // ExtrudeGeometry parameters bevelThickness = 10, bevelSize = 8, bevelSegments = 3, bevelEnabled = false, curveSegments = 12, steps = 1, bendPath, extrudePath, material, extrudeMaterial, // FontUtils.generateShapes parameters size = 100, font = "helvetiker", weight = "normal", style = "normal" ] ) { var textShapes = FontUtils.generateShapes( text, size, curveSegments, font, weight, style); var amount = height; var bendPath = null; if (bend) { var b = textShapes[ textShapes.length - 1 ].getBoundingBox(); var max = b.maxX; bendPath = new QuadraticBezierCurve( new Vector2.zero(), new Vector2( max / 2, 120.0 ), new Vector2( max, 0.0 ) ); } return new TextGeometry._internal( textShapes, amount, bevelThickness, bevelSize, bevelSegments, bevelEnabled, curveSegments, steps, bendPath, extrudePath, material, extrudeMaterial); }
Properties
Operators
Methods
dynamic addShape(Shape shape, amount, bevelThickness, bevelSize, bevelSegments, bevelEnabled, curveSegments, steps, bendPath, extrudePath, TubeGeometry frames, material, extrudeMaterial, {ExtrudeGeometryWorldUVGenerator UVGenerator}) #
inherited from ExtrudeGeometry
addShape( Shape shape, amount, bevelThickness, bevelSize, bevelSegments, bevelEnabled, curveSegments, steps, bendPath, extrudePath, TubeGeometry frames, material, extrudeMaterial, {ExtrudeGeometryWorldUVGenerator UVGenerator }) { var extrudePts, extrudeByPath = false; //shapebb = shape.getBoundingBox(); // set UV generator var uvgen = (UVGenerator!= null) ? UVGenerator : new ExtrudeGeometryWorldUVGenerator(); TubeGeometry splineTube; Vector3 binormal, normal, position2; var nSteps = (steps is List)? steps.length : steps; if ( extrudePath != null ) { if(steps is List){ List divisions = [0]; divisions.addAll(steps); extrudePts = extrudePath.getUPoints(divisions); } else{ extrudePts = extrudePath.getSpacedPoints( steps ); } extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // Reuse TNB from TubeGeomtry for now. // TODO1 - have a .isClosed in spline? splineTube = (frames != null) ? frames : new TubeGeometry.FrenetFrames(extrudePath, steps, false); // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); binormal = new Vector3.zero(); normal = new Vector3.zero(); position2 = new Vector3.zero(); } // Safeguards if bevels are not enabled if ( ! bevelEnabled ) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; } // Variables initalization List<Vector2> ahole; var h, hl; // looping of holes var bevelPoints = []; var shapesOffset = this.vertices.length; if ( bendPath != null ) { shape.addWrapPath( bendPath ); } var shapePoints = shape.extractPoints(); List vertices = shapePoints["shape"]; List<List<Vector2>> holes = shapePoints["holes"]; var reverse = !ShapeUtils.isClockWise( vertices ) ; if ( reverse ) { vertices = _reverse(vertices); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for ( h = 0; h < holes.length; h ++ ) { ahole = holes[ h ]; if ( ShapeUtils.isClockWise( ahole ) ) { holes[ h ] = _reverse(ahole); } } reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! } var faces = ShapeUtils.triangulateShape ( vertices, holes ); //var faces = THREE.Shape.Utils.triangulate2( vertices, holes ); // Would it be better to move points after triangulation? // shapePoints = shape.extractAllPointsWithBend( curveSegments, bendPath ); // vertices = shapePoints.shape; // holes = shapePoints.holes; //console.log(faces); //// /// Handle Vertices //// var contour = vertices; // vertices has all points but contour has only points of circumference for ( h = 0; h < holes.length; h ++ ) { ahole = holes[ h ]; vertices = new List.from(vertices); vertices.addAll( ahole ); } var b, bs, t, z, vert, vlen = vertices.length, face, flen = faces.length, cont, clen = contour.length; //------ // Find directions for point movement // var contourMovements = new List(contour.length); num i = 0, il = contour.length, j = il - 1, k = i + 1; for ( i = 0; i < il; i ++ ) { if ( j == il ) j = 0; if ( k == il ) k = 0; // (j)---(i)---(k) // console.log('i,j,k', i, j , k) var pt_i = contour[ i ]; var pt_j = contour[ j ]; var pt_k = contour[ k ]; contourMovements[ i ]= _getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); j ++; k ++; } List holesMovements = [], oneHoleMovements, verticesMovements = new List.from(contourMovements); for ( h = 0; h < holes.length; h ++ ) { ahole = holes[ h ]; oneHoleMovements = new List(ahole.length); i = 0; il = ahole.length; j = il - 1; k = i + 1; for ( i = 0; i < il; i++) { if ( j == il ) j = 0; if ( k == il ) k = 0; // (j)---(i)---(k) oneHoleMovements[ i ]= _getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); j++; k++; } holesMovements.add( oneHoleMovements ); verticesMovements.addAll( oneHoleMovements ); } // Loop bevelSegments, 1 for the front, 1 for the back for ( b = 0; b < bevelSegments; b ++ ) { //for ( b = bevelSegments; b > 0; b -- ) { t = b / bevelSegments; z = bevelThickness * ( 1 - t ); //z = bevelThickness * t; bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved //bs = bevelSize * t ; // linear // contract shape for ( i = 0; i < contour.length; i ++ ) { vert = _scalePt2( contour[ i ], contourMovements[ i ], bs ); //vert = scalePt( contour[ i ], contourCentroid, bs, false ); _v( vert.x, vert.y, - z ); } // expand holes for ( h = 0; h < holes.length; h++ ) { ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( i = 0; i < ahole.length; i++ ) { vert = _scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); //vert = scalePt( ahole[ i ], holesCentroids[ h ], bs, true ); _v( vert.x, vert.y, -z ); } } } bs = bevelSize; // Back facing vertices for ( i = 0; i < vlen; i ++ ) { vert = bevelEnabled ? _scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( !extrudeByPath ) { _v( vert.x, vert.y, 0.0 ); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.setFrom(splineTube.normals[0]).scale(vert.x); binormal.setFrom(splineTube.binormals[0]).scale(vert.y); position2.setFrom(extrudePts[0]).add(normal).add(binormal); _v(position2.x, position2.y, position2.z); } } // Add stepped vertices... // Including front facing vertices var s; for ( s = 1; s <= nSteps; s ++ ) { for ( i = 0; i < vlen; i ++ ) { vert = bevelEnabled ? _scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( !extrudeByPath ) { _v( vert.x, vert.y, amount / nSteps * s ); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.setFrom(splineTube.normals[s]).scale(vert.x); binormal.setFrom(splineTube.binormals[s]).scale(vert.y); position2.setFrom(extrudePts[s]).add(normal).add(binormal); _v(position2.x, position2.y, position2.z ); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for ( b = bevelSegments - 1; b >= 0; b -- ) { t = b / bevelSegments; z = bevelThickness * ( 1 - t ); //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) ); bs = bevelSize * Math.sin ( t * Math.PI/2 ) ; // contract shape for ( i = 0; i < contour.length; i ++ ) { vert = _scalePt2( contour[ i ], contourMovements[ i ], bs ); _v( vert.x, vert.y, amount + z ); } // expand holes for ( h = 0; h < holes.length; h ++ ) { ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( i = 0; i < ahole.length; i++ ) { vert = _scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); if ( !extrudeByPath ) { _v( vert.x, vert.y, amount + z ); } else { _v( vert.x, vert.y + extrudePts[ nSteps - 1 ].y, extrudePts[ nSteps - 1 ].x + z ); } } } } //// /// Handle Faces //// ///// Internal functions f3( a, b, c, isBottom ) { a += shapesOffset; b += shapesOffset; c += shapesOffset; // normal, color, material this.faces.add( new Face3( a, b, c, null, null, material ) ); var uvs = isBottom ? uvgen.generateBottomUV( this, shape, null, a, b, c) : uvgen.generateTopUV( this, shape, null, a, b, c); this.faceVertexUvs[ 0 ].add(uvs); } f4( a, b, c, d, wallContour, stepIndex, stepsLength ) { a += shapesOffset; b += shapesOffset; c += shapesOffset; d += shapesOffset; this.faces.add( new Face4( a, b, c, d, null, null, extrudeMaterial ) ); var uvs = uvgen.generateSideWallUV( this, shape, wallContour, null, a, b, c, d, stepIndex, stepsLength); this.faceVertexUvs[ 0 ].add(uvs); } // Top and bottom faces //buildLidFaces() { if ( bevelEnabled ) { var layer = 0 ; // steps + 1 var offset = vlen * layer; // Bottom faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset, true ); } layer = nSteps + bevelSegments * 2; offset = vlen * layer; // Top faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset, false ); } } else { // Bottom faces for ( i = 0; i < flen; i++ ) { face = faces[ i ]; f3( face[ 2 ], face[ 1 ], face[ 0 ], true ); } // Top faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 0 ] + vlen * nSteps, face[ 1 ] + vlen * nSteps, face[ 2 ] + vlen * nSteps, false ); } } sidewalls( contour, layeroffset ) { var i, j, k; i = contour.length; while ( --i >= 0 ) { j = i; k = i - 1; if ( k < 0 ) k = contour.length - 1; //console.log('b', i,j, i-1, k,vertices.length); var s = 0, sl = nSteps + bevelSegments * 2; for ( s = 0; s < sl; s ++ ) { var slen1 = vlen * s; var slen2 = vlen * ( s + 1 ); var a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4( a, b, c, d, contour, s, sl ); } } } // Sides faces //buildSideFaces() { // Create faces for the z-sides of the shape var layeroffset = 0; sidewalls( contour, layeroffset ); layeroffset += contour.length; for ( h = 0; h < holes.length; h ++ ) { ahole = holes[ h ]; sidewalls( ahole, layeroffset ); //, true layeroffset += ahole.length; } }
dynamic addShapeList(shapes, amount, bevelThickness, bevelSize, bevelSegments, bevelEnabled, curveSegments, steps, bendPath, extrudePath, frames, material, extrudeMaterial) #
inherited from ExtrudeGeometry
addShapeList(shapes, amount, bevelThickness, bevelSize, bevelSegments,bevelEnabled, curveSegments,steps,bendPath,extrudePath,frames,material, extrudeMaterial) { var sl = shapes.length; for ( var s = 0; s < sl; s ++ ) { var shape = shapes[ s ]; addShape( shape, amount, bevelThickness, bevelSize, bevelSegments,bevelEnabled, curveSegments, steps, bendPath, extrudePath, frames, material, extrudeMaterial ); } }
void applyMatrix(Matrix4 matrix) #
inherited from Geometry
void applyMatrix( Matrix4 matrix ) { Matrix4 matrixRotation = new Matrix4.identity(); extractRotation( matrixRotation, matrix); vertices.forEach((vertex) => vertex.applyProjection(matrix)); faces.forEach((face) { face.normal.applyProjection(matrixRotation); face.vertexNormals.forEach((normal) => normal.applyProjection(matrixRotation)); face.centroid.applyProjection(matrix); }); }
void computeBoundingBox() #
inherited from Geometry
void computeBoundingBox() { if ( boundingBox == null ) { boundingBox = new BoundingBox( min: new Vector3.zero(), max: new Vector3.zero() ); } if ( vertices.length > 0 ) { Vector3 position, firstPosition = vertices[ 0 ]; boundingBox.min.setFrom( firstPosition ); boundingBox.max.setFrom( firstPosition ); Vector3 min = boundingBox.min, max = boundingBox.max; num vl = vertices.length; for ( int v = 1; v < vl; v ++ ) { position = vertices[ v ]; if ( position.x < min.x ) { min.x = position.x; } else if ( position.x > max.x ) { max.x = position.x; } if ( position.y < min.y ) { min.y = position.y; } else if ( position.y > max.y ) { max.y = position.y; } if ( position.z < min.z ) { min.z = position.z; } else if ( position.z > max.z ) { max.z = position.z; } } } }
void computeBoundingSphere() #
inherited from Geometry
void computeBoundingSphere() { num radiusSq; var maxRadiusSq = vertices.fold(0, (num curMaxRadiusSq, Vector3 vertex) { radiusSq = vertex.length2; return ( radiusSq > curMaxRadiusSq ) ? radiusSq : curMaxRadiusSq; }); boundingSphere = new BoundingSphere(radius: Math.sqrt(maxRadiusSq) ); }
void computeCentroids() #
inherited from Geometry
void computeCentroids() { faces.forEach((Face face) { face.centroid.setValues( 0.0, 0.0, 0.0 ); face.indices.forEach((idx) { face.centroid.add( vertices[ idx ] ); }); face.centroid /= face.size.toDouble(); }); }
void computeFaceNormals() #
inherited from Geometry
void computeFaceNormals() { faces.forEach((face) { var vA = vertices[ face.a ], vB = vertices[ face.b ], vC = vertices[ face.c ]; Vector3 cb = vC - vB; Vector3 ab = vA - vB; cb = cb.cross( ab ); cb.normalize(); face.normal = cb; }); }
void computeTangents() #
inherited from Geometry
void computeTangents() { // based on http://www.terathon.com/code/tangent.html // tangents go to vertices var f, fl, face; num i, il, vertexIndex, test, w; Vector3 vA, vB, vC; UV uvA, uvB, uvC; List uv; num x1, x2, y1, y2, z1, z2, s1, s2, t1, t2, r; Vector3 sdir = new Vector3.zero(), tdir = new Vector3.zero(), tmp = new Vector3.zero(), tmp2 = new Vector3.zero(), n = new Vector3.zero(), t; List<Vector3> tan1 = vertices.map((_) => new Vector3.zero()).toList(), tan2 = vertices.map((_) => new Vector3.zero()).toList(); var handleTriangle = ( context, a, b, c, ua, ub, uc ) { vA = context.vertices[ a ]; vB = context.vertices[ b ]; vC = context.vertices[ c ]; uvA = uv[ ua ]; uvB = uv[ ub ]; uvC = uv[ uc ]; x1 = vB.x - vA.x; x2 = vC.x - vA.x; y1 = vB.y - vA.y; y2 = vC.y - vA.y; z1 = vB.z - vA.z; z2 = vC.z - vA.z; s1 = uvB.u - uvA.u; s2 = uvC.u - uvA.u; t1 = uvB.v - uvA.v; t2 = uvC.v - uvA.v; r = 1.0 / ( s1 * t2 - s2 * t1 ); sdir.setValues( ( t2 * x1 - t1 * x2 ) * r, ( t2 * y1 - t1 * y2 ) * r, ( t2 * z1 - t1 * z2 ) * r ); tdir.setValues( ( s1 * x2 - s2 * x1 ) * r, ( s1 * y2 - s2 * y1 ) * r, ( s1 * z2 - s2 * z1 ) * r ); tan1[ a ].add( sdir ); tan1[ b ].add( sdir ); tan1[ c ].add( sdir ); tan2[ a ].add( tdir ); tan2[ b ].add( tdir ); tan2[ c ].add( tdir ); }; fl = this.faces.length; for ( f = 0; f < fl; f ++ ) { face = this.faces[ f ]; UV uv = faceVertexUvs[ 0 ][ f ]; // use UV layer 0 for tangents // TODO - Come up with a way to handle an arbitrary number of vertexes var triangles = []; if ( face.size == 3 ) { triangles.add([0, 1, 2]); } else if ( face.size == 4 ) { triangles.add([0, 1, 3]); triangles.add([1, 2, 3]); } triangles.forEach((t) { handleTriangle( this, face.indices[t[0]], face.indices[t[1]], face.indices[t[2]], t[0], t[1], t[2] ); }); } faces.forEach((face) { il = face.vertexNormals.length; for ( i = 0; i < il; i++ ) { n.setFrom( face.vertexNormals[ i ] ); vertexIndex = face.indices[i]; t = tan1[ vertexIndex ]; // Gram-Schmidt orthogonalize tmp.setFrom( t ); tmp.sub( n.scale( n.dot( t ) ) ).normalize(); // Calculate handedness tmp2 = face.vertexNormals[i].cross(t); test = tmp2.dot( tan2[ vertexIndex ] ); w = (test < 0.0) ? -1.0 : 1.0; face.vertexTangents[ i ] = new Vector4( tmp.x, tmp.y, tmp.z, w ); } }); hasTangents = true; }
void computeVertexNormals() #
inherited from Geometry
void computeVertexNormals() { List<Vector3> vertices; // create internal buffers for reuse when calling this method repeatedly // (otherwise memory allocation / deallocation every frame is big resource hog) if ( __tmpVertices == null ) { __tmpVertices = []; this.vertices.forEach((_) => __tmpVertices.add(new Vector3.zero())); vertices = __tmpVertices; faces.forEach((face) { face.vertexNormals = new List.generate(face.size, (_) => new Vector3.zero(), growable: false); }); } else { vertices = __tmpVertices; var vl = this.vertices.length; for ( var v = 0; v < vl; v ++ ) { vertices[ v ].setValues( 0.0, 0.0, 0.0 ); } } faces.forEach((Face face) { face.indices.forEach((idx) { vertices[ idx ].add( face.normal ); }); }); vertices.forEach((v) => v.normalize()); faces.forEach((Face face) { var i = 0; face.indices.forEach((idx) { face.vertexNormals[ i++ ].setFrom( vertices[ idx ] ); }); }); }
int mergeVertices() #
inherited from Geometry
int mergeVertices() { Map verticesMap = {}; // Hashmap for looking up vertice by position coordinates (and making sure they are unique) List<Vector3> unique = []; List<int> changes = []; String key; int precisionPoints = 4; // number of decimal points, eg. 4 for epsilon of 0.0001 num precision = Math.pow( 10, precisionPoints ); int i, il; var abcd = 'abcd', o, k, j, jl, u; Vector3 v; il = this.vertices.length; for( i = 0; i < il; i++) { v = this.vertices[i]; key = [ ( v.x * precision ).round().toStringAsFixed(0), ( v.y * precision ).round().toStringAsFixed(0), ( v.z * precision ).round().toStringAsFixed(0) ].join('_' ); if ( verticesMap[ key ] == null ) { verticesMap[ key ] = i; unique.add( v ); //TODO: pretty sure this is an acceptable change in syntax here: //changes[ i ] = unique.length - 1; changes.add( unique.length - 1); } else { //print('Duplicate vertex found. $i could be using ${verticesMap[key]}'); //print('changes len ${changes.length} add at i = $i'); //changes[ i ] = changes[ verticesMap[ key ] ]; changes.add( changes[ verticesMap[ key ] ] ); } } // Start to patch face indices faces.forEach((Face face) { for (var i = 0; i < face.size; i++) { face.indices[i] = changes[ face.indices[i] ]; /* TODO // check dups in (a, b, c, d) and convert to -> face3 var o = [ face.a, face.b, face.c, face.d ]; for ( var k = 3; k > 0; k -- ) { if ( o.indexOf( face[ abcd[ k ] ] ) != k ) { // console.log('faces', face.a, face.b, face.c, face.d, 'dup at', k); o.removeAt( k ); this.faces[ i ] = new THREE.Face3( o[0], o[1], o[2], face.normal, face.color, face.materialIndex ); for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { u = this.faceVertexUvs[ j ][ i ]; if ( u ) u.removeAt( k ); } this.faces[ i ].vertexColors = face.vertexColors; break; } }*/ } }); // Use unique set of vertices var diff = vertices.length - unique.length; vertices = unique; return diff; }