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

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

Update repo after prototype presentation

  • 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 var ca = this.dataArray;
24 context.beginPath();
25 var isClosed = false;
26 for (var n = 0; n < ca.length; n++) {
27 var c = ca[n].command;
28 var 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 var points = [];
70 this.dataArray.forEach(function (data) {
71 if (data.command === 'A') {
72 var start = data.points[4];
73 var dTheta = data.points[5];
74 var end = data.points[4] + dTheta;
75 var 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 var minX = points[0];
103 var maxX = points[0];
104 var minY = points[1];
105 var maxY = points[1];
106 var x, y;
107 for (var 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 (var i = 0; i < dataArray.length; ++i) {
138 pathLength += dataArray[i].pathLength;
139 }
140 return pathLength;
141 }
142 static getPointAtLengthOfDataArray(length, dataArray) {
143 var point, 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 point = dataArray[i - 1].points.slice(-2);
153 return {
154 x: point[0],
155 y: point[1],
156 };
157 }
158 if (length < 0.01) {
159 point = dataArray[i].points.slice(0, 2);
160 return {
161 x: point[0],
162 y: point[1],
163 };
164 }
165 var cp = dataArray[i];
166 var 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 var x = P4x * CB1(pct) + P3x * CB2(pct) + P2x * CB3(pct) + P1x * CB4(pct);
224 var 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 var x = P3x * QB1(pct) + P2x * QB2(pct) + P1x * QB3(pct);
241 var 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 var cosPsi = Math.cos(psi), sinPsi = Math.sin(psi);
249 var 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 var cs = data;
263 var 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 var arr = cs.split('|');
290 var ca = [];
291 var coords = [];
292 var cpx = 0;
293 var cpy = 0;
294 var re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/gi;
295 var match;
296 for (n = 1; n < arr.length; n++) {
297 var str = arr[n];
298 var 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 var p = [];
305 for (var j = 0, jlen = coords.length; j < jlen; j++) {
306 if (coords[j] === '00') {
307 p.push(0, 0);
308 continue;
309 }
310 var 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 var cmd = '';
323 var points = [];
324 var 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 (var 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 var len, p1, p2, t;
514 var 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 var psi = psiDeg * (Math.PI / 180.0);
554 var xp = (Math.cos(psi) * (x1 - x2)) / 2.0 + (Math.sin(psi) * (y1 - y2)) / 2.0;
555 var yp = (-1 * Math.sin(psi) * (x1 - x2)) / 2.0 +
556 (Math.cos(psi) * (y1 - y2)) / 2.0;
557 var 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 var 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 var cxp = (f * rx * yp) / ry;
571 var cyp = (f * -ry * xp) / rx;
572 var cx = (x1 + x2) / 2.0 + Math.cos(psi) * cxp - Math.sin(psi) * cyp;
573 var cy = (y1 + y2) / 2.0 + Math.sin(psi) * cxp + Math.cos(psi) * cyp;
574 var vMag = function (v) {
575 return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
576 };
577 var vRatio = function (u, v) {
578 return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
579 };
580 var vAngle = function (u, v) {
581 return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v));
582 };
583 var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);
584 var u = [(xp - cxp) / rx, (yp - cyp) / ry];
585 var v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];
586 var 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.