source: imaps-frontend/node_modules/konva/lib/shapes/Path.js@ 0c6b92a

main
Last change on this file since 0c6b92a was 0c6b92a, checked in by stefan toskovski <stefantoska84@…>, 5 weeks ago

Pred finalna verzija

  • Property mode set to 100644
File size: 22.9 KB
Line 
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.Path = void 0;
4const Factory_1 = require("../Factory");
5const Shape_1 = require("../Shape");
6const Global_1 = require("../Global");
7const BezierFunctions_1 = require("../BezierFunctions");
8class Path extends Shape_1.Shape {
9 constructor(config) {
10 super(config);
11 this.dataArray = [];
12 this.pathLength = 0;
13 this._readDataAttribute();
14 this.on('dataChange.konva', function () {
15 this._readDataAttribute();
16 });
17 }
18 _readDataAttribute() {
19 this.dataArray = Path.parsePathData(this.data());
20 this.pathLength = Path.getPathLength(this.dataArray);
21 }
22 _sceneFunc(context) {
23 const ca = this.dataArray;
24 context.beginPath();
25 let isClosed = false;
26 for (let n = 0; n < ca.length; n++) {
27 const c = ca[n].command;
28 const p = ca[n].points;
29 switch (c) {
30 case 'L':
31 context.lineTo(p[0], p[1]);
32 break;
33 case 'M':
34 context.moveTo(p[0], p[1]);
35 break;
36 case 'C':
37 context.bezierCurveTo(p[0], p[1], p[2], p[3], p[4], p[5]);
38 break;
39 case 'Q':
40 context.quadraticCurveTo(p[0], p[1], p[2], p[3]);
41 break;
42 case 'A':
43 var cx = p[0], cy = p[1], rx = p[2], ry = p[3], theta = p[4], dTheta = p[5], psi = p[6], fs = p[7];
44 var r = rx > ry ? rx : ry;
45 var scaleX = rx > ry ? 1 : rx / ry;
46 var scaleY = rx > ry ? ry / rx : 1;
47 context.translate(cx, cy);
48 context.rotate(psi);
49 context.scale(scaleX, scaleY);
50 context.arc(0, 0, r, theta, theta + dTheta, 1 - fs);
51 context.scale(1 / scaleX, 1 / scaleY);
52 context.rotate(-psi);
53 context.translate(-cx, -cy);
54 break;
55 case 'z':
56 isClosed = true;
57 context.closePath();
58 break;
59 }
60 }
61 if (!isClosed && !this.hasFill()) {
62 context.strokeShape(this);
63 }
64 else {
65 context.fillStrokeShape(this);
66 }
67 }
68 getSelfRect() {
69 let points = [];
70 this.dataArray.forEach(function (data) {
71 if (data.command === 'A') {
72 const start = data.points[4];
73 const dTheta = data.points[5];
74 const end = data.points[4] + dTheta;
75 let inc = Math.PI / 180.0;
76 if (Math.abs(start - end) < inc) {
77 inc = Math.abs(start - end);
78 }
79 if (dTheta < 0) {
80 for (let t = start - inc; t > end; t -= inc) {
81 const point = Path.getPointOnEllipticalArc(data.points[0], data.points[1], data.points[2], data.points[3], t, 0);
82 points.push(point.x, point.y);
83 }
84 }
85 else {
86 for (let t = start + inc; t < end; t += inc) {
87 const point = Path.getPointOnEllipticalArc(data.points[0], data.points[1], data.points[2], data.points[3], t, 0);
88 points.push(point.x, point.y);
89 }
90 }
91 }
92 else if (data.command === 'C') {
93 for (let t = 0.0; t <= 1; t += 0.01) {
94 const point = Path.getPointOnCubicBezier(t, data.start.x, data.start.y, data.points[0], data.points[1], data.points[2], data.points[3], data.points[4], data.points[5]);
95 points.push(point.x, point.y);
96 }
97 }
98 else {
99 points = points.concat(data.points);
100 }
101 });
102 let minX = points[0];
103 let maxX = points[0];
104 let minY = points[1];
105 let maxY = points[1];
106 let x, y;
107 for (let i = 0; i < points.length / 2; i++) {
108 x = points[i * 2];
109 y = points[i * 2 + 1];
110 if (!isNaN(x)) {
111 minX = Math.min(minX, x);
112 maxX = Math.max(maxX, x);
113 }
114 if (!isNaN(y)) {
115 minY = Math.min(minY, y);
116 maxY = Math.max(maxY, y);
117 }
118 }
119 return {
120 x: minX,
121 y: minY,
122 width: maxX - minX,
123 height: maxY - minY,
124 };
125 }
126 getLength() {
127 return this.pathLength;
128 }
129 getPointAtLength(length) {
130 return Path.getPointAtLengthOfDataArray(length, this.dataArray);
131 }
132 static getLineLength(x1, y1, x2, y2) {
133 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
134 }
135 static getPathLength(dataArray) {
136 let pathLength = 0;
137 for (let i = 0; i < dataArray.length; ++i) {
138 pathLength += dataArray[i].pathLength;
139 }
140 return pathLength;
141 }
142 static getPointAtLengthOfDataArray(length, dataArray) {
143 let points, i = 0, ii = dataArray.length;
144 if (!ii) {
145 return null;
146 }
147 while (i < ii && length > dataArray[i].pathLength) {
148 length -= dataArray[i].pathLength;
149 ++i;
150 }
151 if (i === ii) {
152 points = dataArray[i - 1].points.slice(-2);
153 return {
154 x: points[0],
155 y: points[1],
156 };
157 }
158 if (length < 0.01) {
159 points = dataArray[i].points.slice(0, 2);
160 return {
161 x: points[0],
162 y: points[1],
163 };
164 }
165 const cp = dataArray[i];
166 const p = cp.points;
167 switch (cp.command) {
168 case 'L':
169 return Path.getPointOnLine(length, cp.start.x, cp.start.y, p[0], p[1]);
170 case 'C':
171 return Path.getPointOnCubicBezier((0, BezierFunctions_1.t2length)(length, Path.getPathLength(dataArray), (i) => {
172 return (0, BezierFunctions_1.getCubicArcLength)([cp.start.x, p[0], p[2], p[4]], [cp.start.y, p[1], p[3], p[5]], i);
173 }), cp.start.x, cp.start.y, p[0], p[1], p[2], p[3], p[4], p[5]);
174 case 'Q':
175 return Path.getPointOnQuadraticBezier((0, BezierFunctions_1.t2length)(length, Path.getPathLength(dataArray), (i) => {
176 return (0, BezierFunctions_1.getQuadraticArcLength)([cp.start.x, p[0], p[2]], [cp.start.y, p[1], p[3]], i);
177 }), cp.start.x, cp.start.y, p[0], p[1], p[2], p[3]);
178 case 'A':
179 var cx = p[0], cy = p[1], rx = p[2], ry = p[3], theta = p[4], dTheta = p[5], psi = p[6];
180 theta += (dTheta * length) / cp.pathLength;
181 return Path.getPointOnEllipticalArc(cx, cy, rx, ry, theta, psi);
182 }
183 return null;
184 }
185 static getPointOnLine(dist, P1x, P1y, P2x, P2y, fromX, fromY) {
186 fromX = fromX !== null && fromX !== void 0 ? fromX : P1x;
187 fromY = fromY !== null && fromY !== void 0 ? fromY : P1y;
188 const len = this.getLineLength(P1x, P1y, P2x, P2y);
189 if (len < 1e-10) {
190 return { x: P1x, y: P1y };
191 }
192 if (P2x === P1x) {
193 return { x: fromX, y: fromY + (P2y > P1y ? dist : -dist) };
194 }
195 const m = (P2y - P1y) / (P2x - P1x);
196 const run = Math.sqrt((dist * dist) / (1 + m * m)) * (P2x < P1x ? -1 : 1);
197 const rise = m * run;
198 if (Math.abs(fromY - P1y - m * (fromX - P1x)) < 1e-10) {
199 return { x: fromX + run, y: fromY + rise };
200 }
201 const u = ((fromX - P1x) * (P2x - P1x) + (fromY - P1y) * (P2y - P1y)) / (len * len);
202 const ix = P1x + u * (P2x - P1x);
203 const iy = P1y + u * (P2y - P1y);
204 const pRise = this.getLineLength(fromX, fromY, ix, iy);
205 const pRun = Math.sqrt(dist * dist - pRise * pRise);
206 const adjustedRun = Math.sqrt((pRun * pRun) / (1 + m * m)) * (P2x < P1x ? -1 : 1);
207 const adjustedRise = m * adjustedRun;
208 return { x: ix + adjustedRun, y: iy + adjustedRise };
209 }
210 static getPointOnCubicBezier(pct, P1x, P1y, P2x, P2y, P3x, P3y, P4x, P4y) {
211 function CB1(t) {
212 return t * t * t;
213 }
214 function CB2(t) {
215 return 3 * t * t * (1 - t);
216 }
217 function CB3(t) {
218 return 3 * t * (1 - t) * (1 - t);
219 }
220 function CB4(t) {
221 return (1 - t) * (1 - t) * (1 - t);
222 }
223 const x = P4x * CB1(pct) + P3x * CB2(pct) + P2x * CB3(pct) + P1x * CB4(pct);
224 const y = P4y * CB1(pct) + P3y * CB2(pct) + P2y * CB3(pct) + P1y * CB4(pct);
225 return {
226 x: x,
227 y: y,
228 };
229 }
230 static getPointOnQuadraticBezier(pct, P1x, P1y, P2x, P2y, P3x, P3y) {
231 function QB1(t) {
232 return t * t;
233 }
234 function QB2(t) {
235 return 2 * t * (1 - t);
236 }
237 function QB3(t) {
238 return (1 - t) * (1 - t);
239 }
240 const x = P3x * QB1(pct) + P2x * QB2(pct) + P1x * QB3(pct);
241 const y = P3y * QB1(pct) + P2y * QB2(pct) + P1y * QB3(pct);
242 return {
243 x: x,
244 y: y,
245 };
246 }
247 static getPointOnEllipticalArc(cx, cy, rx, ry, theta, psi) {
248 const cosPsi = Math.cos(psi), sinPsi = Math.sin(psi);
249 const pt = {
250 x: rx * Math.cos(theta),
251 y: ry * Math.sin(theta),
252 };
253 return {
254 x: cx + (pt.x * cosPsi - pt.y * sinPsi),
255 y: cy + (pt.x * sinPsi + pt.y * cosPsi),
256 };
257 }
258 static parsePathData(data) {
259 if (!data) {
260 return [];
261 }
262 let cs = data;
263 const cc = [
264 'm',
265 'M',
266 'l',
267 'L',
268 'v',
269 'V',
270 'h',
271 'H',
272 'z',
273 'Z',
274 'c',
275 'C',
276 'q',
277 'Q',
278 't',
279 'T',
280 's',
281 'S',
282 'a',
283 'A',
284 ];
285 cs = cs.replace(new RegExp(' ', 'g'), ',');
286 for (var n = 0; n < cc.length; n++) {
287 cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
288 }
289 const arr = cs.split('|');
290 const ca = [];
291 const coords = [];
292 let cpx = 0;
293 let cpy = 0;
294 const re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/gi;
295 let match;
296 for (n = 1; n < arr.length; n++) {
297 let str = arr[n];
298 let c = str.charAt(0);
299 str = str.slice(1);
300 coords.length = 0;
301 while ((match = re.exec(str))) {
302 coords.push(match[0]);
303 }
304 const p = [];
305 for (let j = 0, jlen = coords.length; j < jlen; j++) {
306 if (coords[j] === '00') {
307 p.push(0, 0);
308 continue;
309 }
310 const parsed = parseFloat(coords[j]);
311 if (!isNaN(parsed)) {
312 p.push(parsed);
313 }
314 else {
315 p.push(0);
316 }
317 }
318 while (p.length > 0) {
319 if (isNaN(p[0])) {
320 break;
321 }
322 let cmd = '';
323 let points = [];
324 const startX = cpx, startY = cpy;
325 var prevCmd, ctlPtx, ctlPty;
326 var rx, ry, psi, fa, fs, x1, y1;
327 switch (c) {
328 case 'l':
329 cpx += p.shift();
330 cpy += p.shift();
331 cmd = 'L';
332 points.push(cpx, cpy);
333 break;
334 case 'L':
335 cpx = p.shift();
336 cpy = p.shift();
337 points.push(cpx, cpy);
338 break;
339 case 'm':
340 var dx = p.shift();
341 var dy = p.shift();
342 cpx += dx;
343 cpy += dy;
344 cmd = 'M';
345 if (ca.length > 2 && ca[ca.length - 1].command === 'z') {
346 for (let idx = ca.length - 2; idx >= 0; idx--) {
347 if (ca[idx].command === 'M') {
348 cpx = ca[idx].points[0] + dx;
349 cpy = ca[idx].points[1] + dy;
350 break;
351 }
352 }
353 }
354 points.push(cpx, cpy);
355 c = 'l';
356 break;
357 case 'M':
358 cpx = p.shift();
359 cpy = p.shift();
360 cmd = 'M';
361 points.push(cpx, cpy);
362 c = 'L';
363 break;
364 case 'h':
365 cpx += p.shift();
366 cmd = 'L';
367 points.push(cpx, cpy);
368 break;
369 case 'H':
370 cpx = p.shift();
371 cmd = 'L';
372 points.push(cpx, cpy);
373 break;
374 case 'v':
375 cpy += p.shift();
376 cmd = 'L';
377 points.push(cpx, cpy);
378 break;
379 case 'V':
380 cpy = p.shift();
381 cmd = 'L';
382 points.push(cpx, cpy);
383 break;
384 case 'C':
385 points.push(p.shift(), p.shift(), p.shift(), p.shift());
386 cpx = p.shift();
387 cpy = p.shift();
388 points.push(cpx, cpy);
389 break;
390 case 'c':
391 points.push(cpx + p.shift(), cpy + p.shift(), cpx + p.shift(), cpy + p.shift());
392 cpx += p.shift();
393 cpy += p.shift();
394 cmd = 'C';
395 points.push(cpx, cpy);
396 break;
397 case 'S':
398 ctlPtx = cpx;
399 ctlPty = cpy;
400 prevCmd = ca[ca.length - 1];
401 if (prevCmd.command === 'C') {
402 ctlPtx = cpx + (cpx - prevCmd.points[2]);
403 ctlPty = cpy + (cpy - prevCmd.points[3]);
404 }
405 points.push(ctlPtx, ctlPty, p.shift(), p.shift());
406 cpx = p.shift();
407 cpy = p.shift();
408 cmd = 'C';
409 points.push(cpx, cpy);
410 break;
411 case 's':
412 ctlPtx = cpx;
413 ctlPty = cpy;
414 prevCmd = ca[ca.length - 1];
415 if (prevCmd.command === 'C') {
416 ctlPtx = cpx + (cpx - prevCmd.points[2]);
417 ctlPty = cpy + (cpy - prevCmd.points[3]);
418 }
419 points.push(ctlPtx, ctlPty, cpx + p.shift(), cpy + p.shift());
420 cpx += p.shift();
421 cpy += p.shift();
422 cmd = 'C';
423 points.push(cpx, cpy);
424 break;
425 case 'Q':
426 points.push(p.shift(), p.shift());
427 cpx = p.shift();
428 cpy = p.shift();
429 points.push(cpx, cpy);
430 break;
431 case 'q':
432 points.push(cpx + p.shift(), cpy + p.shift());
433 cpx += p.shift();
434 cpy += p.shift();
435 cmd = 'Q';
436 points.push(cpx, cpy);
437 break;
438 case 'T':
439 ctlPtx = cpx;
440 ctlPty = cpy;
441 prevCmd = ca[ca.length - 1];
442 if (prevCmd.command === 'Q') {
443 ctlPtx = cpx + (cpx - prevCmd.points[0]);
444 ctlPty = cpy + (cpy - prevCmd.points[1]);
445 }
446 cpx = p.shift();
447 cpy = p.shift();
448 cmd = 'Q';
449 points.push(ctlPtx, ctlPty, cpx, cpy);
450 break;
451 case 't':
452 ctlPtx = cpx;
453 ctlPty = cpy;
454 prevCmd = ca[ca.length - 1];
455 if (prevCmd.command === 'Q') {
456 ctlPtx = cpx + (cpx - prevCmd.points[0]);
457 ctlPty = cpy + (cpy - prevCmd.points[1]);
458 }
459 cpx += p.shift();
460 cpy += p.shift();
461 cmd = 'Q';
462 points.push(ctlPtx, ctlPty, cpx, cpy);
463 break;
464 case 'A':
465 rx = p.shift();
466 ry = p.shift();
467 psi = p.shift();
468 fa = p.shift();
469 fs = p.shift();
470 x1 = cpx;
471 y1 = cpy;
472 cpx = p.shift();
473 cpy = p.shift();
474 cmd = 'A';
475 points = this.convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
476 break;
477 case 'a':
478 rx = p.shift();
479 ry = p.shift();
480 psi = p.shift();
481 fa = p.shift();
482 fs = p.shift();
483 x1 = cpx;
484 y1 = cpy;
485 cpx += p.shift();
486 cpy += p.shift();
487 cmd = 'A';
488 points = this.convertEndpointToCenterParameterization(x1, y1, cpx, cpy, fa, fs, rx, ry, psi);
489 break;
490 }
491 ca.push({
492 command: cmd || c,
493 points: points,
494 start: {
495 x: startX,
496 y: startY,
497 },
498 pathLength: this.calcLength(startX, startY, cmd || c, points),
499 });
500 }
501 if (c === 'z' || c === 'Z') {
502 ca.push({
503 command: 'z',
504 points: [],
505 start: undefined,
506 pathLength: 0,
507 });
508 }
509 }
510 return ca;
511 }
512 static calcLength(x, y, cmd, points) {
513 let len, p1, p2, t;
514 const path = Path;
515 switch (cmd) {
516 case 'L':
517 return path.getLineLength(x, y, points[0], points[1]);
518 case 'C':
519 return (0, BezierFunctions_1.getCubicArcLength)([x, points[0], points[2], points[4]], [y, points[1], points[3], points[5]], 1);
520 case 'Q':
521 return (0, BezierFunctions_1.getQuadraticArcLength)([x, points[0], points[2]], [y, points[1], points[3]], 1);
522 case 'A':
523 len = 0.0;
524 var start = points[4];
525 var dTheta = points[5];
526 var end = points[4] + dTheta;
527 var inc = Math.PI / 180.0;
528 if (Math.abs(start - end) < inc) {
529 inc = Math.abs(start - end);
530 }
531 p1 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], start, 0);
532 if (dTheta < 0) {
533 for (t = start - inc; t > end; t -= inc) {
534 p2 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
535 len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
536 p1 = p2;
537 }
538 }
539 else {
540 for (t = start + inc; t < end; t += inc) {
541 p2 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], t, 0);
542 len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
543 p1 = p2;
544 }
545 }
546 p2 = path.getPointOnEllipticalArc(points[0], points[1], points[2], points[3], end, 0);
547 len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
548 return len;
549 }
550 return 0;
551 }
552 static convertEndpointToCenterParameterization(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg) {
553 const psi = psiDeg * (Math.PI / 180.0);
554 const xp = (Math.cos(psi) * (x1 - x2)) / 2.0 + (Math.sin(psi) * (y1 - y2)) / 2.0;
555 const yp = (-1 * Math.sin(psi) * (x1 - x2)) / 2.0 +
556 (Math.cos(psi) * (y1 - y2)) / 2.0;
557 const lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
558 if (lambda > 1) {
559 rx *= Math.sqrt(lambda);
560 ry *= Math.sqrt(lambda);
561 }
562 let f = Math.sqrt((rx * rx * (ry * ry) - rx * rx * (yp * yp) - ry * ry * (xp * xp)) /
563 (rx * rx * (yp * yp) + ry * ry * (xp * xp)));
564 if (fa === fs) {
565 f *= -1;
566 }
567 if (isNaN(f)) {
568 f = 0;
569 }
570 const cxp = (f * rx * yp) / ry;
571 const cyp = (f * -ry * xp) / rx;
572 const cx = (x1 + x2) / 2.0 + Math.cos(psi) * cxp - Math.sin(psi) * cyp;
573 const cy = (y1 + y2) / 2.0 + Math.sin(psi) * cxp + Math.cos(psi) * cyp;
574 const vMag = function (v) {
575 return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
576 };
577 const vRatio = function (u, v) {
578 return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
579 };
580 const vAngle = function (u, v) {
581 return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v));
582 };
583 const theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);
584 const u = [(xp - cxp) / rx, (yp - cyp) / ry];
585 const v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];
586 let dTheta = vAngle(u, v);
587 if (vRatio(u, v) <= -1) {
588 dTheta = Math.PI;
589 }
590 if (vRatio(u, v) >= 1) {
591 dTheta = 0;
592 }
593 if (fs === 0 && dTheta > 0) {
594 dTheta = dTheta - 2 * Math.PI;
595 }
596 if (fs === 1 && dTheta < 0) {
597 dTheta = dTheta + 2 * Math.PI;
598 }
599 return [cx, cy, rx, ry, theta, dTheta, psi, fs];
600 }
601}
602exports.Path = Path;
603Path.prototype.className = 'Path';
604Path.prototype._attrsAffectingSize = ['data'];
605(0, Global_1._registerNode)(Path);
606Factory_1.Factory.addGetterSetter(Path, 'data');
Note: See TracBrowser for help on using the repository browser.