|
@@ -0,0 +1,474 @@
|
|
|
+/**
|
|
|
+ * @author zz85 / http://www.lab4games.net/zz85/blog
|
|
|
+ * @author alteredq / http://alteredqualia.com/
|
|
|
+ *
|
|
|
+ * For Text operations in three.js (See TextGeometry)
|
|
|
+ *
|
|
|
+ * It uses techniques used in:
|
|
|
+ *
|
|
|
+ * typeface.js and canvastext
|
|
|
+ * For converting fonts and rendering with javascript
|
|
|
+ * http://typeface.neocracy.org
|
|
|
+ *
|
|
|
+ * Triangulation ported from AS3
|
|
|
+ * Simple Polygon Triangulation
|
|
|
+ * http://actionsnippet.com/?p=1462
|
|
|
+ *
|
|
|
+ * A Method to triangulate shapes with holes
|
|
|
+ * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+THREE.FontUtils = {
|
|
|
+
|
|
|
+ faces : {},
|
|
|
+
|
|
|
+ // Just for now. face[weight][style]
|
|
|
+
|
|
|
+ face : "helvetiker",
|
|
|
+ weight: "normal",
|
|
|
+ style : "normal",
|
|
|
+ size : 150,
|
|
|
+ divisions : 10,
|
|
|
+
|
|
|
+ getFace : function() {
|
|
|
+
|
|
|
+ return this.faces[ this.face ][ this.weight ][ this.style ];
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ loadFace : function( data ) {
|
|
|
+
|
|
|
+ var family = data.familyName.toLowerCase();
|
|
|
+
|
|
|
+ var ThreeFont = this;
|
|
|
+
|
|
|
+ ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {};
|
|
|
+
|
|
|
+ ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {};
|
|
|
+ ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
|
|
|
+
|
|
|
+ var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
|
|
|
+
|
|
|
+ return data;
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ drawText : function( text ) {
|
|
|
+
|
|
|
+ var characterPts = [], allPts = [];
|
|
|
+
|
|
|
+ // RenderText
|
|
|
+
|
|
|
+ var i, p,
|
|
|
+ face = this.getFace(),
|
|
|
+ scale = this.size / face.resolution,
|
|
|
+ offset = 0,
|
|
|
+ chars = String( text ).split( '' ),
|
|
|
+ length = chars.length;
|
|
|
+
|
|
|
+ var fontPaths = [];
|
|
|
+
|
|
|
+ for ( i = 0; i < length; i ++ ) {
|
|
|
+
|
|
|
+ var path = new THREE.Path();
|
|
|
+
|
|
|
+ var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path );
|
|
|
+ offset += ret.offset;
|
|
|
+
|
|
|
+ fontPaths.push( ret.path );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // get the width
|
|
|
+
|
|
|
+ var width = offset / 2;
|
|
|
+ //
|
|
|
+ // for ( p = 0; p < allPts.length; p++ ) {
|
|
|
+ //
|
|
|
+ // allPts[ p ].x -= width;
|
|
|
+ //
|
|
|
+ // }
|
|
|
+
|
|
|
+ //var extract = this.extractPoints( allPts, characterPts );
|
|
|
+ //extract.contour = allPts;
|
|
|
+
|
|
|
+ //extract.paths = fontPaths;
|
|
|
+ //extract.offset = width;
|
|
|
+
|
|
|
+ return { paths : fontPaths, offset : width };
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ extractGlyphPoints : function( c, face, scale, offset, path ) {
|
|
|
+
|
|
|
+ var pts = [];
|
|
|
+
|
|
|
+ var i, i2, divisions,
|
|
|
+ outline, action, length,
|
|
|
+ scaleX, scaleY,
|
|
|
+ x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2,
|
|
|
+ laste,
|
|
|
+ glyph = face.glyphs[ c ] || face.glyphs[ '?' ];
|
|
|
+
|
|
|
+ if ( !glyph ) return;
|
|
|
+
|
|
|
+ if ( glyph.o ) {
|
|
|
+
|
|
|
+ outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
|
|
|
+ length = outline.length;
|
|
|
+
|
|
|
+ scaleX = scale;
|
|
|
+ scaleY = scale;
|
|
|
+
|
|
|
+ for ( i = 0; i < length; ) {
|
|
|
+
|
|
|
+ action = outline[ i ++ ];
|
|
|
+
|
|
|
+ //console.log( action );
|
|
|
+
|
|
|
+ switch( action ) {
|
|
|
+
|
|
|
+ case 'm':
|
|
|
+
|
|
|
+ // Move To
|
|
|
+
|
|
|
+ x = outline[ i++ ] * scaleX + offset;
|
|
|
+ y = outline[ i++ ] * scaleY;
|
|
|
+
|
|
|
+ path.moveTo( x, y );
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'l':
|
|
|
+
|
|
|
+ // Line To
|
|
|
+
|
|
|
+ x = outline[ i++ ] * scaleX + offset;
|
|
|
+ y = outline[ i++ ] * scaleY;
|
|
|
+ path.lineTo(x,y);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'q':
|
|
|
+
|
|
|
+ // QuadraticCurveTo
|
|
|
+
|
|
|
+ cpx = outline[ i++ ] * scaleX + offset;
|
|
|
+ cpy = outline[ i++ ] * scaleY;
|
|
|
+ cpx1 = outline[ i++ ] * scaleX + offset;
|
|
|
+ cpy1 = outline[ i++ ] * scaleY;
|
|
|
+
|
|
|
+ path.quadraticCurveTo(cpx1, cpy1, cpx, cpy);
|
|
|
+
|
|
|
+ laste = pts[ pts.length - 1 ];
|
|
|
+
|
|
|
+ if ( laste ) {
|
|
|
+
|
|
|
+ cpx0 = laste.x;
|
|
|
+ cpy0 = laste.y;
|
|
|
+
|
|
|
+ for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
|
|
|
+
|
|
|
+ var t = i2 / divisions;
|
|
|
+ var tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
|
|
|
+ var ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy );
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'b':
|
|
|
+
|
|
|
+ // Cubic Bezier Curve
|
|
|
+
|
|
|
+ cpx = outline[ i++ ] * scaleX + offset;
|
|
|
+ cpy = outline[ i++ ] * scaleY;
|
|
|
+ cpx1 = outline[ i++ ] * scaleX + offset;
|
|
|
+ cpy1 = outline[ i++ ] * -scaleY;
|
|
|
+ cpx2 = outline[ i++ ] * scaleX + offset;
|
|
|
+ cpy2 = outline[ i++ ] * -scaleY;
|
|
|
+
|
|
|
+ path.bezierCurveTo( cpx, cpy, cpx1, cpy1, cpx2, cpy2 );
|
|
|
+
|
|
|
+ laste = pts[ pts.length - 1 ];
|
|
|
+
|
|
|
+ if ( laste ) {
|
|
|
+
|
|
|
+ cpx0 = laste.x;
|
|
|
+ cpy0 = laste.y;
|
|
|
+
|
|
|
+ for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) {
|
|
|
+
|
|
|
+ var t = i2 / divisions;
|
|
|
+ var tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
|
|
|
+ var ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ return { offset: glyph.ha*scale, path:path};
|
|
|
+ }
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+THREE.FontUtils.generateShapes = function( text, parameters ) {
|
|
|
+
|
|
|
+ // Parameters
|
|
|
+
|
|
|
+ parameters = parameters || {};
|
|
|
+
|
|
|
+ var size = parameters.size !== undefined ? parameters.size : 100;
|
|
|
+ var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments: 4;
|
|
|
+
|
|
|
+ var font = parameters.font !== undefined ? parameters.font : "helvetiker";
|
|
|
+ var weight = parameters.weight !== undefined ? parameters.weight : "normal";
|
|
|
+ var style = parameters.style !== undefined ? parameters.style : "normal";
|
|
|
+
|
|
|
+ THREE.FontUtils.size = size;
|
|
|
+ THREE.FontUtils.divisions = curveSegments;
|
|
|
+
|
|
|
+ THREE.FontUtils.face = font;
|
|
|
+ THREE.FontUtils.weight = weight;
|
|
|
+ THREE.FontUtils.style = style;
|
|
|
+
|
|
|
+ // Get a Font data json object
|
|
|
+
|
|
|
+ var data = THREE.FontUtils.drawText( text );
|
|
|
+
|
|
|
+ var paths = data.paths;
|
|
|
+ var shapes = [];
|
|
|
+
|
|
|
+ for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
|
|
|
+
|
|
|
+ Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return shapes;
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * This code is a quick port of code written in C++ which was submitted to
|
|
|
+ * flipcode.com by John W. Ratcliff // July 22, 2000
|
|
|
+ * See original code and more information here:
|
|
|
+ * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
|
|
|
+ *
|
|
|
+ * ported to actionscript by Zevan Rosser
|
|
|
+ * www.actionsnippet.com
|
|
|
+ *
|
|
|
+ * ported to javascript by Joshua Koo
|
|
|
+ * http://www.lab4games.net/zz85/blog
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+
|
|
|
+( function( namespace ) {
|
|
|
+
|
|
|
+ var EPSILON = 0.0000000001;
|
|
|
+
|
|
|
+ // takes in an contour array and returns
|
|
|
+
|
|
|
+ var process = function( contour, indices ) {
|
|
|
+
|
|
|
+ var n = contour.length;
|
|
|
+
|
|
|
+ if ( n < 3 ) return null;
|
|
|
+
|
|
|
+ var result = [],
|
|
|
+ verts = [],
|
|
|
+ vertIndices = [];
|
|
|
+
|
|
|
+ /* we want a counter-clockwise polygon in verts */
|
|
|
+
|
|
|
+ var u, v, w;
|
|
|
+
|
|
|
+ if ( area( contour ) > 0.0 ) {
|
|
|
+
|
|
|
+ for ( v = 0; v < n; v++ ) verts[ v ] = v;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ for ( v = 0; v < n; v++ ) verts[ v ] = ( n - 1 ) - v;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ var nv = n;
|
|
|
+
|
|
|
+ /* remove nv - 2 vertices, creating 1 triangle every time */
|
|
|
+
|
|
|
+ var count = 2 * nv; /* error detection */
|
|
|
+
|
|
|
+ for( v = nv - 1; nv > 2; ) {
|
|
|
+
|
|
|
+ /* if we loop, it is probably a non-simple polygon */
|
|
|
+
|
|
|
+ if ( ( count-- ) <= 0 ) {
|
|
|
+
|
|
|
+ //** Triangulate: ERROR - probable bad polygon!
|
|
|
+
|
|
|
+ //throw ( "Warning, unable to triangulate polygon!" );
|
|
|
+ //return null;
|
|
|
+ // Sometimes warning is fine, especially polygons are triangulated in reverse.
|
|
|
+ console.log( "Warning, unable to triangulate polygon!" );
|
|
|
+
|
|
|
+ if ( indices ) return vertIndices;
|
|
|
+ return result;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /* three consecutive vertices in current polygon, <u,v,w> */
|
|
|
+
|
|
|
+ u = v; if ( nv <= u ) u = 0; /* previous */
|
|
|
+ v = u + 1; if ( nv <= v ) v = 0; /* new v */
|
|
|
+ w = v + 1; if ( nv <= w ) w = 0; /* next */
|
|
|
+
|
|
|
+ if ( snip( contour, u, v, w, nv, verts ) ) {
|
|
|
+
|
|
|
+ var a, b, c, s, t;
|
|
|
+
|
|
|
+ /* true names of the vertices */
|
|
|
+
|
|
|
+ a = verts[ u ];
|
|
|
+ b = verts[ v ];
|
|
|
+ c = verts[ w ];
|
|
|
+
|
|
|
+ /* output Triangle */
|
|
|
+
|
|
|
+ /*
|
|
|
+ result.push( contour[ a ] );
|
|
|
+ result.push( contour[ b ] );
|
|
|
+ result.push( contour[ c ] );
|
|
|
+ */
|
|
|
+ result.push( [ contour[ a ],
|
|
|
+ contour[ b ],
|
|
|
+ contour[ c ] ] );
|
|
|
+
|
|
|
+
|
|
|
+ vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] );
|
|
|
+
|
|
|
+ /* remove v from the remaining polygon */
|
|
|
+
|
|
|
+ for( s = v, t = v + 1; t < nv; s++, t++ ) {
|
|
|
+
|
|
|
+ verts[ s ] = verts[ t ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ nv--;
|
|
|
+
|
|
|
+ /* reset error detection counter */
|
|
|
+
|
|
|
+ count = 2 * nv;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( indices ) return vertIndices;
|
|
|
+ return result;
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ // calculate area of the contour polygon
|
|
|
+
|
|
|
+ var area = function ( contour ) {
|
|
|
+
|
|
|
+ var n = contour.length;
|
|
|
+ var a = 0.0;
|
|
|
+
|
|
|
+ for( var p = n - 1, q = 0; q < n; p = q++ ) {
|
|
|
+
|
|
|
+ a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return a * 0.5;
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ // see if p is inside triangle abc
|
|
|
+
|
|
|
+ var insideTriangle = function( ax, ay,
|
|
|
+ bx, by,
|
|
|
+ cx, cy,
|
|
|
+ px, py ) {
|
|
|
+
|
|
|
+ var aX, aY, bX, bY;
|
|
|
+ var cX, cY, apx, apy;
|
|
|
+ var bpx, bpy, cpx, cpy;
|
|
|
+ var cCROSSap, bCROSScp, aCROSSbp;
|
|
|
+
|
|
|
+ aX = cx - bx; aY = cy - by;
|
|
|
+ bX = ax - cx; bY = ay - cy;
|
|
|
+ cX = bx - ax; cY = by - ay;
|
|
|
+ apx= px -ax; apy= py - ay;
|
|
|
+ bpx= px - bx; bpy= py - by;
|
|
|
+ cpx= px - cx; cpy= py - cy;
|
|
|
+
|
|
|
+ aCROSSbp = aX*bpy - aY*bpx;
|
|
|
+ cCROSSap = cX*apy - cY*apx;
|
|
|
+ bCROSScp = bX*cpy - bY*cpx;
|
|
|
+
|
|
|
+ return ( (aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0) );
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+ var snip = function ( contour, u, v, w, n, verts ) {
|
|
|
+
|
|
|
+ var p;
|
|
|
+ var ax, ay, bx, by;
|
|
|
+ var cx, cy, px, py;
|
|
|
+
|
|
|
+ ax = contour[ verts[ u ] ].x;
|
|
|
+ ay = contour[ verts[ u ] ].y;
|
|
|
+
|
|
|
+ bx = contour[ verts[ v ] ].x;
|
|
|
+ by = contour[ verts[ v ] ].y;
|
|
|
+
|
|
|
+ cx = contour[ verts[ w ] ].x;
|
|
|
+ cy = contour[ verts[ w ] ].y;
|
|
|
+
|
|
|
+ if ( EPSILON > (((bx-ax)*(cy-ay)) - ((by-ay)*(cx-ax))) ) return false;
|
|
|
+
|
|
|
+ for ( p = 0; p < n; p++ ) {
|
|
|
+
|
|
|
+ if( (p == u) || (p == v) || (p == w) ) continue;
|
|
|
+
|
|
|
+ px = contour[ verts[ p ] ].x
|
|
|
+ py = contour[ verts[ p ] ].y
|
|
|
+
|
|
|
+ if ( insideTriangle( ax, ay, bx, by, cx, cy, px, py ) ) return false;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+ namespace.Triangulate = process;
|
|
|
+ namespace.Triangulate.area = area;
|
|
|
+
|
|
|
+ return namespace;
|
|
|
+
|
|
|
+})(THREE.FontUtils);
|
|
|
+
|
|
|
+// To use the typeface.js face files, hook up the API
|
|
|
+self._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace };
|