SVGLoader.js 24 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054
  1. /**
  2. * @author mrdoob / http://mrdoob.com/
  3. * @author zz85 / http://joshuakoo.com/
  4. */
  5. THREE.SVGLoader = function ( manager ) {
  6. this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
  7. };
  8. THREE.SVGLoader.prototype = {
  9. constructor: THREE.SVGLoader,
  10. load: function ( url, onLoad, onProgress, onError ) {
  11. var scope = this;
  12. var loader = new THREE.FileLoader( scope.manager );
  13. loader.load( url, function ( text ) {
  14. onLoad( scope.parse( text ) );
  15. }, onProgress, onError );
  16. },
  17. parse: function ( text ) {
  18. function parseNode( node, style ) {
  19. if ( node.nodeType !== 1 ) return;
  20. var transform = getNodeTransform( node );
  21. var path = null;
  22. switch ( node.nodeName ) {
  23. case 'svg':
  24. break;
  25. case 'g':
  26. style = parseStyle( node, style );
  27. break;
  28. case 'path':
  29. style = parseStyle( node, style );
  30. if ( node.hasAttribute( 'd' ) && isVisible( style ) ) path = parsePathNode( node, style );
  31. break;
  32. case 'rect':
  33. style = parseStyle( node, style );
  34. if ( isVisible( style ) ) path = parseRectNode( node, style );
  35. break;
  36. case 'polygon':
  37. style = parseStyle( node, style );
  38. if ( isVisible( style ) ) path = parsePolygonNode( node, style );
  39. break;
  40. case 'polyline':
  41. style = parseStyle( node, style );
  42. if ( isVisible( style ) ) path = parsePolylineNode( node, style );
  43. break;
  44. case 'circle':
  45. style = parseStyle( node, style );
  46. if ( isVisible( style ) ) path = parseCircleNode( node, style );
  47. break;
  48. case 'ellipse':
  49. style = parseStyle( node, style );
  50. if ( isVisible( style ) ) path = parseEllipseNode( node, style );
  51. break;
  52. case 'line':
  53. style = parseStyle( node, style );
  54. if ( isVisible( style ) ) path = parseLineNode( node, style );
  55. break;
  56. default:
  57. console.log( node );
  58. }
  59. if ( path ) {
  60. transformPath( path, currentTransform );
  61. paths.push( path );
  62. }
  63. var nodes = node.childNodes;
  64. for ( var i = 0; i < nodes.length; i ++ ) {
  65. parseNode( nodes[ i ], style );
  66. }
  67. if ( transform ) {
  68. copyTransform( transformStack.pop(), currentTransform );
  69. }
  70. }
  71. function parsePathNode( node, style ) {
  72. var path = new THREE.ShapePath();
  73. path.color.setStyle( style.fill );
  74. var point = new THREE.Vector2();
  75. var control = new THREE.Vector2();
  76. var firstPoint = new THREE.Vector2();
  77. var isFirstPoint = true;
  78. var doSetFirstPoint = false;
  79. var d = node.getAttribute( 'd' );
  80. // console.log( d );
  81. var commands = d.match( /[a-df-z][^a-df-z]*/ig );
  82. for ( var i = 0, l = commands.length; i < l; i ++ ) {
  83. var command = commands[ i ];
  84. var type = command.charAt( 0 );
  85. var data = command.substr( 1 ).trim();
  86. if ( isFirstPoint ) {
  87. doSetFirstPoint = true;
  88. }
  89. isFirstPoint = false;
  90. switch ( type ) {
  91. case 'M':
  92. var numbers = parseFloats( data );
  93. for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) {
  94. point.x = numbers[ j + 0 ];
  95. point.y = numbers[ j + 1 ];
  96. control.x = point.x;
  97. control.y = point.y;
  98. if ( j === 0 ) {
  99. path.moveTo( point.x, point.y );
  100. }
  101. else {
  102. path.lineTo( point.x, point.y );
  103. }
  104. }
  105. break;
  106. case 'H':
  107. var numbers = parseFloats( data );
  108. for ( var j = 0, jl = numbers.length; j < jl; j ++ ) {
  109. point.x = numbers[ j ];
  110. control.x = point.x;
  111. control.y = point.y;
  112. path.lineTo( point.x, point.y );
  113. }
  114. break;
  115. case 'V':
  116. var numbers = parseFloats( data );
  117. for ( var j = 0, jl = numbers.length; j < jl; j ++ ) {
  118. point.y = numbers[ j ];
  119. control.x = point.x;
  120. control.y = point.y;
  121. path.lineTo( point.x, point.y );
  122. }
  123. break;
  124. case 'L':
  125. var numbers = parseFloats( data );
  126. for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) {
  127. point.x = numbers[ j + 0 ];
  128. point.y = numbers[ j + 1 ];
  129. control.x = point.x;
  130. control.y = point.y;
  131. path.lineTo( point.x, point.y );
  132. }
  133. break;
  134. case 'C':
  135. var numbers = parseFloats( data );
  136. for ( var j = 0, jl = numbers.length; j < jl; j += 6 ) {
  137. path.bezierCurveTo(
  138. numbers[ j + 0 ],
  139. numbers[ j + 1 ],
  140. numbers[ j + 2 ],
  141. numbers[ j + 3 ],
  142. numbers[ j + 4 ],
  143. numbers[ j + 5 ]
  144. );
  145. control.x = numbers[ j + 2 ];
  146. control.y = numbers[ j + 3 ];
  147. point.x = numbers[ j + 4 ];
  148. point.y = numbers[ j + 5 ];
  149. }
  150. break;
  151. case 'S':
  152. var numbers = parseFloats( data );
  153. for ( var j = 0, jl = numbers.length; j < jl; j += 4 ) {
  154. path.bezierCurveTo(
  155. getReflection( point.x, control.x ),
  156. getReflection( point.y, control.y ),
  157. numbers[ j + 0 ],
  158. numbers[ j + 1 ],
  159. numbers[ j + 2 ],
  160. numbers[ j + 3 ]
  161. );
  162. control.x = numbers[ j + 0 ];
  163. control.y = numbers[ j + 1 ];
  164. point.x = numbers[ j + 2 ];
  165. point.y = numbers[ j + 3 ];
  166. }
  167. break;
  168. case 'Q':
  169. var numbers = parseFloats( data );
  170. for ( var j = 0, jl = numbers.length; j < jl; j += 4 ) {
  171. path.quadraticCurveTo(
  172. numbers[ j + 0 ],
  173. numbers[ j + 1 ],
  174. numbers[ j + 2 ],
  175. numbers[ j + 3 ]
  176. );
  177. control.x = numbers[ j + 0 ];
  178. control.y = numbers[ j + 1 ];
  179. point.x = numbers[ j + 2 ];
  180. point.y = numbers[ j + 3 ];
  181. }
  182. break;
  183. case 'T':
  184. var numbers = parseFloats( data );
  185. for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) {
  186. var rx = getReflection( point.x, control.x );
  187. var ry = getReflection( point.y, control.y );
  188. path.quadraticCurveTo(
  189. rx,
  190. ry,
  191. numbers[ j + 0 ],
  192. numbers[ j + 1 ]
  193. );
  194. control.x = rx;
  195. control.y = ry;
  196. point.x = numbers[ j + 0 ];
  197. point.y = numbers[ j + 1 ];
  198. }
  199. break;
  200. case 'A':
  201. var numbers = parseFloats( data );
  202. for ( var j = 0, jl = numbers.length; j < jl; j += 7 ) {
  203. var start = point.clone();
  204. point.x = numbers[ j + 5 ];
  205. point.y = numbers[ j + 6 ];
  206. control.x = point.x;
  207. control.y = point.y;
  208. parseArcCommand(
  209. path, numbers[ j ], numbers[ j + 1 ], numbers[ j + 2 ], numbers[ j + 3 ], numbers[ j + 4 ], start, point
  210. );
  211. }
  212. break;
  213. //
  214. case 'm':
  215. var numbers = parseFloats( data );
  216. for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) {
  217. point.x += numbers[ j + 0 ];
  218. point.y += numbers[ j + 1 ];
  219. control.x = point.x;
  220. control.y = point.y;
  221. if ( j === 0 ) {
  222. path.moveTo( point.x, point.y );
  223. }
  224. else {
  225. path.lineTo( point.x, point.y );
  226. }
  227. }
  228. break;
  229. case 'h':
  230. var numbers = parseFloats( data );
  231. for ( var j = 0, jl = numbers.length; j < jl; j ++ ) {
  232. point.x += numbers[ j ];
  233. control.x = point.x;
  234. control.y = point.y;
  235. path.lineTo( point.x, point.y );
  236. }
  237. break;
  238. case 'v':
  239. var numbers = parseFloats( data );
  240. for ( var j = 0, jl = numbers.length; j < jl; j ++ ) {
  241. point.y += numbers[ j ];
  242. control.x = point.x;
  243. control.y = point.y;
  244. path.lineTo( point.x, point.y );
  245. }
  246. break;
  247. case 'l':
  248. var numbers = parseFloats( data );
  249. for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) {
  250. point.x += numbers[ j + 0 ];
  251. point.y += numbers[ j + 1 ];
  252. control.x = point.x;
  253. control.y = point.y;
  254. path.lineTo( point.x, point.y );
  255. }
  256. break;
  257. case 'c':
  258. var numbers = parseFloats( data );
  259. for ( var j = 0, jl = numbers.length; j < jl; j += 6 ) {
  260. path.bezierCurveTo(
  261. point.x + numbers[ j + 0 ],
  262. point.y + numbers[ j + 1 ],
  263. point.x + numbers[ j + 2 ],
  264. point.y + numbers[ j + 3 ],
  265. point.x + numbers[ j + 4 ],
  266. point.y + numbers[ j + 5 ]
  267. );
  268. control.x = point.x + numbers[ j + 2 ];
  269. control.y = point.y + numbers[ j + 3 ];
  270. point.x += numbers[ j + 4 ];
  271. point.y += numbers[ j + 5 ];
  272. }
  273. break;
  274. case 's':
  275. var numbers = parseFloats( data );
  276. for ( var j = 0, jl = numbers.length; j < jl; j += 4 ) {
  277. path.bezierCurveTo(
  278. getReflection( point.x, control.x ),
  279. getReflection( point.y, control.y ),
  280. point.x + numbers[ j + 0 ],
  281. point.y + numbers[ j + 1 ],
  282. point.x + numbers[ j + 2 ],
  283. point.y + numbers[ j + 3 ]
  284. );
  285. control.x = point.x + numbers[ j + 0 ];
  286. control.y = point.y + numbers[ j + 1 ];
  287. point.x += numbers[ j + 2 ];
  288. point.y += numbers[ j + 3 ];
  289. }
  290. break;
  291. case 'q':
  292. var numbers = parseFloats( data );
  293. for ( var j = 0, jl = numbers.length; j < jl; j += 4 ) {
  294. path.quadraticCurveTo(
  295. point.x + numbers[ j + 0 ],
  296. point.y + numbers[ j + 1 ],
  297. point.x + numbers[ j + 2 ],
  298. point.y + numbers[ j + 3 ]
  299. );
  300. control.x = point.x + numbers[ j + 0 ];
  301. control.y = point.y + numbers[ j + 1 ];
  302. point.x += numbers[ j + 2 ];
  303. point.y += numbers[ j + 3 ];
  304. }
  305. break;
  306. case 't':
  307. var numbers = parseFloats( data );
  308. for ( var j = 0, jl = numbers.length; j < jl; j += 2 ) {
  309. var rx = getReflection( point.x, control.x );
  310. var ry = getReflection( point.y, control.y );
  311. path.quadraticCurveTo(
  312. rx,
  313. ry,
  314. point.x + numbers[ j + 0 ],
  315. point.y + numbers[ j + 1 ]
  316. );
  317. control.x = rx;
  318. control.y = ry;
  319. point.x = point.x + numbers[ j + 0 ];
  320. point.y = point.y + numbers[ j + 1 ];
  321. }
  322. break;
  323. case 'a':
  324. var numbers = parseFloats( data );
  325. for ( var j = 0, jl = numbers.length; j < jl; j += 7 ) {
  326. var start = point.clone();
  327. point.x += numbers[ j + 5 ];
  328. point.y += numbers[ j + 6 ];
  329. control.x = point.x;
  330. control.y = point.y;
  331. parseArcCommand(
  332. path, numbers[ j ], numbers[ j + 1 ], numbers[ j + 2 ], numbers[ j + 3 ], numbers[ j + 4 ], start, point
  333. );
  334. }
  335. break;
  336. //
  337. case 'Z':
  338. case 'z':
  339. path.currentPath.autoClose = true;
  340. if ( path.currentPath.curves.length > 0 ) {
  341. // Reset point to beginning of Path
  342. point.copy( firstPoint );
  343. path.currentPath.currentPoint.copy( point );
  344. isFirstPoint = true;
  345. }
  346. break;
  347. default:
  348. console.warn( command );
  349. }
  350. // console.log( type, parseFloats( data ), parseFloats( data ).length )
  351. if ( doSetFirstPoint ) {
  352. firstPoint.copy( point );
  353. doSetFirstPoint = false;
  354. }
  355. }
  356. return path;
  357. }
  358. /**
  359. * https://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
  360. * https://mortoray.com/2017/02/16/rendering-an-svg-elliptical-arc-as-bezier-curves/ Appendix: Endpoint to center arc conversion
  361. * From
  362. * rx ry x-axis-rotation large-arc-flag sweep-flag x y
  363. * To
  364. * aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation
  365. */
  366. function parseArcCommand( path, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, start, end ) {
  367. x_axis_rotation = x_axis_rotation * Math.PI / 180;
  368. // Ensure radii are positive
  369. rx = Math.abs( rx );
  370. ry = Math.abs( ry );
  371. // Compute (x1′, y1′)
  372. var dx2 = ( start.x - end.x ) / 2.0;
  373. var dy2 = ( start.y - end.y ) / 2.0;
  374. var x1p = Math.cos( x_axis_rotation ) * dx2 + Math.sin( x_axis_rotation ) * dy2;
  375. var y1p = - Math.sin( x_axis_rotation ) * dx2 + Math.cos( x_axis_rotation ) * dy2;
  376. // Compute (cx′, cy′)
  377. var rxs = rx * rx;
  378. var rys = ry * ry;
  379. var x1ps = x1p * x1p;
  380. var y1ps = y1p * y1p;
  381. // Ensure radii are large enough
  382. var cr = x1ps / rxs + y1ps / rys;
  383. if ( cr > 1 ) {
  384. // scale up rx,ry equally so cr == 1
  385. var s = Math.sqrt( cr );
  386. rx = s * rx;
  387. ry = s * ry;
  388. rxs = rx * rx;
  389. rys = ry * ry;
  390. }
  391. var dq = ( rxs * y1ps + rys * x1ps );
  392. var pq = ( rxs * rys - dq ) / dq;
  393. var q = Math.sqrt( Math.max( 0, pq ) );
  394. if ( large_arc_flag === sweep_flag ) q = - q;
  395. var cxp = q * rx * y1p / ry;
  396. var cyp = - q * ry * x1p / rx;
  397. // Step 3: Compute (cx, cy) from (cx′, cy′)
  398. var cx = Math.cos( x_axis_rotation ) * cxp - Math.sin( x_axis_rotation ) * cyp + ( start.x + end.x ) / 2;
  399. var cy = Math.sin( x_axis_rotation ) * cxp + Math.cos( x_axis_rotation ) * cyp + ( start.y + end.y ) / 2;
  400. // Step 4: Compute θ1 and Δθ
  401. var theta = svgAngle( 1, 0, ( x1p - cxp ) / rx, ( y1p - cyp ) / ry );
  402. var delta = svgAngle( ( x1p - cxp ) / rx, ( y1p - cyp ) / ry, ( - x1p - cxp ) / rx, ( - y1p - cyp ) / ry ) % ( Math.PI * 2 );
  403. path.currentPath.absellipse( cx, cy, rx, ry, theta, theta + delta, sweep_flag === 0, x_axis_rotation );
  404. }
  405. function svgAngle( ux, uy, vx, vy ) {
  406. var dot = ux * vx + uy * vy;
  407. var len = Math.sqrt( ux * ux + uy * uy ) * Math.sqrt( vx * vx + vy * vy );
  408. var ang = Math.acos( Math.max( -1, Math.min( 1, dot / len ) ) ); // floating point precision, slightly over values appear
  409. if ( ( ux * vy - uy * vx ) < 0 ) ang = - ang;
  410. return ang;
  411. }
  412. /*
  413. * According to https://www.w3.org/TR/SVG/shapes.html#RectElementRXAttribute
  414. * rounded corner should be rendered to elliptical arc, but bezier curve does the job well enough
  415. */
  416. function parseRectNode( node, style ) {
  417. var x = parseFloat( node.getAttribute( 'x' ) || 0 );
  418. var y = parseFloat( node.getAttribute( 'y' ) || 0 );
  419. var rx = parseFloat( node.getAttribute( 'rx' ) || 0 );
  420. var ry = parseFloat( node.getAttribute( 'ry' ) || 0 );
  421. var w = parseFloat( node.getAttribute( 'width' ) );
  422. var h = parseFloat( node.getAttribute( 'height' ) );
  423. var path = new THREE.ShapePath();
  424. path.color.setStyle( style.fill );
  425. path.moveTo( x + 2 * rx, y );
  426. path.lineTo( x + w - 2 * rx, y );
  427. if ( rx !== 0 || ry !== 0 ) path.bezierCurveTo( x + w, y, x + w, y, x + w, y + 2 * ry );
  428. path.lineTo( x + w, y + h - 2 * ry );
  429. if ( rx !== 0 || ry !== 0 ) path.bezierCurveTo( x + w, y + h, x + w, y + h, x + w - 2 * rx, y + h );
  430. path.lineTo( x + 2 * rx, y + h );
  431. if ( rx !== 0 || ry !== 0 ) {
  432. path.bezierCurveTo( x, y + h, x, y + h, x, y + h - 2 * ry );
  433. }
  434. path.lineTo( x, y + 2 * ry );
  435. if ( rx !== 0 || ry !== 0 ) {
  436. path.bezierCurveTo( x, y, x, y, x + 2 * rx, y );
  437. }
  438. return path;
  439. }
  440. function parsePolygonNode( node, style ) {
  441. function iterator( match, a, b ) {
  442. var x = parseFloat( a );
  443. var y = parseFloat( b );
  444. if ( index === 0 ) {
  445. path.moveTo( x, y );
  446. } else {
  447. path.lineTo( x, y );
  448. }
  449. index ++;
  450. }
  451. var regex = /(-?[\d\.?]+)[,|\s](-?[\d\.?]+)/g;
  452. var path = new THREE.ShapePath();
  453. path.color.setStyle( style.fill );
  454. var index = 0;
  455. node.getAttribute( 'points' ).replace(regex, iterator);
  456. path.currentPath.autoClose = true;
  457. return path;
  458. }
  459. function parsePolylineNode( node, style ) {
  460. function iterator( match, a, b ) {
  461. var x = parseFloat( a );
  462. var y = parseFloat( b );
  463. if ( index === 0 ) {
  464. path.moveTo( x, y );
  465. } else {
  466. path.lineTo( x, y );
  467. }
  468. index ++;
  469. }
  470. var regex = /(-?[\d\.?]+)[,|\s](-?[\d\.?]+)/g;
  471. var path = new THREE.ShapePath();
  472. path.color.setStyle( style.fill );
  473. var index = 0;
  474. node.getAttribute( 'points' ).replace(regex, iterator);
  475. path.currentPath.autoClose = false;
  476. return path;
  477. }
  478. function parseCircleNode( node, style ) {
  479. var x = parseFloat( node.getAttribute( 'cx' ) );
  480. var y = parseFloat( node.getAttribute( 'cy' ) );
  481. var r = parseFloat( node.getAttribute( 'r' ) );
  482. var subpath = new THREE.Path();
  483. subpath.absarc( x, y, r, 0, Math.PI * 2 );
  484. var path = new THREE.ShapePath();
  485. path.color.setStyle( style.fill );
  486. path.subPaths.push( subpath );
  487. return path;
  488. }
  489. function parseEllipseNode( node, style ) {
  490. var x = parseFloat( node.getAttribute( 'cx' ) );
  491. var y = parseFloat( node.getAttribute( 'cy' ) );
  492. var rx = parseFloat( node.getAttribute( 'rx' ) );
  493. var ry = parseFloat( node.getAttribute( 'ry' ) );
  494. var subpath = new THREE.Path();
  495. subpath.absellipse( x, y, rx, ry, 0, Math.PI * 2 );
  496. var path = new THREE.ShapePath();
  497. path.color.setStyle( style.fill );
  498. path.subPaths.push( subpath );
  499. return path;
  500. }
  501. function parseLineNode( node, style ) {
  502. var x1 = parseFloat( node.getAttribute( 'x1' ) );
  503. var y1 = parseFloat( node.getAttribute( 'y1' ) );
  504. var x2 = parseFloat( node.getAttribute( 'x2' ) );
  505. var y2 = parseFloat( node.getAttribute( 'y2' ) );
  506. var path = new THREE.ShapePath();
  507. path.moveTo( x1, y1 );
  508. path.lineTo( x2, y2 );
  509. path.currentPath.autoClose = false;
  510. return path;
  511. }
  512. //
  513. function parseStyle( node, style ) {
  514. style = Object.assign( {}, style ); // clone style
  515. if ( node.hasAttribute( 'fill' ) ) style.fill = node.getAttribute( 'fill' );
  516. if ( node.style.fill !== '' && node.style.fill !== 'none' ) style.fill = node.style.fill;
  517. if ( ! isVisible( style ) ) {
  518. if ( node.hasAttribute( 'stroke' ) ) style.fill = node.getAttribute( 'stroke' );
  519. if ( node.style.stroke !== '' && node.style.stroke !== 'none' ) style.fill = node.style.stroke;
  520. }
  521. return style;
  522. }
  523. function isVisible( style ) {
  524. return ( style.fill !== 'none' && style.fill !== 'transparent' ) || ( style.stroke !== 'none' && style.stroke !== 'transparent' );
  525. }
  526. // http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
  527. function getReflection( a, b ) {
  528. return a - ( b - a );
  529. }
  530. function parseFloats( string ) {
  531. var array = string.split( /[\s,]+|(?=\s?[+\-])/ );
  532. for ( var i = 0; i < array.length; i ++ ) {
  533. var number = array[ i ];
  534. // Handle values like 48.6037.7.8
  535. // TODO Find a regex for this
  536. if ( number.indexOf( '.' ) !== number.lastIndexOf( '.' ) ) {
  537. var split = number.split( '.' );
  538. for ( var s = 2; s < split.length; s ++ ) {
  539. array.splice( i + s - 1, 0, '0.' + split[ s ] );
  540. }
  541. }
  542. array[ i ] = parseFloat( number );
  543. }
  544. return array;
  545. }
  546. function getNodeTransform( node ) {
  547. if ( ! node.hasAttribute( 'transform' ) ) {
  548. return null;
  549. }
  550. var transform = parseTransformNode( node );
  551. if ( transform ) {
  552. if ( transformStack.length > 0 ) {
  553. multiplyTransforms( transform, transformStack[ transformStack.length - 1 ], tempTransform );
  554. copyTransform( tempTransform, transform );
  555. }
  556. copyTransform( transform, currentTransform );
  557. transformStack.push( transform );
  558. }
  559. return transform;
  560. }
  561. function parseTransformNode( node ) {
  562. var transformAttr = node.getAttribute( 'transform' );
  563. var transform = null;
  564. var openParPos = transformAttr.indexOf( "(" );
  565. var closeParPos = transformAttr.indexOf( ")" );
  566. if ( openParPos > 0 && openParPos < closeParPos ) {
  567. var transformType = transformAttr.substr( 0, openParPos );
  568. var array = parseFloats( transformAttr.substr( openParPos + 1, closeParPos - openParPos - 1 ) );
  569. switch ( transformType ) {
  570. case "translate":
  571. if ( array.length >= 1 ) {
  572. transform = createIdTransform();
  573. // Translation X
  574. transform[ 4 ] = array[ 0 ];
  575. }
  576. if ( array.length >= 2 ) {
  577. // Translation Y
  578. transform[ 5 ] = array[ 1 ];
  579. }
  580. break;
  581. case "rotate":
  582. if ( array.length >= 1 ) {
  583. var angle = 0;
  584. var cx = 0;
  585. var cy = 0;
  586. transform = createIdTransform();
  587. // Angle
  588. angle = array[ 0 ] * Math.PI / 180;
  589. if ( array.length >= 3 ) {
  590. // Center x, y
  591. cx = array[ 1 ];
  592. cy = array[ 2 ];
  593. }
  594. // Rotate around center (cx, cy)
  595. var translation = createIdTransform();
  596. translation[ 4 ] = -cx;
  597. translation[ 5 ] = -cy;
  598. var rotation = createRotationTransform( angle );
  599. var translRot = createIdTransform();
  600. multiplyTransforms( translation, rotation, translRot );
  601. translation[ 4 ] = cx;
  602. translation[ 5 ] = cy;
  603. multiplyTransforms( translRot, translation, transform );
  604. }
  605. break;
  606. case "scale":
  607. if ( array.length >= 1 ) {
  608. transform = createIdTransform();
  609. var scaleX = array[ 0 ];
  610. var scaleY = scaleX;
  611. if ( array.length >= 2 ) {
  612. scaleY = array[ 1 ];
  613. }
  614. transform[ 0 ] = scaleX;
  615. transform[ 3 ] = scaleY;
  616. }
  617. break;
  618. case "skewX":
  619. if ( array.length === 1 ) {
  620. transform = createIdTransform();
  621. transform[ 2 ] = Math.tan( array[ 0 ] * Math.PI / 180 );
  622. }
  623. break;
  624. case "skewY":
  625. if ( array.length === 1 ) {
  626. transform = createIdTransform();
  627. transform[ 1 ] = Math.tan( array[ 0 ] * Math.PI / 180 );
  628. }
  629. break;
  630. case "matrix":
  631. if ( array.length === 6 ) {
  632. transform = array;
  633. }
  634. break;
  635. }
  636. }
  637. return transform;
  638. }
  639. function transformPath( path, m ) {
  640. var isRotated = isTransformRotated( m );
  641. function transfVec2( v ) {
  642. var x = v.x * m[ 0 ] + v.y * m[ 2 ] + m[ 4 ];
  643. var y = v.x * m[ 1 ] + v.y * m[ 3 ] + m[ 5 ];
  644. v.set( x, y );
  645. }
  646. var subPaths = path.subPaths;
  647. for ( var i = 0, n = subPaths.length; i < n; i++ ) {
  648. var subPath = subPaths[ i ];
  649. var curves = subPath.curves;
  650. for ( var j = 0; j < curves.length; j++ ) {
  651. var curve = curves[ j ];
  652. if ( curve.isLineCurve ) {
  653. transfVec2( curve.v1 );
  654. transfVec2( curve.v2 );
  655. }
  656. else if ( curve.isCubicBezierCurve ) {
  657. transfVec2( curve.v0 );
  658. transfVec2( curve.v1 );
  659. transfVec2( curve.v2 );
  660. transfVec2( curve.v3 );
  661. }
  662. else if ( curve.isQuadraticBezierCurve ) {
  663. transfVec2( curve.v0 );
  664. transfVec2( curve.v1 );
  665. transfVec2( curve.v2 );
  666. }
  667. else if ( curve.isEllipseCurve ) {
  668. if ( isRotated ) {
  669. console.warn( "SVGLoader: Elliptic arc or ellipse rotation or skewing is not implemented." );
  670. }
  671. curve.xRadius *= getTransformScaleX( m );
  672. curve.yRadius *= getTransformScaleY( m );
  673. }
  674. }
  675. }
  676. }
  677. function createIdTransform( m ) {
  678. // 2 x 3 matrix:
  679. // 1 0
  680. // 0 1
  681. // tx ty
  682. if ( ! m ) {
  683. m = [];
  684. }
  685. m[ 0 ] = 1;
  686. m[ 1 ] = 0;
  687. m[ 2 ] = 0;
  688. m[ 3 ] = 1;
  689. m[ 4 ] = 0;
  690. m[ 5 ] = 0;
  691. return m;
  692. }
  693. function copyTransform( orig, dest ) {
  694. for ( var i = 0; i < 6; i++ ) {
  695. dest[ i ] = orig[ i ]
  696. }
  697. }
  698. function multiplyTransforms( a, b, r ) {
  699. r[ 0 ] = a[ 0 ] * b[ 0 ] + a[ 1 ] * b[ 2 ];
  700. r[ 1 ] = a[ 0 ] * b[ 1 ] + a[ 1 ] * b[ 3 ];
  701. r[ 2 ] = a[ 2 ] * b[ 0 ] + a[ 3 ] * b[ 2 ];
  702. r[ 3 ] = a[ 2 ] * b[ 1 ] + a[ 3 ] * b[ 3 ];
  703. r[ 4 ] = a[ 4 ] * b[ 0 ] + a[ 5 ] * b[ 2 ] + b[ 4 ];
  704. r[ 5 ] = a[ 4 ] * b[ 1 ] + a[ 5 ] * b[ 3 ] + b[ 5 ];
  705. }
  706. function isTransformRotated( m ) {
  707. return m[ 1 ] !== 0 || m[ 2 ] !== 0;
  708. }
  709. function getTransformScaleX( m ) {
  710. return Math.sqrt( m[ 0 ] * m[ 0 ] + m[ 1 ] * m[ 1 ] )
  711. }
  712. function getTransformScaleY( m ) {
  713. return Math.sqrt( m[ 2 ] * m[ 2 ] + m[ 3 ] * m[ 3 ] )
  714. }
  715. function createRotationTransform( angle ) {
  716. var rotation = createIdTransform();
  717. var c = Math.cos( angle );
  718. var s = Math.sin( angle );
  719. rotation[ 0 ] = c;
  720. rotation[ 1 ] = s;
  721. rotation[ 2 ] = - s;
  722. rotation[ 3 ] = c;
  723. return rotation;
  724. }
  725. //
  726. console.log( 'THREE.SVGLoader' );
  727. var paths = [];
  728. var transformStack = [];
  729. var currentTransform = createIdTransform();
  730. var tempTransform = createIdTransform();
  731. console.time( 'THREE.SVGLoader: DOMParser' );
  732. var xml = new DOMParser().parseFromString( text, 'image/svg+xml' ); // application/xml
  733. console.timeEnd( 'THREE.SVGLoader: DOMParser' );
  734. console.time( 'THREE.SVGLoader: Parse' );
  735. parseNode( xml.documentElement, { fill: '#000' } );
  736. // console.log( paths );
  737. console.timeEnd( 'THREE.SVGLoader: Parse' );
  738. return paths;
  739. }
  740. };