├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── build
├── svg-path-utils.js
└── svg-path-utils.min.js
├── index.js
├── package.json
├── src
└── svg-path-utils.js
└── test
└── svg-path-utils-test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | node_modules
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | sudo: false
3 | node_js:
4 | - '4.2'
5 |
6 | branch:
7 | only:
8 | - master
9 |
10 | script:
11 | - npm test
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2016 Konstantin Skipor
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software
5 | and associated documentation files (the "Software"), to deal in the Software without restriction,
6 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
8 | subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
11 |
12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
13 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
14 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
15 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
16 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # svg-path-utils
2 | [](https://travis-ci.org/krispo/svg-path-utils)
3 | [](https://www.npmjs.org/package/svg-path-utils)
4 |
5 | Some utilities for svg [path](https://www.w3.org/TR/SVG/paths.html) element to help operating with [path data](https://www.w3.org/TR/SVG/paths.html#PathData),
6 | like calculating inverse path data...
7 |
8 | ## Install
9 |
10 | npm install svg-path-utils
11 |
12 | ## Usage
13 |
14 | ```js
15 | var spu = require('svg-path-utils');
16 | var utils = new spu.SVGPathUtils();
17 | ```
18 | or in es6
19 | ```js
20 | import {SVGPathUtils} from 'svg-path-utils';
21 | const utils = new SVGPathUtils();
22 | ```
23 | then
24 | ```js
25 | // generate path data
26 | const d = utils.join(utils.M(50,100), utils.L(200,300), utils.Z()); // d = "M50,100 L200,300 Z"
27 |
28 | // calculate inverse path data
29 | const inverse_d = utils.inversePath(d); // inverse_d = "M200,300 L50,100 Z"
30 | ```
31 |
32 | ## Example
33 | Inverse path data calculation
34 |
35 | | Direct Path | Inverse Path |
36 | |:-------------:|:-------------:|
37 | |
|
|
38 | | `d="M50,300 L50,250 C50,150 75,150 100,250 C150,450 200,450 200,250 Q200,100 400,100"` | `d="M400,100 Q200,100 200,250 C200,450 150,450 100,250 C75,150 50,150 50,250 L50,300"`|
39 |
40 | Check this [online demo](http://plnkr.co/edit/rIhZfI?p=preview).
41 |
42 | It is also used in [Yarrow](http://krispo.github.io/yarrow/).
43 |
44 | ## API Reference
45 |
46 | #### Path data commands (operators)
47 |
48 | Uppercase (M, L, H, ...) - uses absolute coordinates, lowercase (m, l, h, ...) - uses relative coordinates
49 |
50 | # utils.M(x, y) - [“moveto” commands](http://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands).
51 |
52 | # utils.m(x, y)
53 |
54 | # utils.L(x, y) - [“lineto” commands](http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands).
55 |
56 | # utils.l(x, y)
57 |
58 | # utils.H(x) - horizontal [“lineto” commands](http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands).
59 |
60 | # utils.h(x)
61 |
62 | # utils.V(y) - vertical [“lineto” commands](http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands).
63 |
64 | # utils.v(y)
65 |
66 | # utils.C(x1, y1, x2, y2, x, y) - [cubic Bézier curve commands](http://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands).
67 |
68 | # utils.c(x1, y1, x2, y2, x, y)
69 |
70 | # utils.S(x2, y2, x, y) - smooth [cubic Bézier curve commands](http://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands).
71 |
72 | # utils.s(x2, y2, x, y)
73 |
74 | # utils.Q(x1, y1, x, y) - [quadratic Bézier curve commands](http://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands).
75 |
76 | # utils.q(x1, y1, x, y)
77 |
78 | # utils.T(x, y) - smooth [quadratic Bézier curve commands](http://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands).
79 |
80 | # utils.t(x, y)
81 |
82 | # utils.Z() - [“closepath” commands](http://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand).
83 |
84 | # utils.z()
85 |
86 | > Todo: add .A(...) and .a(...) - [elliptical arc curve commands](http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands).
87 |
88 | Example of usage:
89 |
90 | ```js
91 | utils.M(50,100) // return: "M50,100"
92 | utils.L(200,300) // return: "L200,300"
93 | utils.c(10,20,30,40,50,60) // return: "c10,20 30,40 50,60"
94 | utils.Z() // return: "Z"
95 | ```
96 |
97 | #### Path data utils
98 |
99 | # utils.parse(d)
100 |
101 | Parse path data *d* into sequence of operators ['M', 'L', ...] and sequence of principal points [*(x,y)*].
102 |
103 | ```js
104 | utils.parse("M50,100 L200,300 H100");
105 | /*
106 | return:
107 | {
108 | operators: ["M", "L", "H"],
109 | points: [
110 | {x: 50, y: 100},
111 | {x: 200, y: 300},
112 | {x: 100}
113 | ]
114 | }
115 | */
116 | ```
117 |
118 | # utils.generate({ operators: [operators], points: [points]})
119 |
120 | Generate path data *d* from a sequence of operators ['M', 'L', ...] and sequence of principal points [*(x,y)*].
121 |
122 | ```js
123 | const data = {
124 | operators: ["M", "L", "H"],
125 | points: [
126 | {x: 50, y: 100},
127 | {x: 200, y: 300},
128 | {x: 100}
129 | ]
130 | }
131 | utils.generate(data); // return: "M50,100 L200,300 H100"
132 | ```
133 |
134 | # utils.inversePath(d)
135 |
136 | Calculate and return inverse path data for path data *d*.
137 |
138 | ```js
139 | utils.inversePath("M50,100 L200,300 Z"); // return: "M200,300 L50,100 Z"
140 | ```
141 |
142 | # utils.join(args)
143 |
144 | Join input args into a string with space (' ') separator.
145 |
146 | ```js
147 | utils.join("M50,100", "L200,300", "Z"); // return: "M50,100 L200,300 Z"
148 | utils.join(utils.M(50,100), utils.L(200,300), utils.Z()); // return: "M50,100 L200,300 Z"
149 | ```
150 |
151 | ## Licence
152 | MIT
--------------------------------------------------------------------------------
/build/svg-path-utils.js:
--------------------------------------------------------------------------------
1 | /* svg-path-utils, v0.0.3 */
2 | (function (global, factory) {
3 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
4 | typeof define === 'function' && define.amd ? define(['exports'], factory) :
5 | (factory((global.svg_path_utils = global.svg_path_utils || {})));
6 | }(this, function (exports) { 'use strict';
7 |
8 | var SVGPathUtils = function(){
9 | var utils = {};
10 |
11 | // uppercase (M) - absolute coordinates, lowercase (m) - relative coordinates
12 | // m - moveto
13 | // l - lineto
14 | // h - horizontal lineto
15 | // v - vertical lineto
16 | // c - cubic bezier curveto
17 | // s - smooth cubic bezier curveto
18 | // q - quadratic bezier curveto
19 | // t - smooth quadratic bezier
20 | // a - elliptical arc (+ to add)
21 | // z - close path
22 | utils.M = function M(x,y){ return 'M' + x + ',' + y };
23 | utils.m = function m(x,y){ return 'm' + x + ',' + y };
24 | utils.L = function L(x,y){ return 'L' + x + ',' + y };
25 | utils.l = function l(x,y){ return 'l' + x + ',' + y };
26 | utils.H = function H(x){ return 'H' + x };
27 | utils.h = function h(x){ return 'h' + x };
28 | utils.V = function V(y){ return 'V' + y };
29 | utils.v = function v(y){ return 'v' + y };
30 | utils.C = function C(x1,y1,x2,y2,x,y){ return 'C' + x1 + ',' + y1 + ' ' + x2 + ',' + y2 + ' ' + x + ',' + y };
31 | utils.c = function c(x1,y1,x2,y2,x,y){ return 'c' + x1 + ',' + y1 + ' ' + x2 + ',' + y2 + ' ' + x + ',' + y };
32 | utils.S = function S(x2,y2,x,y){ return 'S' + x2 + ',' + y2 + ' ' + x + ',' + y };
33 | utils.s = function s(x2,y2,x,y){ return 's' + x2 + ',' + y2 + ' ' + x + ',' + y };
34 | utils.Q = function Q(x1,y1,x,y){ return 'Q' + x1 + ',' + y1 + ' ' + x + ',' + y };
35 | utils.q = function q(x1,y1,x,y){ return 'q' + x1 + ',' + y1 + ' ' + x + ',' + y };
36 | utils.T = function T(x,y){ return 'T' + x + ',' + y };
37 | utils.t = function t(x,y){ return 't' + x + ',' + y };
38 | utils.Z = function Z(){ return 'Z' };
39 | utils.z = function z(){ return 'z' };
40 |
41 | utils.angle = function angle(p1, p2){
42 | return Math.atan2(p2.y - p1.y, p2.x - p1.x)*180/Math.PI;
43 | }
44 |
45 | utils.parse = function(d){
46 | var operators = d.replace(/[\d,\-\s]+/g, '').split('');
47 | var points = [];
48 |
49 | var nums = d.replace(/[A-Za-z,]+/g, ' ').trim().replace(/\s\s+/g, ' ').split(' ');
50 |
51 | var f, i = -1;
52 | operators.forEach(function(key){
53 | if (typeof (f = utils[key]) === 'function') {
54 | if (f.length === 1) {
55 | points.push({ x: +nums[++i] })
56 | } else {
57 | for (var j = -1, l = f.length/2; ++j < l;) {
58 | points.push({ x: +nums[++i], y: +nums[++i] })
59 | }
60 | }
61 | }
62 | });
63 |
64 | return { operators: operators, points: points }
65 | }
66 |
67 | utils.generate = function(_){
68 | var p = _.points.slice();
69 | var str = [];
70 | var f;
71 | _.operators.forEach(function(key){
72 | if (typeof (f = utils[key]) === 'function') {
73 | var args = [];
74 | if (f.length === 1) {
75 | args.push(p.shift().x);
76 | } else {
77 | for (var i = -1, l = f.length/2; ++i < l;) {
78 | var point = p.shift();
79 | args.push(point.x, point.y);
80 | }
81 | }
82 | str.push(f.apply(null, args));
83 | }
84 | });
85 | return str.join(' ');
86 | }
87 |
88 | utils.inversePath = function(d){
89 | var _ = utils.parse(d);
90 | var ro = _.operators.reverse();
91 | var rp = _.points.reverse();
92 |
93 | //define first and last operators (M, Z)
94 | var first, last;
95 | first = ro.pop();
96 | if ((last = ro[0]).toLowerCase() === 'z') {
97 | ro.push(last);
98 | ro.shift();
99 | }
100 | ro.unshift(first);
101 |
102 | return utils.generate({ operators: ro, points: rp });
103 | }
104 |
105 | utils.join = function(){
106 | if (!arguments.length) return;
107 | return Array.prototype.join.call(arguments, ' ');
108 | }
109 |
110 | return utils;
111 | }
112 |
113 | exports.SVGPathUtils = SVGPathUtils;
114 |
115 | }));
--------------------------------------------------------------------------------
/build/svg-path-utils.min.js:
--------------------------------------------------------------------------------
1 | !function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(n.svg_path_utils=n.svg_path_utils||{})}(this,function(n){"use strict";var t=function(){var n={};return n.M=function(n,t){return"M"+n+","+t},n.m=function(n,t){return"m"+n+","+t},n.L=function(n,t){return"L"+n+","+t},n.l=function(n,t){return"l"+n+","+t},n.H=function(n){return"H"+n},n.h=function(n){return"h"+n},n.V=function(n){return"V"+n},n.v=function(n){return"v"+n},n.C=function(n,t,r,e,u,o){return"C"+n+","+t+" "+r+","+e+" "+u+","+o},n.c=function(n,t,r,e,u,o){return"c"+n+","+t+" "+r+","+e+" "+u+","+o},n.S=function(n,t,r,e){return"S"+n+","+t+" "+r+","+e},n.s=function(n,t,r,e){return"s"+n+","+t+" "+r+","+e},n.Q=function(n,t,r,e){return"Q"+n+","+t+" "+r+","+e},n.q=function(n,t,r,e){return"q"+n+","+t+" "+r+","+e},n.T=function(n,t){return"T"+n+","+t},n.t=function(n,t){return"t"+n+","+t},n.Z=function(){return"Z"},n.z=function(){return"z"},n.angle=function(n,t){return 180*Math.atan2(t.y-n.y,t.x-n.x)/Math.PI},n.parse=function(t){var r,e=t.replace(/[\d,\-\s]+/g,"").split(""),u=[],o=t.replace(/[A-Za-z,]+/g," ").trim().replace(/\s\s+/g," ").split(" "),i=-1;return e.forEach(function(t){if("function"==typeof(r=n[t]))if(1===r.length)u.push({x:+o[++i]});else for(var e=-1,f=r.length/2;++e