svgpath-umd

A UMD build of svgpath

Script này sẽ không được không được cài đặt trực tiếp. Nó là một thư viện cho các script khác để bao gồm các chỉ thị meta // @require https://update.greatest.deepsurf.us/scripts/525014/1527476/svgpath-umd.js

  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  3. typeof define === 'function' && define.amd ? define(factory) :
  4. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.SvgPath = factory());
  5. })(this, (() => {
  6. const epsilon = 0.0000000001;
  7. const torad = Math.PI / 180;
  8. class Ellipse {
  9. constructor(rx, ry, ax) {
  10. if (!(this instanceof Ellipse)) return new Ellipse(rx, ry, ax);
  11. this.rx = rx;
  12. this.ry = ry;
  13. this.ax = ax;
  14. }
  15. transform(m) {
  16. const c = Math.cos(this.ax * torad);
  17. const s = Math.sin(this.ax * torad);
  18. const ma = [
  19. this.rx * (m[0] * c + m[2] * s),
  20. this.rx * (m[1] * c + m[3] * s),
  21. this.ry * (-m[0] * s + m[2] * c),
  22. this.ry * (-m[1] * s + m[3] * c)
  23. ];
  24. const J = ma[0] * ma[0] + ma[2] * ma[2];
  25. const K = ma[1] * ma[1] + ma[3] * ma[3];
  26. let D = (
  27. (ma[0] - ma[3]) *
  28. (ma[0] - ma[3]) +
  29. (ma[2] + ma[1]) *
  30. (ma[2] + ma[1])
  31. ) * (
  32. (ma[0] + ma[3]) *
  33. (ma[0] + ma[3]) +
  34. (ma[2] - ma[1]) *
  35. (ma[2] - ma[1])
  36. );
  37. const JK = (J + K) / 2;
  38. if (D < epsilon * JK) {
  39. this.rx = this.ry = Math.sqrt(JK);
  40. this.ax = 0;
  41. return this;
  42. }
  43. const L = ma[0] * ma[1] + ma[2] * ma[3];
  44. D = Math.sqrt(D);
  45. const l1 = JK + D / 2;
  46. const l2 = JK - D / 2;
  47. this.ax = (Math.abs(L) < epsilon && Math.abs(l1 - K) < epsilon)
  48. ? 90
  49. : Math.atan(
  50. Math.abs(L) > Math.abs(l1 - K)
  51. ? (l1 - J) / L
  52. : L / (l1 - K)
  53. ) * 180 / Math.PI;
  54. if (this.ax >= 0) {
  55. this.rx = Math.sqrt(l1);
  56. this.ry = Math.sqrt(l2);
  57. } else {
  58. this.ax += 90;
  59. this.rx = Math.sqrt(l2);
  60. this.ry = Math.sqrt(l1);
  61. }
  62. return this;
  63. }
  64. isDegenerate() {
  65. return (this.rx < epsilon * this.ry || this.ry < epsilon * this.rx);
  66. }
  67. }
  68. const TAU = Math.PI * 2;
  69. const unit_vector_angle = (ux, uy, vx, vy) => {
  70. const sign = (ux * vy - uy * vx < 0) ? -1 : 1;
  71. let dot = ux * vx + uy * vy;
  72. if (dot > 1.0) { dot = 1.0; }
  73. if (dot < -1) { dot = -1; }
  74. return sign * Math.acos(dot);
  75. };
  76. const get_arc_center = (x1, y1, x2, y2, fa, fs, rx, ry, sin_phi, cos_phi) => {
  77. const x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2;
  78. const y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2;
  79. const rx_sq = rx * rx;
  80. const ry_sq = ry * ry;
  81. const x1p_sq = x1p * x1p;
  82. const y1p_sq = y1p * y1p;
  83. let radicant = (rx_sq * ry_sq) - (rx_sq * y1p_sq) - (ry_sq * x1p_sq);
  84. if (radicant < 0) {
  85. radicant = 0;
  86. }
  87. radicant /= (rx_sq * y1p_sq) + (ry_sq * x1p_sq);
  88. radicant = Math.sqrt(radicant) * (fa === fs ? -1 : 1);
  89. const cxp = radicant * rx/ry * y1p;
  90. const cyp = radicant * -ry/rx * x1p;
  91. const cx = cos_phi*cxp - sin_phi*cyp + (x1+x2)/2;
  92. const cy = sin_phi*cxp + cos_phi*cyp + (y1+y2)/2;
  93. const v1x = (x1p - cxp) / rx;
  94. const v1y = (y1p - cyp) / ry;
  95. const v2x = (-x1p - cxp) / rx;
  96. const v2y = (-y1p - cyp) / ry;
  97. const theta1 = unit_vector_angle(1, 0, v1x, v1y);
  98. let delta_theta = unit_vector_angle(v1x, v1y, v2x, v2y);
  99. if (fs === 0 && delta_theta > 0) {
  100. delta_theta -= TAU;
  101. }
  102. if (fs === 1 && delta_theta < 0) {
  103. delta_theta += TAU;
  104. }
  105. return [ cx, cy, theta1, delta_theta ];
  106. };
  107. const approximate_unit_arc = (theta1, delta_theta) => {
  108. const alpha = 4/3 * Math.tan(delta_theta/4);
  109. const x1 = Math.cos(theta1);
  110. const y1 = Math.sin(theta1);
  111. const x2 = Math.cos(theta1 + delta_theta);
  112. const y2 = Math.sin(theta1 + delta_theta);
  113. return [ x1, y1, x1 - y1*alpha, y1 + x1*alpha, x2 + y2*alpha, y2 - x2*alpha, x2, y2 ];
  114. };
  115. const a2c = (x1, y1, x2, y2, fa, fs, rx, ry, phi) => {
  116. const sin_phi = Math.sin(phi * TAU / 360);
  117. const cos_phi = Math.cos(phi * TAU / 360);
  118. const x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2;
  119. const y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2;
  120. if (x1p === 0 && y1p === 0) return [];
  121. if (rx === 0 || ry === 0) return [];
  122. rx = Math.abs(rx);
  123. ry = Math.abs(ry);
  124. const lambda = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry);
  125. if (lambda > 1) {
  126. rx *= Math.sqrt(lambda);
  127. ry *= Math.sqrt(lambda);
  128. }
  129. const cc = get_arc_center(x1, y1, x2, y2, fa, fs, rx, ry, sin_phi, cos_phi);
  130. const result = [];
  131. let theta1 = cc[2];
  132. let delta_theta = cc[3];
  133. const segments = Math.max(Math.ceil(Math.abs(delta_theta) / (TAU / 4)), 1);
  134. delta_theta /= segments;
  135. for (let i = 0; i < segments; i++) {
  136. result.push(approximate_unit_arc(theta1, delta_theta));
  137. theta1 += delta_theta;
  138. }
  139. return result.map(curve => {
  140. for (let i = 0; i < curve.length; i += 2) {
  141. let x = curve[i + 0];
  142. let y = curve[i + 1];
  143. x *= rx;
  144. y *= ry;
  145. const xp = cos_phi*x - sin_phi*y;
  146. const yp = sin_phi*x + cos_phi*y;
  147. curve[i + 0] = xp + cc[0];
  148. curve[i + 1] = yp + cc[1];
  149. }
  150. return curve;
  151. });
  152. };
  153. const combine = (m1, m2) => {
  154. return [
  155. m1[0] * m2[0] + m1[2] * m2[1],
  156. m1[1] * m2[0] + m1[3] * m2[1],
  157. m1[0] * m2[2] + m1[2] * m2[3],
  158. m1[1] * m2[2] + m1[3] * m2[3],
  159. m1[0] * m2[4] + m1[2] * m2[5] + m1[4],
  160. m1[1] * m2[4] + m1[3] * m2[5] + m1[5]
  161. ];
  162. };
  163. class Matrix {
  164. constructor() {
  165. if (!(this instanceof Matrix)) return new Matrix();
  166. this.queue = [];
  167. this.cache = null;
  168. }
  169. matrix(m) {
  170. if (m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] === 0 && m[5] === 0) return this;
  171. this.cache = null;
  172. this.queue.push(m);
  173. return this;
  174. }
  175. translate(tx, ty) {
  176. if (tx !== 0 || ty !== 0) {
  177. this.cache = null;
  178. this.queue.push([1, 0, 0, 1, tx, ty]);
  179. }
  180. return this;
  181. }
  182. scale(sx, sy) {
  183. if (sx !== 1 || sy !== 1) {
  184. this.cache = null;
  185. this.queue.push([sx, 0, 0, sy, 0, 0]);
  186. }
  187. return this;
  188. }
  189. rotate(angle, rx, ry) {
  190. if (angle !== 0) {
  191. this.translate(rx, ry);
  192. const rad = angle * Math.PI / 180;
  193. const cos = Math.cos(rad);
  194. const sin = Math.sin(rad);
  195. this.queue.push([cos, sin, -sin, cos, 0, 0]);
  196. this.cache = null;
  197. this.translate(-rx, -ry);
  198. }
  199. return this;
  200. }
  201. skewX(angle) {
  202. if (angle !== 0) {
  203. this.cache = null;
  204. this.queue.push([1, 0, Math.tan(angle * Math.PI / 180), 1, 0, 0]);
  205. }
  206. return this;
  207. }
  208. skewY(angle) {
  209. if (angle !== 0) {
  210. this.cache = null;
  211. this.queue.push([1, Math.tan(angle * Math.PI / 180), 0, 1, 0, 0]);
  212. }
  213. return this;
  214. }
  215. toArray() {
  216. if (this.cache) return this.cache;
  217. if (!this.queue.length) {
  218. this.cache = [1, 0, 0, 1, 0, 0];
  219. return this.cache;
  220. }
  221. this.cache = this.queue[0];
  222. if (this.queue.length === 1) {
  223. return this.cache;
  224. }
  225. for (let i = 1; i < this.queue.length; i++) {
  226. this.cache = combine(this.cache, this.queue[i]);
  227. }
  228. return this.cache;
  229. }
  230. calc(x, y, isRelative) {
  231. if (!this.queue.length) return [x, y];
  232. if (!this.cache) {
  233. this.cache = this.toArray();
  234. }
  235. const m = this.cache;
  236. return [
  237. x * m[0] + y * m[2] + (isRelative ? 0 : m[4]),
  238. x * m[1] + y * m[3] + (isRelative ? 0 : m[5])
  239. ];
  240. }
  241. }
  242. const paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0 };
  243. const SPECIAL_SPACES = [
  244. 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006,
  245. 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF
  246. ];
  247. const isSpace = ch => (
  248. (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029) ||
  249. (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) ||
  250. (ch >= 0x1680 && SPECIAL_SPACES.indexOf(ch) >= 0)
  251. );
  252. const isCommand = code => {
  253. switch (code | 0x20) {
  254. case 0x6D:
  255. case 0x7A:
  256. case 0x6C:
  257. case 0x68:
  258. case 0x76:
  259. case 0x63:
  260. case 0x73:
  261. case 0x71:
  262. case 0x74:
  263. case 0x61:
  264. case 0x72:
  265. return true;
  266. }
  267. return false;
  268. };
  269. const isArc = code => (code | 0x20) === 0x61;
  270. const isDigit = code => (code >= 48 && code <= 57);
  271. const isDigitStart = code => (
  272. (code >= 48 && code <= 57) ||
  273. code === 0x2B ||
  274. code === 0x2D ||
  275. code === 0x2E
  276. );
  277. class State {
  278. constructor(path) {
  279. this.index = 0;
  280. this.path = path;
  281. this.max = path.length;
  282. this.result = [];
  283. this.param = 0.0;
  284. this.err = '';
  285. this.segmentStart = 0;
  286. this.data = [];
  287. }
  288. }
  289. const skipSpaces = state => {
  290. while (state.index < state.max && isSpace(state.path.charCodeAt(state.index))) state.index++;
  291. };
  292. const scanFlag = state => {
  293. const ch = state.path.charCodeAt(state.index);
  294. if (ch === 0x30) {
  295. state.param = 0;
  296. state.index++;
  297. return;
  298. }
  299. if (ch === 0x31) {
  300. state.param = 1;
  301. state.index++;
  302. return;
  303. }
  304. state.err = `SvgPath: arc flag can be 0 or 1 only (at pos ${ state.index })`;
  305. };
  306. const scanParam = state => {
  307. const start = state.index;
  308. let index = start;
  309. const max = state.max;
  310. let zeroFirst = false;
  311. let hasCeiling = false;
  312. let hasDecimal = false;
  313. let hasDot = false;
  314. let ch;
  315. if (index >= max) {
  316. state.err = `SvgPath: missed param (at pos ${ index })`;
  317. return;
  318. }
  319. ch = state.path.charCodeAt(index);
  320. if (ch === 0x2B || ch === 0x2D) {
  321. index++;
  322. ch = (index < max) ? state.path.charCodeAt(index) : 0;
  323. }
  324. if (!isDigit(ch) && ch !== 0x2E) {
  325. state.err = `SvgPath: param should start with 0..9 or \`.\` (at pos ${ index })`;
  326. return;
  327. }
  328. if (ch !== 0x2E) {
  329. zeroFirst = (ch === 0x30);
  330. index++;
  331. ch = (index < max) ? state.path.charCodeAt(index) : 0;
  332. if (zeroFirst && index < max) {
  333. if (ch && isDigit(ch)) {
  334. state.err = `SvgPath: numbers started with \`0\` such as \`09\` are illegal (at pos ${ start })`;
  335. return;
  336. }
  337. }
  338. while (index < max && isDigit(state.path.charCodeAt(index))) {
  339. index++;
  340. hasCeiling = true;
  341. }
  342. ch = (index < max) ? state.path.charCodeAt(index) : 0;
  343. }
  344. if (ch === 0x2E) {
  345. hasDot = true;
  346. index++;
  347. while (isDigit(state.path.charCodeAt(index))) {
  348. index++;
  349. hasDecimal = true;
  350. }
  351. ch = (index < max) ? state.path.charCodeAt(index) : 0;
  352. }
  353. if (ch === 0x65 || ch === 0x45) {
  354. if (hasDot && !hasCeiling && !hasDecimal) {
  355. state.err = `SvgPath: invalid float exponent (at pos ${ index })`;
  356. return;
  357. }
  358. index++;
  359. ch = (index < max) ? state.path.charCodeAt(index) : 0;
  360. if (ch === 0x2B || ch === 0x2D) index++;
  361. if (index < max && isDigit(state.path.charCodeAt(index))) {
  362. while (index < max && isDigit(state.path.charCodeAt(index))) index++;
  363. } else {
  364. state.err = `SvgPath: invalid float exponent (at pos ${ index })`;
  365. return;
  366. }
  367. }
  368. state.index = index;
  369. state.param = parseFloat(state.path.slice(start, index)) + 0.0;
  370. };
  371. const finalizeSegment = state => {
  372. let cmd = state.path[state.segmentStart];
  373. let cmdLC = cmd.toLowerCase();
  374. let params = state.data;
  375. if (cmdLC === 'm' && params.length > 2) {
  376. state.result.push([ cmd, params[0], params[1] ]);
  377. params = params.slice(2);
  378. cmdLC = 'l';
  379. cmd = (cmd === 'm') ? 'l' : 'L';
  380. }
  381. if (cmdLC === 'r') state.result.push([ cmd ].concat(params));
  382. else {
  383. while (params.length >= paramCounts[cmdLC]) {
  384. state.result.push([ cmd ].concat(params.splice(0, paramCounts[cmdLC])));
  385. if (!paramCounts[cmdLC]) break;
  386. }
  387. }
  388. };
  389. const scanSegment = state => {
  390. const max = state.max;
  391. state.segmentStart = state.index;
  392. const cmdCode = state.path.charCodeAt(state.index);
  393. const is_arc = isArc(cmdCode);
  394. if (!isCommand(cmdCode)) {
  395. state.err = `SvgPath: bad command ${ state.path[state.index] } (at pos ${ state.index })`;
  396. return;
  397. }
  398. const need_params = paramCounts[state.path[state.index].toLowerCase()];
  399. state.index++;
  400. skipSpaces(state);
  401. state.data = [];
  402. if (!need_params) {
  403. finalizeSegment(state);
  404. return;
  405. }
  406. let comma_found = false;
  407. for (;;) {
  408. for (let i = need_params; i > 0; i--) {
  409. if (is_arc && (i === 3 || i === 4)) scanFlag(state);
  410. else scanParam(state);
  411. if (state.err.length) {
  412. finalizeSegment(state);
  413. return;
  414. }
  415. state.data.push(state.param);
  416. skipSpaces(state);
  417. comma_found = false;
  418. if (state.index < max && state.path.charCodeAt(state.index) === 0x2C) {
  419. state.index++;
  420. skipSpaces(state);
  421. comma_found = true;
  422. }
  423. }
  424. if (comma_found) continue;
  425. if (state.index >= state.max) break;
  426. if (!isDigitStart(state.path.charCodeAt(state.index))) break;
  427. }
  428. finalizeSegment(state);
  429. };
  430. const pathParse = svgPath => {
  431. const state = new State(svgPath);
  432. const max = state.max;
  433. skipSpaces(state);
  434. while (state.index < max && !state.err.length) scanSegment(state);
  435. if (state.result.length) {
  436. if ('mM'.indexOf(state.result[0][0]) < 0) {
  437. state.err = 'SvgPath: string should start with `M` or `m`';
  438. state.result = [];
  439. } else {
  440. state.result[0][0] = 'M';
  441. }
  442. }
  443. return {
  444. err: state.err,
  445. segments: state.result
  446. };
  447. };
  448. const operations = {
  449. matrix: true,
  450. scale: true,
  451. rotate: true,
  452. translate: true,
  453. skewX: true,
  454. skewY: true
  455. };
  456. const CMD_SPLIT_RE = /\s*(matrix|translate|scale|rotate|skewX|skewY)\s*\(\s*(.+?)\s*\)[\s,]*/;
  457. const PARAMS_SPLIT_RE = /[\s,]+/;
  458. const transformParse = transformString => {
  459. const matrix = new Matrix();
  460. let cmd;
  461. let params;
  462. transformString.split(CMD_SPLIT_RE).forEach(item => {
  463. if (!item.length) return;
  464. if (typeof operations[item] !== 'undefined') {
  465. cmd = item;
  466. return;
  467. }
  468. params = item.split(PARAMS_SPLIT_RE).map(i => +i || 0);
  469. switch (cmd) {
  470. case 'matrix':
  471. if (params.length === 6) matrix.matrix(params);
  472. return;
  473. case 'scale':
  474. if (params.length === 1) matrix.scale(params[0], params[0]);
  475. else if (params.length === 2) matrix.scale(params[0], params[1]);
  476. return;
  477. case 'rotate':
  478. if (params.length === 1) matrix.rotate(params[0], 0, 0);
  479. else if (params.length === 3) matrix.rotate(params[0], params[1], params[2]);
  480. return;
  481. case 'translate':
  482. if (params.length === 1) matrix.translate(params[0], 0);
  483. else if (params.length === 2) matrix.translate(params[0], params[1]);
  484. return;
  485. case 'skewX':
  486. if (params.length === 1) matrix.skewX(params[0]);
  487. return;
  488. case 'skewY':
  489. if (params.length === 1) matrix.skewY(params[0]);
  490. return;
  491. }
  492. });
  493. return matrix;
  494. };
  495. class SvgPath {
  496. constructor(path) {
  497. if (!(this instanceof SvgPath)) return new SvgPath(path);
  498. const pstate = pathParse(path);
  499. this.segments = pstate.segments;
  500. this.err = pstate.err;
  501. this.__stack = [];
  502. }
  503. static from(src) {
  504. if (typeof src === 'string') return new SvgPath(src);
  505. if (src instanceof SvgPath) {
  506. const s = new SvgPath('');
  507. s.err = src.err;
  508. s.segments = src.segments.map(sgm => sgm.slice());
  509. s.__stack = src.__stack.map(m => new Matrix().matrix(m.toArray()));
  510. return s;
  511. }
  512. throw new Error(`SvgPath.from: invalid param type ${ src}`);
  513. }
  514. __matrix(m) {
  515. const self = this;
  516. if (!m.queue.length) return;
  517. this.iterate((s, index, x, y) => {
  518. let p;
  519. let result;
  520. let name;
  521. let isRelative;
  522. switch (s[0]) {
  523. case 'v':
  524. p = m.calc(0, s[1], true);
  525. result = (p[0] === 0) ? ['v', p[1]] : ['l', p[0], p[1]];
  526. break;
  527. case 'V':
  528. p = m.calc(x, s[1], false);
  529. result = (p[0] === m.calc(x, y, false)[0]) ? ['V', p[1]] : ['L', p[0], p[1]];
  530. break;
  531. case 'h':
  532. p = m.calc(s[1], 0, true);
  533. result = (p[1] === 0) ? ['h', p[0]] : ['l', p[0], p[1]];
  534. break;
  535. case 'H':
  536. p = m.calc(s[1], y, false);
  537. result = (p[1] === m.calc(x, y, false)[1]) ? ['H', p[0]] : ['L', p[0], p[1]];
  538. break;
  539. case 'a':
  540. case 'A': {
  541. /*if ((s[0] === 'A' && s[6] === x && s[7] === y) ||
  542. (s[0] === 'a' && s[6] === 0 && s[7] === 0)) {
  543. return [];
  544. }*/
  545. const ma = m.toArray();
  546. const e = new Ellipse(s[1], s[2], s[3]).transform(ma);
  547. if (ma[0] * ma[3] - ma[1] * ma[2] < 0) {
  548. s[5] = s[5] ? '0' : '1';
  549. }
  550. p = m.calc(s[6], s[7], s[0] === 'a');
  551. if (
  552. (s[0] === 'A' && s[6] === x && s[7] === y) ||
  553. (s[0] === 'a' && s[6] === 0 && s[7] === 0)
  554. ) {
  555. result = [s[0] === 'a' ? 'l' : 'L', p[0], p[1]];
  556. break;
  557. }
  558. result = e.isDegenerate() ? [s[0] === 'a' ? 'l' : 'L', p[0], p[1]] : [s[0], e.rx, e.ry, e.ax, s[4], s[5], p[0], p[1]];
  559. break;
  560. }
  561. case 'm':
  562. isRelative = index > 0;
  563. p = m.calc(s[1], s[2], isRelative);
  564. result = ['m', p[0], p[1]];
  565. break;
  566. default:
  567. name = s[0];
  568. result = [name];
  569. isRelative = (name.toLowerCase() === name);
  570. for (let i = 1; i < s.length; i += 2) {
  571. p = m.calc(s[i], s[i + 1], isRelative);
  572. result.push(p[0], p[1]);
  573. }
  574. }
  575. self.segments[index] = result;
  576. }, true);
  577. }
  578. __evaluateStack() {
  579. if (!this.__stack.length) return;
  580. if (this.__stack.length === 1) {
  581. this.__matrix(this.__stack[0]);
  582. this.__stack = [];
  583. return;
  584. }
  585. const m = new Matrix();
  586. let i = this.__stack.length;
  587. while (--i >= 0) m.matrix(this.__stack[i].toArray());
  588. this.__matrix(m);
  589. this.__stack = [];
  590. }
  591. toString() {
  592. let result = '';
  593. let prevCmd = '';
  594. let cmdSkipped = false;
  595. this.__evaluateStack();
  596. for (let i = 0, len = this.segments.length; i < len; i++) {
  597. const segment = this.segments[i];
  598. const cmd = segment[0];
  599. if (cmd !== prevCmd || cmd === 'm' || cmd === 'M') {
  600. if (cmd === 'm' && prevCmd === 'z') result += ' ';
  601. result += cmd;
  602. cmdSkipped = false;
  603. } else {
  604. cmdSkipped = true;
  605. }
  606. for (let pos = 1; pos < segment.length; pos++) {
  607. const val = segment[pos];
  608. if (pos === 1) {
  609. if (cmdSkipped && val >= 0) result += ' ';
  610. } else if (val >= 0) result += ' ';
  611. result += val;
  612. }
  613. prevCmd = cmd;
  614. }
  615. return result;
  616. }
  617. translate(x, y) {
  618. this.__stack.push(new Matrix().translate(x, y || 0));
  619. return this;
  620. }
  621. scale(sx, sy) {
  622. this.__stack.push(new Matrix().scale(sx, (!sy && (sy !== 0)) ? sx : sy));
  623. return this;
  624. }
  625. rotate(angle, rx, ry) {
  626. this.__stack.push(new Matrix().rotate(angle, rx || 0, ry || 0));
  627. return this;
  628. }
  629. skewX(degrees) {
  630. this.__stack.push(new Matrix().skewX(degrees));
  631. return this;
  632. }
  633. skewY(degrees) {
  634. this.__stack.push(new Matrix().skewY(degrees));
  635. return this;
  636. }
  637. matrix(m) {
  638. this.__stack.push(new Matrix().matrix(m));
  639. return this;
  640. }
  641. transform(transformString) {
  642. if (!transformString.trim()) return this;
  643. this.__stack.push(transformParse(transformString));
  644. return this;
  645. }
  646. round(d) {
  647. let contourStartDeltaX = 0;
  648. let contourStartDeltaY = 0;
  649. let deltaX = 0;
  650. let deltaY = 0;
  651. let l;
  652. d = d || 0;
  653. this.__evaluateStack();
  654. this.segments.forEach(s => {
  655. const isRelative = (s[0].toLowerCase() === s[0]);
  656. switch (s[0]) {
  657. case 'H':
  658. case 'h':
  659. if (isRelative) { s[1] += deltaX; }
  660. deltaX = s[1] - s[1].toFixed(d);
  661. s[1] = +s[1].toFixed(d);
  662. return;
  663. case 'V':
  664. case 'v':
  665. if (isRelative) { s[1] += deltaY; }
  666. deltaY = s[1] - s[1].toFixed(d);
  667. s[1] = +s[1].toFixed(d);
  668. return;
  669. case 'Z':
  670. case 'z':
  671. deltaX = contourStartDeltaX;
  672. deltaY = contourStartDeltaY;
  673. return;
  674. case 'M':
  675. case 'm':
  676. if (isRelative) {
  677. s[1] += deltaX;
  678. s[2] += deltaY;
  679. }
  680. deltaX = s[1] - s[1].toFixed(d);
  681. deltaY = s[2] - s[2].toFixed(d);
  682. contourStartDeltaX = deltaX;
  683. contourStartDeltaY = deltaY;
  684. s[1] = +s[1].toFixed(d);
  685. s[2] = +s[2].toFixed(d);
  686. return;
  687. case 'A':
  688. case 'a':
  689. if (isRelative) {
  690. s[6] += deltaX;
  691. s[7] += deltaY;
  692. }
  693. deltaX = s[6] - s[6].toFixed(d);
  694. deltaY = s[7] - s[7].toFixed(d);
  695. s[1] = +s[1].toFixed(d);
  696. s[2] = +s[2].toFixed(d);
  697. s[3] = +s[3].toFixed(d + 2);
  698. s[6] = +s[6].toFixed(d);
  699. s[7] = +s[7].toFixed(d);
  700. return;
  701. default:
  702. l = s.length;
  703. if (isRelative) {
  704. s[l - 2] += deltaX;
  705. s[l - 1] += deltaY;
  706. }
  707. deltaX = s[l - 2] - s[l - 2].toFixed(d);
  708. deltaY = s[l - 1] - s[l - 1].toFixed(d);
  709. s.forEach((val, i) => {
  710. if (!i) return;
  711. s[i] = +s[i].toFixed(d);
  712. });
  713. return;
  714. }
  715. });
  716. return this;
  717. }
  718. iterate(iterator, keepLazyStack) {
  719. const segments = this.segments;
  720. const replacements = {};
  721. let needReplace = false;
  722. let lastX = 0;
  723. let lastY = 0;
  724. let countourStartX = 0;
  725. let countourStartY = 0;
  726. if (!keepLazyStack) this.__evaluateStack();
  727. segments.forEach((s, index) => {
  728. const res = iterator(s, index, lastX, lastY);
  729. if (Array.isArray(res)) {
  730. replacements[index] = res;
  731. needReplace = true;
  732. }
  733. const isRelative = (s[0] === s[0].toLowerCase());
  734. switch (s[0]) {
  735. case 'm':
  736. case 'M':
  737. lastX = s[1] + (isRelative ? lastX : 0);
  738. lastY = s[2] + (isRelative ? lastY : 0);
  739. countourStartX = lastX;
  740. countourStartY = lastY;
  741. return;
  742. case 'h':
  743. case 'H':
  744. lastX = s[1] + (isRelative ? lastX : 0);
  745. return;
  746. case 'v':
  747. case 'V':
  748. lastY = s[1] + (isRelative ? lastY : 0);
  749. return;
  750. case 'z':
  751. case 'Z':
  752. lastX = countourStartX;
  753. lastY = countourStartY;
  754. return;
  755. default:
  756. lastX = s[s.length - 2] + (isRelative ? lastX : 0);
  757. lastY = s[s.length - 1] + (isRelative ? lastY : 0);
  758. }
  759. });
  760. if (!needReplace) return this;
  761. const newSegments = [];
  762. for (let i = 0; i < segments.length; i++) {
  763. if (typeof replacements[i] !== 'undefined') {
  764. for (let j = 0; j < replacements[i].length; j++) {
  765. newSegments.push(replacements[i][j]);
  766. }
  767. } else {
  768. newSegments.push(segments[i]);
  769. }
  770. }
  771. this.segments = newSegments;
  772. return this;
  773. }
  774. abs() {
  775. this.iterate((s, index, x, y) => {
  776. const name = s[0];
  777. const nameUC = name.toUpperCase();
  778. if (name === nameUC) return;
  779. s[0] = nameUC;
  780. switch (name) {
  781. case 'v':
  782. s[1] += y;
  783. return;
  784. case 'a':
  785. s[6] += x;
  786. s[7] += y;
  787. return;
  788. default:
  789. for (let i = 1; i < s.length; i++) {
  790. s[i] += i % 2 ? x : y;
  791. }
  792. }
  793. }, true);
  794. return this;
  795. }
  796. rel() {
  797. this.iterate((s, index, x, y) => {
  798. const name = s[0];
  799. const nameLC = name.toLowerCase();
  800. if (name === nameLC) return;
  801. if (index === 0 && name === 'M') return;
  802. s[0] = nameLC;
  803. switch (name) {
  804. case 'V':
  805. s[1] -= y;
  806. return;
  807. case 'A':
  808. s[6] -= x;
  809. s[7] -= y;
  810. return;
  811. default:
  812. for (let i = 1; i < s.length; i++) {
  813. s[i] -= i % 2 ? x : y;
  814. }
  815. }
  816. }, true);
  817. return this;
  818. }
  819. unarc() {
  820. this.iterate((s, index, x, y) => {
  821. let nextX;
  822. let nextY;
  823. const result = [];
  824. const name = s[0];
  825. if (name !== 'A' && name !== 'a') return null;
  826. if (name === 'a') {
  827. nextX = x + s[6];
  828. nextY = y + s[7];
  829. } else {
  830. nextX = s[6];
  831. nextY = s[7];
  832. }
  833. const new_segments = a2c(x, y, nextX, nextY, s[4], s[5], s[1], s[2], s[3]);
  834. if (new_segments.length === 0) return [[s[0] === 'a' ? 'l' : 'L', s[6], s[7]]];
  835. new_segments.forEach(s => result.push(['C', s[2], s[3], s[4], s[5], s[6], s[7]]));
  836. return result;
  837. });
  838. return this;
  839. }
  840. unshort() {
  841. const segments = this.segments;
  842. let prevControlX;
  843. let prevControlY;
  844. let prevSegment;
  845. let curControlX;
  846. let curControlY;
  847. this.iterate((s, idx, x, y) => {
  848. const name = s[0];
  849. const nameUC = name.toUpperCase();
  850. let isRelative;
  851. if (!idx) return;
  852. if (nameUC === 'T') {
  853. isRelative = (name === 't');
  854. prevSegment = segments[idx - 1];
  855. if (prevSegment[0] === 'Q') {
  856. prevControlX = prevSegment[1] - x;
  857. prevControlY = prevSegment[2] - y;
  858. } else if (prevSegment[0] === 'q') {
  859. prevControlX = prevSegment[1] - prevSegment[3];
  860. prevControlY = prevSegment[2] - prevSegment[4];
  861. } else {
  862. prevControlX = 0;
  863. prevControlY = 0;
  864. }
  865. curControlX = -prevControlX;
  866. curControlY = -prevControlY;
  867. if (!isRelative) {
  868. curControlX += x;
  869. curControlY += y;
  870. }
  871. segments[idx] = [
  872. isRelative ? 'q' : 'Q',
  873. curControlX, curControlY,
  874. s[1], s[2]
  875. ];
  876. } else if (nameUC === 'S') {
  877. isRelative = (name === 's');
  878. prevSegment = segments[idx - 1];
  879. if (prevSegment[0] === 'C') {
  880. prevControlX = prevSegment[3] - x;
  881. prevControlY = prevSegment[4] - y;
  882. } else if (prevSegment[0] === 'c') {
  883. prevControlX = prevSegment[3] - prevSegment[5];
  884. prevControlY = prevSegment[4] - prevSegment[6];
  885. } else {
  886. prevControlX = 0;
  887. prevControlY = 0;
  888. }
  889. curControlX = -prevControlX;
  890. curControlY = -prevControlY;
  891. if (!isRelative) {
  892. curControlX += x;
  893. curControlY += y;
  894. }
  895. segments[idx] = [
  896. isRelative ? 'c' : 'C',
  897. curControlX, curControlY,
  898. s[1], s[2], s[3], s[4]
  899. ];
  900. }
  901. });
  902. return this;
  903. }
  904. }
  905. return SvgPath;
  906. }));