@nasser-sw/fabric 7.0.1-beta3 → 7.0.1-beta4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/0 +0 -0
- package/dist/index.js +323 -155
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/index.min.mjs +1 -1
- package/dist/index.min.mjs.map +1 -1
- package/dist/index.mjs +323 -155
- package/dist/index.mjs.map +1 -1
- package/dist/index.node.cjs +323 -155
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.mjs +323 -155
- package/dist/index.node.mjs.map +1 -1
- package/dist/package.json.min.mjs +1 -1
- package/dist/package.json.mjs +1 -1
- package/dist/src/shapes/Line.d.ts +31 -86
- package/dist/src/shapes/Line.d.ts.map +1 -1
- package/dist/src/shapes/Line.min.mjs +1 -1
- package/dist/src/shapes/Line.min.mjs.map +1 -1
- package/dist/src/shapes/Line.mjs +323 -154
- package/dist/src/shapes/Line.mjs.map +1 -1
- package/dist-extensions/src/shapes/CustomLine.d.ts +10 -0
- package/dist-extensions/src/shapes/CustomLine.d.ts.map +1 -0
- package/dist-extensions/src/shapes/Line.d.ts +31 -86
- package/dist-extensions/src/shapes/Line.d.ts.map +1 -1
- package/fabric-test-editor.html +157 -8
- package/fabric-test2.html +513 -0
- package/fabric.ts +182 -182
- package/package.json +1 -1
- package/src/shapes/Line.ts +372 -158
- package/debug/konva/CHANGELOG.md +0 -1474
- package/debug/konva/LICENSE +0 -22
- package/debug/konva/README.md +0 -205
- package/debug/konva/gulpfile.mjs +0 -110
- package/debug/konva/package.json +0 -139
- package/debug/konva/release.sh +0 -65
- package/debug/konva/resources/doc-includes/ContainerParams.txt +0 -6
- package/debug/konva/resources/doc-includes/NodeParams.txt +0 -20
- package/debug/konva/resources/doc-includes/ShapeParams.txt +0 -53
- package/debug/konva/resources/jsdoc.conf.json +0 -28
- package/debug/konva/rollup.config.mjs +0 -32
- package/debug/konva/src/Animation.ts +0 -237
- package/debug/konva/src/BezierFunctions.ts +0 -826
- package/debug/konva/src/Canvas.ts +0 -193
- package/debug/konva/src/Container.ts +0 -649
- package/debug/konva/src/Context.ts +0 -1017
- package/debug/konva/src/Core.ts +0 -5
- package/debug/konva/src/DragAndDrop.ts +0 -173
- package/debug/konva/src/Factory.ts +0 -246
- package/debug/konva/src/FastLayer.ts +0 -29
- package/debug/konva/src/Global.ts +0 -210
- package/debug/konva/src/Group.ts +0 -31
- package/debug/konva/src/Layer.ts +0 -546
- package/debug/konva/src/Node.ts +0 -3477
- package/debug/konva/src/PointerEvents.ts +0 -67
- package/debug/konva/src/Shape.ts +0 -2081
- package/debug/konva/src/Stage.ts +0 -1000
- package/debug/konva/src/Tween.ts +0 -811
- package/debug/konva/src/Util.ts +0 -1123
- package/debug/konva/src/Validators.ts +0 -210
- package/debug/konva/src/_CoreInternals.ts +0 -85
- package/debug/konva/src/_FullInternals.ts +0 -171
- package/debug/konva/src/canvas-backend.ts +0 -36
- package/debug/konva/src/filters/Blur.ts +0 -388
- package/debug/konva/src/filters/Brighten.ts +0 -48
- package/debug/konva/src/filters/Brightness.ts +0 -30
- package/debug/konva/src/filters/Contrast.ts +0 -75
- package/debug/konva/src/filters/Emboss.ts +0 -207
- package/debug/konva/src/filters/Enhance.ts +0 -154
- package/debug/konva/src/filters/Grayscale.ts +0 -25
- package/debug/konva/src/filters/HSL.ts +0 -108
- package/debug/konva/src/filters/HSV.ts +0 -106
- package/debug/konva/src/filters/Invert.ts +0 -23
- package/debug/konva/src/filters/Kaleidoscope.ts +0 -274
- package/debug/konva/src/filters/Mask.ts +0 -220
- package/debug/konva/src/filters/Noise.ts +0 -44
- package/debug/konva/src/filters/Pixelate.ts +0 -107
- package/debug/konva/src/filters/Posterize.ts +0 -46
- package/debug/konva/src/filters/RGB.ts +0 -82
- package/debug/konva/src/filters/RGBA.ts +0 -103
- package/debug/konva/src/filters/Sepia.ts +0 -27
- package/debug/konva/src/filters/Solarize.ts +0 -29
- package/debug/konva/src/filters/Threshold.ts +0 -44
- package/debug/konva/src/index.ts +0 -3
- package/debug/konva/src/shapes/Arc.ts +0 -176
- package/debug/konva/src/shapes/Arrow.ts +0 -231
- package/debug/konva/src/shapes/Circle.ts +0 -76
- package/debug/konva/src/shapes/Ellipse.ts +0 -121
- package/debug/konva/src/shapes/Image.ts +0 -319
- package/debug/konva/src/shapes/Label.ts +0 -386
- package/debug/konva/src/shapes/Line.ts +0 -364
- package/debug/konva/src/shapes/Path.ts +0 -1013
- package/debug/konva/src/shapes/Rect.ts +0 -79
- package/debug/konva/src/shapes/RegularPolygon.ts +0 -167
- package/debug/konva/src/shapes/Ring.ts +0 -94
- package/debug/konva/src/shapes/Sprite.ts +0 -370
- package/debug/konva/src/shapes/Star.ts +0 -125
- package/debug/konva/src/shapes/Text.ts +0 -1065
- package/debug/konva/src/shapes/TextPath.ts +0 -583
- package/debug/konva/src/shapes/Transformer.ts +0 -1889
- package/debug/konva/src/shapes/Wedge.ts +0 -129
- package/debug/konva/src/skia-backend.ts +0 -35
- package/debug/konva/src/types.ts +0 -84
- package/debug/konva/tsconfig.json +0 -31
- package/debug/konva/tsconfig.test.json +0 -7
|
@@ -1,1013 +0,0 @@
|
|
|
1
|
-
import { Factory } from '../Factory.ts';
|
|
2
|
-
import { _registerNode } from '../Global.ts';
|
|
3
|
-
import type { ShapeConfig } from '../Shape.ts';
|
|
4
|
-
import { Shape } from '../Shape.ts';
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
getCubicArcLength,
|
|
8
|
-
getQuadraticArcLength,
|
|
9
|
-
t2length,
|
|
10
|
-
} from '../BezierFunctions.ts';
|
|
11
|
-
import type { GetSet, PathSegment } from '../types.ts';
|
|
12
|
-
|
|
13
|
-
export interface PathConfig extends ShapeConfig {
|
|
14
|
-
data?: string;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Path constructor.
|
|
18
|
-
* @author Jason Follas
|
|
19
|
-
* @constructor
|
|
20
|
-
* @memberof Konva
|
|
21
|
-
* @augments Konva.Shape
|
|
22
|
-
* @param {Object} config
|
|
23
|
-
* @param {String} config.data SVG data string
|
|
24
|
-
* @@shapeParams
|
|
25
|
-
* @@nodeParams
|
|
26
|
-
* @example
|
|
27
|
-
* var path = new Konva.Path({
|
|
28
|
-
* x: 240,
|
|
29
|
-
* y: 40,
|
|
30
|
-
* data: 'M12.582,9.551C3.251,16.237,0.921,29.021,7.08,38.564l-2.36,1.689l4.893,2.262l4.893,2.262l-0.568-5.36l-0.567-5.359l-2.365,1.694c-4.657-7.375-2.83-17.185,4.352-22.33c7.451-5.338,17.817-3.625,23.156,3.824c5.337,7.449,3.625,17.813-3.821,23.152l2.857,3.988c9.617-6.893,11.827-20.277,4.935-29.896C35.591,4.87,22.204,2.658,12.582,9.551z',
|
|
31
|
-
* fill: 'green',
|
|
32
|
-
* scaleX: 2,
|
|
33
|
-
* scaleY: 2
|
|
34
|
-
* });
|
|
35
|
-
*/
|
|
36
|
-
export class Path extends Shape<PathConfig> {
|
|
37
|
-
dataArray: PathSegment[] = [];
|
|
38
|
-
pathLength = 0;
|
|
39
|
-
|
|
40
|
-
constructor(config?: PathConfig) {
|
|
41
|
-
super(config);
|
|
42
|
-
this._readDataAttribute();
|
|
43
|
-
|
|
44
|
-
this.on('dataChange.konva', function () {
|
|
45
|
-
this._readDataAttribute();
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
_readDataAttribute() {
|
|
50
|
-
this.dataArray = Path.parsePathData(this.data());
|
|
51
|
-
this.pathLength = Path.getPathLength(this.dataArray);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
_sceneFunc(context) {
|
|
55
|
-
const ca = this.dataArray;
|
|
56
|
-
|
|
57
|
-
// context position
|
|
58
|
-
context.beginPath();
|
|
59
|
-
let isClosed = false;
|
|
60
|
-
for (let n = 0; n < ca.length; n++) {
|
|
61
|
-
const c = ca[n].command;
|
|
62
|
-
const p = ca[n].points;
|
|
63
|
-
switch (c) {
|
|
64
|
-
case 'L':
|
|
65
|
-
context.lineTo(p[0], p[1]);
|
|
66
|
-
break;
|
|
67
|
-
case 'M':
|
|
68
|
-
context.moveTo(p[0], p[1]);
|
|
69
|
-
break;
|
|
70
|
-
case 'C':
|
|
71
|
-
context.bezierCurveTo(p[0], p[1], p[2], p[3], p[4], p[5]);
|
|
72
|
-
break;
|
|
73
|
-
case 'Q':
|
|
74
|
-
context.quadraticCurveTo(p[0], p[1], p[2], p[3]);
|
|
75
|
-
break;
|
|
76
|
-
case 'A':
|
|
77
|
-
const cx = p[0],
|
|
78
|
-
cy = p[1],
|
|
79
|
-
rx = p[2],
|
|
80
|
-
ry = p[3],
|
|
81
|
-
theta = p[4],
|
|
82
|
-
dTheta = p[5],
|
|
83
|
-
psi = p[6],
|
|
84
|
-
fs = p[7];
|
|
85
|
-
|
|
86
|
-
const r = rx > ry ? rx : ry;
|
|
87
|
-
const scaleX = rx > ry ? 1 : rx / ry;
|
|
88
|
-
const scaleY = rx > ry ? ry / rx : 1;
|
|
89
|
-
|
|
90
|
-
context.translate(cx, cy);
|
|
91
|
-
context.rotate(psi);
|
|
92
|
-
context.scale(scaleX, scaleY);
|
|
93
|
-
context.arc(0, 0, r, theta, theta + dTheta, 1 - fs);
|
|
94
|
-
context.scale(1 / scaleX, 1 / scaleY);
|
|
95
|
-
context.rotate(-psi);
|
|
96
|
-
context.translate(-cx, -cy);
|
|
97
|
-
|
|
98
|
-
break;
|
|
99
|
-
case 'z':
|
|
100
|
-
isClosed = true;
|
|
101
|
-
context.closePath();
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (!isClosed && !this.hasFill()) {
|
|
107
|
-
context.strokeShape(this);
|
|
108
|
-
} else {
|
|
109
|
-
context.fillStrokeShape(this);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
getSelfRect() {
|
|
113
|
-
let points: Array<number> = [];
|
|
114
|
-
this.dataArray.forEach(function (data) {
|
|
115
|
-
if (data.command === 'A') {
|
|
116
|
-
// Approximates by breaking curve into line segments
|
|
117
|
-
const start = data.points[4];
|
|
118
|
-
// 4 = theta
|
|
119
|
-
const dTheta = data.points[5];
|
|
120
|
-
// 5 = dTheta
|
|
121
|
-
const end = data.points[4] + dTheta;
|
|
122
|
-
let inc = Math.PI / 180.0;
|
|
123
|
-
// 1 degree resolution
|
|
124
|
-
if (Math.abs(start - end) < inc) {
|
|
125
|
-
inc = Math.abs(start - end);
|
|
126
|
-
}
|
|
127
|
-
if (dTheta < 0) {
|
|
128
|
-
// clockwise
|
|
129
|
-
for (let t = start - inc; t > end; t -= inc) {
|
|
130
|
-
const point = Path.getPointOnEllipticalArc(
|
|
131
|
-
data.points[0],
|
|
132
|
-
data.points[1],
|
|
133
|
-
data.points[2],
|
|
134
|
-
data.points[3],
|
|
135
|
-
t,
|
|
136
|
-
0
|
|
137
|
-
);
|
|
138
|
-
points.push(point.x, point.y);
|
|
139
|
-
}
|
|
140
|
-
} else {
|
|
141
|
-
// counter-clockwise
|
|
142
|
-
for (let t = start + inc; t < end; t += inc) {
|
|
143
|
-
const point = Path.getPointOnEllipticalArc(
|
|
144
|
-
data.points[0],
|
|
145
|
-
data.points[1],
|
|
146
|
-
data.points[2],
|
|
147
|
-
data.points[3],
|
|
148
|
-
t,
|
|
149
|
-
0
|
|
150
|
-
);
|
|
151
|
-
points.push(point.x, point.y);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
} else if (data.command === 'C') {
|
|
155
|
-
// Approximates by breaking curve into 100 line segments
|
|
156
|
-
for (let t = 0.0; t <= 1; t += 0.01) {
|
|
157
|
-
const point = Path.getPointOnCubicBezier(
|
|
158
|
-
t,
|
|
159
|
-
data.start.x,
|
|
160
|
-
data.start.y,
|
|
161
|
-
data.points[0],
|
|
162
|
-
data.points[1],
|
|
163
|
-
data.points[2],
|
|
164
|
-
data.points[3],
|
|
165
|
-
data.points[4],
|
|
166
|
-
data.points[5]
|
|
167
|
-
);
|
|
168
|
-
points.push(point.x, point.y);
|
|
169
|
-
}
|
|
170
|
-
} else {
|
|
171
|
-
// TODO: how can we calculate bezier curves better?
|
|
172
|
-
points = points.concat(data.points);
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
let minX = points[0];
|
|
176
|
-
let maxX = points[0];
|
|
177
|
-
let minY = points[1];
|
|
178
|
-
let maxY = points[1];
|
|
179
|
-
let x, y;
|
|
180
|
-
for (let i = 0; i < points.length / 2; i++) {
|
|
181
|
-
x = points[i * 2];
|
|
182
|
-
y = points[i * 2 + 1];
|
|
183
|
-
|
|
184
|
-
// skip bad values
|
|
185
|
-
if (!isNaN(x)) {
|
|
186
|
-
minX = Math.min(minX, x);
|
|
187
|
-
maxX = Math.max(maxX, x);
|
|
188
|
-
}
|
|
189
|
-
if (!isNaN(y)) {
|
|
190
|
-
minY = Math.min(minY, y);
|
|
191
|
-
maxY = Math.max(maxY, y);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return {
|
|
195
|
-
x: minX,
|
|
196
|
-
y: minY,
|
|
197
|
-
width: maxX - minX,
|
|
198
|
-
height: maxY - minY,
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Return length of the path.
|
|
203
|
-
* @method
|
|
204
|
-
* @name Konva.Path#getLength
|
|
205
|
-
* @returns {Number} length
|
|
206
|
-
* @example
|
|
207
|
-
* var length = path.getLength();
|
|
208
|
-
*/
|
|
209
|
-
getLength() {
|
|
210
|
-
return this.pathLength;
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Get point on path at specific length of the path
|
|
214
|
-
* @method
|
|
215
|
-
* @name Konva.Path#getPointAtLength
|
|
216
|
-
* @param {Number} length length
|
|
217
|
-
* @returns {Object} point {x,y} point
|
|
218
|
-
* @example
|
|
219
|
-
* var point = path.getPointAtLength(10);
|
|
220
|
-
*/
|
|
221
|
-
getPointAtLength(length: number) {
|
|
222
|
-
return Path.getPointAtLengthOfDataArray(length, this.dataArray);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
data: GetSet<string, this>;
|
|
226
|
-
|
|
227
|
-
static getLineLength(x1, y1, x2, y2) {
|
|
228
|
-
return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
static getPathLength(dataArray: PathSegment[]) {
|
|
232
|
-
let pathLength = 0;
|
|
233
|
-
for (let i = 0; i < dataArray.length; ++i) {
|
|
234
|
-
pathLength += dataArray[i].pathLength;
|
|
235
|
-
}
|
|
236
|
-
return pathLength;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
static getPointAtLengthOfDataArray(length: number, dataArray: PathSegment[]) {
|
|
240
|
-
let points: number[],
|
|
241
|
-
i = 0,
|
|
242
|
-
ii = dataArray.length;
|
|
243
|
-
|
|
244
|
-
if (!ii) {
|
|
245
|
-
return null;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
while (i < ii && length > dataArray[i].pathLength) {
|
|
249
|
-
length -= dataArray[i].pathLength;
|
|
250
|
-
++i;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (i === ii) {
|
|
254
|
-
points = dataArray[i - 1].points.slice(-2);
|
|
255
|
-
return {
|
|
256
|
-
x: points[0],
|
|
257
|
-
y: points[1],
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (length < 0.01) {
|
|
262
|
-
const cmd = dataArray[i].command;
|
|
263
|
-
if (cmd === 'M') {
|
|
264
|
-
points = dataArray[i].points.slice(0, 2);
|
|
265
|
-
return {
|
|
266
|
-
x: points[0],
|
|
267
|
-
y: points[1],
|
|
268
|
-
};
|
|
269
|
-
} else {
|
|
270
|
-
return {
|
|
271
|
-
x: dataArray[i].start.x,
|
|
272
|
-
y: dataArray[i].start.y,
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const cp = dataArray[i];
|
|
278
|
-
const p = cp.points;
|
|
279
|
-
switch (cp.command) {
|
|
280
|
-
case 'L':
|
|
281
|
-
return Path.getPointOnLine(length, cp.start.x, cp.start.y, p[0], p[1]);
|
|
282
|
-
case 'C':
|
|
283
|
-
return Path.getPointOnCubicBezier(
|
|
284
|
-
t2length(length, Path.getPathLength(dataArray), (i) => {
|
|
285
|
-
return getCubicArcLength(
|
|
286
|
-
[cp.start.x, p[0], p[2], p[4]],
|
|
287
|
-
[cp.start.y, p[1], p[3], p[5]],
|
|
288
|
-
i
|
|
289
|
-
);
|
|
290
|
-
}),
|
|
291
|
-
cp.start.x,
|
|
292
|
-
cp.start.y,
|
|
293
|
-
p[0],
|
|
294
|
-
p[1],
|
|
295
|
-
p[2],
|
|
296
|
-
p[3],
|
|
297
|
-
p[4],
|
|
298
|
-
p[5]
|
|
299
|
-
);
|
|
300
|
-
case 'Q':
|
|
301
|
-
return Path.getPointOnQuadraticBezier(
|
|
302
|
-
t2length(length, Path.getPathLength(dataArray), (i) => {
|
|
303
|
-
return getQuadraticArcLength(
|
|
304
|
-
[cp.start.x, p[0], p[2]],
|
|
305
|
-
[cp.start.y, p[1], p[3]],
|
|
306
|
-
i
|
|
307
|
-
);
|
|
308
|
-
}),
|
|
309
|
-
cp.start.x,
|
|
310
|
-
cp.start.y,
|
|
311
|
-
p[0],
|
|
312
|
-
p[1],
|
|
313
|
-
p[2],
|
|
314
|
-
p[3]
|
|
315
|
-
);
|
|
316
|
-
case 'A':
|
|
317
|
-
const cx = p[0],
|
|
318
|
-
cy = p[1],
|
|
319
|
-
rx = p[2],
|
|
320
|
-
ry = p[3],
|
|
321
|
-
dTheta = p[5],
|
|
322
|
-
psi = p[6];
|
|
323
|
-
let theta = p[4];
|
|
324
|
-
theta += (dTheta * length) / cp.pathLength;
|
|
325
|
-
return Path.getPointOnEllipticalArc(cx, cy, rx, ry, theta, psi);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
return null;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
static getPointOnLine(
|
|
332
|
-
dist: number,
|
|
333
|
-
P1x: number,
|
|
334
|
-
P1y: number,
|
|
335
|
-
P2x: number,
|
|
336
|
-
P2y: number,
|
|
337
|
-
fromX?: number,
|
|
338
|
-
fromY?: number
|
|
339
|
-
) {
|
|
340
|
-
fromX = fromX ?? P1x;
|
|
341
|
-
fromY = fromY ?? P1y;
|
|
342
|
-
|
|
343
|
-
const len = this.getLineLength(P1x, P1y, P2x, P2y);
|
|
344
|
-
if (len < 1e-10) {
|
|
345
|
-
return { x: P1x, y: P1y };
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
if (P2x === P1x) {
|
|
349
|
-
// Vertical line
|
|
350
|
-
return { x: fromX, y: fromY + (P2y > P1y ? dist : -dist) };
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const m = (P2y - P1y) / (P2x - P1x);
|
|
354
|
-
const run = Math.sqrt((dist * dist) / (1 + m * m)) * (P2x < P1x ? -1 : 1);
|
|
355
|
-
const rise = m * run;
|
|
356
|
-
|
|
357
|
-
if (Math.abs(fromY - P1y - m * (fromX - P1x)) < 1e-10) {
|
|
358
|
-
return { x: fromX + run, y: fromY + rise };
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
const u =
|
|
362
|
-
((fromX - P1x) * (P2x - P1x) + (fromY - P1y) * (P2y - P1y)) / (len * len);
|
|
363
|
-
const ix = P1x + u * (P2x - P1x);
|
|
364
|
-
const iy = P1y + u * (P2y - P1y);
|
|
365
|
-
const pRise = this.getLineLength(fromX, fromY, ix, iy);
|
|
366
|
-
const pRun = Math.sqrt(dist * dist - pRise * pRise);
|
|
367
|
-
const adjustedRun =
|
|
368
|
-
Math.sqrt((pRun * pRun) / (1 + m * m)) * (P2x < P1x ? -1 : 1);
|
|
369
|
-
const adjustedRise = m * adjustedRun;
|
|
370
|
-
|
|
371
|
-
return { x: ix + adjustedRun, y: iy + adjustedRise };
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
static getPointOnCubicBezier(
|
|
375
|
-
pct: number,
|
|
376
|
-
P1x: number,
|
|
377
|
-
P1y: number,
|
|
378
|
-
P2x: number,
|
|
379
|
-
P2y: number,
|
|
380
|
-
P3x: number,
|
|
381
|
-
P3y: number,
|
|
382
|
-
P4x: number,
|
|
383
|
-
P4y: number
|
|
384
|
-
) {
|
|
385
|
-
function CB1(t: number) {
|
|
386
|
-
return t * t * t;
|
|
387
|
-
}
|
|
388
|
-
function CB2(t: number) {
|
|
389
|
-
return 3 * t * t * (1 - t);
|
|
390
|
-
}
|
|
391
|
-
function CB3(t: number) {
|
|
392
|
-
return 3 * t * (1 - t) * (1 - t);
|
|
393
|
-
}
|
|
394
|
-
function CB4(t: number) {
|
|
395
|
-
return (1 - t) * (1 - t) * (1 - t);
|
|
396
|
-
}
|
|
397
|
-
const x = P4x * CB1(pct) + P3x * CB2(pct) + P2x * CB3(pct) + P1x * CB4(pct);
|
|
398
|
-
const y = P4y * CB1(pct) + P3y * CB2(pct) + P2y * CB3(pct) + P1y * CB4(pct);
|
|
399
|
-
|
|
400
|
-
return { x, y };
|
|
401
|
-
}
|
|
402
|
-
static getPointOnQuadraticBezier(pct, P1x, P1y, P2x, P2y, P3x, P3y) {
|
|
403
|
-
function QB1(t) {
|
|
404
|
-
return t * t;
|
|
405
|
-
}
|
|
406
|
-
function QB2(t) {
|
|
407
|
-
return 2 * t * (1 - t);
|
|
408
|
-
}
|
|
409
|
-
function QB3(t) {
|
|
410
|
-
return (1 - t) * (1 - t);
|
|
411
|
-
}
|
|
412
|
-
const x = P3x * QB1(pct) + P2x * QB2(pct) + P1x * QB3(pct);
|
|
413
|
-
const y = P3y * QB1(pct) + P2y * QB2(pct) + P1y * QB3(pct);
|
|
414
|
-
|
|
415
|
-
return { x, y };
|
|
416
|
-
}
|
|
417
|
-
static getPointOnEllipticalArc(
|
|
418
|
-
cx: number,
|
|
419
|
-
cy: number,
|
|
420
|
-
rx: number,
|
|
421
|
-
ry: number,
|
|
422
|
-
theta: number,
|
|
423
|
-
psi: number
|
|
424
|
-
) {
|
|
425
|
-
const cosPsi = Math.cos(psi),
|
|
426
|
-
sinPsi = Math.sin(psi);
|
|
427
|
-
const pt = {
|
|
428
|
-
x: rx * Math.cos(theta),
|
|
429
|
-
y: ry * Math.sin(theta),
|
|
430
|
-
};
|
|
431
|
-
return {
|
|
432
|
-
x: cx + (pt.x * cosPsi - pt.y * sinPsi),
|
|
433
|
-
y: cy + (pt.x * sinPsi + pt.y * cosPsi),
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
/*
|
|
437
|
-
* get parsed data array from the data
|
|
438
|
-
* string. V, v, H, h, and l data are converted to
|
|
439
|
-
* L data for the purpose of high performance Path
|
|
440
|
-
* rendering
|
|
441
|
-
*/
|
|
442
|
-
static parsePathData(data): PathSegment[] {
|
|
443
|
-
// Path Data Segment must begin with a moveTo
|
|
444
|
-
//m (x y)+ Relative moveTo (subsequent points are treated as lineTo)
|
|
445
|
-
//M (x y)+ Absolute moveTo (subsequent points are treated as lineTo)
|
|
446
|
-
//l (x y)+ Relative lineTo
|
|
447
|
-
//L (x y)+ Absolute LineTo
|
|
448
|
-
//h (x)+ Relative horizontal lineTo
|
|
449
|
-
//H (x)+ Absolute horizontal lineTo
|
|
450
|
-
//v (y)+ Relative vertical lineTo
|
|
451
|
-
//V (y)+ Absolute vertical lineTo
|
|
452
|
-
//z (closepath)
|
|
453
|
-
//Z (closepath)
|
|
454
|
-
//c (x1 y1 x2 y2 x y)+ Relative Bezier curve
|
|
455
|
-
//C (x1 y1 x2 y2 x y)+ Absolute Bezier curve
|
|
456
|
-
//q (x1 y1 x y)+ Relative Quadratic Bezier
|
|
457
|
-
//Q (x1 y1 x y)+ Absolute Quadratic Bezier
|
|
458
|
-
//t (x y)+ Shorthand/Smooth Relative Quadratic Bezier
|
|
459
|
-
//T (x y)+ Shorthand/Smooth Absolute Quadratic Bezier
|
|
460
|
-
//s (x2 y2 x y)+ Shorthand/Smooth Relative Bezier curve
|
|
461
|
-
//S (x2 y2 x y)+ Shorthand/Smooth Absolute Bezier curve
|
|
462
|
-
//a (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ Relative Elliptical Arc
|
|
463
|
-
//A (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+ Absolute Elliptical Arc
|
|
464
|
-
|
|
465
|
-
// return early if data is not defined
|
|
466
|
-
if (!data) {
|
|
467
|
-
return [];
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// command string
|
|
471
|
-
let cs = data;
|
|
472
|
-
|
|
473
|
-
// command chars
|
|
474
|
-
const cc = [
|
|
475
|
-
'm',
|
|
476
|
-
'M',
|
|
477
|
-
'l',
|
|
478
|
-
'L',
|
|
479
|
-
'v',
|
|
480
|
-
'V',
|
|
481
|
-
'h',
|
|
482
|
-
'H',
|
|
483
|
-
'z',
|
|
484
|
-
'Z',
|
|
485
|
-
'c',
|
|
486
|
-
'C',
|
|
487
|
-
'q',
|
|
488
|
-
'Q',
|
|
489
|
-
't',
|
|
490
|
-
'T',
|
|
491
|
-
's',
|
|
492
|
-
'S',
|
|
493
|
-
'a',
|
|
494
|
-
'A',
|
|
495
|
-
];
|
|
496
|
-
// convert white spaces to commas
|
|
497
|
-
cs = cs.replace(new RegExp(' ', 'g'), ',');
|
|
498
|
-
// create pipes so that we can split the data
|
|
499
|
-
for (let n = 0; n < cc.length; n++) {
|
|
500
|
-
cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);
|
|
501
|
-
}
|
|
502
|
-
// create array
|
|
503
|
-
const arr = cs.split('|');
|
|
504
|
-
const ca: PathSegment[] = [];
|
|
505
|
-
const coords: string[] = [];
|
|
506
|
-
// init context point
|
|
507
|
-
let cpx = 0;
|
|
508
|
-
let cpy = 0;
|
|
509
|
-
|
|
510
|
-
const re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/gi;
|
|
511
|
-
let match;
|
|
512
|
-
for (let n = 1; n < arr.length; n++) {
|
|
513
|
-
let str = arr[n];
|
|
514
|
-
let c = str.charAt(0);
|
|
515
|
-
str = str.slice(1);
|
|
516
|
-
|
|
517
|
-
coords.length = 0;
|
|
518
|
-
while ((match = re.exec(str))) {
|
|
519
|
-
coords.push(match[0]);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// while ((match = re.exec(str))) {
|
|
523
|
-
// coords.push(match[0]);
|
|
524
|
-
// }
|
|
525
|
-
let p: number[] = [];
|
|
526
|
-
// Track param position for A/a commands: 0..6 => rx, ry, psi, fa, fs, x, y
|
|
527
|
-
let arcParamIndex = c === 'A' || c === 'a' ? 0 : -1;
|
|
528
|
-
|
|
529
|
-
for (let j = 0, jlen = coords.length; j < jlen; j++) {
|
|
530
|
-
const token = coords[j];
|
|
531
|
-
// extra case for merged flags
|
|
532
|
-
if (token === '00') {
|
|
533
|
-
p.push(0, 0);
|
|
534
|
-
if (arcParamIndex >= 0) {
|
|
535
|
-
arcParamIndex += 2;
|
|
536
|
-
if (arcParamIndex >= 7) arcParamIndex -= 7;
|
|
537
|
-
}
|
|
538
|
-
continue;
|
|
539
|
-
}
|
|
540
|
-
if (arcParamIndex >= 0) {
|
|
541
|
-
// index-aware minimal handling for merged flags
|
|
542
|
-
if (arcParamIndex === 3) {
|
|
543
|
-
// expecting large-arc-flag; token may contain fa+fs(+x)
|
|
544
|
-
if (/^[01]{2}\d+(?:\.\d+)?$/.test(token)) {
|
|
545
|
-
p.push(parseInt(token[0], 10));
|
|
546
|
-
p.push(parseInt(token[1], 10));
|
|
547
|
-
p.push(parseFloat(token.slice(2)));
|
|
548
|
-
arcParamIndex += 3;
|
|
549
|
-
if (arcParamIndex >= 7) arcParamIndex -= 7;
|
|
550
|
-
continue;
|
|
551
|
-
}
|
|
552
|
-
if (token === '11' || token === '10' || token === '01') {
|
|
553
|
-
p.push(parseInt(token[0], 10));
|
|
554
|
-
p.push(parseInt(token[1], 10));
|
|
555
|
-
arcParamIndex += 2;
|
|
556
|
-
if (arcParamIndex >= 7) arcParamIndex -= 7;
|
|
557
|
-
continue;
|
|
558
|
-
}
|
|
559
|
-
if (token === '0' || token === '1') {
|
|
560
|
-
p.push(parseInt(token, 10));
|
|
561
|
-
arcParamIndex += 1;
|
|
562
|
-
if (arcParamIndex >= 7) arcParamIndex -= 7;
|
|
563
|
-
continue;
|
|
564
|
-
}
|
|
565
|
-
} else if (arcParamIndex === 4) {
|
|
566
|
-
// expecting sweep-flag; token may contain fs(+x)
|
|
567
|
-
if (/^[01]\d+(?:\.\d+)?$/.test(token)) {
|
|
568
|
-
p.push(parseInt(token[0], 10));
|
|
569
|
-
p.push(parseFloat(token.slice(1)));
|
|
570
|
-
arcParamIndex += 2;
|
|
571
|
-
if (arcParamIndex >= 7) arcParamIndex -= 7;
|
|
572
|
-
continue;
|
|
573
|
-
}
|
|
574
|
-
if (token === '0' || token === '1') {
|
|
575
|
-
p.push(parseInt(token, 10));
|
|
576
|
-
arcParamIndex += 1;
|
|
577
|
-
if (arcParamIndex >= 7) arcParamIndex -= 7;
|
|
578
|
-
continue;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
const parsedArc = parseFloat(token);
|
|
582
|
-
if (!isNaN(parsedArc)) {
|
|
583
|
-
p.push(parsedArc);
|
|
584
|
-
} else {
|
|
585
|
-
p.push(0);
|
|
586
|
-
}
|
|
587
|
-
arcParamIndex += 1;
|
|
588
|
-
if (arcParamIndex >= 7) arcParamIndex -= 7;
|
|
589
|
-
} else {
|
|
590
|
-
const parsed = parseFloat(token);
|
|
591
|
-
if (!isNaN(parsed)) {
|
|
592
|
-
p.push(parsed);
|
|
593
|
-
} else {
|
|
594
|
-
p.push(0);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
while (p.length > 0) {
|
|
600
|
-
if (isNaN(p[0])) {
|
|
601
|
-
// case for a trailing comma before next command
|
|
602
|
-
break;
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
let cmd: string = '';
|
|
606
|
-
let points: number[] = [];
|
|
607
|
-
const startX = cpx,
|
|
608
|
-
startY = cpy;
|
|
609
|
-
// Move var from within the switch to up here (jshint)
|
|
610
|
-
let prevCmd, ctlPtx, ctlPty; // Ss, Tt
|
|
611
|
-
let rx, ry, psi, fa, fs, x1, y1; // Aa
|
|
612
|
-
|
|
613
|
-
// convert l, H, h, V, and v to L
|
|
614
|
-
switch (c) {
|
|
615
|
-
// Note: Keep the lineTo's above the moveTo's in this switch
|
|
616
|
-
case 'l':
|
|
617
|
-
cpx += p.shift()!;
|
|
618
|
-
cpy += p.shift()!;
|
|
619
|
-
cmd = 'L';
|
|
620
|
-
points.push(cpx, cpy);
|
|
621
|
-
break;
|
|
622
|
-
case 'L':
|
|
623
|
-
cpx = p.shift()!;
|
|
624
|
-
cpy = p.shift()!;
|
|
625
|
-
points.push(cpx, cpy);
|
|
626
|
-
break;
|
|
627
|
-
// Note: lineTo handlers need to be above this point
|
|
628
|
-
case 'm':
|
|
629
|
-
const dx = p.shift()!;
|
|
630
|
-
const dy = p.shift()!;
|
|
631
|
-
cpx += dx;
|
|
632
|
-
cpy += dy;
|
|
633
|
-
cmd = 'M';
|
|
634
|
-
// After closing the path move the current position
|
|
635
|
-
// to the the first point of the path (if any).
|
|
636
|
-
if (ca.length > 2 && ca[ca.length - 1].command === 'z') {
|
|
637
|
-
for (let idx = ca.length - 2; idx >= 0; idx--) {
|
|
638
|
-
if (ca[idx].command === 'M') {
|
|
639
|
-
cpx = ca[idx].points[0] + dx;
|
|
640
|
-
cpy = ca[idx].points[1] + dy;
|
|
641
|
-
break;
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
points.push(cpx, cpy);
|
|
646
|
-
c = 'l';
|
|
647
|
-
// subsequent points are treated as relative lineTo
|
|
648
|
-
break;
|
|
649
|
-
case 'M':
|
|
650
|
-
cpx = p.shift()!;
|
|
651
|
-
cpy = p.shift()!;
|
|
652
|
-
cmd = 'M';
|
|
653
|
-
points.push(cpx, cpy);
|
|
654
|
-
c = 'L';
|
|
655
|
-
// subsequent points are treated as absolute lineTo
|
|
656
|
-
break;
|
|
657
|
-
|
|
658
|
-
case 'h':
|
|
659
|
-
cpx += p.shift()!;
|
|
660
|
-
cmd = 'L';
|
|
661
|
-
points.push(cpx, cpy);
|
|
662
|
-
break;
|
|
663
|
-
case 'H':
|
|
664
|
-
cpx = p.shift()!;
|
|
665
|
-
cmd = 'L';
|
|
666
|
-
points.push(cpx, cpy);
|
|
667
|
-
break;
|
|
668
|
-
case 'v':
|
|
669
|
-
cpy += p.shift()!;
|
|
670
|
-
cmd = 'L';
|
|
671
|
-
points.push(cpx, cpy);
|
|
672
|
-
break;
|
|
673
|
-
case 'V':
|
|
674
|
-
cpy = p.shift()!;
|
|
675
|
-
cmd = 'L';
|
|
676
|
-
points.push(cpx, cpy);
|
|
677
|
-
break;
|
|
678
|
-
case 'C':
|
|
679
|
-
points.push(p.shift()!, p.shift()!, p.shift()!, p.shift()!);
|
|
680
|
-
cpx = p.shift()!;
|
|
681
|
-
cpy = p.shift()!;
|
|
682
|
-
points.push(cpx, cpy);
|
|
683
|
-
break;
|
|
684
|
-
case 'c':
|
|
685
|
-
points.push(
|
|
686
|
-
cpx + p.shift()!,
|
|
687
|
-
cpy + p.shift()!,
|
|
688
|
-
cpx + p.shift()!,
|
|
689
|
-
cpy + p.shift()!
|
|
690
|
-
);
|
|
691
|
-
cpx += p.shift()!;
|
|
692
|
-
cpy += p.shift()!;
|
|
693
|
-
cmd = 'C';
|
|
694
|
-
points.push(cpx, cpy);
|
|
695
|
-
break;
|
|
696
|
-
case 'S':
|
|
697
|
-
ctlPtx = cpx;
|
|
698
|
-
ctlPty = cpy;
|
|
699
|
-
prevCmd = ca[ca.length - 1];
|
|
700
|
-
if (prevCmd.command === 'C') {
|
|
701
|
-
ctlPtx = cpx + (cpx - prevCmd.points[2]);
|
|
702
|
-
ctlPty = cpy + (cpy - prevCmd.points[3]);
|
|
703
|
-
}
|
|
704
|
-
points.push(ctlPtx, ctlPty, p.shift()!, p.shift()!);
|
|
705
|
-
cpx = p.shift()!;
|
|
706
|
-
cpy = p.shift()!;
|
|
707
|
-
cmd = 'C';
|
|
708
|
-
points.push(cpx, cpy);
|
|
709
|
-
break;
|
|
710
|
-
case 's':
|
|
711
|
-
ctlPtx = cpx;
|
|
712
|
-
ctlPty = cpy;
|
|
713
|
-
prevCmd = ca[ca.length - 1];
|
|
714
|
-
if (prevCmd.command === 'C') {
|
|
715
|
-
ctlPtx = cpx + (cpx - prevCmd.points[2]);
|
|
716
|
-
ctlPty = cpy + (cpy - prevCmd.points[3]);
|
|
717
|
-
}
|
|
718
|
-
points.push(ctlPtx, ctlPty, cpx + p.shift()!, cpy + p.shift()!);
|
|
719
|
-
cpx += p.shift()!;
|
|
720
|
-
cpy += p.shift()!;
|
|
721
|
-
cmd = 'C';
|
|
722
|
-
points.push(cpx, cpy);
|
|
723
|
-
break;
|
|
724
|
-
case 'Q':
|
|
725
|
-
points.push(p.shift()!, p.shift()!);
|
|
726
|
-
cpx = p.shift()!;
|
|
727
|
-
cpy = p.shift()!;
|
|
728
|
-
points.push(cpx, cpy);
|
|
729
|
-
break;
|
|
730
|
-
case 'q':
|
|
731
|
-
points.push(cpx + p.shift()!, cpy + p.shift()!);
|
|
732
|
-
cpx += p.shift()!;
|
|
733
|
-
cpy += p.shift()!;
|
|
734
|
-
cmd = 'Q';
|
|
735
|
-
points.push(cpx, cpy);
|
|
736
|
-
break;
|
|
737
|
-
case 'T':
|
|
738
|
-
ctlPtx = cpx;
|
|
739
|
-
ctlPty = cpy;
|
|
740
|
-
prevCmd = ca[ca.length - 1];
|
|
741
|
-
if (prevCmd.command === 'Q') {
|
|
742
|
-
ctlPtx = cpx + (cpx - prevCmd.points[0]);
|
|
743
|
-
ctlPty = cpy + (cpy - prevCmd.points[1]);
|
|
744
|
-
}
|
|
745
|
-
cpx = p.shift()!;
|
|
746
|
-
cpy = p.shift()!;
|
|
747
|
-
cmd = 'Q';
|
|
748
|
-
points.push(ctlPtx, ctlPty, cpx, cpy);
|
|
749
|
-
break;
|
|
750
|
-
case 't':
|
|
751
|
-
ctlPtx = cpx;
|
|
752
|
-
ctlPty = cpy;
|
|
753
|
-
prevCmd = ca[ca.length - 1];
|
|
754
|
-
if (prevCmd.command === 'Q') {
|
|
755
|
-
ctlPtx = cpx + (cpx - prevCmd.points[0]);
|
|
756
|
-
ctlPty = cpy + (cpy - prevCmd.points[1]);
|
|
757
|
-
}
|
|
758
|
-
cpx += p.shift()!;
|
|
759
|
-
cpy += p.shift()!;
|
|
760
|
-
cmd = 'Q';
|
|
761
|
-
points.push(ctlPtx, ctlPty, cpx, cpy);
|
|
762
|
-
break;
|
|
763
|
-
case 'A':
|
|
764
|
-
rx = p.shift()!;
|
|
765
|
-
ry = p.shift()!;
|
|
766
|
-
psi = p.shift()!;
|
|
767
|
-
fa = p.shift()!;
|
|
768
|
-
fs = p.shift()!;
|
|
769
|
-
x1 = cpx;
|
|
770
|
-
y1 = cpy;
|
|
771
|
-
cpx = p.shift()!;
|
|
772
|
-
cpy = p.shift()!;
|
|
773
|
-
cmd = 'A';
|
|
774
|
-
points = this.convertEndpointToCenterParameterization(
|
|
775
|
-
x1,
|
|
776
|
-
y1,
|
|
777
|
-
cpx,
|
|
778
|
-
cpy,
|
|
779
|
-
fa,
|
|
780
|
-
fs,
|
|
781
|
-
rx,
|
|
782
|
-
ry,
|
|
783
|
-
psi
|
|
784
|
-
);
|
|
785
|
-
break;
|
|
786
|
-
case 'a':
|
|
787
|
-
rx = p.shift();
|
|
788
|
-
ry = p.shift();
|
|
789
|
-
psi = p.shift();
|
|
790
|
-
fa = p.shift();
|
|
791
|
-
fs = p.shift();
|
|
792
|
-
x1 = cpx;
|
|
793
|
-
y1 = cpy;
|
|
794
|
-
cpx += p.shift()!;
|
|
795
|
-
cpy += p.shift()!;
|
|
796
|
-
cmd = 'A';
|
|
797
|
-
points = this.convertEndpointToCenterParameterization(
|
|
798
|
-
x1,
|
|
799
|
-
y1,
|
|
800
|
-
cpx,
|
|
801
|
-
cpy,
|
|
802
|
-
fa,
|
|
803
|
-
fs,
|
|
804
|
-
rx,
|
|
805
|
-
ry,
|
|
806
|
-
psi
|
|
807
|
-
);
|
|
808
|
-
break;
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
ca.push({
|
|
812
|
-
command: cmd || c,
|
|
813
|
-
points: points,
|
|
814
|
-
start: {
|
|
815
|
-
x: startX,
|
|
816
|
-
y: startY,
|
|
817
|
-
},
|
|
818
|
-
pathLength: this.calcLength(startX, startY, cmd || c, points),
|
|
819
|
-
});
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
if (c === 'z' || c === 'Z') {
|
|
823
|
-
ca.push({
|
|
824
|
-
command: 'z',
|
|
825
|
-
points: [],
|
|
826
|
-
start: undefined as any,
|
|
827
|
-
pathLength: 0,
|
|
828
|
-
});
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
return ca;
|
|
833
|
-
}
|
|
834
|
-
static calcLength(x, y, cmd, points) {
|
|
835
|
-
let len, p1, p2, t;
|
|
836
|
-
const path = Path;
|
|
837
|
-
|
|
838
|
-
switch (cmd) {
|
|
839
|
-
case 'L':
|
|
840
|
-
return path.getLineLength(x, y, points[0], points[1]);
|
|
841
|
-
case 'C':
|
|
842
|
-
return getCubicArcLength(
|
|
843
|
-
[x, points[0], points[2], points[4]],
|
|
844
|
-
[y, points[1], points[3], points[5]],
|
|
845
|
-
1
|
|
846
|
-
);
|
|
847
|
-
case 'Q':
|
|
848
|
-
return getQuadraticArcLength(
|
|
849
|
-
[x, points[0], points[2]],
|
|
850
|
-
[y, points[1], points[3]],
|
|
851
|
-
1
|
|
852
|
-
);
|
|
853
|
-
case 'A':
|
|
854
|
-
// Approximates by breaking curve into line segments
|
|
855
|
-
len = 0.0;
|
|
856
|
-
const start = points[4];
|
|
857
|
-
// 4 = theta
|
|
858
|
-
const dTheta = points[5];
|
|
859
|
-
// 5 = dTheta
|
|
860
|
-
const end = points[4] + dTheta;
|
|
861
|
-
let inc = Math.PI / 180.0;
|
|
862
|
-
// 1 degree resolution
|
|
863
|
-
if (Math.abs(start - end) < inc) {
|
|
864
|
-
inc = Math.abs(start - end);
|
|
865
|
-
}
|
|
866
|
-
// Note: for purpose of calculating arc length, not going to worry about rotating X-axis by angle psi
|
|
867
|
-
p1 = path.getPointOnEllipticalArc(
|
|
868
|
-
points[0],
|
|
869
|
-
points[1],
|
|
870
|
-
points[2],
|
|
871
|
-
points[3],
|
|
872
|
-
start,
|
|
873
|
-
0
|
|
874
|
-
);
|
|
875
|
-
if (dTheta < 0) {
|
|
876
|
-
// clockwise
|
|
877
|
-
for (t = start - inc; t > end; t -= inc) {
|
|
878
|
-
p2 = path.getPointOnEllipticalArc(
|
|
879
|
-
points[0],
|
|
880
|
-
points[1],
|
|
881
|
-
points[2],
|
|
882
|
-
points[3],
|
|
883
|
-
t,
|
|
884
|
-
0
|
|
885
|
-
);
|
|
886
|
-
len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
|
|
887
|
-
p1 = p2;
|
|
888
|
-
}
|
|
889
|
-
} else {
|
|
890
|
-
// counter-clockwise
|
|
891
|
-
for (t = start + inc; t < end; t += inc) {
|
|
892
|
-
p2 = path.getPointOnEllipticalArc(
|
|
893
|
-
points[0],
|
|
894
|
-
points[1],
|
|
895
|
-
points[2],
|
|
896
|
-
points[3],
|
|
897
|
-
t,
|
|
898
|
-
0
|
|
899
|
-
);
|
|
900
|
-
len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
|
|
901
|
-
p1 = p2;
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
p2 = path.getPointOnEllipticalArc(
|
|
905
|
-
points[0],
|
|
906
|
-
points[1],
|
|
907
|
-
points[2],
|
|
908
|
-
points[3],
|
|
909
|
-
end,
|
|
910
|
-
0
|
|
911
|
-
);
|
|
912
|
-
len += path.getLineLength(p1.x, p1.y, p2.x, p2.y);
|
|
913
|
-
|
|
914
|
-
return len;
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
return 0;
|
|
918
|
-
}
|
|
919
|
-
static convertEndpointToCenterParameterization(
|
|
920
|
-
x1: number,
|
|
921
|
-
y1: number,
|
|
922
|
-
x2: number,
|
|
923
|
-
y2: number,
|
|
924
|
-
fa: number,
|
|
925
|
-
fs: number,
|
|
926
|
-
rx: number,
|
|
927
|
-
ry: number,
|
|
928
|
-
psiDeg: number
|
|
929
|
-
) {
|
|
930
|
-
// Derived from: http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
|
|
931
|
-
const psi = psiDeg * (Math.PI / 180.0);
|
|
932
|
-
const xp =
|
|
933
|
-
(Math.cos(psi) * (x1 - x2)) / 2.0 + (Math.sin(psi) * (y1 - y2)) / 2.0;
|
|
934
|
-
const yp =
|
|
935
|
-
(-1 * Math.sin(psi) * (x1 - x2)) / 2.0 +
|
|
936
|
-
(Math.cos(psi) * (y1 - y2)) / 2.0;
|
|
937
|
-
|
|
938
|
-
const lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry);
|
|
939
|
-
|
|
940
|
-
if (lambda > 1) {
|
|
941
|
-
rx *= Math.sqrt(lambda);
|
|
942
|
-
ry *= Math.sqrt(lambda);
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
let f = Math.sqrt(
|
|
946
|
-
(rx * rx * (ry * ry) - rx * rx * (yp * yp) - ry * ry * (xp * xp)) /
|
|
947
|
-
(rx * rx * (yp * yp) + ry * ry * (xp * xp))
|
|
948
|
-
);
|
|
949
|
-
|
|
950
|
-
if (fa === fs) {
|
|
951
|
-
f *= -1;
|
|
952
|
-
}
|
|
953
|
-
if (isNaN(f)) {
|
|
954
|
-
f = 0;
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
const cxp = (f * rx * yp) / ry;
|
|
958
|
-
const cyp = (f * -ry * xp) / rx;
|
|
959
|
-
|
|
960
|
-
const cx = (x1 + x2) / 2.0 + Math.cos(psi) * cxp - Math.sin(psi) * cyp;
|
|
961
|
-
const cy = (y1 + y2) / 2.0 + Math.sin(psi) * cxp + Math.cos(psi) * cyp;
|
|
962
|
-
|
|
963
|
-
const vMag = function (v) {
|
|
964
|
-
return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
|
|
965
|
-
};
|
|
966
|
-
const vRatio = function (u, v) {
|
|
967
|
-
return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v));
|
|
968
|
-
};
|
|
969
|
-
const vAngle = function (u, v) {
|
|
970
|
-
return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(vRatio(u, v));
|
|
971
|
-
};
|
|
972
|
-
const theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]);
|
|
973
|
-
const u = [(xp - cxp) / rx, (yp - cyp) / ry];
|
|
974
|
-
const v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry];
|
|
975
|
-
let dTheta = vAngle(u, v);
|
|
976
|
-
|
|
977
|
-
if (vRatio(u, v) <= -1) {
|
|
978
|
-
dTheta = Math.PI;
|
|
979
|
-
}
|
|
980
|
-
if (vRatio(u, v) >= 1) {
|
|
981
|
-
dTheta = 0;
|
|
982
|
-
}
|
|
983
|
-
if (fs === 0 && dTheta > 0) {
|
|
984
|
-
dTheta = dTheta - 2 * Math.PI;
|
|
985
|
-
}
|
|
986
|
-
if (fs === 1 && dTheta < 0) {
|
|
987
|
-
dTheta = dTheta + 2 * Math.PI;
|
|
988
|
-
}
|
|
989
|
-
return [cx, cy, rx, ry, theta, dTheta, psi, fs];
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
Path.prototype.className = 'Path';
|
|
994
|
-
Path.prototype._attrsAffectingSize = ['data'];
|
|
995
|
-
_registerNode(Path);
|
|
996
|
-
|
|
997
|
-
/**
|
|
998
|
-
* get/set SVG path data string. This method
|
|
999
|
-
* also automatically parses the data string
|
|
1000
|
-
* into a data array. Currently supported SVG data:
|
|
1001
|
-
* M, m, L, l, H, h, V, v, Q, q, T, t, C, c, S, s, A, a, Z, z
|
|
1002
|
-
* @name Konva.Path#data
|
|
1003
|
-
* @method
|
|
1004
|
-
* @param {String} data svg path string
|
|
1005
|
-
* @returns {String}
|
|
1006
|
-
* @example
|
|
1007
|
-
* // get data
|
|
1008
|
-
* var data = path.data();
|
|
1009
|
-
*
|
|
1010
|
-
* // set data
|
|
1011
|
-
* path.data('M200,100h100v50z');
|
|
1012
|
-
*/
|
|
1013
|
-
Factory.addGetterSetter(Path, 'data');
|