~koehr/catmullrom2bezier

c395251d780c5336c7b79fc106250f3e8d2b1098 — koehr 3 years ago 30788a5 master
es6 package
6 files changed, 255 insertions(+), 59 deletions(-)

A .babelrc
A .gitignore
A catmullrom2bezier.es.js
M catmullrom2bezier.js
A catmullrom2bezier.min.js
A package.json
A .babelrc => .babelrc +1 -0
@@ 0,0 1,1 @@
{ "presets": ["@babel/preset-env"] }

A .gitignore => .gitignore +3 -0
@@ 0,0 1,3 @@
node_modules
yarn.lock
yarn-error.log

A catmullrom2bezier.es.js => catmullrom2bezier.es.js +113 -0
@@ 0,0 1,113 @@
/* Catmull-Rom Spline to Bezier Spline Converter
 *
 * Forked from github.com/ariutta/catmullrom2bezier
 * further: http://schepers.cc/getting-to-the-point
 *
 * License: MIT, see LICENSE file
 *
 * Contact info:
 * if it is about the algorithm or original source:
 *   www-svg@w3.org for public comments (preferred),
 *   schepers@w3.org for personal comments.
 * if it is about this library please use Github issues.
 *
 * © schepers 2010
 * ES6 library adaption @ koehr 2018
 */

// expects String describing path (d property of <path>)
// returns String describing Bezier equivalent
// NOTE: This code supports only absolute command coordinates.
function parsePath (d) {
  let pathArray = []
  let lastX = ''
  let lastY = ''

  // no need to redraw the path if no Catmull-Rom segments are found
  if (d.search(/[rR]/) < 0) {
    return d
  }

  const pathSplit = d.split(/([A-Za-z])/)

  for (let i = 0, iLen = pathSplit.length; iLen > i; i++) {
    const segment = pathSplit[i]
    const command = segment.toLowerCase() // lower case for easier matching

    // whoops, not a command
    if (command.search(/[a-z]/) < 0) continue

    let val = ''
    if (command !== 'z') {
      i++
      val = pathSplit[i].replace(/\s+$/, '')
    }

    // "R" and "r" are the a Catmull-Rom spline segment
    if (command === 'r') {
      const points = `${lastX},${lastY} ${val}`
      const beziers = catmullRom2bezier(points) // convert to Bézier
      pathArray.push(beziers) // and put it back
    } else {
      pathArray.push(segment + val) // not our business, put back directly

      // find last x,y points, for feeding into Catmull-Rom conversion algorithm
      if (command === 'h') {
        lastX = val
      } else if (command === 'v') {
        lastY = val
      } else if (command !== 'z') {
        const c = val.split(/[,\s]/)
        lastY = c.pop()
        lastX = c.pop()
      }
    }
  }

  // return recombined path segments
  return pathArray.join(' ')
}

function catmullRom2bezier (points) {
  const crp = points.split(/[,\s]/).map(n => parseFloat(n))
  let d = ''

  for (let i = 0, iLen = crp.length; iLen - 2 > i; i += 2) {
    let p = []

    if (i === 0) {
      p.push({ x: crp[i], y: crp[i + 1] })
      p.push({ x: crp[i], y: crp[i + 1] })
      p.push({ x: crp[i + 2], y: crp[i + 3] })
      p.push({ x: crp[i + 4], y: crp[i + 5] })
    } else if (i === iLen - 4) {
      p.push({ x: crp[i - 2], y: crp[i - 1] })
      p.push({ x: crp[i], y: crp[i + 1] })
      p.push({ x: crp[i + 2], y: crp[i + 3] })
      p.push({ x: crp[i + 2], y: crp[i + 3] })
    } else {
      p.push({ x: crp[i - 2], y: crp[i - 1] })
      p.push({ x: crp[i], y: crp[i + 1] })
      p.push({ x: crp[i + 2], y: crp[i + 3] })
      p.push({ x: crp[i + 4], y: crp[i + 5] })
    }

    // Catmull-Rom to Cubic Bezier conversion matrix
    //    0       1       0       0
    //  -1/6      1      1/6      0
    //    0      1/6      1     -1/6
    //    0       0       1       0

    let bp = []
    bp.push({ x: p[1].x, y: p[1].y })
    bp.push({ x: ((-p[0].x + 6 * p[1].x + p[2].x) / 6), y: ((-p[0].y + 6 * p[1].y + p[2].y) / 6) })
    bp.push({ x: ((p[1].x + 6 * p[2].x - p[3].x) / 6), y: ((p[1].y + 6 * p[2].y - p[3].y) / 6) })
    bp.push({ x: p[2].x, y: p[2].y })

    d += `C${bp[1].x},${bp[1].y} ${bp[2].x},${bp[2].y} ${bp[3].x},${bp[3].y} `
  }

  return d
}

export default parsePath

M catmullrom2bezier.js => catmullrom2bezier.js +114 -59
@@ 1,3 1,10 @@
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

/* Catmull-Rom Spline to Bezier Spline Converter
 *
 * Forked from github.com/ariutta/catmullrom2bezier


@@ 14,100 21,148 @@
 * © schepers 2010
 * ES6 library adaption @ koehr 2018
 */

// expects String describing path (d property of <path>)
// returns String describing Bezier equivalent
// NOTE: This code supports only absolute command coordinates.
function parsePath (d) {
  let pathArray = []
  let lastX = ''
  let lastY = ''
function parsePath(d) {
  var pathArray = [];
  var lastX = '';
  var lastY = ''; // no need to redraw the path if no Catmull-Rom segments are found

  // no need to redraw the path if no Catmull-Rom segments are found
  if (d.search(/[rR]/) < 0) {
    return d
    return d;
  }

  const pathSplit = d.split(/([A-Za-z])/)

  for (let i = 0, iLen = pathSplit.length; iLen > i; i++) {
    const segment = pathSplit[i]
    const command = segment.toLowerCase() // lower case for easier matching
  var pathSplit = d.split(/([A-Za-z])/);

  for (var i = 0, iLen = pathSplit.length; iLen > i; i++) {
    var segment = pathSplit[i];
    var command = segment.toLowerCase(); // lower case for easier matching
    // whoops, not a command
    if (command.search(/[a-z]/) < 0) continue

    let val = ''
    if (command.search(/[a-z]/) < 0) continue;
    var val = '';

    if (command !== 'z') {
      i++
      val = pathSplit[i].replace(/\s+$/, '')
    }
      i++;
      val = pathSplit[i].replace(/\s+$/, '');
    } // "R" and "r" are the a Catmull-Rom spline segment


    // "R" and "r" are the a Catmull-Rom spline segment
    if (command === 'r') {
      const points = `${lastX},${lastY} ${val}`
      const beziers = catmullRom2bezier(points) // convert to Bézier
      pathArray.push(beziers) // and put it back
    } else {
      pathArray.push(segment + val) // not our business, put back directly
      var points = "".concat(lastX, ",").concat(lastY, " ").concat(val);
      var beziers = catmullRom2bezier(points); // convert to Bézier

      pathArray.push(beziers); // and put it back
    } else {
      pathArray.push(segment + val); // not our business, put back directly
      // find last x,y points, for feeding into Catmull-Rom conversion algorithm

      if (command === 'h') {
        lastX = val
        lastX = val;
      } else if (command === 'v') {
        lastY = val
        lastY = val;
      } else if (command !== 'z') {
        const c = val.split(/[,\s]/)
        lastY = c.pop()
        lastX = c.pop()
        var c = val.split(/[,\s]/);
        lastY = c.pop();
        lastX = c.pop();
      }
    }
  }
  } // return recombined path segments

  // return recombined path segments
  return pathArray.join(' ')

  return pathArray.join(' ');
}

function catmullRom2bezier (points) {
  const crp = points.split(/[,\s]/).map(n => parseFloat(n))
  let d = ''
function catmullRom2bezier(points) {
  var crp = points.split(/[,\s]/).map(function (n) {
    return parseFloat(n);
  });
  var d = '';

  for (let i = 0, iLen = crp.length; iLen - 2 > i; i += 2) {
    let p = []
  for (var i = 0, iLen = crp.length; iLen - 2 > i; i += 2) {
    var p = [];

    if (i === 0) {
      p.push({ x: crp[i], y: crp[i + 1] })
      p.push({ x: crp[i], y: crp[i + 1] })
      p.push({ x: crp[i + 2], y: crp[i + 3] })
      p.push({ x: crp[i + 4], y: crp[i + 5] })
      p.push({
        x: crp[i],
        y: crp[i + 1]
      });
      p.push({
        x: crp[i],
        y: crp[i + 1]
      });
      p.push({
        x: crp[i + 2],
        y: crp[i + 3]
      });
      p.push({
        x: crp[i + 4],
        y: crp[i + 5]
      });
    } else if (i === iLen - 4) {
      p.push({ x: crp[i - 2], y: crp[i - 1] })
      p.push({ x: crp[i], y: crp[i + 1] })
      p.push({ x: crp[i + 2], y: crp[i + 3] })
      p.push({ x: crp[i + 2], y: crp[i + 3] })
      p.push({
        x: crp[i - 2],
        y: crp[i - 1]
      });
      p.push({
        x: crp[i],
        y: crp[i + 1]
      });
      p.push({
        x: crp[i + 2],
        y: crp[i + 3]
      });
      p.push({
        x: crp[i + 2],
        y: crp[i + 3]
      });
    } else {
      p.push({ x: crp[i - 2], y: crp[i - 1] })
      p.push({ x: crp[i], y: crp[i + 1] })
      p.push({ x: crp[i + 2], y: crp[i + 3] })
      p.push({ x: crp[i + 4], y: crp[i + 5] })
    }

    // Catmull-Rom to Cubic Bezier conversion matrix
      p.push({
        x: crp[i - 2],
        y: crp[i - 1]
      });
      p.push({
        x: crp[i],
        y: crp[i + 1]
      });
      p.push({
        x: crp[i + 2],
        y: crp[i + 3]
      });
      p.push({
        x: crp[i + 4],
        y: crp[i + 5]
      });
    } // Catmull-Rom to Cubic Bezier conversion matrix
    //    0       1       0       0
    //  -1/6      1      1/6      0
    //    0      1/6      1     -1/6
    //    0       0       1       0

    let bp = []
    bp.push({ x: p[1].x, y: p[1].y })
    bp.push({ x: ((-p[0].x + 6 * p[1].x + p[2].x) / 6), y: ((-p[0].y + 6 * p[1].y + p[2].y) / 6) })
    bp.push({ x: ((p[1].x + 6 * p[2].x - p[3].x) / 6), y: ((p[1].y + 6 * p[2].y - p[3].y) / 6) })
    bp.push({ x: p[2].x, y: p[2].y })

    d += `C${bp[1].x},${bp[1].y} ${bp[2].x},${bp[2].y} ${bp[3].x},${bp[3].y} `
    var bp = [];
    bp.push({
      x: p[1].x,
      y: p[1].y
    });
    bp.push({
      x: (-p[0].x + 6 * p[1].x + p[2].x) / 6,
      y: (-p[0].y + 6 * p[1].y + p[2].y) / 6
    });
    bp.push({
      x: (p[1].x + 6 * p[2].x - p[3].x) / 6,
      y: (p[1].y + 6 * p[2].y - p[3].y) / 6
    });
    bp.push({
      x: p[2].x,
      y: p[2].y
    });
    d += "C".concat(bp[1].x, ",").concat(bp[1].y, " ").concat(bp[2].x, ",").concat(bp[2].y, " ").concat(bp[3].x, ",").concat(bp[3].y, " ");
  }

  return d
  return d;
}

export default parsePath
var _default = parsePath;
exports.default = _default;

A catmullrom2bezier.min.js => catmullrom2bezier.min.js +1 -0
@@ 0,0 1,1 @@
"use strict";function parsePath(e){var a=[],s="",t="";if(e.search(/[rR]/)<0)return e;for(var r=e.split(/([A-Za-z])/),p=0,u=r.length;p<u;p++){var x=r[p],c=x.toLowerCase();if(!(c.search(/[a-z]/)<0)){var o="";if("z"!==c&&(o=r[++p].replace(/\s+$/,"")),"r"===c){var y=catmullRom2bezier("".concat(s,",").concat(t," ").concat(o));a.push(y)}else if(a.push(x+o),"h"===c)s=o;else if("v"===c)t=o;else if("z"!==c){var h=o.split(/[,\s]/);t=h.pop(),s=h.pop()}}}return a.join(" ")}function catmullRom2bezier(e){for(var a=e.split(/[,\s]/).map(function(e){return parseFloat(e)}),s="",t=0,r=a.length;t<r-2;t+=2){var p=[];0===t?(p.push({x:a[t],y:a[t+1]}),p.push({x:a[t],y:a[t+1]}),p.push({x:a[t+2],y:a[t+3]}),p.push({x:a[t+4],y:a[t+5]})):t===r-4?(p.push({x:a[t-2],y:a[t-1]}),p.push({x:a[t],y:a[t+1]}),p.push({x:a[t+2],y:a[t+3]}),p.push({x:a[t+2],y:a[t+3]})):(p.push({x:a[t-2],y:a[t-1]}),p.push({x:a[t],y:a[t+1]}),p.push({x:a[t+2],y:a[t+3]}),p.push({x:a[t+4],y:a[t+5]}));var u=[];u.push({x:p[1].x,y:p[1].y}),u.push({x:(-p[0].x+6*p[1].x+p[2].x)/6,y:(-p[0].y+6*p[1].y+p[2].y)/6}),u.push({x:(p[1].x+6*p[2].x-p[3].x)/6,y:(p[1].y+6*p[2].y-p[3].y)/6}),u.push({x:p[2].x,y:p[2].y}),s+="C".concat(u[1].x,",").concat(u[1].y," ").concat(u[2].x,",").concat(u[2].y," ").concat(u[3].x,",").concat(u[3].y," ")}return s}Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var _default=parsePath;exports.default=_default;
\ No newline at end of file

A package.json => package.json +23 -0
@@ 0,0 1,23 @@
{
  "name": "catmullrom2bezier",
  "description": "Catmull-Rom Spline to Bezier Spline Converter",
  "main": "catmullrom2bezier.js",
  "jsnext:main": "catmullrom2bezier.es.js",
  "license": "MIT",
  "version": "0.1.0",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/nkoehring/catmullrom2bezier.git"
  },
  "scripts": {
    "build": "babel catmullrom2bezier.es.js -o catmullrom2bezier.js",
    "minify": "uglifyjs catmullrom2bezier.js  -o catmullrom2bezier.min.js -c -m",
    "prepublish": "yarn build && yarn minify"
  },
  "devDependencies": {
    "@babel/cli": "^7.1.2",
    "@babel/core": "^7.1.2",
    "@babel/preset-env": "^7.1.0",
    "uglify-js": "^3.4.9"
  }
}