/* global THREE */
(function() {
  'use strict';

  function createPoints( string ) {
    var lines = string.split( '\n' );
    var line;

    var points = [];
    var point;
    var x, y;
    for ( var i = 0; i < lines.length; i++ ) {
      line = lines[i].trim();

      if ( !line.length ) {
        continue;
      }

      point = line.split( ' ' ).map( parseFloat );

      if ( !point.length || point.some( isNaN ) ) {
        continue;
      }

      x = point[0];
      y = point[1];

      if ( point.length === 1 ) {
        y = x;
      }

      points.push( new THREE.Vector2( x, y ) );
    }

    return points;
  }

  // SVG path operations.
  var ops = {
    M: 'moveTo',
    L: 'lineTo',
    Q: 'quadraticCurveTo',
    B: 'bezierCurveTo'
  };

  function createPathPoints( string ) {
    var lines = string.split( '\n' );
    var line;

    var path = new THREE.Path();
    var point;
    var op, opcode;
    for ( var i = 0; i < lines.length; i++ ) {
      line = lines[i].trim();

      if ( !line.length ) {
        continue;
      }

      if ( /^[A-Za-z]/.test( line ) ) {
        opcode = line.slice( 0, 1 );
        line = line.slice( 1 ).trim();
      }

      point = line.split( ' ' ).map( parseFloat );

      if ( !point.length || point.some( isNaN ) ) {
        continue;
      }

      if ( !i ) {
        path.moveTo.apply( path, point );
      } else {
        op = path[ops[opcode]];
        if (op) {
          op.apply( path, point );
        }
      }
    }

    return path
      .getPoints()
      .map( function( v ) { return new THREE.Vector2( v.x, v.y ) } );
  }

  window.createPoints = createPoints;
  window.createPathPoints = createPathPoints;

})();

@@ -650,16 +650,65 @@
 ations.%0A
+  // %5Boperation name, offset to point position%5D.%0A
   var op
@@ -717,23 +717,37 @@
 = %7B%0A
+    // M x y%0A
     M: 
+%5B
 'moveTo'
 ,%0A  
@@ -746,25 +746,40 @@
 eTo'
+%5D
 ,%0A
+    // L x y%0A
     L: 
+%5B
 'lineTo'
 ,%0A  
@@ -778,17 +778,38 @@
 eTo'
+%5D
 ,%0A
+    // Q x1 y1 x y%0A
     Q: 
+%5B
 'qua
@@ -827,16 +827,256 @@
 To',
+ 2%5D,
 %0A    
-B: 
+// C x1 y1 x2 y2 x y%0A    C: %5B'bezierCurveTo', 4%5D%0A  %7D;%0A%0A  var relativeOps = %7B%0A    // m dx dy%0A    m: %5B'moveTo'%5D,%0A    // l dx dy%0A    l: %5B'lineTo'%5D,%0A    // q dx1 dy2 dx dy%0A    q: %5B'quadraticCurveTo', 2%5D,%0A    // c dx1 dt1 dx2 dt2 dx dt%0A    c: %5B
 'bez
@@ -1086,16 +1086,20 @@
 CurveTo'
+, 4%5D
 %0A  %7D;%0A%0A 
@@ -1230,32 +1230,70 @@
 %0A    var point;%0A
+    var previousPoint, absolutePoint;%0A
     var op, opco
@@ -1421,32 +1421,78 @@
 tinue;%0A      %7D%0A%0A
+      // Does this line start with an opcode?%0A
       if ( /%5E%5BA-
@@ -1727,24 +1727,41 @@
 e;%0A      %7D%0A%0A
+      // moveTo.%0A
       if ( !
@@ -1818,48 +1818,108 @@
     
-%7D else %7B%0A        op = path%5B
+  previousPoint = point;%0A      %7D%0A%0A      // Absolute path commands.%0A      else if ( 
 ops%5B
+ 
 opcode
-%5D%5D;
+ %5D ) %7B
 %0A   
@@ -1927,62 +1927,122 @@
     
-if (op) %7B%0A          op.apply( path, point );%0A        %7D
+op = ops%5B opcode %5D;%0A        path%5B op%5B0%5D %5D.apply( path, point );%0A        previousPoint = point.slice( op%5B1%5D %7C%7C 0 );
 %0A   
@@ -2050,62 +2050,261 @@
   %7D%0A
+%0A
     
-%7D%0A%0A    return path%0A      .getPoints()%0A      
+  // Relative path commands.%0A      else if ( relativeOps%5B opcode %5D ) %7B%0A        op = relativeOps%5B opcode %5D;%0A        if ( !previousPoint ) %7B%0A          continue;%0A        %7D%0A%0A        // Convert to absolute coordinates.%0A        absolutePoint = point
 .map(
- 
 func
@@ -2313,53 +2313,235 @@
 on( 
-v ) %7B return new THREE.Vector2( v.x, v.y ) %7D 
+n, index ) %7B%0A          return n + previousPoint%5B index %25 2 %5D;%0A        %7D);%0A%0A        path%5B op%5B0%5D %5D.apply( path, absolutePoint );%0A        previousPoint = absolutePoint.slice( op%5B1%5D %7C%7C 0 );%0A      %7D%0A    %7D%0A%0A    return path.getPoints(
 );%0A 

Support relative SVG path commands.