path-data-parser-umd

UMD version of path-data-parser

目前为 2024-03-18 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/490146/1344867/path-data-parser-umd.js

  1. (function (global, factory) {
  2. global = typeof globalThis !== 'undefined' ? globalThis : global || self;
  3. factory(global.pathDataParser = {});
  4. })(this, (function (exports) { 'use strict';
  5.  
  6. const COMMAND = 0;
  7. const NUMBER = 1;
  8. const EOD = 2;
  9. const PARAMS = {A: 7, a: 7, C: 6, c: 6, H: 1, h: 1, L: 2, l: 2, M: 2, m: 2, Q: 4, q: 4, S: 4, s: 4, T: 2, t: 2, V: 1, v: 1, Z: 0, z: 0};
  10. function tokenize(d) {
  11. const tokens = new Array();
  12. while (d !== "") {
  13. if (d.match(/^([ \t\r\n,]+)/)) {
  14. d = d.substr(RegExp.$1.length);
  15. } else if (d.match(/^([aAcChHlLmMqQsStTvVzZ])/)) {
  16. tokens[tokens.length] = {type: COMMAND, text: RegExp.$1};
  17. d = d.substr(RegExp.$1.length);
  18. } else if (d.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/)) {
  19. tokens[tokens.length] = {type: NUMBER, text: `${parseFloat(RegExp.$1)}`};
  20. d = d.substr(RegExp.$1.length);
  21. } else {
  22. return [];
  23. }
  24. }
  25. tokens[tokens.length] = {type: EOD, text: ""};
  26. return tokens;
  27. }
  28. function isType(token, type) {
  29. return token.type === type;
  30. }
  31. function parsePath(d) {
  32. const segments = [];
  33. const tokens = tokenize(d);
  34. let mode = "BOD";
  35. let index = 0;
  36. let token = tokens[index];
  37. while (!isType(token, EOD)) {
  38. let paramsCount = 0;
  39. const params = [];
  40. if (mode === "BOD") {
  41. if (token.text === "M" || token.text === "m") {
  42. index++;
  43. paramsCount = PARAMS[token.text];
  44. mode = token.text;
  45. } else {
  46. return parsePath("M0,0" + d);
  47. }
  48. } else if (isType(token, NUMBER)) {
  49. paramsCount = PARAMS[mode];
  50. } else {
  51. index++;
  52. paramsCount = PARAMS[token.text];
  53. mode = token.text;
  54. }
  55. if (index + paramsCount < tokens.length) {
  56. for (let i = index; i < index + paramsCount; i++) {
  57. const numbeToken = tokens[i];
  58. if (isType(numbeToken, NUMBER)) {
  59. params[params.length] = +numbeToken.text;
  60. } else {
  61. throw new Error("Param not a number: " + mode + "," + numbeToken.text);
  62. }
  63. }
  64. if (typeof PARAMS[mode] === "number") {
  65. const segment = {key: mode, data: params};
  66. segments.push(segment);
  67. index += paramsCount;
  68. token = tokens[index];
  69. if (mode === "M")
  70. mode = "L";
  71. if (mode === "m")
  72. mode = "l";
  73. } else {
  74. throw new Error("Bad segment: " + mode);
  75. }
  76. } else {
  77. throw new Error("Path data ended short");
  78. }
  79. }
  80. return segments;
  81. }
  82. function serialize(segments) {
  83. const tokens = [];
  84. for (const {key, data} of segments) {
  85. tokens.push(key);
  86. switch (key) {
  87. case "C":
  88. case "c":
  89. tokens.push(data[0], `${data[1]},`, data[2], `${data[3]},`, data[4], data[5]);
  90. break;
  91. case "S":
  92. case "s":
  93. case "Q":
  94. case "q":
  95. tokens.push(data[0], `${data[1]},`, data[2], data[3]);
  96. break;
  97. default:
  98. tokens.push(...data);
  99. break;
  100. }
  101. }
  102. return tokens.join(" ");
  103. }
  104. function absolutize(segments) {
  105. let cx = 0, cy = 0;
  106. let subx = 0, suby = 0;
  107. const out = [];
  108. for (const {key, data} of segments) {
  109. switch (key) {
  110. case "M":
  111. out.push({key: "M", data: [...data]});
  112. [cx, cy] = data;
  113. [subx, suby] = data;
  114. break;
  115. case "m":
  116. cx += data[0];
  117. cy += data[1];
  118. out.push({key: "M", data: [cx, cy]});
  119. subx = cx;
  120. suby = cy;
  121. break;
  122. case "L":
  123. out.push({key: "L", data: [...data]});
  124. [cx, cy] = data;
  125. break;
  126. case "l":
  127. cx += data[0];
  128. cy += data[1];
  129. out.push({key: "L", data: [cx, cy]});
  130. break;
  131. case "C":
  132. out.push({key: "C", data: [...data]});
  133. cx = data[4];
  134. cy = data[5];
  135. break;
  136. case "c": {
  137. const newdata = data.map((d, i) => i % 2 ? d + cy : d + cx);
  138. out.push({key: "C", data: newdata});
  139. cx = newdata[4];
  140. cy = newdata[5];
  141. break;
  142. }
  143. case "Q":
  144. out.push({key: "Q", data: [...data]});
  145. cx = data[2];
  146. cy = data[3];
  147. break;
  148. case "q": {
  149. const newdata = data.map((d, i) => i % 2 ? d + cy : d + cx);
  150. out.push({key: "Q", data: newdata});
  151. cx = newdata[2];
  152. cy = newdata[3];
  153. break;
  154. }
  155. case "A":
  156. out.push({key: "A", data: [...data]});
  157. cx = data[5];
  158. cy = data[6];
  159. break;
  160. case "a":
  161. cx += data[5];
  162. cy += data[6];
  163. out.push({key: "A", data: [data[0], data[1], data[2], data[3], data[4], cx, cy]});
  164. break;
  165. case "H":
  166. out.push({key: "H", data: [...data]});
  167. cx = data[0];
  168. break;
  169. case "h":
  170. cx += data[0];
  171. out.push({key: "H", data: [cx]});
  172. break;
  173. case "V":
  174. out.push({key: "V", data: [...data]});
  175. cy = data[0];
  176. break;
  177. case "v":
  178. cy += data[0];
  179. out.push({key: "V", data: [cy]});
  180. break;
  181. case "S":
  182. out.push({key: "S", data: [...data]});
  183. cx = data[2];
  184. cy = data[3];
  185. break;
  186. case "s": {
  187. const newdata = data.map((d, i) => i % 2 ? d + cy : d + cx);
  188. out.push({key: "S", data: newdata});
  189. cx = newdata[2];
  190. cy = newdata[3];
  191. break;
  192. }
  193. case "T":
  194. out.push({key: "T", data: [...data]});
  195. cx = data[0];
  196. cy = data[1];
  197. break;
  198. case "t":
  199. cx += data[0];
  200. cy += data[1];
  201. out.push({key: "T", data: [cx, cy]});
  202. break;
  203. case "Z":
  204. case "z":
  205. out.push({key: "Z", data: []});
  206. cx = subx;
  207. cy = suby;
  208. break;
  209. }
  210. }
  211. return out;
  212. }
  213. function normalize(segments) {
  214. const out = [];
  215. let lastType = "";
  216. let cx = 0, cy = 0;
  217. let subx = 0, suby = 0;
  218. let lcx = 0, lcy = 0;
  219. for (const {key, data} of segments) {
  220. switch (key) {
  221. case "M":
  222. out.push({key: "M", data: [...data]});
  223. [cx, cy] = data;
  224. [subx, suby] = data;
  225. break;
  226. case "C":
  227. out.push({key: "C", data: [...data]});
  228. cx = data[4];
  229. cy = data[5];
  230. lcx = data[2];
  231. lcy = data[3];
  232. break;
  233. case "L":
  234. out.push({key: "L", data: [...data]});
  235. [cx, cy] = data;
  236. break;
  237. case "H":
  238. cx = data[0];
  239. out.push({key: "L", data: [cx, cy]});
  240. break;
  241. case "V":
  242. cy = data[0];
  243. out.push({key: "L", data: [cx, cy]});
  244. break;
  245. case "S": {
  246. let cx1 = 0, cy1 = 0;
  247. if (lastType === "C" || lastType === "S") {
  248. cx1 = cx + (cx - lcx);
  249. cy1 = cy + (cy - lcy);
  250. } else {
  251. cx1 = cx;
  252. cy1 = cy;
  253. }
  254. out.push({key: "C", data: [cx1, cy1, ...data]});
  255. lcx = data[0];
  256. lcy = data[1];
  257. cx = data[2];
  258. cy = data[3];
  259. break;
  260. }
  261. case "T": {
  262. const [x, y] = data;
  263. let x1 = 0, y1 = 0;
  264. if (lastType === "Q" || lastType === "T") {
  265. x1 = cx + (cx - lcx);
  266. y1 = cy + (cy - lcy);
  267. } else {
  268. x1 = cx;
  269. y1 = cy;
  270. }
  271. const cx1 = cx + 2 * (x1 - cx) / 3;
  272. const cy1 = cy + 2 * (y1 - cy) / 3;
  273. const cx2 = x + 2 * (x1 - x) / 3;
  274. const cy2 = y + 2 * (y1 - y) / 3;
  275. out.push({key: "C", data: [cx1, cy1, cx2, cy2, x, y]});
  276. lcx = x1;
  277. lcy = y1;
  278. cx = x;
  279. cy = y;
  280. break;
  281. }
  282. case "Q": {
  283. const [x1, y1, x, y] = data;
  284. const cx1 = cx + 2 * (x1 - cx) / 3;
  285. const cy1 = cy + 2 * (y1 - cy) / 3;
  286. const cx2 = x + 2 * (x1 - x) / 3;
  287. const cy2 = y + 2 * (y1 - y) / 3;
  288. out.push({key: "C", data: [cx1, cy1, cx2, cy2, x, y]});
  289. lcx = x1;
  290. lcy = y1;
  291. cx = x;
  292. cy = y;
  293. break;
  294. }
  295. case "A": {
  296. const r1 = Math.abs(data[0]);
  297. const r2 = Math.abs(data[1]);
  298. const angle = data[2];
  299. const largeArcFlag = data[3];
  300. const sweepFlag = data[4];
  301. const x = data[5];
  302. const y = data[6];
  303. if (r1 === 0 || r2 === 0) {
  304. out.push({key: "C", data: [cx, cy, x, y, x, y]});
  305. cx = x;
  306. cy = y;
  307. } else {
  308. if (cx !== x || cy !== y) {
  309. const curves = arcToCubicCurves(cx, cy, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
  310. curves.forEach(function(curve) {
  311. out.push({key: "C", data: curve});
  312. });
  313. cx = x;
  314. cy = y;
  315. }
  316. }
  317. break;
  318. }
  319. case "Z":
  320. out.push({key: "Z", data: []});
  321. cx = subx;
  322. cy = suby;
  323. break;
  324. }
  325. lastType = key;
  326. }
  327. return out;
  328. }
  329. function degToRad(degrees) {
  330. return Math.PI * degrees / 180;
  331. }
  332. function rotate(x, y, angleRad) {
  333. const X = x * Math.cos(angleRad) - y * Math.sin(angleRad);
  334. const Y = x * Math.sin(angleRad) + y * Math.cos(angleRad);
  335. return [X, Y];
  336. }
  337. function arcToCubicCurves(x1, y1, x2, y2, r1, r2, angle, largeArcFlag, sweepFlag, recursive) {
  338. const angleRad = degToRad(angle);
  339. let params = [];
  340. let f1 = 0, f2 = 0, cx = 0, cy = 0;
  341. if (recursive) {
  342. [f1, f2, cx, cy] = recursive;
  343. } else {
  344. [x1, y1] = rotate(x1, y1, -angleRad);
  345. [x2, y2] = rotate(x2, y2, -angleRad);
  346. const x = (x1 - x2) / 2;
  347. const y = (y1 - y2) / 2;
  348. let h = x * x / (r1 * r1) + y * y / (r2 * r2);
  349. if (h > 1) {
  350. h = Math.sqrt(h);
  351. r1 = h * r1;
  352. r2 = h * r2;
  353. }
  354. const sign = largeArcFlag === sweepFlag ? -1 : 1;
  355. const r1Pow = r1 * r1;
  356. const r2Pow = r2 * r2;
  357. const left = r1Pow * r2Pow - r1Pow * y * y - r2Pow * x * x;
  358. const right = r1Pow * y * y + r2Pow * x * x;
  359. const k = sign * Math.sqrt(Math.abs(left / right));
  360. cx = k * r1 * y / r2 + (x1 + x2) / 2;
  361. cy = k * -r2 * x / r1 + (y1 + y2) / 2;
  362. f1 = Math.asin(parseFloat(((y1 - cy) / r2).toFixed(9)));
  363. f2 = Math.asin(parseFloat(((y2 - cy) / r2).toFixed(9)));
  364. if (x1 < cx) {
  365. f1 = Math.PI - f1;
  366. }
  367. if (x2 < cx) {
  368. f2 = Math.PI - f2;
  369. }
  370. if (f1 < 0) {
  371. f1 = Math.PI * 2 + f1;
  372. }
  373. if (f2 < 0) {
  374. f2 = Math.PI * 2 + f2;
  375. }
  376. if (sweepFlag && f1 > f2) {
  377. f1 = f1 - Math.PI * 2;
  378. }
  379. if (!sweepFlag && f2 > f1) {
  380. f2 = f2 - Math.PI * 2;
  381. }
  382. }
  383. let df = f2 - f1;
  384. if (Math.abs(df) > Math.PI * 120 / 180) {
  385. const f2old = f2;
  386. const x2old = x2;
  387. const y2old = y2;
  388. if (sweepFlag && f2 > f1) {
  389. f2 = f1 + Math.PI * 120 / 180 * 1;
  390. } else {
  391. f2 = f1 + Math.PI * 120 / 180 * -1;
  392. }
  393. x2 = cx + r1 * Math.cos(f2);
  394. y2 = cy + r2 * Math.sin(f2);
  395. params = arcToCubicCurves(x2, y2, x2old, y2old, r1, r2, angle, 0, sweepFlag, [f2, f2old, cx, cy]);
  396. }
  397. df = f2 - f1;
  398. const c1 = Math.cos(f1);
  399. const s1 = Math.sin(f1);
  400. const c2 = Math.cos(f2);
  401. const s2 = Math.sin(f2);
  402. const t = Math.tan(df / 4);
  403. const hx = 4 / 3 * r1 * t;
  404. const hy = 4 / 3 * r2 * t;
  405. const m1 = [x1, y1];
  406. const m2 = [x1 + hx * s1, y1 - hy * c1];
  407. const m3 = [x2 + hx * s2, y2 - hy * c2];
  408. const m4 = [x2, y2];
  409. m2[0] = 2 * m1[0] - m2[0];
  410. m2[1] = 2 * m1[1] - m2[1];
  411. if (recursive) {
  412. return [m2, m3, m4].concat(params);
  413. } else {
  414. params = [m2, m3, m4].concat(params);
  415. const curves = [];
  416. for (let i = 0; i < params.length; i += 3) {
  417. const r12 = rotate(params[i][0], params[i][1], angleRad);
  418. const r22 = rotate(params[i + 1][0], params[i + 1][1], angleRad);
  419. const r3 = rotate(params[i + 2][0], params[i + 2][1], angleRad);
  420. curves.push([r12[0], r12[1], r22[0], r22[1], r3[0], r3[1]]);
  421. }
  422. return curves;
  423. }
  424. }
  425. exports.absolutize = absolutize;
  426. exports.normalize = normalize;
  427. exports.parsePath = parsePath;
  428. exports.serialize = serialize;
  429. }));

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址