mirror of
https://github.com/Doodle3D/Doodle3D-Slicer.git
synced 2024-07-02 17:11:22 +02:00
469 lines
9.4 KiB
JavaScript
Executable File
469 lines
9.4 KiB
JavaScript
Executable File
/**
|
|
* @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 () {
|
|
|
|
try {
|
|
|
|
return this.faces[ this.face ][ this.weight ][ this.style ];
|
|
|
|
} catch (e) {
|
|
|
|
throw "The font " + this.face + " with " + this.weight + " weight and " + this.style + " style is missing."
|
|
|
|
};
|
|
|
|
},
|
|
|
|
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;
|
|
|
|
ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data;
|
|
|
|
return data;
|
|
|
|
},
|
|
|
|
drawText: function ( text ) {
|
|
|
|
// RenderText
|
|
|
|
var i,
|
|
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;
|
|
THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx );
|
|
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( cpx1, cpy1, cpx2, cpy2, 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;
|
|
THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx );
|
|
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.
|
|
THREE.warn( 'THREE.FontUtils: Warning, unable to triangulate polygon! in Triangulate.process()' );
|
|
|
|
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 ],
|
|
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;
|
|
|
|
};
|
|
|
|
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;
|
|
|
|
var aX, aY, bX, bY, cX, cY;
|
|
var apx, apy, 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;
|
|
|
|
for ( p = 0; p < n; p ++ ) {
|
|
|
|
px = contour[ verts[ p ] ].x
|
|
py = contour[ verts[ p ] ].y
|
|
|
|
if ( ( ( px === ax ) && ( py === ay ) ) ||
|
|
( ( px === bx ) && ( py === by ) ) ||
|
|
( ( px === cx ) && ( py === cy ) ) ) continue;
|
|
|
|
apx = px - ax; apy = py - ay;
|
|
bpx = px - bx; bpy = py - by;
|
|
cpx = px - cx; cpy = py - cy;
|
|
|
|
// see if p is inside triangle abc
|
|
|
|
aCROSSbp = aX * bpy - aY * bpx;
|
|
cCROSSap = cX * apy - cY * apx;
|
|
bCROSScp = bX * cpy - bY * cpx;
|
|
|
|
if ( ( aCROSSbp >= - EPSILON ) && ( bCROSScp >= - EPSILON ) && ( cCROSSap >= - EPSILON ) ) 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 };
|
|
THREE.typeface_js = self._typeface_js;
|