| |
| |
| |
| |
| |
|
|
| THREE.SVGLoader = function ( manager ) { |
|
|
| this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; |
|
|
| }; |
|
|
| THREE.SVGLoader.prototype = { |
|
|
| constructor: THREE.SVGLoader, |
|
|
| load: function ( url, onLoad, onProgress, onError ) { |
|
|
| var scope = this; |
|
|
| var loader = new THREE.FileLoader( scope.manager ); |
| loader.setPath( scope.path ); |
| loader.load( url, function ( text ) { |
|
|
| onLoad( scope.parse( text ) ); |
|
|
| }, onProgress, onError ); |
|
|
| }, |
|
|
| setPath: function ( value ) { |
|
|
| this.path = value; |
| return this; |
|
|
| }, |
|
|
| parse: function ( text ) { |
|
|
| function parseNode( node, style ) { |
|
|
| if ( node.nodeType !== 1 ) return; |
|
|
| var transform = getNodeTransform( node ); |
|
|
| var path = null; |
|
|
| switch ( node.nodeName ) { |
|
|
| case 'svg': |
| break; |
|
|
| case 'g': |
| style = parseStyle( node, style ); |
| break; |
|
|
| case 'path': |
| style = parseStyle( node, style ); |
| if ( node.hasAttribute( 'd' ) ) path = parsePathNode( node, style ); |
| break; |
|
|
| case 'rect': |
| style = parseStyle( node, style ); |
| path = parseRectNode( node, style ); |
| break; |
|
|
| case 'polygon': |
| style = parseStyle( node, style ); |
| path = parsePolygonNode( node, style ); |
| break; |
|
|
| case 'polyline': |
| style = parseStyle( node, style ); |
| path = parsePolylineNode( node, style ); |
| break; |
|
|
| case 'circle': |
| style = parseStyle( node, style ); |
| path = parseCircleNode( node, style ); |
| break; |
|
|
| case 'ellipse': |
| style = parseStyle( node, style ); |
| path = parseEllipseNode( node, style ); |
| break; |
|
|
| case 'line': |
| style = parseStyle( node, style ); |
| path = parseLineNode( node, style ); |
| break; |
|
|
| default: |
| console.log( node ); |
|
|
| } |
|
|
| if ( path ) { |
|
|
| if ( style.fill !== undefined && style.fill !== 'none' ) { |
|
|
| path.color.setStyle( style.fill ); |
|
|
| } |
|
|
| transformPath( path, currentTransform ); |
|
|
| paths.push( path ); |
|
|
| path.userData = { node: node, style: style }; |
|
|
| } |
|
|
| var nodes = node.childNodes; |
|
|
| for ( var i = 0; i < nodes.length; i ++ ) { |
|
|
| parseNode( nodes[ i ], style ); |
|
|
| } |
|
|
| if ( transform ) { |
|
|
| currentTransform.copy( transformStack.pop() ); |
|
|
| } |
|
|
| } |
|
|
| function parsePathNode( node, style ) { |
|
|
| var path = new THREE.ShapePath(); |
|
|
| var point = new THREE.Vector2(); |
| var control = new THREE.Vector2(); |
|
|
| var firstPoint = new THREE.Vector2(); |
| var isFirstPoint = true; |
| var doSetFirstPoint = false; |
|
|
| var d = node.getAttribute( 'd' ); |
|
|
| |
|
|
| var commands = d.match( /[a-df-z][^a-df-z]*/ig ); |
|
|
| for ( var i = 0, l = commands.length; i < l; i ++ ) { |
|
|
| var command = commands[ i ]; |
|
|
| var type = command.charAt( 0 ); |
| var data = command.substr( 1 ).trim(); |
|
|
| if ( isFirstPoint === true ) { |
| doSetFirstPoint = true; |
| isFirstPoint = false; |
| } |
|
|
| switch ( type ) { |
|
|
| case 'M': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) { |
| point.x = numbers[ j + 0 ]; |
| point.y = numbers[ j + 1 ]; |
| control.x = point.x; |
| control.y = point.y; |
| if ( j === 0 ) { |
| path.moveTo( point.x, point.y ); |
| } else { |
| path.lineTo( point.x, point.y ); |
| } |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| case 'H': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j ++ ) { |
| point.x = numbers[ j ]; |
| control.x = point.x; |
| control.y = point.y; |
| path.lineTo( point.x, point.y ); |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| case 'V': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j ++ ) { |
| point.y = numbers[ j ]; |
| control.x = point.x; |
| control.y = point.y; |
| path.lineTo( point.x, point.y ); |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| case 'L': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) { |
| point.x = numbers[ j + 0 ]; |
| point.y = numbers[ j + 1 ]; |
| control.x = point.x; |
| control.y = point.y; |
| path.lineTo( point.x, point.y ); |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| case 'C': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j += 6 ) { |
| path.bezierCurveTo( |
| numbers[ j + 0 ], |
| numbers[ j + 1 ], |
| numbers[ j + 2 ], |
| numbers[ j + 3 ], |
| numbers[ j + 4 ], |
| numbers[ j + 5 ] |
| ); |
| control.x = numbers[ j + 2 ]; |
| control.y = numbers[ j + 3 ]; |
| point.x = numbers[ j + 4 ]; |
| point.y = numbers[ j + 5 ]; |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| case 'S': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j += 4 ) { |
| path.bezierCurveTo( |
| getReflection( point.x, control.x ), |
| getReflection( point.y, control.y ), |
| numbers[ j + 0 ], |
| numbers[ j + 1 ], |
| numbers[ j + 2 ], |
| numbers[ j + 3 ] |
| ); |
| control.x = numbers[ j + 0 ]; |
| control.y = numbers[ j + 1 ]; |
| point.x = numbers[ j + 2 ]; |
| point.y = numbers[ j + 3 ]; |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| case 'Q': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j += 4 ) { |
| path.quadraticCurveTo( |
| numbers[ j + 0 ], |
| numbers[ j + 1 ], |
| numbers[ j + 2 ], |
| numbers[ j + 3 ] |
| ); |
| control.x = numbers[ j + 0 ]; |
| control.y = numbers[ j + 1 ]; |
| point.x = numbers[ j + 2 ]; |
| point.y = numbers[ j + 3 ]; |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| case 'T': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) { |
| var rx = getReflection( point.x, control.x ); |
| var ry = getReflection( point.y, control.y ); |
| path.quadraticCurveTo( |
| rx, |
| ry, |
| numbers[ j + 0 ], |
| numbers[ j + 1 ] |
| ); |
| control.x = rx; |
| control.y = ry; |
| point.x = numbers[ j + 0 ]; |
| point.y = numbers[ j + 1 ]; |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| case 'A': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j += 7 ) { |
| var start = point.clone(); |
| point.x = numbers[ j + 5 ]; |
| point.y = numbers[ j + 6 ]; |
| control.x = point.x; |
| control.y = point.y; |
| parseArcCommand( |
| path, numbers[ j ], numbers[ j + 1 ], numbers[ j + 2 ], numbers[ j + 3 ], numbers[ j + 4 ], start, point |
| ); |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| |
|
|
| case 'm': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) { |
| point.x += numbers[ j + 0 ]; |
| point.y += numbers[ j + 1 ]; |
| control.x = point.x; |
| control.y = point.y; |
| if ( j === 0 ) { |
| path.moveTo( point.x, point.y ); |
| } else { |
| path.lineTo( point.x, point.y ); |
| } |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| case 'h': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j ++ ) { |
| point.x += numbers[ j ]; |
| control.x = point.x; |
| control.y = point.y; |
| path.lineTo( point.x, point.y ); |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| case 'v': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j ++ ) { |
| point.y += numbers[ j ]; |
| control.x = point.x; |
| control.y = point.y; |
| path.lineTo( point.x, point.y ); |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| case 'l': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) { |
| point.x += numbers[ j + 0 ]; |
| point.y += numbers[ j + 1 ]; |
| control.x = point.x; |
| control.y = point.y; |
| path.lineTo( point.x, point.y ); |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| case 'c': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j += 6 ) { |
| path.bezierCurveTo( |
| point.x + numbers[ j + 0 ], |
| point.y + numbers[ j + 1 ], |
| point.x + numbers[ j + 2 ], |
| point.y + numbers[ j + 3 ], |
| point.x + numbers[ j + 4 ], |
| point.y + numbers[ j + 5 ] |
| ); |
| control.x = point.x + numbers[ j + 2 ]; |
| control.y = point.y + numbers[ j + 3 ]; |
| point.x += numbers[ j + 4 ]; |
| point.y += numbers[ j + 5 ]; |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| case 's': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j += 4 ) { |
| path.bezierCurveTo( |
| getReflection( point.x, control.x ), |
| getReflection( point.y, control.y ), |
| point.x + numbers[ j + 0 ], |
| point.y + numbers[ j + 1 ], |
| point.x + numbers[ j + 2 ], |
| point.y + numbers[ j + 3 ] |
| ); |
| control.x = point.x + numbers[ j + 0 ]; |
| control.y = point.y + numbers[ j + 1 ]; |
| point.x += numbers[ j + 2 ]; |
| point.y += numbers[ j + 3 ]; |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| case 'q': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j += 4 ) { |
| path.quadraticCurveTo( |
| point.x + numbers[ j + 0 ], |
| point.y + numbers[ j + 1 ], |
| point.x + numbers[ j + 2 ], |
| point.y + numbers[ j + 3 ] |
| ); |
| control.x = point.x + numbers[ j + 0 ]; |
| control.y = point.y + numbers[ j + 1 ]; |
| point.x += numbers[ j + 2 ]; |
| point.y += numbers[ j + 3 ]; |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| case 't': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) { |
| var rx = getReflection( point.x, control.x ); |
| var ry = getReflection( point.y, control.y ); |
| path.quadraticCurveTo( |
| rx, |
| ry, |
| point.x + numbers[ j + 0 ], |
| point.y + numbers[ j + 1 ] |
| ); |
| control.x = rx; |
| control.y = ry; |
| point.x = point.x + numbers[ j + 0 ]; |
| point.y = point.y + numbers[ j + 1 ]; |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| case 'a': |
| var numbers = parseFloats( data ); |
| for ( var j = 0, jl = numbers.length; j < jl; j += 7 ) { |
| var start = point.clone(); |
| point.x += numbers[ j + 5 ]; |
| point.y += numbers[ j + 6 ]; |
| control.x = point.x; |
| control.y = point.y; |
| parseArcCommand( |
| path, numbers[ j ], numbers[ j + 1 ], numbers[ j + 2 ], numbers[ j + 3 ], numbers[ j + 4 ], start, point |
| ); |
| if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point ); |
| } |
| break; |
|
|
| |
|
|
| case 'Z': |
| case 'z': |
| path.currentPath.autoClose = true; |
| if ( path.currentPath.curves.length > 0 ) { |
| |
| point.copy( firstPoint ); |
| path.currentPath.currentPoint.copy( point ); |
| isFirstPoint = true; |
| } |
| break; |
|
|
| default: |
| console.warn( command ); |
|
|
| } |
|
|
| |
|
|
| doSetFirstPoint = false; |
|
|
| } |
|
|
| return path; |
|
|
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| function parseArcCommand( path, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, start, end ) { |
|
|
| x_axis_rotation = x_axis_rotation * Math.PI / 180; |
|
|
| |
| rx = Math.abs( rx ); |
| ry = Math.abs( ry ); |
|
|
| |
| var dx2 = ( start.x - end.x ) / 2.0; |
| var dy2 = ( start.y - end.y ) / 2.0; |
| var x1p = Math.cos( x_axis_rotation ) * dx2 + Math.sin( x_axis_rotation ) * dy2; |
| var y1p = - Math.sin( x_axis_rotation ) * dx2 + Math.cos( x_axis_rotation ) * dy2; |
|
|
| |
| var rxs = rx * rx; |
| var rys = ry * ry; |
| var x1ps = x1p * x1p; |
| var y1ps = y1p * y1p; |
|
|
| |
| var cr = x1ps / rxs + y1ps / rys; |
|
|
| if ( cr > 1 ) { |
|
|
| |
| var s = Math.sqrt( cr ); |
| rx = s * rx; |
| ry = s * ry; |
| rxs = rx * rx; |
| rys = ry * ry; |
|
|
| } |
|
|
| var dq = ( rxs * y1ps + rys * x1ps ); |
| var pq = ( rxs * rys - dq ) / dq; |
| var q = Math.sqrt( Math.max( 0, pq ) ); |
| if ( large_arc_flag === sweep_flag ) q = - q; |
| var cxp = q * rx * y1p / ry; |
| var cyp = - q * ry * x1p / rx; |
|
|
| |
| var cx = Math.cos( x_axis_rotation ) * cxp - Math.sin( x_axis_rotation ) * cyp + ( start.x + end.x ) / 2; |
| var cy = Math.sin( x_axis_rotation ) * cxp + Math.cos( x_axis_rotation ) * cyp + ( start.y + end.y ) / 2; |
|
|
| |
| var theta = svgAngle( 1, 0, ( x1p - cxp ) / rx, ( y1p - cyp ) / ry ); |
| var delta = svgAngle( ( x1p - cxp ) / rx, ( y1p - cyp ) / ry, ( - x1p - cxp ) / rx, ( - y1p - cyp ) / ry ) % ( Math.PI * 2 ); |
|
|
| path.currentPath.absellipse( cx, cy, rx, ry, theta, theta + delta, sweep_flag === 0, x_axis_rotation ); |
|
|
| } |
|
|
| function svgAngle( ux, uy, vx, vy ) { |
|
|
| var dot = ux * vx + uy * vy; |
| var len = Math.sqrt( ux * ux + uy * uy ) * Math.sqrt( vx * vx + vy * vy ); |
| var ang = Math.acos( Math.max( -1, Math.min( 1, dot / len ) ) ); |
| if ( ( ux * vy - uy * vx ) < 0 ) ang = - ang; |
| return ang; |
|
|
| } |
|
|
| |
| |
| |
| |
| function parseRectNode( node, style ) { |
|
|
| var x = parseFloat( node.getAttribute( 'x' ) || 0 ); |
| var y = parseFloat( node.getAttribute( 'y' ) || 0 ); |
| var rx = parseFloat( node.getAttribute( 'rx' ) || 0 ); |
| var ry = parseFloat( node.getAttribute( 'ry' ) || 0 ); |
| var w = parseFloat( node.getAttribute( 'width' ) ); |
| var h = parseFloat( node.getAttribute( 'height' ) ); |
|
|
| var path = new THREE.ShapePath(); |
| path.moveTo( x + 2 * rx, y ); |
| path.lineTo( x + w - 2 * rx, y ); |
| if ( rx !== 0 || ry !== 0 ) path.bezierCurveTo( x + w, y, x + w, y, x + w, y + 2 * ry ); |
| path.lineTo( x + w, y + h - 2 * ry ); |
| if ( rx !== 0 || ry !== 0 ) path.bezierCurveTo( x + w, y + h, x + w, y + h, x + w - 2 * rx, y + h ); |
| path.lineTo( x + 2 * rx, y + h ); |
|
|
| if ( rx !== 0 || ry !== 0 ) { |
|
|
| path.bezierCurveTo( x, y + h, x, y + h, x, y + h - 2 * ry ); |
|
|
| } |
|
|
| path.lineTo( x, y + 2 * ry ); |
|
|
| if ( rx !== 0 || ry !== 0 ) { |
|
|
| path.bezierCurveTo( x, y, x, y, x + 2 * rx, y ); |
|
|
| } |
|
|
| return path; |
|
|
| } |
|
|
| function parsePolygonNode( node, style ) { |
|
|
| function iterator( match, a, b ) { |
|
|
| var x = parseFloat( a ); |
| var y = parseFloat( b ); |
|
|
| if ( index === 0 ) { |
| path.moveTo( x, y ); |
| } else { |
| path.lineTo( x, y ); |
| } |
|
|
| index ++; |
|
|
| } |
|
|
| var regex = /(-?[\d\.?]+)[,|\s](-?[\d\.?]+)/g; |
|
|
| var path = new THREE.ShapePath(); |
|
|
| var index = 0; |
|
|
| node.getAttribute( 'points' ).replace(regex, iterator); |
|
|
| path.currentPath.autoClose = true; |
|
|
| return path; |
|
|
| } |
|
|
| function parsePolylineNode( node, style ) { |
|
|
| function iterator( match, a, b ) { |
|
|
| var x = parseFloat( a ); |
| var y = parseFloat( b ); |
|
|
| if ( index === 0 ) { |
| path.moveTo( x, y ); |
| } else { |
| path.lineTo( x, y ); |
| } |
|
|
| index ++; |
|
|
| } |
|
|
| var regex = /(-?[\d\.?]+)[,|\s](-?[\d\.?]+)/g; |
|
|
| var path = new THREE.ShapePath(); |
|
|
| var index = 0; |
|
|
| node.getAttribute( 'points' ).replace(regex, iterator); |
|
|
| path.currentPath.autoClose = false; |
|
|
| return path; |
|
|
| } |
|
|
| function parseCircleNode( node, style ) { |
|
|
| var x = parseFloat( node.getAttribute( 'cx' ) ); |
| var y = parseFloat( node.getAttribute( 'cy' ) ); |
| var r = parseFloat( node.getAttribute( 'r' ) ); |
|
|
| var subpath = new THREE.Path(); |
| subpath.absarc( x, y, r, 0, Math.PI * 2 ); |
|
|
| var path = new THREE.ShapePath(); |
| path.subPaths.push( subpath ); |
|
|
| return path; |
|
|
| } |
|
|
| function parseEllipseNode( node, style ) { |
|
|
| var x = parseFloat( node.getAttribute( 'cx' ) ); |
| var y = parseFloat( node.getAttribute( 'cy' ) ); |
| var rx = parseFloat( node.getAttribute( 'rx' ) ); |
| var ry = parseFloat( node.getAttribute( 'ry' ) ); |
|
|
| var subpath = new THREE.Path(); |
| subpath.absellipse( x, y, rx, ry, 0, Math.PI * 2 ); |
|
|
| var path = new THREE.ShapePath(); |
| path.subPaths.push( subpath ); |
|
|
| return path; |
|
|
| } |
|
|
| function parseLineNode( node, style ) { |
|
|
| var x1 = parseFloat( node.getAttribute( 'x1' ) ); |
| var y1 = parseFloat( node.getAttribute( 'y1' ) ); |
| var x2 = parseFloat( node.getAttribute( 'x2' ) ); |
| var y2 = parseFloat( node.getAttribute( 'y2' ) ); |
|
|
| var path = new THREE.ShapePath(); |
| path.moveTo( x1, y1 ); |
| path.lineTo( x2, y2 ); |
| path.currentPath.autoClose = false; |
|
|
| return path; |
|
|
| } |
|
|
| |
|
|
| function parseStyle( node, style ) { |
|
|
| style = Object.assign( {}, style ); |
|
|
| function addStyle( svgName, jsName, adjustFunction ) { |
|
|
| if ( adjustFunction === undefined ) adjustFunction = function copy( v ) { return v; }; |
|
|
| if ( node.hasAttribute( svgName ) ) style[ jsName ] = adjustFunction( node.getAttribute( svgName ) ); |
| if ( node.style[ svgName ] !== '' ) style[ jsName ] = adjustFunction( node.style[ svgName ] ); |
|
|
| } |
|
|
| function clamp( v ) { |
|
|
| return Math.max( 0, Math.min( 1, v ) ); |
|
|
| } |
|
|
| function positive( v ) { |
|
|
| return Math.max( 0, v ); |
|
|
| } |
|
|
| addStyle( 'fill', 'fill' ); |
| addStyle( 'fill-opacity', 'fillOpacity', clamp ); |
| addStyle( 'stroke', 'stroke' ); |
| addStyle( 'stroke-opacity', 'strokeOpacity', clamp ); |
| addStyle( 'stroke-width', 'strokeWidth', positive ); |
| addStyle( 'stroke-linejoin', 'strokeLineJoin' ); |
| addStyle( 'stroke-linecap', 'strokeLineCap' ); |
| addStyle( 'stroke-miterlimit', 'strokeMiterLimit', positive ); |
|
|
| return style; |
|
|
| } |
|
|
| |
|
|
| function getReflection( a, b ) { |
|
|
| return a - ( b - a ); |
|
|
| } |
|
|
| function parseFloats( string ) { |
|
|
| var array = string.split( /[\s,]+|(?=\s?[+\-])/ ); |
|
|
| for ( var i = 0; i < array.length; i ++ ) { |
|
|
| var number = array[ i ]; |
|
|
| |
| |
|
|
| if ( number.indexOf( '.' ) !== number.lastIndexOf( '.' ) ) { |
|
|
| var split = number.split( '.' ); |
|
|
| for ( var s = 2; s < split.length; s ++ ) { |
|
|
| array.splice( i + s - 1, 0, '0.' + split[ s ] ); |
|
|
| } |
|
|
| } |
|
|
| array[ i ] = parseFloat( number ); |
|
|
| } |
|
|
| return array; |
|
|
|
|
| } |
|
|
| function getNodeTransform( node ) { |
|
|
| if ( ! node.hasAttribute( 'transform' ) ) { |
| return null; |
| } |
|
|
| var transform = parseNodeTransform( node ); |
|
|
| if ( transform ) { |
|
|
| if ( transformStack.length > 0 ) { |
| transform.premultiply( transformStack[ transformStack.length - 1 ] ); |
| } |
|
|
| currentTransform.copy( transform ); |
| transformStack.push( transform ); |
|
|
| } |
|
|
| return transform; |
|
|
| } |
|
|
| function parseNodeTransform( node ) { |
|
|
| var transform = new THREE.Matrix3(); |
| var currentTransform = tempTransform0; |
| var transformsTexts = node.getAttribute( 'transform' ).split( ' ' ); |
|
|
| for ( var tIndex = transformsTexts.length - 1; tIndex >= 0; tIndex -- ) { |
|
|
| var transformText = transformsTexts[ tIndex ]; |
| var openParPos = transformText.indexOf( "(" ); |
| var closeParPos = transformText.indexOf( ")" ); |
|
|
| if ( openParPos > 0 && openParPos < closeParPos ) { |
|
|
| var transformType = transformText.substr( 0, openParPos ); |
|
|
| var array = parseFloats( transformText.substr( openParPos + 1, closeParPos - openParPos - 1 ) ); |
|
|
| currentTransform.identity(); |
|
|
| switch ( transformType ) { |
|
|
| case "translate": |
|
|
| if ( array.length >= 1 ) { |
|
|
| var tx = array[ 0 ]; |
| var ty = tx; |
|
|
| if ( array.length >= 2 ) { |
|
|
| ty = array[ 1 ]; |
|
|
| } |
|
|
| currentTransform.translate( tx, ty ); |
|
|
| } |
|
|
| break; |
|
|
| case "rotate": |
|
|
| if ( array.length >= 1 ) { |
|
|
| var angle = 0; |
| var cx = 0; |
| var cy = 0; |
|
|
| |
| angle = - array[ 0 ] * Math.PI / 180; |
|
|
| if ( array.length >= 3 ) { |
|
|
| |
| cx = array[ 1 ]; |
| cy = array[ 2 ]; |
|
|
| } |
|
|
| |
| tempTransform1.identity().translate( -cx, -cy ); |
| tempTransform2.identity().rotate( angle ); |
| tempTransform3.multiplyMatrices( tempTransform2, tempTransform1 ); |
| tempTransform1.identity().translate( cx, cy ); |
| currentTransform.multiplyMatrices( tempTransform1, tempTransform3 ); |
|
|
| } |
|
|
| break; |
|
|
| case "scale": |
|
|
| if ( array.length >= 1 ) { |
|
|
| var scaleX = array[ 0 ]; |
| var scaleY = scaleX; |
|
|
| if ( array.length >= 2 ) { |
| scaleY = array[ 1 ]; |
| } |
|
|
| currentTransform.scale( scaleX, scaleY ); |
|
|
| } |
|
|
| break; |
|
|
| case "skewX": |
|
|
| if ( array.length === 1 ) { |
|
|
| currentTransform.set( |
| 1, Math.tan( array[ 0 ] * Math.PI / 180 ), 0, |
| 0, 1, 0, |
| 0, 0, 1 |
| ); |
|
|
| } |
|
|
| break; |
|
|
| case "skewY": |
|
|
| if ( array.length === 1 ) { |
|
|
| currentTransform.set( |
| 1, 0, 0, |
| Math.tan( array[ 0 ] * Math.PI / 180 ), 1, 0, |
| 0, 0, 1 |
| ); |
|
|
| } |
|
|
| break; |
|
|
| case "matrix": |
|
|
| if ( array.length === 6 ) { |
|
|
| currentTransform.set( |
| array[ 0 ], array[ 2 ], array[ 4 ], |
| array[ 1 ], array[ 3 ], array[ 5 ], |
| 0, 0, 1 |
| ); |
|
|
| } |
|
|
| break; |
| } |
|
|
| } |
|
|
| transform.premultiply( currentTransform ); |
|
|
| } |
|
|
| return transform; |
|
|
| } |
|
|
| function transformPath( path, m ) { |
|
|
| function transfVec2( v2 ) { |
|
|
| tempV3.set( v2.x, v2.y, 1 ).applyMatrix3( m ); |
|
|
| v2.set( tempV3.x, tempV3.y ); |
|
|
| } |
|
|
| var isRotated = isTransformRotated( m ); |
|
|
| var subPaths = path.subPaths; |
|
|
| for ( var i = 0, n = subPaths.length; i < n; i++ ) { |
|
|
| var subPath = subPaths[ i ]; |
| var curves = subPath.curves; |
|
|
| for ( var j = 0; j < curves.length; j++ ) { |
|
|
| var curve = curves[ j ]; |
|
|
| if ( curve.isLineCurve ) { |
|
|
| transfVec2( curve.v1 ); |
| transfVec2( curve.v2 ); |
|
|
| } else if ( curve.isCubicBezierCurve ) { |
|
|
| transfVec2( curve.v0 ); |
| transfVec2( curve.v1 ); |
| transfVec2( curve.v2 ); |
| transfVec2( curve.v3 ); |
|
|
| } else if ( curve.isQuadraticBezierCurve ) { |
|
|
| transfVec2( curve.v0 ); |
| transfVec2( curve.v1 ); |
| transfVec2( curve.v2 ); |
|
|
| } else if ( curve.isEllipseCurve ) { |
|
|
| if ( isRotated ) { |
| console.warn( "SVGLoader: Elliptic arc or ellipse rotation or skewing is not implemented." ); |
| } |
|
|
| tempV2.set( curve.aX, curve.aY ); |
| transfVec2( tempV2 ); |
| curve.aX = tempV2.x; |
| curve.aY = tempV2.y; |
|
|
| curve.xRadius *= getTransformScaleX( m ); |
| curve.yRadius *= getTransformScaleY( m ); |
|
|
| } |
|
|
| } |
|
|
| } |
|
|
| } |
|
|
| function isTransformRotated( m ) { |
| return m.elements[ 1 ] !== 0 || m.elements[ 3 ] !== 0; |
| } |
|
|
| function getTransformScaleX( m ) { |
| var te = m.elements; |
| return Math.sqrt( te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] ) |
| } |
|
|
| function getTransformScaleY( m ) { |
| var te = m.elements; |
| return Math.sqrt( te[ 3 ] * te[ 3 ] + te[ 4 ] * te[ 4 ] ) |
| } |
|
|
| |
|
|
| console.log( 'THREE.SVGLoader' ); |
|
|
| var paths = []; |
|
|
| var transformStack = []; |
|
|
| var tempTransform0 = new THREE.Matrix3(); |
| var tempTransform1 = new THREE.Matrix3(); |
| var tempTransform2 = new THREE.Matrix3(); |
| var tempTransform3 = new THREE.Matrix3(); |
| var tempV2 = new THREE.Vector2(); |
| var tempV3 = new THREE.Vector3(); |
|
|
| var currentTransform = new THREE.Matrix3(); |
|
|
| var scope = this; |
|
|
| console.time( 'THREE.SVGLoader: DOMParser' ); |
|
|
| var xml = new DOMParser().parseFromString( text, 'image/svg+xml' ); |
|
|
| console.timeEnd( 'THREE.SVGLoader: DOMParser' ); |
|
|
| console.time( 'THREE.SVGLoader: Parse' ); |
|
|
| parseNode( xml.documentElement, { |
| fill: '#000', |
| fillOpacity: 1, |
| strokeOpacity: 1, |
| strokeWidth: 1, |
| strokeLineJoin: 'miter', |
| strokeLineCap: 'butt', |
| strokeMiterLimit: 4 |
| } ); |
|
|
| var data = { paths: paths, xml: xml.documentElement }; |
|
|
| |
|
|
|
|
| console.timeEnd( 'THREE.SVGLoader: Parse' ); |
|
|
| return data; |
|
|
| } |
|
|
| }; |
|
|
| THREE.SVGLoader.getStrokeStyle = function ( width, color, opacity, lineJoin, lineCap, miterLimit ) { |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| width = width !== undefined ? width : 1; |
| color = color !== undefined ? color : '#000'; |
| opacity = opacity !== undefined ? opacity : 1; |
| lineJoin = lineJoin !== undefined ? lineJoin : 'miter'; |
| lineCap = lineCap !== undefined ? lineCap : 'butt'; |
| miterLimit = miterLimit !== undefined ? miterLimit : 4; |
|
|
| return { |
| strokeColor: color, |
| strokeWidth: width, |
| strokeLineJoin: lineJoin, |
| strokeLineCap: lineCap, |
| strokeMiterLimit: miterLimit |
| }; |
|
|
| }; |
|
|
| THREE.SVGLoader.pointsToStroke = function ( points, style, arcDivisions, minDistance ) { |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| var vertices = []; |
| var normals = []; |
| var uvs = []; |
|
|
| if ( THREE.SVGLoader.pointsToStrokeWithBuffers( points, style, arcDivisions, minDistance, vertices, normals, uvs ) === 0 ) { |
|
|
| return null; |
|
|
| } |
|
|
| var geometry = new THREE.BufferGeometry(); |
| geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) ); |
| geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) ); |
| geometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) ); |
|
|
| return geometry; |
|
|
| }; |
|
|
| THREE.SVGLoader.pointsToStrokeWithBuffers = function () { |
|
|
| var tempV2_1 = new THREE.Vector2(); |
| var tempV2_2 = new THREE.Vector2(); |
| var tempV2_3 = new THREE.Vector2(); |
| var tempV2_4 = new THREE.Vector2(); |
| var tempV2_5 = new THREE.Vector2(); |
| var tempV2_6 = new THREE.Vector2(); |
| var tempV2_7 = new THREE.Vector2(); |
| var tempV3_1 = new THREE.Vector3(); |
| var lastPointL = new THREE.Vector2(); |
| var lastPointR = new THREE.Vector2(); |
| var point0L = new THREE.Vector2(); |
| var point0R = new THREE.Vector2(); |
| var currentPointL = new THREE.Vector2(); |
| var currentPointR = new THREE.Vector2(); |
| var nextPointL = new THREE.Vector2(); |
| var nextPointR = new THREE.Vector2(); |
| var innerPoint = new THREE.Vector2(); |
| var outerPoint = new THREE.Vector2(); |
| var tempTransform0 = new THREE.Matrix3(); |
| var tempTransform1 = new THREE.Matrix3(); |
| var tempTransform2 = new THREE.Matrix3(); |
|
|
| return function ( points, style, arcDivisions, minDistance, vertices, normals, uvs, vertexOffset ) { |
|
|
| |
| |
| |
| |
| |
| |
|
|
| arcLengthDivisions = arcDivisions !== undefined ? arcDivisions : 12; |
| minDistance = minDistance !== undefined ? minDistance : 0.001; |
| vertexOffset = vertexOffset !== undefined ? vertexOffset : 0; |
|
|
| |
| points = removeDuplicatedPoints( points ); |
|
|
| var numPoints = points.length; |
|
|
| if ( numPoints < 2 ) return 0; |
|
|
| var isClosed = points[ 0 ].equals( points[ numPoints - 1 ] ); |
|
|
| var currentPoint; |
| var previousPoint = points[ 0 ]; |
| var nextPoint; |
|
|
| var strokeWidth2 = style.strokeWidth / 2; |
|
|
| var deltaU = 1 / ( numPoints - 1 ); |
| var u0 = 0; |
|
|
| var innerSideModified; |
| var joinIsOnLeftSide; |
| var isMiter; |
| var initialJoinIsOnLeftSide = false; |
|
|
| var numVertices = 0; |
| var currentCoordinate = vertexOffset * 3; |
| var currentCoordinateUV = vertexOffset * 2; |
|
|
| |
| getNormal( points[ 0 ], points[ 1 ], tempV2_1 ).multiplyScalar( strokeWidth2 ); |
| lastPointL.copy( points[ 0 ] ).sub( tempV2_1 ); |
| lastPointR.copy( points[ 0 ] ).add( tempV2_1 ); |
| point0L.copy( lastPointL ); |
| point0R.copy( lastPointR ); |
|
|
| for ( var iPoint = 1; iPoint < numPoints; iPoint ++ ) { |
|
|
| currentPoint = points[ iPoint ]; |
|
|
| |
| if ( iPoint === numPoints - 1 ) { |
|
|
| if ( isClosed ) { |
|
|
| |
| nextPoint = points[ 1 ]; |
|
|
| } |
| else nextPoint = undefined; |
|
|
| } |
| else { |
|
|
| nextPoint = points[ iPoint + 1 ]; |
|
|
| } |
|
|
| |
| var normal1 = tempV2_1; |
| getNormal( previousPoint, currentPoint, normal1 ); |
|
|
| tempV2_3.copy( normal1 ).multiplyScalar( strokeWidth2 ); |
| currentPointL.copy( currentPoint ).sub( tempV2_3 ); |
| currentPointR.copy( currentPoint ).add( tempV2_3 ); |
|
|
| var u1 = u0 + deltaU; |
|
|
| innerSideModified = false; |
|
|
| if ( nextPoint !== undefined ) { |
|
|
| |
| getNormal( currentPoint, nextPoint, tempV2_2 ); |
|
|
| tempV2_3.copy( tempV2_2 ).multiplyScalar( strokeWidth2 ); |
| nextPointL.copy( currentPoint ).sub( tempV2_3 ); |
| nextPointR.copy( currentPoint ).add( tempV2_3 ); |
|
|
| joinIsOnLeftSide = true; |
| tempV2_3.subVectors( nextPoint, previousPoint ); |
| if ( normal1.dot( tempV2_3 ) < 0 ) { |
|
|
| joinIsOnLeftSide = false; |
|
|
| } |
| if ( iPoint === 1 ) initialJoinIsOnLeftSide = joinIsOnLeftSide; |
|
|
| tempV2_3.subVectors( nextPoint, currentPoint ) |
| var maxInnerDistance = tempV2_3.normalize(); |
| var dot = Math.abs( normal1.dot( tempV2_3 ) ); |
|
|
| |
| if ( dot !== 0 ) { |
|
|
| |
| var miterSide = strokeWidth2 / dot; |
| tempV2_3.multiplyScalar( - miterSide ); |
| tempV2_4.subVectors( currentPoint, previousPoint ); |
| tempV2_5.copy( tempV2_4 ).setLength( miterSide ).add( tempV2_3 ); |
| innerPoint.copy( tempV2_5 ).negate(); |
| var miterLength2 = tempV2_5.length(); |
| var segmentLengthPrev = tempV2_4.length(); |
| tempV2_4.divideScalar( segmentLengthPrev ); |
| tempV2_6.subVectors( nextPoint, currentPoint ); |
| var segmentLengthNext = tempV2_6.length(); |
| tempV2_6.divideScalar( segmentLengthNext ); |
| |
| if ( tempV2_4.dot( innerPoint ) < segmentLengthPrev && tempV2_6.dot( innerPoint ) < segmentLengthNext ) { |
|
|
| innerSideModified = true; |
|
|
| } |
| outerPoint.copy( tempV2_5 ).add( currentPoint ); |
| innerPoint.add( currentPoint ); |
|
|
| isMiter = false; |
|
|
| if ( innerSideModified ) { |
|
|
| if ( joinIsOnLeftSide ) { |
|
|
| nextPointR.copy( innerPoint ); |
| currentPointR.copy( innerPoint ); |
|
|
| } |
| else { |
|
|
| nextPointL.copy( innerPoint ); |
| currentPointL.copy( innerPoint ); |
|
|
| } |
|
|
| } |
| else { |
|
|
| |
|
|
| makeSegmentTriangles(); |
|
|
| } |
|
|
| switch ( style.strokeLineJoin ) { |
|
|
| case 'bevel': |
|
|
| makeSegmentWithBevelJoin( joinIsOnLeftSide, innerSideModified, u1 ); |
|
|
| break; |
|
|
| case 'round': |
|
|
| |
|
|
| createSegmentTrianglesWithMiddleSection( joinIsOnLeftSide, innerSideModified ); |
|
|
| |
|
|
| if ( joinIsOnLeftSide ) { |
|
|
| makeCircularSector( currentPoint, currentPointL, nextPointL, u1, 0 ); |
|
|
| } |
| else { |
|
|
| makeCircularSector( currentPoint, nextPointR, currentPointR, u1, 1 ); |
|
|
| } |
|
|
| break; |
|
|
| case 'miter': |
| case 'miter-clip': |
| default: |
|
|
| var miterFraction = ( strokeWidth2 * style.strokeMiterLimit ) / miterLength2; |
|
|
| if ( miterFraction < 1 ) { |
|
|
| |
|
|
| if ( style.strokeLineJoin !== 'miter-clip' ) { |
|
|
| makeSegmentWithBevelJoin( joinIsOnLeftSide, innerSideModified, u1 ); |
| break; |
|
|
| } |
| else { |
|
|
| |
|
|
| createSegmentTrianglesWithMiddleSection( joinIsOnLeftSide, innerSideModified ); |
|
|
| |
|
|
| if ( joinIsOnLeftSide ) { |
|
|
| tempV2_6.subVectors( outerPoint, currentPointL ).multiplyScalar( miterFraction ).add( currentPointL ); |
| tempV2_7.subVectors( outerPoint, nextPointL ).multiplyScalar( miterFraction ).add( nextPointL ); |
|
|
| addVertex( currentPointL, u1, 0 ); |
| addVertex( tempV2_6, u1, 0 ); |
| addVertex( currentPoint, u1, 0.5 ); |
|
|
| addVertex( currentPoint, u1, 0.5 ); |
| addVertex( tempV2_6, u1, 0 ); |
| addVertex( tempV2_7, u1, 0 ); |
|
|
| addVertex( currentPoint, u1, 0.5 ); |
| addVertex( tempV2_7, u1, 0 ); |
| addVertex( nextPointL, u1, 0 ); |
|
|
| } |
| else { |
|
|
| tempV2_6.subVectors( outerPoint, currentPointR ).multiplyScalar( miterFraction ).add( currentPointR ); |
| tempV2_7.subVectors( outerPoint, nextPointR ).multiplyScalar( miterFraction ).add( nextPointR ); |
|
|
| addVertex( currentPointR, u1, 1 ); |
| addVertex( tempV2_6, u1, 1 ); |
| addVertex( currentPoint, u1, 0.5 ); |
|
|
| addVertex( currentPoint, u1, 0.5 ); |
| addVertex( tempV2_6, u1, 1 ); |
| addVertex( tempV2_7, u1, 1 ); |
|
|
| addVertex( currentPoint, u1, 0.5 ); |
| addVertex( tempV2_7, u1, 1 ); |
| addVertex( nextPointR, u1, 1 ); |
|
|
| } |
|
|
| } |
|
|
| } |
| else { |
|
|
| |
|
|
| if ( innerSideModified ) { |
|
|
| |
|
|
| if ( joinIsOnLeftSide ) { |
|
|
| addVertex( lastPointR, u0, 1 ); |
| addVertex( lastPointL, u0, 0 ); |
| addVertex( outerPoint, u1, 0 ); |
|
|
| addVertex( lastPointR, u0, 1 ); |
| addVertex( outerPoint, u1, 0 ); |
| addVertex( innerPoint, u1, 1 ); |
|
|
| } |
| else { |
|
|
| addVertex( lastPointR, u0, 1 ); |
| addVertex( lastPointL, u0, 0 ); |
| addVertex( outerPoint, u1, 1 ); |
|
|
| addVertex( lastPointL, u0, 0 ); |
| addVertex( innerPoint, u1, 0 ); |
| addVertex( outerPoint, u1, 1 ); |
|
|
| } |
|
|
|
|
| if ( joinIsOnLeftSide ) { |
|
|
| nextPointL.copy( outerPoint ); |
|
|
| } |
| else { |
|
|
| nextPointR.copy( outerPoint ); |
|
|
| } |
|
|
|
|
| } |
| else { |
|
|
| |
|
|
| if ( joinIsOnLeftSide ) { |
|
|
| addVertex( currentPointL, u1, 0 ); |
| addVertex( outerPoint, u1, 0 ); |
| addVertex( currentPoint, u1, 0.5 ); |
|
|
| addVertex( currentPoint, u1, 0.5 ); |
| addVertex( outerPoint, u1, 0 ); |
| addVertex( nextPointL, u1, 0 ); |
|
|
| } |
| else { |
|
|
| addVertex( currentPointR, u1, 1 ); |
| addVertex( outerPoint, u1, 1 ); |
| addVertex( currentPoint, u1, 0.5 ); |
|
|
| addVertex( currentPoint, u1, 0.5 ); |
| addVertex( outerPoint, u1, 1 ); |
| addVertex( nextPointR, u1, 1 ); |
|
|
| } |
|
|
| } |
|
|
| isMiter = true; |
|
|
| } |
|
|
| break; |
|
|
| } |
|
|
| } |
| else { |
|
|
| |
|
|
| makeSegmentTriangles(); |
|
|
| } |
|
|
| } |
| else { |
|
|
| |
|
|
| makeSegmentTriangles(); |
|
|
| } |
|
|
| if ( ! isClosed && iPoint === numPoints - 1 ) { |
|
|
| |
| addCapGeometry( points[ 0 ], point0L, point0R, joinIsOnLeftSide, true, u0 ); |
|
|
| } |
|
|
| |
|
|
| u0 = u1; |
|
|
| previousPoint = currentPoint; |
|
|
| lastPointL.copy( nextPointL ); |
| lastPointR.copy( nextPointR ); |
|
|
| } |
|
|
| if ( ! isClosed ) { |
|
|
| |
| addCapGeometry( currentPoint, currentPointL, currentPointR, joinIsOnLeftSide, false, u1 ); |
|
|
| } |
| else if ( innerSideModified && vertices ) { |
|
|
| |
|
|
| var lastOuter = outerPoint; |
| var lastInner = innerPoint; |
| if ( initialJoinIsOnLeftSide !== joinIsOnLeftSide) { |
| lastOuter = innerPoint; |
| lastInner = outerPoint; |
| } |
|
|
| if ( joinIsOnLeftSide ) { |
|
|
| lastInner.toArray( vertices, 0 * 3 ); |
| lastInner.toArray( vertices, 3 * 3 ); |
|
|
| if ( isMiter ) { |
|
|
| lastOuter.toArray( vertices, 1 * 3 ); |
| } |
|
|
| } |
| else { |
|
|
| lastInner.toArray( vertices, 1 * 3 ); |
| lastInner.toArray( vertices, 3 * 3 ); |
|
|
| if ( isMiter ) { |
|
|
| lastOuter.toArray( vertices, 0 * 3 ); |
| } |
|
|
| } |
|
|
| } |
|
|
| return numVertices; |
|
|
| |
|
|
| |
|
|
| function getNormal( p1, p2, result ) { |
|
|
| result.subVectors( p2, p1 ); |
| return result.set( - result.y, result.x ).normalize(); |
|
|
| } |
|
|
| function addVertex( position, u, v ) { |
|
|
| if ( vertices ) { |
|
|
| vertices[ currentCoordinate ] = position.x; |
| vertices[ currentCoordinate + 1 ] = position.y; |
| vertices[ currentCoordinate + 2 ] = 0; |
|
|
| if ( normals ) { |
|
|
| normals[ currentCoordinate ] = 0; |
| normals[ currentCoordinate + 1 ] = 0; |
| normals[ currentCoordinate + 2 ] = 1; |
|
|
| } |
|
|
| currentCoordinate += 3; |
|
|
| if ( uvs ) { |
|
|
| uvs[ currentCoordinateUV ] = u; |
| uvs[ currentCoordinateUV + 1 ] = v; |
|
|
| currentCoordinateUV += 2; |
|
|
| } |
|
|
| } |
|
|
| numVertices += 3; |
|
|
| } |
|
|
| function makeCircularSector( center, p1, p2, u, v ) { |
|
|
| |
| |
|
|
| tempV2_1.copy( p1 ).sub( center ).normalize(); |
| tempV2_2.copy( p2 ).sub( center ).normalize(); |
|
|
| var angle = Math.PI; |
| var dot = tempV2_1.dot( tempV2_2 ); |
| if ( Math.abs( dot ) < 1 ) angle = Math.abs( Math.acos( dot ) ); |
|
|
| angle /= arcLengthDivisions; |
|
|
| tempV2_3.copy( p1 ); |
|
|
| for ( var i = 0, il = arcLengthDivisions - 1; i < il; i++ ) { |
|
|
| tempV2_4.copy( tempV2_3 ).rotateAround( center, angle ); |
|
|
| addVertex( tempV2_3, u, v ); |
| addVertex( tempV2_4, u, v ); |
| addVertex( center, u, 0.5 ); |
|
|
| tempV2_3.copy( tempV2_4 ); |
| } |
|
|
| addVertex( tempV2_4, u, v ); |
| addVertex( p2, u, v ); |
| addVertex( center, u, 0.5 ); |
|
|
| } |
|
|
| function makeSegmentTriangles() { |
|
|
| addVertex( lastPointR, u0, 1 ); |
| addVertex( lastPointL, u0, 0 ); |
| addVertex( currentPointL, u1, 0 ); |
|
|
| addVertex( lastPointR, u0, 1 ); |
| addVertex( currentPointL, u1, 1 ); |
| addVertex( currentPointR, u1, 0 ); |
|
|
| } |
|
|
| function makeSegmentWithBevelJoin( joinIsOnLeftSide, innerSideModified, u ) { |
|
|
| if ( innerSideModified ) { |
|
|
| |
|
|
| if ( joinIsOnLeftSide ) { |
|
|
| |
|
|
| addVertex( lastPointR, u0, 1 ); |
| addVertex( lastPointL, u0, 0 ); |
| addVertex( currentPointL, u1, 0 ); |
|
|
| addVertex( lastPointR, u0, 1 ); |
| addVertex( currentPointL, u1, 0 ); |
| addVertex( innerPoint, u1, 1 ); |
|
|
| |
|
|
| addVertex( currentPointL, u, 0 ); |
| addVertex( nextPointL, u, 0 ); |
| addVertex( innerPoint, u, 0.5 ); |
|
|
| } |
| else { |
|
|
| |
|
|
| addVertex( lastPointR, u0, 1 ); |
| addVertex( lastPointL, u0, 0 ); |
| addVertex( currentPointR, u1, 1 ); |
|
|
| addVertex( lastPointL, u0, 0 ); |
| addVertex( innerPoint, u1, 0 ); |
| addVertex( currentPointR, u1, 1 ); |
|
|
| |
|
|
| addVertex( currentPointR, u, 1 ); |
| addVertex( nextPointR, u, 0 ); |
| addVertex( innerPoint, u, 0.5 ); |
|
|
| } |
|
|
| } |
| else { |
|
|
| |
|
|
| if ( joinIsOnLeftSide ) { |
|
|
| addVertex( currentPointL, u, 0 ); |
| addVertex( nextPointL, u, 0 ); |
| addVertex( currentPoint, u, 0.5 ); |
|
|
| } |
| else { |
|
|
| addVertex( currentPointR, u, 1 ); |
| addVertex( nextPointR, u, 0 ); |
| addVertex( currentPoint, u, 0.5 ); |
|
|
| } |
|
|
| } |
|
|
| } |
|
|
| function createSegmentTrianglesWithMiddleSection( joinIsOnLeftSide, innerSideModified ) { |
|
|
| if ( innerSideModified ) { |
|
|
| if ( joinIsOnLeftSide ) { |
|
|
| addVertex( lastPointR, u0, 1 ); |
| addVertex( lastPointL, u0, 0 ); |
| addVertex( currentPointL, u1, 0 ); |
|
|
| addVertex( lastPointR, u0, 1 ); |
| addVertex( currentPointL, u1, 0 ); |
| addVertex( innerPoint, u1, 1 ); |
|
|
| addVertex( currentPointL, u0, 0 ); |
| addVertex( currentPoint, u1, 0.5 ); |
| addVertex( innerPoint, u1, 1 ); |
|
|
| addVertex( currentPoint, u1, 0.5 ); |
| addVertex( nextPointL, u0, 0 ); |
| addVertex( innerPoint, u1, 1 ); |
|
|
| } |
| else { |
|
|
| addVertex( lastPointR, u0, 1 ); |
| addVertex( lastPointL, u0, 0 ); |
| addVertex( currentPointR, u1, 1 ); |
|
|
| addVertex( lastPointL, u0, 0 ); |
| addVertex( innerPoint, u1, 0 ); |
| addVertex( currentPointR, u1, 1 ); |
|
|
| addVertex( currentPointR, u0, 1 ); |
| addVertex( innerPoint, u1, 0 ); |
| addVertex( currentPoint, u1, 0.5 ); |
|
|
| addVertex( currentPoint, u1, 0.5 ); |
| addVertex( innerPoint, u1, 0 ); |
| addVertex( nextPointR, u0, 1 ); |
|
|
| } |
|
|
| } |
|
|
| } |
|
|
| function addCapGeometry( center, p1, p2, joinIsOnLeftSide, start, u ) { |
|
|
| |
| |
|
|
| switch ( style.strokeLineCap ) { |
|
|
| case 'round': |
|
|
| if ( start ) { |
|
|
| makeCircularSector( center, p2, p1, u, 0.5 ); |
|
|
| } |
| else { |
|
|
| makeCircularSector( center, p1, p2, u, 0.5 ); |
|
|
| } |
|
|
| break; |
|
|
| case 'square': |
|
|
| if ( start ) { |
|
|
| tempV2_1.subVectors( p1, center ); |
| tempV2_2.set( tempV2_1.y, - tempV2_1.x ); |
|
|
| tempV2_3.addVectors( tempV2_1, tempV2_2 ).add( center ); |
| tempV2_4.subVectors( tempV2_2, tempV2_1 ).add( center ); |
|
|
| |
| if ( joinIsOnLeftSide ) { |
|
|
| tempV2_3.toArray( vertices, 1 * 3 ); |
| tempV2_4.toArray( vertices, 0 * 3 ); |
| tempV2_4.toArray( vertices, 3 * 3 ); |
|
|
| } |
| else { |
|
|
| tempV2_3.toArray( vertices, 1 * 3 ); |
| tempV2_3.toArray( vertices, 3 * 3 ); |
| tempV2_4.toArray( vertices, 0 * 3 ); |
|
|
| } |
|
|
| } |
| else { |
|
|
| tempV2_1.subVectors( p2, center ); |
| tempV2_2.set( tempV2_1.y, - tempV2_1.x ); |
|
|
| tempV2_3.addVectors( tempV2_1, tempV2_2 ).add( center ); |
| tempV2_4.subVectors( tempV2_2, tempV2_1 ).add( center ); |
|
|
| var vl = vertices.length; |
|
|
| |
| if ( joinIsOnLeftSide ) { |
|
|
| tempV2_3.toArray( vertices, vl - 1 * 3 ); |
| tempV2_4.toArray( vertices, vl - 2 * 3 ); |
| tempV2_4.toArray( vertices, vl - 4 * 3 ); |
|
|
| } |
| else { |
|
|
| tempV2_3.toArray( vertices, vl - 2 * 3 ); |
| tempV2_4.toArray( vertices, vl - 1 * 3 ); |
| tempV2_4.toArray( vertices, vl - 4 * 3 ); |
|
|
| } |
|
|
| } |
|
|
| break; |
|
|
| case 'butt': |
| default: |
|
|
| |
| break; |
|
|
| } |
|
|
| } |
|
|
| function removeDuplicatedPoints( points ) { |
|
|
| |
| |
|
|
| var dupPoints = false; |
| for ( var i = 1, n = points.length - 1; i < n; i ++ ) { |
|
|
| if ( points[ i ].distanceTo( points[ i + 1 ] ) < minDistance ) { |
|
|
| dupPoints = true; |
| break; |
|
|
| } |
|
|
| } |
|
|
| if ( ! dupPoints ) return points; |
|
|
| var newPoints = []; |
| newPoints.push( points[ 0 ] ); |
|
|
| for ( var i = 1, n = points.length - 1; i < n; i ++ ) { |
|
|
| if ( points[ i ].distanceTo( points[ i + 1 ] ) >= minDistance ) { |
|
|
| newPoints.push( points[ i ] ); |
|
|
| } |
| } |
|
|
| newPoints.push( points[ points.length - 1 ] ); |
|
|
| return newPoints; |
|
|
| } |
| }; |
|
|
| }(); |
|
|