| changeset 598 | eb4f4eceada0 |
| parent 495 | 444b80998255 |
| 597:ebdd1d769158 | 598:eb4f4eceada0 |
|---|---|
1 /*! |
1 /*! |
2 * Paper.js v0.9.24 - The Swiss Army Knife of Vector Graphics Scripting. |
2 * Paper.js v0.9.25 - The Swiss Army Knife of Vector Graphics Scripting. |
3 * http://paperjs.org/ |
3 * http://paperjs.org/ |
4 * |
4 * |
5 * Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey |
5 * Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey |
6 * http://scratchdisk.com/ & http://jonathanpuckey.com/ |
6 * http://scratchdisk.com/ & http://jonathanpuckey.com/ |
7 * |
7 * |
8 * Distributed under the MIT license. See LICENSE file for details. |
8 * Distributed under the MIT license. See LICENSE file for details. |
9 * |
9 * |
10 * All rights reserved. |
10 * All rights reserved. |
11 * |
11 * |
12 * Date: Fri Aug 21 16:39:41 2015 +0200 |
12 * Date: Sun Oct 25 11:23:38 2015 +0100 |
13 * |
13 * |
14 *** |
14 *** |
15 * |
15 * |
16 * Straps.js - Class inheritance library with support for bean-style accessors |
16 * Straps.js - Class inheritance library with support for bean-style accessors |
17 * |
17 * |
579 entry = types && types[type], |
579 entry = types && types[type], |
580 handlers = this._callbacks = this._callbacks || {}; |
580 handlers = this._callbacks = this._callbacks || {}; |
581 handlers = handlers[type] = handlers[type] || []; |
581 handlers = handlers[type] = handlers[type] || []; |
582 if (handlers.indexOf(func) === -1) { |
582 if (handlers.indexOf(func) === -1) { |
583 handlers.push(func); |
583 handlers.push(func); |
584 if (entry && entry.install && handlers.length == 1) |
584 if (entry && entry.install && handlers.length === 1) |
585 entry.install.call(this, type); |
585 entry.install.call(this, type); |
586 } |
586 } |
587 } |
587 } |
588 return this; |
588 return this; |
589 }, |
589 }, |
744 if (browser.atom) |
744 if (browser.atom) |
745 delete browser.chrome; |
745 delete browser.chrome; |
746 } |
746 } |
747 }, |
747 }, |
748 |
748 |
749 version: '0.9.24', |
749 version: "0.9.25", |
750 |
750 |
751 getView: function() { |
751 getView: function() { |
752 return this.project && this.project.getView(); |
752 return this.project && this.project.getView(); |
753 }, |
753 }, |
754 |
754 |
927 ]; |
927 ]; |
928 |
928 |
929 var abs = Math.abs, |
929 var abs = Math.abs, |
930 sqrt = Math.sqrt, |
930 sqrt = Math.sqrt, |
931 pow = Math.pow, |
931 pow = Math.pow, |
932 TOLERANCE = 1e-6, |
|
933 EPSILON = 1e-12, |
932 EPSILON = 1e-12, |
934 MACHINE_EPSILON = 1.12e-16; |
933 MACHINE_EPSILON = 1.12e-16; |
935 |
934 |
935 function clip(value, min, max) { |
|
936 return value < min ? min : value > max ? max : value; |
|
937 } |
|
938 |
|
936 return { |
939 return { |
937 TOLERANCE: TOLERANCE, |
940 TOLERANCE: 1e-6, |
938 EPSILON: EPSILON, |
941 EPSILON: EPSILON, |
939 MACHINE_EPSILON: MACHINE_EPSILON, |
942 MACHINE_EPSILON: MACHINE_EPSILON, |
943 CURVETIME_EPSILON: 4e-7, |
|
944 GEOMETRIC_EPSILON: 2e-7, |
|
945 WINDING_EPSILON: 2e-7, |
|
946 TRIGONOMETRIC_EPSILON: 1e-7, |
|
947 CLIPPING_EPSILON: 1e-7, |
|
940 KAPPA: 4 * (sqrt(2) - 1) / 3, |
948 KAPPA: 4 * (sqrt(2) - 1) / 3, |
941 |
949 |
942 isZero: function(val) { |
950 isZero: function(val) { |
943 return abs(val) <= EPSILON; |
951 return val >= -EPSILON && val <= EPSILON; |
944 }, |
952 }, |
945 |
953 |
946 integrate: function(f, a, b, n) { |
954 integrate: function(f, a, b, n) { |
947 var x = abscissas[n - 2], |
955 var x = abscissas[n - 2], |
948 w = weights[n - 2], |
956 w = weights[n - 2], |
976 return x; |
984 return x; |
977 }, |
985 }, |
978 |
986 |
979 solveQuadratic: function(a, b, c, roots, min, max) { |
987 solveQuadratic: function(a, b, c, roots, min, max) { |
980 var count = 0, |
988 var count = 0, |
989 eMin = min - EPSILON, |
|
990 eMax = max + EPSILON, |
|
981 x1, x2 = Infinity, |
991 x1, x2 = Infinity, |
982 B = b, |
992 B = b, |
983 D; |
993 D; |
984 b /= 2; |
994 b /= -2; |
985 D = b * b - a * c; |
995 D = b * b - a * c; |
986 if (D !== 0 && abs(D) < MACHINE_EPSILON) { |
996 if (D !== 0 && abs(D) < MACHINE_EPSILON) { |
987 var gmC = pow(abs(a * b * c), 1 / 3); |
997 var gmC = pow(abs(a * b * c), 1 / 3); |
988 if (gmC < 1e-8) { |
998 if (gmC < 1e-8) { |
989 var mult = pow(10, abs( |
999 var mult = pow(10, |
990 Math.floor(Math.log(gmC) * Math.LOG10E))); |
1000 abs(Math.floor(Math.log(gmC) * Math.LOG10E))); |
991 if (!isFinite(mult)) |
1001 if (!isFinite(mult)) |
992 mult = 0; |
1002 mult = 0; |
993 a *= mult; |
1003 a *= mult; |
994 b *= mult; |
1004 b *= mult; |
995 c *= mult; |
1005 c *= mult; |
998 } |
1008 } |
999 if (abs(a) < EPSILON) { |
1009 if (abs(a) < EPSILON) { |
1000 if (abs(B) < EPSILON) |
1010 if (abs(B) < EPSILON) |
1001 return abs(c) < EPSILON ? -1 : 0; |
1011 return abs(c) < EPSILON ? -1 : 0; |
1002 x1 = -c / B; |
1012 x1 = -c / B; |
1003 } else { |
1013 } else if (D >= -MACHINE_EPSILON) { |
1004 if (D >= -MACHINE_EPSILON) { |
1014 var Q = D < 0 ? 0 : sqrt(D), |
1005 D = D < 0 ? 0 : D; |
1015 R = b + (b < 0 ? -Q : Q); |
1006 var R = sqrt(D); |
1016 if (R === 0) { |
1007 if (b >= MACHINE_EPSILON && b <= MACHINE_EPSILON) { |
1017 x1 = c / a; |
1008 x1 = abs(a) >= abs(c) ? R / a : -c / R; |
1018 x2 = -x1; |
1009 x2 = -x1; |
1019 } else { |
1010 } else { |
1020 x1 = R / a; |
1011 var q = -(b + (b < 0 ? -1 : 1) * R); |
1021 x2 = c / R; |
1012 x1 = q / a; |
|
1013 x2 = c / q; |
|
1014 } |
|
1015 } |
1022 } |
1016 } |
1023 } |
1017 if (isFinite(x1) && (min == null || x1 >= min && x1 <= max)) |
1024 if (isFinite(x1) && (min == null || x1 > eMin && x1 < eMax)) |
1018 roots[count++] = x1; |
1025 roots[count++] = min == null ? x1 : clip(x1, min, max); |
1019 if (x2 !== x1 |
1026 if (x2 !== x1 |
1020 && isFinite(x2) && (min == null || x2 >= min && x2 <= max)) |
1027 && isFinite(x2) && (min == null || x2 > eMin && x2 < eMax)) |
1021 roots[count++] = x2; |
1028 roots[count++] = min == null ? x2 : clip(x2, min, max); |
1022 return count; |
1029 return count; |
1023 }, |
1030 }, |
1024 |
1031 |
1025 solveCubic: function(a, b, c, d, roots, min, max) { |
1032 solveCubic: function(a, b, c, d, roots, min, max) { |
1026 var count = 0, |
1033 var count = 0, |
1069 } |
1076 } |
1070 } |
1077 } |
1071 } |
1078 } |
1072 var count = Numerical.solveQuadratic(a, b1, c2, roots, min, max); |
1079 var count = Numerical.solveQuadratic(a, b1, c2, roots, min, max); |
1073 if (isFinite(x) && (count === 0 || x !== roots[count - 1]) |
1080 if (isFinite(x) && (count === 0 || x !== roots[count - 1]) |
1074 && (min == null || x >= min && x <= max)) |
1081 && (min == null || x > min - EPSILON && x < max + EPSILON)) |
1075 roots[count++] = x; |
1082 roots[count++] = min == null ? x : clip(x, min, max); |
1076 return count; |
1083 return count; |
1077 } |
1084 } |
1078 }; |
1085 }; |
1079 }; |
1086 }; |
1080 |
1087 |
1257 rotate: function(angle, center) { |
1264 rotate: function(angle, center) { |
1258 if (angle === 0) |
1265 if (angle === 0) |
1259 return this.clone(); |
1266 return this.clone(); |
1260 angle = angle * Math.PI / 180; |
1267 angle = angle * Math.PI / 180; |
1261 var point = center ? this.subtract(center) : this, |
1268 var point = center ? this.subtract(center) : this, |
1262 s = Math.sin(angle), |
1269 sin = Math.sin(angle), |
1263 c = Math.cos(angle); |
1270 cos = Math.cos(angle); |
1264 point = new Point( |
1271 point = new Point( |
1265 point.x * c - point.y * s, |
1272 point.x * cos - point.y * sin, |
1266 point.x * s + point.y * c |
1273 point.x * sin + point.y * cos |
1267 ); |
1274 ); |
1268 return center ? point.add(center) : point; |
1275 return center ? point.add(center) : point; |
1269 }, |
1276 }, |
1270 |
1277 |
1271 transform: function(matrix) { |
1278 transform: function(matrix) { |
1303 |
1310 |
1304 isInside: function() { |
1311 isInside: function() { |
1305 return Rectangle.read(arguments).contains(this); |
1312 return Rectangle.read(arguments).contains(this); |
1306 }, |
1313 }, |
1307 |
1314 |
1308 isClose: function(point, tolerance) { |
1315 isClose: function() { |
1316 var point = Point.read(arguments), |
|
1317 tolerance = Base.read(arguments); |
|
1309 return this.getDistance(point) < tolerance; |
1318 return this.getDistance(point) < tolerance; |
1310 }, |
1319 }, |
1311 |
1320 |
1312 isCollinear: function(point) { |
1321 isCollinear: function() { |
1313 return Math.abs(this.cross(point)) < 0.000001; |
1322 var point = Point.read(arguments); |
1323 return Point.isCollinear(this.x, this.y, point.x, point.y); |
|
1314 }, |
1324 }, |
1315 |
1325 |
1316 isColinear: '#isCollinear', |
1326 isColinear: '#isCollinear', |
1317 |
1327 |
1318 isOrthogonal: function(point) { |
1328 isOrthogonal: function() { |
1319 return Math.abs(this.dot(point)) < 0.000001; |
1329 var point = Point.read(arguments); |
1330 return Point.isOrthogonal(this.x, this.y, point.x, point.y); |
|
1320 }, |
1331 }, |
1321 |
1332 |
1322 isZero: function() { |
1333 isZero: function() { |
1323 return Numerical.isZero(this.x) && Numerical.isZero(this.y); |
1334 return Numerical.isZero(this.x) && Numerical.isZero(this.y); |
1324 }, |
1335 }, |
1336 var point = Point.read(arguments); |
1347 var point = Point.read(arguments); |
1337 return this.x * point.y - this.y * point.x; |
1348 return this.x * point.y - this.y * point.x; |
1338 }, |
1349 }, |
1339 |
1350 |
1340 project: function() { |
1351 project: function() { |
1341 var point = Point.read(arguments); |
1352 var point = Point.read(arguments), |
1342 if (point.isZero()) { |
1353 scale = point.isZero() ? 0 : this.dot(point) / point.dot(point); |
1343 return new Point(0, 0); |
1354 return new Point( |
1344 } else { |
1355 point.x * scale, |
1345 var scale = this.dot(point) / point.dot(point); |
1356 point.y * scale |
1346 return new Point( |
1357 ); |
1347 point.x * scale, |
|
1348 point.y * scale |
|
1349 ); |
|
1350 } |
|
1351 }, |
1358 }, |
1352 |
1359 |
1353 statics: { |
1360 statics: { |
1354 min: function() { |
1361 min: function() { |
1355 var point1 = Point.read(arguments), |
1362 var point1 = Point.read(arguments), |
1369 ); |
1376 ); |
1370 }, |
1377 }, |
1371 |
1378 |
1372 random: function() { |
1379 random: function() { |
1373 return new Point(Math.random(), Math.random()); |
1380 return new Point(Math.random(), Math.random()); |
1381 }, |
|
1382 |
|
1383 isCollinear: function(x1, y1, x2, y2) { |
|
1384 return Math.abs(x1 * y2 - y1 * x2) |
|
1385 <= Math.sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2)) |
|
1386 * 1e-7; |
|
1387 }, |
|
1388 |
|
1389 isOrthogonal: function(x1, y1, x2, y2) { |
|
1390 return Math.abs(x1 * x2 + y1 * y2) |
|
1391 <= Math.sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2)) |
|
1392 * 1e-7; |
|
1374 } |
1393 } |
1375 } |
1394 } |
1376 }, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) { |
1395 }, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) { |
1377 var op = Math[name]; |
1396 var op = Math[name]; |
1378 this[name] = function() { |
1397 this[name] = function() { |
1920 this._height = height; |
1939 this._height = height; |
1921 if (!_dontNotify) |
1940 if (!_dontNotify) |
1922 this._owner[this._setter](this); |
1941 this._owner[this._setter](this); |
1923 return this; |
1942 return this; |
1924 } |
1943 } |
1925 }, new function() { |
1944 }, |
1945 new function() { |
|
1926 var proto = Rectangle.prototype; |
1946 var proto = Rectangle.prototype; |
1927 |
1947 |
1928 return Base.each(['x', 'y', 'width', 'height'], function(key) { |
1948 return Base.each(['x', 'y', 'width', 'height'], function(key) { |
1929 var part = Base.capitalize(key); |
1949 var part = Base.capitalize(key); |
1930 var internal = '_' + key; |
1950 var internal = '_' + key; |
2253 }, |
2273 }, |
2254 |
2274 |
2255 _transformBounds: function(bounds, dest, _dontNotify) { |
2275 _transformBounds: function(bounds, dest, _dontNotify) { |
2256 var coords = this._transformCorners(bounds), |
2276 var coords = this._transformCorners(bounds), |
2257 min = coords.slice(0, 2), |
2277 min = coords.slice(0, 2), |
2258 max = coords.slice(); |
2278 max = min.slice(); |
2259 for (var i = 2; i < 8; i++) { |
2279 for (var i = 2; i < 8; i++) { |
2260 var val = coords[i], |
2280 var val = coords[i], |
2261 j = i & 1; |
2281 j = i & 1; |
2262 if (val < min[j]) |
2282 if (val < min[j]) |
2263 min[j] = val; |
2283 min[j] = val; |
2415 this._px, this._py, this._vx, this._vy, |
2435 this._px, this._py, this._vx, this._vy, |
2416 line._px, line._py, line._vx, line._vy, |
2436 line._px, line._py, line._vx, line._vy, |
2417 true, isInfinite); |
2437 true, isInfinite); |
2418 }, |
2438 }, |
2419 |
2439 |
2420 getSide: function(point) { |
2440 getSide: function(point, isInfinite) { |
2421 return Line.getSide( |
2441 return Line.getSide( |
2422 this._px, this._py, this._vx, this._vy, |
2442 this._px, this._py, this._vx, this._vy, |
2423 point.x, point.y, true); |
2443 point.x, point.y, true, isInfinite); |
2424 }, |
2444 }, |
2425 |
2445 |
2426 getDistance: function(point) { |
2446 getDistance: function(point) { |
2427 return Math.abs(Line.getSignedDistance( |
2447 return Math.abs(Line.getSignedDistance( |
2428 this._px, this._py, this._vx, this._vy, |
2448 this._px, this._py, this._vx, this._vy, |
2429 point.x, point.y, true)); |
2449 point.x, point.y, true)); |
2430 }, |
2450 }, |
2431 |
2451 |
2452 isCollinear: function(line) { |
|
2453 return Point.isCollinear(this._vx, this._vy, line._vx, line._vy); |
|
2454 }, |
|
2455 |
|
2456 isOrthogonal: function(line) { |
|
2457 return Point.isOrthogonal(this._vx, this._vy, line._vx, line._vy); |
|
2458 }, |
|
2459 |
|
2432 statics: { |
2460 statics: { |
2433 intersect: function(apx, apy, avx, avy, bpx, bpy, bvx, bvy, asVector, |
2461 intersect: function(p1x, p1y, v1x, v1y, p2x, p2y, v2x, v2y, asVector, |
2434 isInfinite) { |
2462 isInfinite) { |
2435 if (!asVector) { |
2463 if (!asVector) { |
2436 avx -= apx; |
2464 v1x -= p1x; |
2437 avy -= apy; |
2465 v1y -= p1y; |
2438 bvx -= bpx; |
2466 v2x -= p2x; |
2439 bvy -= bpy; |
2467 v2y -= p2y; |
2440 } |
2468 } |
2441 var cross = avx * bvy - avy * bvx; |
2469 var cross = v1x * v2y - v1y * v2x; |
2442 if (!Numerical.isZero(cross)) { |
2470 if (!Numerical.isZero(cross)) { |
2443 var dx = apx - bpx, |
2471 var dx = p1x - p2x, |
2444 dy = apy - bpy, |
2472 dy = p1y - p2y, |
2445 ta = (bvx * dy - bvy * dx) / cross, |
2473 u1 = (v2x * dy - v2y * dx) / cross, |
2446 tb = (avx * dy - avy * dx) / cross; |
2474 u2 = (v1x * dy - v1y * dx) / cross, |
2447 if (isInfinite || 0 <= ta && ta <= 1 && 0 <= tb && tb <= 1) |
2475 epsilon = 1e-12, |
2476 uMin = -epsilon, |
|
2477 uMax = 1 + epsilon; |
|
2478 if (isInfinite |
|
2479 || uMin < u1 && u1 < uMax && uMin < u2 && u2 < uMax) { |
|
2480 if (!isInfinite) { |
|
2481 u1 = u1 <= 0 ? 0 : u1 >= 1 ? 1 : u1; |
|
2482 } |
|
2448 return new Point( |
2483 return new Point( |
2449 apx + ta * avx, |
2484 p1x + u1 * v1x, |
2450 apy + ta * avy); |
2485 p1y + u1 * v1y); |
2451 } |
2486 } |
2452 }, |
2487 } |
2453 |
2488 }, |
2454 getSide: function(px, py, vx, vy, x, y, asVector) { |
2489 |
2490 getSide: function(px, py, vx, vy, x, y, asVector, isInfinite) { |
|
2455 if (!asVector) { |
2491 if (!asVector) { |
2456 vx -= px; |
2492 vx -= px; |
2457 vy -= py; |
2493 vy -= py; |
2458 } |
2494 } |
2459 var v2x = x - px, |
2495 var v2x = x - px, |
2460 v2y = y - py, |
2496 v2y = y - py, |
2461 ccw = v2x * vy - v2y * vx; |
2497 ccw = v2x * vy - v2y * vx; |
2462 if (ccw === 0) { |
2498 if (ccw === 0 && !isInfinite) { |
2463 ccw = v2x * vx + v2y * vy; |
2499 ccw = (v2x * vx + v2x * vx) / (vx * vx + vy * vy); |
2464 if (ccw > 0) { |
2500 if (ccw >= 0 && ccw <= 1) |
2465 v2x -= vx; |
2501 ccw = 0; |
2466 v2y -= vy; |
|
2467 ccw = v2x * vx + v2y * vy; |
|
2468 if (ccw < 0) |
|
2469 ccw = 0; |
|
2470 } |
|
2471 } |
2502 } |
2472 return ccw < 0 ? -1 : ccw > 0 ? 1 : 0; |
2503 return ccw < 0 ? -1 : ccw > 0 ? 1 : 0; |
2473 }, |
2504 }, |
2474 |
2505 |
2475 getSignedDistance: function(px, py, vx, vy, x, y, asVector) { |
2506 getSignedDistance: function(px, py, vx, vy, x, y, asVector) { |
2476 if (!asVector) { |
2507 if (!asVector) { |
2477 vx -= px; |
2508 vx -= px; |
2478 vy -= py; |
2509 vy -= py; |
2479 } |
2510 } |
2480 return Numerical.isZero(vx) |
2511 return vx === 0 ? vy > 0 ? x - px : px - x |
2481 ? vy >= 0 ? px - x : x - px |
2512 : vy === 0 ? vx < 0 ? y - py : py - y |
2482 : Numerical.isZero(vy) |
2513 : ((x-px) * vy - (y-py) * vx) / Math.sqrt(vx * vx + vy * vy); |
2483 ? vx >= 0 ? y - py : py - y |
|
2484 : (vx * (y - py) - vy * (x - px)) / Math.sqrt(vx * vx + vy * vy); |
|
2485 } |
2514 } |
2486 } |
2515 } |
2487 }); |
2516 }); |
2488 |
2517 |
2489 var Project = PaperScopeItem.extend({ |
2518 var Project = PaperScopeItem.extend({ |
2781 this._set(props, { insert: true, project: true, parent: true }, |
2810 this._set(props, { insert: true, project: true, parent: true }, |
2782 true); |
2811 true); |
2783 return hasProps; |
2812 return hasProps; |
2784 }, |
2813 }, |
2785 |
2814 |
2786 _events: new function() { |
2815 _events: Base.each(['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onClick', |
2787 |
2816 'onDoubleClick', 'onMouseMove', 'onMouseEnter', 'onMouseLeave'], |
2788 var mouseFlags = { |
2817 function(name) { |
2789 mousedown: { |
2818 this[name] = { |
2790 mousedown: 1, |
2819 install: function(type) { |
2791 mousedrag: 1, |
2820 this.getView()._installEvent(type); |
2792 click: 1, |
2821 }, |
2793 doubleclick: 1 |
2822 |
2794 }, |
2823 uninstall: function(type) { |
2795 mouseup: { |
2824 this.getView()._uninstallEvent(type); |
2796 mouseup: 1, |
2825 } |
2797 mousedrag: 1, |
2826 }; |
2798 click: 1, |
2827 }, { |
2799 doubleclick: 1 |
2828 onFrame: { |
2800 }, |
2829 install: function() { |
2801 mousemove: { |
2830 this.getView()._animateItem(this, true); |
2802 mousedrag: 1, |
2831 }, |
2803 mousemove: 1, |
2832 |
2804 mouseenter: 1, |
2833 uninstall: function() { |
2805 mouseleave: 1 |
2834 this.getView()._animateItem(this, false); |
2806 } |
|
2807 }; |
|
2808 |
|
2809 var mouseEvent = { |
|
2810 install: function(type) { |
|
2811 var counters = this.getView()._eventCounters; |
|
2812 if (counters) { |
|
2813 for (var key in mouseFlags) { |
|
2814 counters[key] = (counters[key] || 0) |
|
2815 + (mouseFlags[key][type] || 0); |
|
2816 } |
|
2817 } |
2835 } |
2818 }, |
2836 }, |
2819 uninstall: function(type) { |
2837 |
2820 var counters = this.getView()._eventCounters; |
2838 onLoad: {} |
2821 if (counters) { |
2839 } |
2822 for (var key in mouseFlags) |
2840 ), |
2823 counters[key] -= mouseFlags[key][type] || 0; |
|
2824 } |
|
2825 } |
|
2826 }; |
|
2827 |
|
2828 return Base.each(['onMouseDown', 'onMouseUp', 'onMouseDrag', 'onClick', |
|
2829 'onDoubleClick', 'onMouseMove', 'onMouseEnter', 'onMouseLeave'], |
|
2830 function(name) { |
|
2831 this[name] = mouseEvent; |
|
2832 }, { |
|
2833 onFrame: { |
|
2834 install: function() { |
|
2835 this._animateItem(true); |
|
2836 }, |
|
2837 uninstall: function() { |
|
2838 this._animateItem(false); |
|
2839 } |
|
2840 }, |
|
2841 |
|
2842 onLoad: {} |
|
2843 } |
|
2844 ); |
|
2845 }, |
|
2846 |
|
2847 _animateItem: function(animate) { |
|
2848 this.getView()._animateItem(this, animate); |
|
2849 }, |
|
2850 |
2841 |
2851 _serialize: function(options, dictionary) { |
2842 _serialize: function(options, dictionary) { |
2852 var props = {}, |
2843 var props = {}, |
2853 that = this; |
2844 that = this; |
2854 |
2845 |
3450 }, |
3441 }, |
3451 |
3442 |
3452 intersects: function(item, _matrix) { |
3443 intersects: function(item, _matrix) { |
3453 if (!(item instanceof Item)) |
3444 if (!(item instanceof Item)) |
3454 return false; |
3445 return false; |
3455 return this._asPathItem().getIntersections(item._asPathItem(), |
3446 return this._asPathItem().getIntersections(item._asPathItem(), null, |
3456 _matrix || item._matrix).length > 0; |
3447 _matrix || item._matrix, true).length > 0; |
3457 }, |
3448 }, |
3458 |
3449 |
3459 hitTest: function() { |
3450 hitTest: function() { |
3460 return this._hitTest( |
3451 return this._hitTest( |
3461 Point.read(arguments), |
3452 Point.read(arguments), |
3474 ? parentTotalMatrix.chain(matrix) |
3465 ? parentTotalMatrix.chain(matrix) |
3475 : this.getGlobalMatrix().preConcatenate(view._matrix), |
3466 : this.getGlobalMatrix().preConcatenate(view._matrix), |
3476 tolerancePadding = options._tolerancePadding = new Size( |
3467 tolerancePadding = options._tolerancePadding = new Size( |
3477 Path._getPenPadding(1, totalMatrix.inverted()) |
3468 Path._getPenPadding(1, totalMatrix.inverted()) |
3478 ).multiply( |
3469 ).multiply( |
3479 Math.max(options.tolerance, 0.000001) |
3470 Math.max(options.tolerance, 1e-6) |
3480 ); |
3471 ); |
3481 point = matrix._inverseTransform(point); |
3472 point = matrix._inverseTransform(point); |
3482 |
3473 |
3483 if (!this._children && !this.getInternalRoughBounds() |
3474 if (!this._children && !this.getInternalRoughBounds() |
3484 .expand(tolerancePadding.multiply(2))._containsPoint(point)) |
3475 .expand(tolerancePadding.multiply(2))._containsPoint(point)) |
3597 firstOnly) { |
3588 firstOnly) { |
3598 if (!param && typeof match === 'object') { |
3589 if (!param && typeof match === 'object') { |
3599 var overlapping = match.overlapping, |
3590 var overlapping = match.overlapping, |
3600 inside = match.inside, |
3591 inside = match.inside, |
3601 bounds = overlapping || inside, |
3592 bounds = overlapping || inside, |
3602 rect = bounds && Rectangle.read([bounds]); |
3593 rect = bounds && Rectangle.read([bounds]); |
3603 param = { |
3594 param = { |
3604 items: [], |
3595 items: [], |
3605 inside: !!inside, |
3596 inside: !!inside, |
3606 overlapping: !!overlapping, |
3597 overlapping: !!overlapping, |
3607 rect: rect, |
3598 rect: rect, |
3899 return false; |
3890 return false; |
3900 }, |
3891 }, |
3901 |
3892 |
3902 isAncestor: function(item) { |
3893 isAncestor: function(item) { |
3903 return item ? item.isDescendant(this) : false; |
3894 return item ? item.isDescendant(this) : false; |
3895 }, |
|
3896 |
|
3897 isSibling: function(item) { |
|
3898 return this._parent === item._parent; |
|
3904 }, |
3899 }, |
3905 |
3900 |
3906 isGroupedWith: function(item) { |
3901 isGroupedWith: function(item) { |
3907 var parent = this._parent; |
3902 var parent = this._parent; |
3908 while (parent) { |
3903 while (parent) { |
4176 ctx.strokeStyle = ctx.fillStyle = color |
4171 ctx.strokeStyle = ctx.fillStyle = color |
4177 ? color.toCanvasStyle(ctx) : '#009dec'; |
4172 ? color.toCanvasStyle(ctx) : '#009dec'; |
4178 if (this._drawSelected) |
4173 if (this._drawSelected) |
4179 this._drawSelected(ctx, mx, selectedItems); |
4174 this._drawSelected(ctx, mx, selectedItems); |
4180 if (this._boundsSelected) { |
4175 if (this._boundsSelected) { |
4181 var half = size / 2; |
4176 var half = size / 2, |
4182 coords = mx._transformCorners(this.getInternalBounds()); |
4177 coords = mx._transformCorners(this.getInternalBounds()); |
4183 ctx.beginPath(); |
4178 ctx.beginPath(); |
4184 for (var i = 0; i < 8; i++) |
4179 for (var i = 0; i < 8; i++) |
4185 ctx[i === 0 ? 'moveTo' : 'lineTo'](coords[i], coords[++i]); |
4180 ctx[i === 0 ? 'moveTo' : 'lineTo'](coords[i], coords[++i]); |
4186 ctx.closePath(); |
4181 ctx.closePath(); |
4555 rect = rect.expand(this.getStrokeWidth()); |
4550 rect = rect.expand(this.getStrokeWidth()); |
4556 return matrix ? matrix._transformBounds(rect) : rect; |
4551 return matrix ? matrix._transformBounds(rect) : rect; |
4557 } |
4552 } |
4558 }, |
4553 }, |
4559 new function() { |
4554 new function() { |
4560 |
|
4561 function getCornerCenter(that, point, expand) { |
4555 function getCornerCenter(that, point, expand) { |
4562 var radius = that._radius; |
4556 var radius = that._radius; |
4563 if (!radius.isZero()) { |
4557 if (!radius.isZero()) { |
4564 var halfSize = that._size.divide(2); |
4558 var halfSize = that._size.divide(2); |
4565 for (var i = 0; i < 4; i++) { |
4559 for (var i = 0; i < 4; i++) { |
4900 toDataURL: function() { |
4894 toDataURL: function() { |
4901 var src = this._image && this._image.src; |
4895 var src = this._image && this._image.src; |
4902 if (/^data:/.test(src)) |
4896 if (/^data:/.test(src)) |
4903 return src; |
4897 return src; |
4904 var canvas = this.getCanvas(); |
4898 var canvas = this.getCanvas(); |
4905 return canvas ? canvas.toDataURL() : null; |
4899 return canvas ? canvas.toDataURL.apply(canvas, arguments) : null; |
4906 }, |
4900 }, |
4907 |
4901 |
4908 drawImage: function(image ) { |
4902 drawImage: function(image ) { |
4909 var point = Point.read(arguments, 1); |
4903 var point = Point.read(arguments, 1); |
4910 this.getContext(true).drawImage(image, point.x, point.y); |
4904 this.getContext(true).drawImage(image, point.x, point.y); |
5131 initialize: function Segment(arg0, arg1, arg2, arg3, arg4, arg5) { |
5125 initialize: function Segment(arg0, arg1, arg2, arg3, arg4, arg5) { |
5132 var count = arguments.length, |
5126 var count = arguments.length, |
5133 point, handleIn, handleOut; |
5127 point, handleIn, handleOut; |
5134 if (count === 0) { |
5128 if (count === 0) { |
5135 } else if (count === 1) { |
5129 } else if (count === 1) { |
5136 if (arg0.point) { |
5130 if ('point' in arg0) { |
5137 point = arg0.point; |
5131 point = arg0.point; |
5138 handleIn = arg0.handleIn; |
5132 handleIn = arg0.handleIn; |
5139 handleOut = arg0.handleOut; |
5133 handleOut = arg0.handleOut; |
5140 } else { |
5134 } else { |
5141 point = arg0; |
5135 point = arg0; |
5155 new SegmentPoint(handleIn, this, '_handleIn'); |
5149 new SegmentPoint(handleIn, this, '_handleIn'); |
5156 new SegmentPoint(handleOut, this, '_handleOut'); |
5150 new SegmentPoint(handleOut, this, '_handleOut'); |
5157 }, |
5151 }, |
5158 |
5152 |
5159 _serialize: function(options) { |
5153 _serialize: function(options) { |
5160 return Base.serialize(this.isStraight() ? this._point |
5154 return Base.serialize(this.hasHandles() |
5161 : [this._point, this._handleIn, this._handleOut], |
5155 ? [this._point, this._handleIn, this._handleOut] |
5156 : this._point, |
|
5162 options, true); |
5157 options, true); |
5163 }, |
5158 }, |
5164 |
5159 |
5165 _changed: function(point) { |
5160 _changed: function(point) { |
5166 var path = this._path; |
5161 var path = this._path; |
5207 var point = Point.read(arguments); |
5202 var point = Point.read(arguments); |
5208 this._handleOut.set(point.x, point.y); |
5203 this._handleOut.set(point.x, point.y); |
5209 }, |
5204 }, |
5210 |
5205 |
5211 hasHandles: function() { |
5206 hasHandles: function() { |
5212 return !this.isStraight(); |
5207 return !this._handleIn.isZero() || !this._handleOut.isZero(); |
5213 }, |
5208 }, |
5214 |
5209 |
5215 isStraight: function() { |
5210 clearHandles: function() { |
5216 return this._handleIn.isZero() && this._handleOut.isZero(); |
5211 this._handleIn.set(0, 0); |
5217 }, |
5212 this._handleOut.set(0, 0); |
5218 |
5213 }, |
5219 isLinear: function() { |
|
5220 return Segment.isLinear(this, this.getNext()); |
|
5221 }, |
|
5222 |
|
5223 isCollinear: function(segment) { |
|
5224 return Segment.isCollinear(this, this.getNext(), |
|
5225 segment, segment.getNext()); |
|
5226 }, |
|
5227 |
|
5228 isColinear: '#isCollinear', |
|
5229 |
|
5230 isOrthogonal: function() { |
|
5231 return Segment.isOrthogonal(this.getPrevious(), this, this.getNext()); |
|
5232 }, |
|
5233 |
|
5234 isOrthogonalArc: function() { |
|
5235 return Segment.isOrthogonalArc(this, this.getNext()); |
|
5236 }, |
|
5237 |
|
5238 isArc: '#isOrthogonalArc', |
|
5239 |
5214 |
5240 _selectionState: 0, |
5215 _selectionState: 0, |
5241 |
5216 |
5242 isSelected: function(_point) { |
5217 isSelected: function(_point) { |
5243 var state = this._selectionState; |
5218 var state = this._selectionState; |
5307 var segments = this._path && this._path._segments; |
5282 var segments = this._path && this._path._segments; |
5308 return segments && (segments[this._index - 1] |
5283 return segments && (segments[this._index - 1] |
5309 || this._path._closed && segments[segments.length - 1]) || null; |
5284 || this._path._closed && segments[segments.length - 1]) || null; |
5310 }, |
5285 }, |
5311 |
5286 |
5287 isFirst: function() { |
|
5288 return this._index === 0; |
|
5289 }, |
|
5290 |
|
5291 isLast: function() { |
|
5292 var path = this._path; |
|
5293 return path && this._index === path._segments.length - 1 || false; |
|
5294 }, |
|
5295 |
|
5312 reverse: function() { |
5296 reverse: function() { |
5297 var handleIn = this._handleIn, |
|
5298 handleOut = this._handleOut, |
|
5299 inX = handleIn._x, |
|
5300 inY = handleIn._y; |
|
5301 handleIn.set(handleOut._x, handleOut._y); |
|
5302 handleOut.set(inX, inY); |
|
5303 }, |
|
5304 |
|
5305 reversed: function() { |
|
5313 return new Segment(this._point, this._handleOut, this._handleIn); |
5306 return new Segment(this._point, this._handleOut, this._handleIn); |
5314 }, |
5307 }, |
5315 |
5308 |
5316 remove: function() { |
5309 remove: function() { |
5317 return this._path ? !!this._path.removeSegment(this._index) : false; |
5310 return this._path ? !!this._path.removeSegment(this._index) : false; |
5388 coords[i++] = y; |
5381 coords[i++] = y; |
5389 } |
5382 } |
5390 } |
5383 } |
5391 } |
5384 } |
5392 return coords; |
5385 return coords; |
5393 }, |
|
5394 |
|
5395 statics: { |
|
5396 |
|
5397 isLinear: function(seg1, seg2) { |
|
5398 var l = seg2._point.subtract(seg1._point); |
|
5399 return l.isCollinear(seg1._handleOut) |
|
5400 && l.isCollinear(seg2._handleIn); |
|
5401 }, |
|
5402 |
|
5403 isCollinear: function(seg1, seg2, seg3, seg4) { |
|
5404 return seg1._handleOut.isZero() && seg2._handleIn.isZero() |
|
5405 && seg3._handleOut.isZero() && seg4._handleIn.isZero() |
|
5406 && seg2._point.subtract(seg1._point).isCollinear( |
|
5407 seg4._point.subtract(seg3._point)); |
|
5408 }, |
|
5409 |
|
5410 isOrthogonal: function(seg1, seg2, seg3) { |
|
5411 return seg1._handleOut.isZero() && seg2._handleIn.isZero() |
|
5412 && seg2._handleOut.isZero() && seg3._handleIn.isZero() |
|
5413 && seg2._point.subtract(seg1._point).isOrthogonal( |
|
5414 seg3._point.subtract(seg2._point)); |
|
5415 }, |
|
5416 |
|
5417 isOrthogonalArc: function(seg1, seg2) { |
|
5418 var handle1 = seg1._handleOut, |
|
5419 handle2 = seg2._handleIn, |
|
5420 kappa = 0.5522847498307936; |
|
5421 if (handle1.isOrthogonal(handle2)) { |
|
5422 var pt1 = seg1._point, |
|
5423 pt2 = seg2._point, |
|
5424 corner = new Line(pt1, handle1, true).intersect( |
|
5425 new Line(pt2, handle2, true), true); |
|
5426 return corner && Numerical.isZero(handle1.getLength() / |
|
5427 corner.subtract(pt1).getLength() - kappa) |
|
5428 && Numerical.isZero(handle2.getLength() / |
|
5429 corner.subtract(pt2).getLength() - kappa); |
|
5430 } |
|
5431 return false; |
|
5432 }, |
|
5433 } |
5386 } |
5434 }); |
5387 }); |
5435 |
5388 |
5436 var SegmentPoint = Point.extend({ |
5389 var SegmentPoint = Point.extend({ |
5437 initialize: function SegmentPoint(point, owner, key) { |
5390 initialize: function SegmentPoint(point, owner, key) { |
5506 |
5459 |
5507 var Curve = Base.extend({ |
5460 var Curve = Base.extend({ |
5508 _class: 'Curve', |
5461 _class: 'Curve', |
5509 |
5462 |
5510 initialize: function Curve(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) { |
5463 initialize: function Curve(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) { |
5511 var count = arguments.length; |
5464 var count = arguments.length, |
5465 seg1, seg2, |
|
5466 point1, point2, |
|
5467 handle1, handle2; |
|
5512 if (count === 3) { |
5468 if (count === 3) { |
5513 this._path = arg0; |
5469 this._path = arg0; |
5514 this._segment1 = arg1; |
5470 seg1 = arg1; |
5515 this._segment2 = arg2; |
5471 seg2 = arg2; |
5516 } else if (count === 0) { |
5472 } else if (count === 0) { |
5517 this._segment1 = new Segment(); |
5473 seg1 = new Segment(); |
5518 this._segment2 = new Segment(); |
5474 seg2 = new Segment(); |
5519 } else if (count === 1) { |
5475 } else if (count === 1) { |
5520 this._segment1 = new Segment(arg0.segment1); |
5476 if ('segment1' in arg0) { |
5521 this._segment2 = new Segment(arg0.segment2); |
5477 seg1 = new Segment(arg0.segment1); |
5478 seg2 = new Segment(arg0.segment2); |
|
5479 } else if ('point1' in arg0) { |
|
5480 point1 = arg0.point1; |
|
5481 handle1 = arg0.handle1; |
|
5482 handle2 = arg0.handle2; |
|
5483 point2 = arg0.point2; |
|
5484 } else if (Array.isArray(arg0)) { |
|
5485 point1 = [arg0[0], arg0[1]]; |
|
5486 point2 = [arg0[6], arg0[7]]; |
|
5487 handle1 = [arg0[2] - arg0[0], arg0[3] - arg0[1]]; |
|
5488 handle2 = [arg0[4] - arg0[6], arg0[5] - arg0[7]]; |
|
5489 } |
|
5522 } else if (count === 2) { |
5490 } else if (count === 2) { |
5523 this._segment1 = new Segment(arg0); |
5491 seg1 = new Segment(arg0); |
5524 this._segment2 = new Segment(arg1); |
5492 seg2 = new Segment(arg1); |
5525 } else { |
5493 } else if (count === 4) { |
5526 var point1, handle1, handle2, point2; |
5494 point1 = arg0; |
5527 if (count === 4) { |
5495 handle1 = arg1; |
5528 point1 = arg0; |
5496 handle2 = arg2; |
5529 handle1 = arg1; |
5497 point2 = arg3; |
5530 handle2 = arg2; |
5498 } else if (count === 8) { |
5531 point2 = arg3; |
5499 point1 = [arg0, arg1]; |
5532 } else if (count === 8) { |
5500 point2 = [arg6, arg7]; |
5533 point1 = [arg0, arg1]; |
5501 handle1 = [arg2 - arg0, arg3 - arg1]; |
5534 point2 = [arg6, arg7]; |
5502 handle2 = [arg4 - arg6, arg5 - arg7]; |
5535 handle1 = [arg2 - arg0, arg3 - arg1]; |
5503 } |
5536 handle2 = [arg4 - arg6, arg5 - arg7]; |
5504 this._segment1 = seg1 || new Segment(point1, null, handle1); |
5537 } |
5505 this._segment2 = seg2 || new Segment(point2, handle2, null); |
5538 this._segment1 = new Segment(point1, null, handle1); |
5506 }, |
5539 this._segment2 = new Segment(point2, handle2, null); |
5507 |
5540 } |
5508 _serialize: function(options) { |
5509 return Base.serialize(this.hasHandles() |
|
5510 ? [this.getPoint1(), this.getHandle1(), this.getHandle2(), |
|
5511 this.getPoint2()] |
|
5512 : [this.getPoint1(), this.getPoint2()], |
|
5513 options, true); |
|
5541 }, |
5514 }, |
5542 |
5515 |
5543 _changed: function() { |
5516 _changed: function() { |
5544 this._length = this._bounds = undefined; |
5517 this._length = this._bounds = undefined; |
5518 }, |
|
5519 |
|
5520 clone: function() { |
|
5521 return new Curve(this._segment1, this._segment2); |
|
5522 }, |
|
5523 |
|
5524 toString: function() { |
|
5525 var parts = [ 'point1: ' + this._segment1._point ]; |
|
5526 if (!this._segment1._handleOut.isZero()) |
|
5527 parts.push('handle1: ' + this._segment1._handleOut); |
|
5528 if (!this._segment2._handleIn.isZero()) |
|
5529 parts.push('handle2: ' + this._segment2._handleIn); |
|
5530 parts.push('point2: ' + this._segment2._point); |
|
5531 return '{ ' + parts.join(', ') + ' }'; |
|
5532 }, |
|
5533 |
|
5534 remove: function() { |
|
5535 var removed = false; |
|
5536 if (this._path) { |
|
5537 var segment2 = this._segment2, |
|
5538 handleOut = segment2._handleOut; |
|
5539 removed = segment2.remove(); |
|
5540 if (removed) |
|
5541 this._segment1._handleOut.set(handleOut.x, handleOut.y); |
|
5542 } |
|
5543 return removed; |
|
5545 }, |
5544 }, |
5546 |
5545 |
5547 getPoint1: function() { |
5546 getPoint1: function() { |
5548 return this._segment1._point; |
5547 return this._segment1._point; |
5549 }, |
5548 }, |
5604 |
5603 |
5605 getPrevious: function() { |
5604 getPrevious: function() { |
5606 var curves = this._path && this._path._curves; |
5605 var curves = this._path && this._path._curves; |
5607 return curves && (curves[this._segment1._index - 1] |
5606 return curves && (curves[this._segment1._index - 1] |
5608 || this._path._closed && curves[curves.length - 1]) || null; |
5607 || this._path._closed && curves[curves.length - 1]) || null; |
5608 }, |
|
5609 |
|
5610 isFirst: function() { |
|
5611 return this._segment1._index === 0; |
|
5612 }, |
|
5613 |
|
5614 isLast: function() { |
|
5615 var path = this._path; |
|
5616 return path && this._segment1._index === path._curves.length - 1 |
|
5617 || false; |
|
5609 }, |
5618 }, |
5610 |
5619 |
5611 isSelected: function() { |
5620 isSelected: function() { |
5612 return this.getPoint1().isSelected() |
5621 return this.getPoint1().isSelected() |
5613 && this.getHandle2().isSelected() |
5622 && this.getHandle2().isSelected() |
5633 points.push(new Point(coords[i], coords[i + 1])); |
5642 points.push(new Point(coords[i], coords[i + 1])); |
5634 return points; |
5643 return points; |
5635 }, |
5644 }, |
5636 |
5645 |
5637 getLength: function() { |
5646 getLength: function() { |
5638 if (this._length == null) { |
5647 if (this._length == null) |
5639 this._length = this.isLinear() |
5648 this._length = Curve.getLength(this.getValues(), 0, 1); |
5640 ? this._segment2._point.getDistance(this._segment1._point) |
|
5641 : Curve.getLength(this.getValues(), 0, 1); |
|
5642 } |
|
5643 return this._length; |
5649 return this._length; |
5644 }, |
5650 }, |
5645 |
5651 |
5646 getArea: function() { |
5652 getArea: function() { |
5647 return Curve.getArea(this.getValues()); |
5653 return Curve.getArea(this.getValues()); |
5648 }, |
5654 }, |
5649 |
5655 |
5656 getLine: function() { |
|
5657 return new Line(this._segment1._point, this._segment2._point); |
|
5658 }, |
|
5659 |
|
5650 getPart: function(from, to) { |
5660 getPart: function(from, to) { |
5651 return new Curve(Curve.getPart(this.getValues(), from, to)); |
5661 return new Curve(Curve.getPart(this.getValues(), from, to)); |
5652 }, |
5662 }, |
5653 |
5663 |
5654 getPartLength: function(from, to) { |
5664 getPartLength: function(from, to) { |
5655 return Curve.getLength(this.getValues(), from, to); |
5665 return Curve.getLength(this.getValues(), from, to); |
5656 }, |
5666 }, |
5657 |
5667 |
5658 hasHandles: function() { |
|
5659 return !this._segment1._handleOut.isZero() |
|
5660 || !this._segment2._handleIn.isZero(); |
|
5661 }, |
|
5662 |
|
5663 isLinear: function() { |
|
5664 return Segment.isLinear(this._segment1, this._segment2); |
|
5665 }, |
|
5666 |
|
5667 isCollinear: function(curve) { |
|
5668 return Ssegment.isCollinear(this._segment1, this._segment2, |
|
5669 curve._segment1, curve._segment2); |
|
5670 }, |
|
5671 |
|
5672 isOrthogonalArc: function() { |
|
5673 return Segment.isOrthogonalArc(this._segment1, this._segment2); |
|
5674 }, |
|
5675 |
|
5676 getIntersections: function(curve) { |
5668 getIntersections: function(curve) { |
5677 return Curve.filterIntersections(Curve.getIntersections( |
5669 return Curve._getIntersections(this.getValues(), |
5678 this.getValues(), curve.getValues(), this, curve, [])); |
5670 curve && curve !== this ? curve.getValues() : null, |
5671 this, curve, [], {}); |
|
5679 }, |
5672 }, |
5680 |
5673 |
5681 _getParameter: function(offset, isParameter) { |
5674 _getParameter: function(offset, isParameter) { |
5682 return isParameter |
5675 return isParameter |
5683 ? offset |
5676 ? offset |
5686 : offset === undefined && isParameter === undefined |
5679 : offset === undefined && isParameter === undefined |
5687 ? 0.5 |
5680 ? 0.5 |
5688 : this.getParameterAt(offset, 0); |
5681 : this.getParameterAt(offset, 0); |
5689 }, |
5682 }, |
5690 |
5683 |
5691 divide: function(offset, isParameter, ignoreLinear) { |
5684 divide: function(offset, isParameter, _setHandles) { |
5692 var parameter = this._getParameter(offset, isParameter), |
5685 var parameter = this._getParameter(offset, isParameter), |
5693 tolerance = 0.000001, |
5686 tMin = 4e-7, |
5687 tMax = 1 - tMin, |
|
5694 res = null; |
5688 res = null; |
5695 if (parameter > tolerance && parameter < 1 - tolerance) { |
5689 if (parameter >= tMin && parameter <= tMax) { |
5696 var parts = Curve.subdivide(this.getValues(), parameter), |
5690 var parts = Curve.subdivide(this.getValues(), parameter), |
5697 isLinear = ignoreLinear ? false : this.isLinear(), |
|
5698 left = parts[0], |
5691 left = parts[0], |
5699 right = parts[1]; |
5692 right = parts[1], |
5700 |
5693 setHandles = _setHandles || this.hasHandles(), |
5701 if (!isLinear) { |
5694 segment1 = this._segment1, |
5702 this._segment1._handleOut.set(left[2] - left[0], |
5695 segment2 = this._segment2, |
5696 path = this._path; |
|
5697 if (setHandles) { |
|
5698 segment1._handleOut.set(left[2] - left[0], |
|
5703 left[3] - left[1]); |
5699 left[3] - left[1]); |
5704 this._segment2._handleIn.set(right[4] - right[6], |
5700 segment2._handleIn.set(right[4] - right[6], |
5705 right[5] - right[7]); |
5701 right[5] - right[7]); |
5706 } |
5702 } |
5707 |
|
5708 var x = left[6], y = left[7], |
5703 var x = left[6], y = left[7], |
5709 segment = new Segment(new Point(x, y), |
5704 segment = new Segment(new Point(x, y), |
5710 !isLinear && new Point(left[4] - x, left[5] - y), |
5705 setHandles && new Point(left[4] - x, left[5] - y), |
5711 !isLinear && new Point(right[2] - x, right[3] - y)); |
5706 setHandles && new Point(right[2] - x, right[3] - y)); |
5712 |
5707 if (path) { |
5713 if (this._path) { |
5708 path.insert(segment1._index + 1, segment); |
5714 if (this._segment1._index > 0 && this._segment2._index === 0) { |
5709 res = this.getNext(); |
5715 this._path.add(segment); |
|
5716 } else { |
|
5717 this._path.insert(this._segment2._index, segment); |
|
5718 } |
|
5719 res = this; |
|
5720 } else { |
5710 } else { |
5721 var end = this._segment2; |
|
5722 this._segment2 = segment; |
5711 this._segment2 = segment; |
5723 res = new Curve(segment, end); |
5712 res = new Curve(segment, segment2); |
5724 } |
5713 } |
5725 } |
5714 } |
5726 return res; |
5715 return res; |
5727 }, |
5716 }, |
5728 |
5717 |
5731 ? this._path.split(this._segment1._index, |
5720 ? this._path.split(this._segment1._index, |
5732 this._getParameter(offset, isParameter)) |
5721 this._getParameter(offset, isParameter)) |
5733 : null; |
5722 : null; |
5734 }, |
5723 }, |
5735 |
5724 |
5736 reverse: function() { |
5725 reversed: function() { |
5737 return new Curve(this._segment2.reverse(), this._segment1.reverse()); |
5726 return new Curve(this._segment2.reversed(), this._segment1.reversed()); |
5738 }, |
5727 }, |
5739 |
5728 |
5740 remove: function() { |
5729 clearHandles: function() { |
5741 var removed = false; |
5730 this._segment1._handleOut.set(0, 0); |
5742 if (this._path) { |
5731 this._segment2._handleIn.set(0, 0); |
5743 var segment2 = this._segment2, |
|
5744 handleOut = segment2._handleOut; |
|
5745 removed = segment2.remove(); |
|
5746 if (removed) |
|
5747 this._segment1._handleOut.set(handleOut.x, handleOut.y); |
|
5748 } |
|
5749 return removed; |
|
5750 }, |
|
5751 |
|
5752 clone: function() { |
|
5753 return new Curve(this._segment1, this._segment2); |
|
5754 }, |
|
5755 |
|
5756 toString: function() { |
|
5757 var parts = [ 'point1: ' + this._segment1._point ]; |
|
5758 if (!this._segment1._handleOut.isZero()) |
|
5759 parts.push('handle1: ' + this._segment1._handleOut); |
|
5760 if (!this._segment2._handleIn.isZero()) |
|
5761 parts.push('handle2: ' + this._segment2._handleIn); |
|
5762 parts.push('point2: ' + this._segment2._point); |
|
5763 return '{ ' + parts.join(', ') + ' }'; |
|
5764 }, |
5732 }, |
5765 |
5733 |
5766 statics: { |
5734 statics: { |
5767 getValues: function(segment1, segment2, matrix) { |
5735 getValues: function(segment1, segment2, matrix) { |
5768 var p1 = segment1._point, |
5736 var p1 = segment1._point, |
5809 b = 3 * (c2 - c1) - c, |
5777 b = 3 * (c2 - c1) - c, |
5810 a = p2 - p1 - c - b; |
5778 a = p2 - p1 - c - b; |
5811 return Numerical.solveCubic(a, b, c, p1 - val, roots, min, max); |
5779 return Numerical.solveCubic(a, b, c, p1 - val, roots, min, max); |
5812 }, |
5780 }, |
5813 |
5781 |
5814 getParameterOf: function(v, x, y) { |
5782 getParameterOf: function(v, point) { |
5815 var tolerance = 0.000001; |
5783 var p1 = new Point(v[0], v[1]), |
5816 if (Math.abs(v[0] - x) < tolerance && Math.abs(v[1] - y) < tolerance) |
5784 p2 = new Point(v[6], v[7]), |
5817 return 0; |
5785 epsilon = 1e-12, |
5818 if (Math.abs(v[6] - x) < tolerance && Math.abs(v[7] - y) < tolerance) |
5786 t = point.isClose(p1, epsilon) ? 0 |
5819 return 1; |
5787 : point.isClose(p2, epsilon) ? 1 |
5820 var txs = [], |
5788 : null; |
5821 tys = [], |
5789 if (t !== null) |
5822 sx = Curve.solveCubic(v, 0, x, txs, 0, 1), |
5790 return t; |
5823 sy = Curve.solveCubic(v, 1, y, tys, 0, 1), |
5791 var coords = [point.x, point.y], |
5824 tx, ty; |
5792 roots = [], |
5825 for (var cx = 0; sx === -1 || cx < sx;) { |
5793 geomEpsilon = 2e-7; |
5826 if (sx === -1 || (tx = txs[cx++]) > 0 && tx < 1) { |
5794 for (var c = 0; c < 2; c++) { |
5827 for (var cy = 0; sy === -1 || cy < sy;) { |
5795 var count = Curve.solveCubic(v, c, coords[c], roots, 0, 1); |
5828 if (sy === -1 || (ty = tys[cy++]) > 0 && ty < 1) { |
5796 for (var i = 0; i < count; i++) { |
5829 if (sx === -1) { |
5797 t = roots[i]; |
5830 tx = ty; |
5798 if (point.isClose(Curve.getPoint(v, t), geomEpsilon)) |
5831 } else if (sy === -1) { |
5799 return t; |
5832 ty = tx; |
5800 } |
5833 } |
5801 } |
5834 if (Math.abs(tx - ty) < tolerance) |
5802 return point.isClose(p1, geomEpsilon) ? 0 |
5835 return (tx + ty) * 0.5; |
5803 : point.isClose(p2, geomEpsilon) ? 1 |
5836 } |
5804 : null; |
5805 }, |
|
5806 |
|
5807 getNearestParameter: function(v, point) { |
|
5808 if (Curve.isStraight(v)) { |
|
5809 var p1x = v[0], p1y = v[1], |
|
5810 p2x = v[6], p2y = v[7], |
|
5811 vx = p2x - p1x, vy = p2y - p1y, |
|
5812 det = vx * vx + vy * vy; |
|
5813 if (det === 0) |
|
5814 return 0; |
|
5815 var u = ((point.x - p1x) * vx + (point.y - p1y) * vy) / det; |
|
5816 return u < 1e-12 ? 0 |
|
5817 : u > 0.999999999999 ? 1 |
|
5818 : Curve.getParameterOf(v, |
|
5819 new Point(p1x + u * vx, p1y + u * vy)); |
|
5820 } |
|
5821 |
|
5822 var count = 100, |
|
5823 minDist = Infinity, |
|
5824 minT = 0; |
|
5825 |
|
5826 function refine(t) { |
|
5827 if (t >= 0 && t <= 1) { |
|
5828 var dist = point.getDistance(Curve.getPoint(v, t), true); |
|
5829 if (dist < minDist) { |
|
5830 minDist = dist; |
|
5831 minT = t; |
|
5832 return true; |
|
5837 } |
5833 } |
5838 if (sx === -1) |
5834 } |
5839 break; |
5835 } |
5840 } |
5836 |
5841 } |
5837 for (var i = 0; i <= count; i++) |
5842 return null; |
5838 refine(i / count); |
5839 |
|
5840 var step = 1 / (count * 2); |
|
5841 while (step > 4e-7) { |
|
5842 if (!refine(minT - step) && !refine(minT + step)) |
|
5843 step /= 2; |
|
5844 } |
|
5845 return minT; |
|
5843 }, |
5846 }, |
5844 |
5847 |
5845 getPart: function(v, from, to) { |
5848 getPart: function(v, from, to) { |
5849 var flip = from > to; |
|
5850 if (flip) { |
|
5851 var tmp = from; |
|
5852 from = to; |
|
5853 to = tmp; |
|
5854 } |
|
5846 if (from > 0) |
5855 if (from > 0) |
5847 v = Curve.subdivide(v, from)[1]; |
5856 v = Curve.subdivide(v, from)[1]; |
5848 if (to < 1) |
5857 if (to < 1) |
5849 v = Curve.subdivide(v, (to - from) / (1 - from))[0]; |
5858 v = Curve.subdivide(v, (to - from) / (1 - from))[0]; |
5850 return v; |
5859 return flip |
5860 ? [v[6], v[7], v[4], v[5], v[2], v[3], v[0], v[1]] |
|
5861 : v; |
|
5851 }, |
5862 }, |
5852 |
5863 |
5853 hasHandles: function(v) { |
5864 hasHandles: function(v) { |
5854 var isZero = Numerical.isZero; |
5865 var isZero = Numerical.isZero; |
5855 return !(isZero(v[0] - v[2]) && isZero(v[1] - v[3]) |
5866 return !(isZero(v[0] - v[2]) && isZero(v[1] - v[3]) |
5856 && isZero(v[4] - v[6]) && isZero(v[5] - v[7])); |
5867 && isZero(v[4] - v[6]) && isZero(v[5] - v[7])); |
5857 }, |
|
5858 |
|
5859 isLinear: function(v) { |
|
5860 var p1x = v[0], p1y = v[1], |
|
5861 p2x = v[6], p2y = v[7], |
|
5862 l = new Point(p2x - p1x, p2y - p1y); |
|
5863 return l.isCollinear(new Point(v[2] - p1x, v[3] - p1y)) |
|
5864 && l.isCollinear(new Point(v[4] - p2x, v[5] - p2y)); |
|
5865 }, |
5868 }, |
5866 |
5869 |
5867 isFlatEnough: function(v, tolerance) { |
5870 isFlatEnough: function(v, tolerance) { |
5868 var p1x = v[0], p1y = v[1], |
5871 var p1x = v[0], p1y = v[1], |
5869 c1x = v[2], c1y = v[3], |
5872 c1x = v[2], c1y = v[3], |
5877 < 10 * tolerance * tolerance; |
5880 < 10 * tolerance * tolerance; |
5878 }, |
5881 }, |
5879 |
5882 |
5880 getArea: function(v) { |
5883 getArea: function(v) { |
5881 var p1x = v[0], p1y = v[1], |
5884 var p1x = v[0], p1y = v[1], |
5882 c1x = v[2], c1y = v[3], |
5885 p2x = v[6], p2y = v[7], |
5883 c2x = v[4], c2y = v[5], |
5886 h1x = (v[2] + p1x) / 2, |
5884 p2x = v[6], p2y = v[7]; |
5887 h1y = (v[3] + p1y) / 2, |
5885 return ( 3.0 * c1y * p1x - 1.5 * c1y * c2x |
5888 h2x = (v[4] + v[6]) / 2, |
5886 - 1.5 * c1y * p2x - 3.0 * p1y * c1x |
5889 h2y = (v[5] + v[7]) / 2; |
5887 - 1.5 * p1y * c2x - 0.5 * p1y * p2x |
5890 return 6 * ((p1x - h1x) * (h1y + p1y) |
5888 + 1.5 * c2y * p1x + 1.5 * c2y * c1x |
5891 + (h1x - h2x) * (h2y + h1y) |
5889 - 3.0 * c2y * p2x + 0.5 * p2y * p1x |
5892 + (h2x - p2x) * (p2y + h2y)) / 10; |
5890 + 1.5 * p2y * c1x + 3.0 * p2y * c2x) / 10; |
|
5891 }, |
|
5892 |
|
5893 getEdgeSum: function(v) { |
|
5894 return (v[0] - v[2]) * (v[3] + v[1]) |
|
5895 + (v[2] - v[4]) * (v[5] + v[3]) |
|
5896 + (v[4] - v[6]) * (v[7] + v[5]); |
|
5897 }, |
5893 }, |
5898 |
5894 |
5899 getBounds: function(v) { |
5895 getBounds: function(v) { |
5900 var min = v.slice(0, 2), |
5896 var min = v.slice(0, 2), |
5901 max = min.slice(), |
5897 max = min.slice(), |
5917 } |
5913 } |
5918 var a = 3 * (v1 - v2) - v0 + v3, |
5914 var a = 3 * (v1 - v2) - v0 + v3, |
5919 b = 2 * (v0 + v2) - 4 * v1, |
5915 b = 2 * (v0 + v2) - 4 * v1, |
5920 c = v1 - v0, |
5916 c = v1 - v0, |
5921 count = Numerical.solveQuadratic(a, b, c, roots), |
5917 count = Numerical.solveQuadratic(a, b, c, roots), |
5922 tMin = 0.000001, |
5918 tMin = 4e-7, |
5923 tMax = 1 - tMin; |
5919 tMax = 1 - tMin; |
5924 add(v3, 0); |
5920 add(v3, 0); |
5925 for (var i = 0; i < count; i++) { |
5921 for (var i = 0; i < count; i++) { |
5926 var t = roots[i], |
5922 var t = roots[i], |
5927 u = 1 - t; |
5923 u = 1 - t; |
5939 this[name] = function() { |
5935 this[name] = function() { |
5940 if (!this._bounds) |
5936 if (!this._bounds) |
5941 this._bounds = {}; |
5937 this._bounds = {}; |
5942 var bounds = this._bounds[name]; |
5938 var bounds = this._bounds[name]; |
5943 if (!bounds) { |
5939 if (!bounds) { |
5944 bounds = this._bounds[name] = Path[name]([this._segment1, |
5940 var path = this._path; |
5945 this._segment2], false, this._path.getStyle()); |
5941 bounds = this._bounds[name] = Path[name]( |
5942 [this._segment1, this._segment2], false, |
|
5943 path && path.getStyle()); |
|
5946 } |
5944 } |
5947 return bounds.clone(); |
5945 return bounds.clone(); |
5948 }; |
5946 }; |
5949 }, |
5947 }, |
5950 { |
5948 { |
5951 |
5949 |
5950 }), Base.each({ |
|
5951 isStraight: function(l, h1, h2) { |
|
5952 if (h1.isZero() && h2.isZero()) { |
|
5953 return true; |
|
5954 } else if (l.isZero()) { |
|
5955 return false; |
|
5956 } else if (h1.isCollinear(l) && h2.isCollinear(l)) { |
|
5957 var div = l.dot(l), |
|
5958 p1 = l.dot(h1) / div, |
|
5959 p2 = l.dot(h2) / div; |
|
5960 return p1 >= 0 && p1 <= 1 && p2 <= 0 && p2 >= -1; |
|
5961 } |
|
5962 return false; |
|
5963 }, |
|
5964 |
|
5965 isLinear: function(l, h1, h2) { |
|
5966 var third = l.divide(3); |
|
5967 return h1.equals(third) && h2.negate().equals(third); |
|
5968 } |
|
5969 }, function(test, name) { |
|
5970 this[name] = function() { |
|
5971 var seg1 = this._segment1, |
|
5972 seg2 = this._segment2; |
|
5973 return test(seg2._point.subtract(seg1._point), |
|
5974 seg1._handleOut, seg2._handleIn); |
|
5975 }; |
|
5976 |
|
5977 this.statics[name] = function(v) { |
|
5978 var p1x = v[0], p1y = v[1], |
|
5979 p2x = v[6], p2y = v[7]; |
|
5980 return test(new Point(p2x - p1x, p2y - p1y), |
|
5981 new Point(v[2] - p1x, v[3] - p1y), |
|
5982 new Point(v[4] - p2x, v[5] - p2y)); |
|
5983 }; |
|
5984 }, { |
|
5985 statics: {}, |
|
5986 |
|
5987 hasHandles: function() { |
|
5988 return !this._segment1._handleOut.isZero() |
|
5989 || !this._segment2._handleIn.isZero(); |
|
5990 }, |
|
5991 |
|
5992 isCollinear: function(curve) { |
|
5993 return curve && this.isStraight() && curve.isStraight() |
|
5994 && this.getLine().isCollinear(curve.getLine()); |
|
5995 }, |
|
5996 |
|
5997 isHorizontal: function() { |
|
5998 return this.isStraight() && Math.abs(this.getTangentAt(0.5, true).y) |
|
5999 < 1e-7; |
|
6000 }, |
|
6001 |
|
6002 isVertical: function() { |
|
6003 return this.isStraight() && Math.abs(this.getTangentAt(0.5, true).x) |
|
6004 < 1e-7; |
|
6005 } |
|
5952 }), { |
6006 }), { |
5953 beans: false, |
6007 beans: false, |
5954 |
6008 |
5955 getParameterAt: function(offset, start) { |
6009 getParameterAt: function(offset, start) { |
5956 return Curve.getParameterAt(this.getValues(), offset, start); |
6010 return Curve.getParameterAt(this.getValues(), offset, start); |
5957 }, |
6011 }, |
5958 |
6012 |
5959 getParameterOf: function() { |
6013 getParameterOf: function() { |
5960 var point = Point.read(arguments); |
6014 return Curve.getParameterOf(this.getValues(), Point.read(arguments)); |
5961 return Curve.getParameterOf(this.getValues(), point.x, point.y); |
|
5962 }, |
6015 }, |
5963 |
6016 |
5964 getLocationAt: function(offset, isParameter) { |
6017 getLocationAt: function(offset, isParameter) { |
5965 var t = isParameter ? offset : this.getParameterAt(offset); |
6018 var t = isParameter ? offset : this.getParameterAt(offset); |
5966 return t != null && t >= 0 && t <= 1 |
6019 return t != null && t >= 0 && t <= 1 |
5979 }, |
6032 }, |
5980 |
6033 |
5981 getNearestLocation: function() { |
6034 getNearestLocation: function() { |
5982 var point = Point.read(arguments), |
6035 var point = Point.read(arguments), |
5983 values = this.getValues(), |
6036 values = this.getValues(), |
5984 count = 100, |
6037 t = Curve.getNearestParameter(values, point), |
5985 minDist = Infinity, |
6038 pt = Curve.getPoint(values, t); |
5986 minT = 0; |
6039 return new CurveLocation(this, t, pt, null, point.getDistance(pt)); |
5987 |
|
5988 function refine(t) { |
|
5989 if (t >= 0 && t <= 1) { |
|
5990 var dist = point.getDistance(Curve.getPoint(values, t), true); |
|
5991 if (dist < minDist) { |
|
5992 minDist = dist; |
|
5993 minT = t; |
|
5994 return true; |
|
5995 } |
|
5996 } |
|
5997 } |
|
5998 |
|
5999 for (var i = 0; i <= count; i++) |
|
6000 refine(i / count); |
|
6001 |
|
6002 var step = 1 / (count * 2); |
|
6003 while (step > 0.000001) { |
|
6004 if (!refine(minT - step) && !refine(minT + step)) |
|
6005 step /= 2; |
|
6006 } |
|
6007 var pt = Curve.getPoint(values, minT); |
|
6008 return new CurveLocation(this, minT, pt, null, null, null, |
|
6009 point.getDistance(pt)); |
|
6010 }, |
6040 }, |
6011 |
6041 |
6012 getNearestPoint: function() { |
6042 getNearestPoint: function() { |
6013 return this.getNearestLocation.apply(this, arguments).getPoint(); |
6043 return this.getNearestLocation.apply(this, arguments).getPoint(); |
6014 } |
6044 } |
6062 return null; |
6092 return null; |
6063 var p1x = v[0], p1y = v[1], |
6093 var p1x = v[0], p1y = v[1], |
6064 c1x = v[2], c1y = v[3], |
6094 c1x = v[2], c1y = v[3], |
6065 c2x = v[4], c2y = v[5], |
6095 c2x = v[4], c2y = v[5], |
6066 p2x = v[6], p2y = v[7], |
6096 p2x = v[6], p2y = v[7], |
6067 tolerance = 0.000001, |
6097 tMin = 4e-7, |
6098 tMax = 1 - tMin, |
|
6068 x, y; |
6099 x, y; |
6069 |
6100 |
6070 if (type === 0 && (t < tolerance || t > 1 - tolerance)) { |
6101 if (type === 0 && (t < tMin || t > tMax)) { |
6071 var isZero = t < tolerance; |
6102 var isZero = t < tMin; |
6072 x = isZero ? p1x : p2x; |
6103 x = isZero ? p1x : p2x; |
6073 y = isZero ? p1y : p2y; |
6104 y = isZero ? p1y : p2y; |
6074 } else { |
6105 } else { |
6075 var cx = 3 * (c1x - p1x), |
6106 var cx = 3 * (c1x - p1x), |
6076 bx = 3 * (c2x - c1x) - cx, |
6107 bx = 3 * (c2x - c1x) - cx, |
6081 ay = p2y - p1y - cy - by; |
6112 ay = p2y - p1y - cy - by; |
6082 if (type === 0) { |
6113 if (type === 0) { |
6083 x = ((ax * t + bx) * t + cx) * t + p1x; |
6114 x = ((ax * t + bx) * t + cx) * t + p1x; |
6084 y = ((ay * t + by) * t + cy) * t + p1y; |
6115 y = ((ay * t + by) * t + cy) * t + p1y; |
6085 } else { |
6116 } else { |
6086 if (t < tolerance) { |
6117 if (t < tMin) { |
6087 x = cx; |
6118 x = cx; |
6088 y = cy; |
6119 y = cy; |
6089 } else if (t > 1 - tolerance) { |
6120 } else if (t > tMax) { |
6090 x = 3 * (p2x - c2x); |
6121 x = 3 * (p2x - c2x); |
6091 y = 3 * (p2y - c2y); |
6122 y = 3 * (p2y - c2y); |
6092 } else { |
6123 } else { |
6093 x = (3 * ax * t + 2 * bx) * t + cx; |
6124 x = (3 * ax * t + 2 * bx) * t + cx; |
6094 y = (3 * ay * t + 2 * by) * t + cy; |
6125 y = (3 * ay * t + 2 * by) * t + cy; |
6095 } |
6126 } |
6096 if (normalized) { |
6127 if (normalized) { |
6097 if (x === 0 && y === 0 |
6128 if (x === 0 && y === 0 && (t < tMin || t > tMax)) { |
6098 && (t < tolerance || t > 1 - tolerance)) { |
|
6099 x = c2x - c1x; |
6129 x = c2x - c1x; |
6100 y = c2y - c1y; |
6130 y = c2y - c1y; |
6101 } |
6131 } |
6102 var len = Math.sqrt(x * x + y * y); |
6132 var len = Math.sqrt(x * x + y * y); |
6103 x /= len; |
6133 if (len) { |
6104 y /= len; |
6134 x /= len; |
6135 y /= len; |
|
6136 } |
|
6105 } |
6137 } |
6106 if (type === 3) { |
6138 if (type === 3) { |
6107 var x2 = 6 * ax * t + 2 * bx, |
6139 var x2 = 6 * ax * t + 2 * bx, |
6108 y2 = 6 * ay * t + 2 * by, |
6140 y2 = 6 * ay * t + 2 * by, |
6109 d = Math.pow(x * x + y * y, 3 / 2); |
6141 d = Math.pow(x * x + y * y, 3 / 2); |
6113 } |
6145 } |
6114 } |
6146 } |
6115 return type === 2 ? new Point(y, -x) : new Point(x, y); |
6147 return type === 2 ? new Point(y, -x) : new Point(x, y); |
6116 } |
6148 } |
6117 |
6149 |
6118 return { |
6150 return { statics: { |
6119 statics: true, |
|
6120 |
6151 |
6121 getLength: function(v, a, b) { |
6152 getLength: function(v, a, b) { |
6122 if (a === undefined) |
6153 if (a === undefined) |
6123 a = 0; |
6154 a = 0; |
6124 if (b === undefined) |
6155 if (b === undefined) |
6125 b = 1; |
6156 b = 1; |
6126 var isZero = Numerical.isZero; |
6157 if (a === 0 && b === 1 && Curve.isStraight(v)) { |
6127 if (a === 0 && b === 1 |
|
6128 && isZero(v[0] - v[2]) && isZero(v[1] - v[3]) |
|
6129 && isZero(v[6] - v[4]) && isZero(v[7] - v[5])) { |
|
6130 var dx = v[6] - v[0], |
6158 var dx = v[6] - v[0], |
6131 dy = v[7] - v[1]; |
6159 dy = v[7] - v[1]; |
6132 return Math.sqrt(dx * dx + dy * dy); |
6160 return Math.sqrt(dx * dx + dy * dy); |
6133 } |
6161 } |
6134 var ds = getLengthIntegrand(v); |
6162 var ds = getLengthIntegrand(v); |
6138 getParameterAt: function(v, offset, start) { |
6166 getParameterAt: function(v, offset, start) { |
6139 if (start === undefined) |
6167 if (start === undefined) |
6140 start = offset < 0 ? 1 : 0 |
6168 start = offset < 0 ? 1 : 0 |
6141 if (offset === 0) |
6169 if (offset === 0) |
6142 return start; |
6170 return start; |
6143 var tolerance = 0.000001, |
6171 var abs = Math.abs, |
6144 abs = Math.abs, |
|
6145 forward = offset > 0, |
6172 forward = offset > 0, |
6146 a = forward ? start : 0, |
6173 a = forward ? start : 0, |
6147 b = forward ? 1 : start, |
6174 b = forward ? 1 : start, |
6148 ds = getLengthIntegrand(v), |
6175 ds = getLengthIntegrand(v), |
6149 rangeLength = Numerical.integrate(ds, a, b, |
6176 rangeLength = Numerical.integrate(ds, a, b, |
6150 getIterations(a, b)); |
6177 getIterations(a, b)); |
6151 if (abs(offset - rangeLength) < tolerance) { |
6178 if (abs(offset - rangeLength) < 1e-12) { |
6152 return forward ? b : a; |
6179 return forward ? b : a; |
6153 } else if (abs(offset) > rangeLength) { |
6180 } else if (abs(offset) > rangeLength) { |
6154 return null; |
6181 return null; |
6155 } |
6182 } |
6156 var guess = offset / rangeLength, |
6183 var guess = offset / rangeLength, |
6159 length += Numerical.integrate(ds, start, t, |
6186 length += Numerical.integrate(ds, start, t, |
6160 getIterations(start, t)); |
6187 getIterations(start, t)); |
6161 start = t; |
6188 start = t; |
6162 return length - offset; |
6189 return length - offset; |
6163 } |
6190 } |
6164 return Numerical.findRoot(f, ds, start + guess, a, b, 16, |
6191 return Numerical.findRoot(f, ds, start + guess, a, b, 32, |
6165 tolerance); |
6192 1e-12); |
6166 }, |
6193 }, |
6167 |
6194 |
6168 getPoint: function(v, t) { |
6195 getPoint: function(v, t) { |
6169 return evaluate(v, t, 0, false); |
6196 return evaluate(v, t, 0, false); |
6170 }, |
6197 }, |
6186 }, |
6213 }, |
6187 |
6214 |
6188 getCurvature: function(v, t) { |
6215 getCurvature: function(v, t) { |
6189 return evaluate(v, t, 3, false).x; |
6216 return evaluate(v, t, 3, false).x; |
6190 } |
6217 } |
6191 }; |
6218 }}; |
6192 }, new function() { |
6219 }, |
6193 function addLocation(locations, include, curve1, t1, point1, curve2, t2, |
6220 new function() { |
6194 point2) { |
6221 |
6195 var loc = new CurveLocation(curve1, t1, point1, curve2, t2, point2); |
6222 function addLocation(locations, param, v1, c1, t1, p1, v2, c2, t2, p2, |
6196 if (!include || include(loc)) |
6223 overlap) { |
6197 locations.push(loc); |
6224 var startConnected = param.startConnected, |
6198 } |
6225 endConnected = param.endConnected, |
6199 |
6226 tMin = 4e-7, |
6200 function addCurveIntersections(v1, v2, curve1, curve2, locations, include, |
6227 tMax = 1 - tMin; |
6228 if (t1 == null) |
|
6229 t1 = Curve.getParameterOf(v1, p1); |
|
6230 if (t1 !== null && t1 >= (startConnected ? tMin : 0) && |
|
6231 t1 <= (endConnected ? tMax : 1)) { |
|
6232 if (t2 == null) |
|
6233 t2 = Curve.getParameterOf(v2, p2); |
|
6234 if (t2 !== null && t2 >= (endConnected ? tMin : 0) && |
|
6235 t2 <= (startConnected ? tMax : 1)) { |
|
6236 var renormalize = param.renormalize; |
|
6237 if (renormalize) { |
|
6238 var res = renormalize(t1, t2); |
|
6239 t1 = res[0]; |
|
6240 t2 = res[1]; |
|
6241 } |
|
6242 var loc1 = new CurveLocation(c1, t1, |
|
6243 p1 || Curve.getPoint(v1, t1), overlap), |
|
6244 loc2 = new CurveLocation(c2, t2, |
|
6245 p2 || Curve.getPoint(v2, t2), overlap), |
|
6246 flip = loc1.getPath() === loc2.getPath() |
|
6247 && loc1.getIndex() > loc2.getIndex(), |
|
6248 loc = flip ? loc2 : loc1, |
|
6249 include = param.include; |
|
6250 loc1._intersection = loc2; |
|
6251 loc2._intersection = loc1; |
|
6252 if (!include || include(loc)) { |
|
6253 CurveLocation.insert(locations, loc, true); |
|
6254 } |
|
6255 } |
|
6256 } |
|
6257 } |
|
6258 |
|
6259 function addCurveIntersections(v1, v2, c1, c2, locations, param, |
|
6201 tMin, tMax, uMin, uMax, oldTDiff, reverse, recursion) { |
6260 tMin, tMax, uMin, uMax, oldTDiff, reverse, recursion) { |
6202 if (recursion > 32) |
6261 if (++recursion >= 24) |
6203 return; |
6262 return; |
6204 var q0x = v2[0], q0y = v2[1], q3x = v2[6], q3y = v2[7], |
6263 var q0x = v2[0], q0y = v2[1], q3x = v2[6], q3y = v2[7], |
6205 tolerance = 0.000001, |
|
6206 getSignedDistance = Line.getSignedDistance, |
6264 getSignedDistance = Line.getSignedDistance, |
6207 d1 = getSignedDistance(q0x, q0y, q3x, q3y, v2[2], v2[3]) || 0, |
6265 d1 = getSignedDistance(q0x, q0y, q3x, q3y, v2[2], v2[3]), |
6208 d2 = getSignedDistance(q0x, q0y, q3x, q3y, v2[4], v2[5]) || 0, |
6266 d2 = getSignedDistance(q0x, q0y, q3x, q3y, v2[4], v2[5]), |
6209 factor = d1 * d2 > 0 ? 3 / 4 : 4 / 9, |
6267 factor = d1 * d2 > 0 ? 3 / 4 : 4 / 9, |
6210 dMin = factor * Math.min(0, d1, d2), |
6268 dMin = factor * Math.min(0, d1, d2), |
6211 dMax = factor * Math.max(0, d1, d2), |
6269 dMax = factor * Math.max(0, d1, d2), |
6212 dp0 = getSignedDistance(q0x, q0y, q3x, q3y, v1[0], v1[1]), |
6270 dp0 = getSignedDistance(q0x, q0y, q3x, q3y, v1[0], v1[1]), |
6213 dp1 = getSignedDistance(q0x, q0y, q3x, q3y, v1[2], v1[3]), |
6271 dp1 = getSignedDistance(q0x, q0y, q3x, q3y, v1[2], v1[3]), |
6214 dp2 = getSignedDistance(q0x, q0y, q3x, q3y, v1[4], v1[5]), |
6272 dp2 = getSignedDistance(q0x, q0y, q3x, q3y, v1[4], v1[5]), |
6215 dp3 = getSignedDistance(q0x, q0y, q3x, q3y, v1[6], v1[7]), |
6273 dp3 = getSignedDistance(q0x, q0y, q3x, q3y, v1[6], v1[7]), |
6216 tMinNew, tMaxNew, tDiff; |
6274 hull = getConvexHull(dp0, dp1, dp2, dp3), |
6217 if (q0x === q3x && uMax - uMin < tolerance && recursion > 3) { |
6275 top = hull[0], |
6218 tMaxNew = tMinNew = (tMax + tMin) / 2; |
6276 bottom = hull[1], |
6219 tDiff = 0; |
6277 tMinClip, |
6220 } else { |
6278 tMaxClip; |
6221 var hull = getConvexHull(dp0, dp1, dp2, dp3), |
6279 if ((tMinClip = clipConvexHull(top, bottom, dMin, dMax)) == null || |
6222 top = hull[0], |
6280 (tMaxClip = clipConvexHull(top.reverse(), bottom.reverse(), |
6223 bottom = hull[1], |
6281 dMin, dMax)) == null) |
6224 tMinClip, tMaxClip; |
6282 return; |
6225 tMinClip = clipConvexHull(top, bottom, dMin, dMax); |
6283 v1 = Curve.getPart(v1, tMinClip, tMaxClip); |
6226 top.reverse(); |
6284 var tDiff = tMaxClip - tMinClip, |
6227 bottom.reverse(); |
6285 tMinNew = tMin + (tMax - tMin) * tMinClip, |
6228 tMaxClip = clipConvexHull(top, bottom, dMin, dMax); |
6286 tMaxNew = tMin + (tMax - tMin) * tMaxClip; |
6229 if (tMinClip == null || tMaxClip == null) |
|
6230 return; |
|
6231 v1 = Curve.getPart(v1, tMinClip, tMaxClip); |
|
6232 tDiff = tMaxClip - tMinClip; |
|
6233 tMinNew = tMax * tMinClip + tMin * (1 - tMinClip); |
|
6234 tMaxNew = tMax * tMaxClip + tMin * (1 - tMaxClip); |
|
6235 } |
|
6236 if (oldTDiff > 0.5 && tDiff > 0.5) { |
6287 if (oldTDiff > 0.5 && tDiff > 0.5) { |
6237 if (tMaxNew - tMinNew > uMax - uMin) { |
6288 if (tMaxNew - tMinNew > uMax - uMin) { |
6238 var parts = Curve.subdivide(v1, 0.5), |
6289 var parts = Curve.subdivide(v1, 0.5), |
6239 t = tMinNew + (tMaxNew - tMinNew) / 2; |
6290 t = tMinNew + (tMaxNew - tMinNew) / 2; |
6240 addCurveIntersections( |
6291 addCurveIntersections( |
6241 v2, parts[0], curve2, curve1, locations, include, |
6292 v2, parts[0], c2, c1, locations, param, |
6242 uMin, uMax, tMinNew, t, tDiff, !reverse, ++recursion); |
6293 uMin, uMax, tMinNew, t, tDiff, !reverse, recursion); |
6243 addCurveIntersections( |
6294 addCurveIntersections( |
6244 v2, parts[1], curve2, curve1, locations, include, |
6295 v2, parts[1], c2, c1, locations, param, |
6245 uMin, uMax, t, tMaxNew, tDiff, !reverse, recursion); |
6296 uMin, uMax, t, tMaxNew, tDiff, !reverse, recursion); |
6246 } else { |
6297 } else { |
6247 var parts = Curve.subdivide(v2, 0.5), |
6298 var parts = Curve.subdivide(v2, 0.5), |
6248 t = uMin + (uMax - uMin) / 2; |
6299 t = uMin + (uMax - uMin) / 2; |
6249 addCurveIntersections( |
6300 addCurveIntersections( |
6250 parts[0], v1, curve2, curve1, locations, include, |
6301 parts[0], v1, c2, c1, locations, param, |
6251 uMin, t, tMinNew, tMaxNew, tDiff, !reverse, ++recursion); |
6302 uMin, t, tMinNew, tMaxNew, tDiff, !reverse, recursion); |
6252 addCurveIntersections( |
6303 addCurveIntersections( |
6253 parts[1], v1, curve2, curve1, locations, include, |
6304 parts[1], v1, c2, c1, locations, param, |
6254 t, uMax, tMinNew, tMaxNew, tDiff, !reverse, recursion); |
6305 t, uMax, tMinNew, tMaxNew, tDiff, !reverse, recursion); |
6255 } |
6306 } |
6256 } else if (Math.max(uMax - uMin, tMaxNew - tMinNew) < tolerance) { |
6307 } else if (Math.max(uMax - uMin, tMaxNew - tMinNew) |
6308 < 1e-7) { |
|
6257 var t1 = tMinNew + (tMaxNew - tMinNew) / 2, |
6309 var t1 = tMinNew + (tMaxNew - tMinNew) / 2, |
6258 t2 = uMin + (uMax - uMin) / 2; |
6310 t2 = uMin + (uMax - uMin) / 2; |
6259 if (reverse) { |
6311 v1 = c1.getValues(); |
6260 addLocation(locations, include, |
6312 v2 = c2.getValues(); |
6261 curve2, t2, Curve.getPoint(v2, t2), |
6313 addLocation(locations, param, |
6262 curve1, t1, Curve.getPoint(v1, t1)); |
6314 reverse ? v2 : v1, reverse ? c2 : c1, reverse ? t2 : t1, null, |
6263 } else { |
6315 reverse ? v1 : v2, reverse ? c1 : c2, reverse ? t1 : t2, null); |
6264 addLocation(locations, include, |
6316 } else if (tDiff > 1e-12) { |
6265 curve1, t1, Curve.getPoint(v1, t1), |
6317 addCurveIntersections(v2, v1, c2, c1, locations, param, |
6266 curve2, t2, Curve.getPoint(v2, t2)); |
6318 uMin, uMax, tMinNew, tMaxNew, tDiff, !reverse, recursion); |
6267 } |
|
6268 } else if (tDiff > 0) { |
|
6269 addCurveIntersections(v2, v1, curve2, curve1, locations, include, |
|
6270 uMin, uMax, tMinNew, tMaxNew, tDiff, !reverse, ++recursion); |
|
6271 } |
6319 } |
6272 } |
6320 } |
6273 |
6321 |
6274 function getConvexHull(dq0, dq1, dq2, dq3) { |
6322 function getConvexHull(dq0, dq1, dq2, dq3) { |
6275 var p0 = [ 0, dq0 ], |
6323 var p0 = [ 0, dq0 ], |
6276 p1 = [ 1 / 3, dq1 ], |
6324 p1 = [ 1 / 3, dq1 ], |
6277 p2 = [ 2 / 3, dq2 ], |
6325 p2 = [ 2 / 3, dq2 ], |
6278 p3 = [ 1, dq3 ], |
6326 p3 = [ 1, dq3 ], |
6279 getSignedDistance = Line.getSignedDistance, |
6327 dist1 = dq1 - (2 * dq0 + dq3) / 3, |
6280 dist1 = getSignedDistance(0, dq0, 1, dq3, 1 / 3, dq1), |
6328 dist2 = dq2 - (dq0 + 2 * dq3) / 3, |
6281 dist2 = getSignedDistance(0, dq0, 1, dq3, 2 / 3, dq2), |
|
6282 flip = false, |
|
6283 hull; |
6329 hull; |
6284 if (dist1 * dist2 < 0) { |
6330 if (dist1 * dist2 < 0) { |
6285 hull = [[p0, p1, p3], [p0, p2, p3]]; |
6331 hull = [[p0, p1, p3], [p0, p2, p3]]; |
6286 flip = dist1 < 0; |
|
6287 } else { |
6332 } else { |
6288 var pmax, cross = 0, |
6333 var distRatio = dist1 / dist2; |
6289 distZero = dist1 === 0 || dist2 === 0; |
6334 hull = [ |
6290 if (Math.abs(dist1) > Math.abs(dist2)) { |
6335 distRatio >= 2 ? [p0, p1, p3] |
6291 pmax = p1; |
6336 : distRatio <= .5 ? [p0, p2, p3] |
6292 cross = (dq3 - dq2 - (dq3 - dq0) / 3) |
6337 : [p0, p1, p2, p3], |
6293 * (2 * (dq3 - dq2) - dq3 + dq1) / 3; |
6338 [p0, p3] |
6294 } else { |
6339 ]; |
6295 pmax = p2; |
6340 } |
6296 cross = (dq1 - dq0 + (dq0 - dq3) / 3) |
6341 return (dist1 || dist2) < 0 ? hull.reverse() : hull; |
6297 * (-2 * (dq0 - dq1) + dq0 - dq2) / 3; |
|
6298 } |
|
6299 hull = cross < 0 || distZero |
|
6300 ? [[p0, pmax, p3], [p0, p3]] |
|
6301 : [[p0, p1, p2, p3], [p0, p3]]; |
|
6302 flip = dist1 ? dist1 < 0 : dist2 < 0; |
|
6303 } |
|
6304 return flip ? hull.reverse() : hull; |
|
6305 } |
6342 } |
6306 |
6343 |
6307 function clipConvexHull(hullTop, hullBottom, dMin, dMax) { |
6344 function clipConvexHull(hullTop, hullBottom, dMin, dMax) { |
6308 if (hullTop[0][1] < dMin) { |
6345 if (hullTop[0][1] < dMin) { |
6309 return clipConvexHullPart(hullTop, true, dMin); |
6346 return clipConvexHullPart(hullTop, true, dMin); |
6318 var px = part[0][0], |
6355 var px = part[0][0], |
6319 py = part[0][1]; |
6356 py = part[0][1]; |
6320 for (var i = 1, l = part.length; i < l; i++) { |
6357 for (var i = 1, l = part.length; i < l; i++) { |
6321 var qx = part[i][0], |
6358 var qx = part[i][0], |
6322 qy = part[i][1]; |
6359 qy = part[i][1]; |
6323 if (top ? qy >= threshold : qy <= threshold) |
6360 if (top ? qy >= threshold : qy <= threshold) { |
6324 return px + (threshold - py) * (qx - px) / (qy - py); |
6361 return qy === threshold ? qx |
6362 : px + (threshold - py) * (qx - px) / (qy - py); |
|
6363 } |
|
6325 px = qx; |
6364 px = qx; |
6326 py = qy; |
6365 py = qy; |
6327 } |
6366 } |
6328 return null; |
6367 return null; |
6329 } |
6368 } |
6330 |
6369 |
6331 function addCurveLineIntersections(v1, v2, curve1, curve2, locations, |
6370 function addCurveLineIntersections(v1, v2, c1, c2, locations, param) { |
6332 include) { |
6371 var flip = Curve.isStraight(v1), |
6333 var flip = Curve.isLinear(v1), |
|
6334 vc = flip ? v2 : v1, |
6372 vc = flip ? v2 : v1, |
6335 vl = flip ? v1 : v2, |
6373 vl = flip ? v1 : v2, |
6336 lx1 = vl[0], ly1 = vl[1], |
6374 lx1 = vl[0], ly1 = vl[1], |
6337 lx2 = vl[6], ly2 = vl[7], |
6375 lx2 = vl[6], ly2 = vl[7], |
6338 ldx = lx2 - lx1, |
6376 ldx = lx2 - lx1, |
6339 ldy = ly2 - ly1, |
6377 ldy = ly2 - ly1, |
6340 angle = Math.atan2(-ldy, ldx), |
6378 angle = Math.atan2(-ldy, ldx), |
6341 sin = Math.sin(angle), |
6379 sin = Math.sin(angle), |
6342 cos = Math.cos(angle), |
6380 cos = Math.cos(angle), |
6343 rlx2 = ldx * cos - ldy * sin, |
|
6344 rvl = [0, 0, 0, 0, rlx2, 0, rlx2, 0], |
|
6345 rvc = []; |
6381 rvc = []; |
6346 for(var i = 0; i < 8; i += 2) { |
6382 for(var i = 0; i < 8; i += 2) { |
6347 var x = vc[i] - lx1, |
6383 var x = vc[i] - lx1, |
6348 y = vc[i + 1] - ly1; |
6384 y = vc[i + 1] - ly1; |
6349 rvc.push( |
6385 rvc.push( |
6350 x * cos - y * sin, |
6386 x * cos - y * sin, |
6351 y * cos + x * sin); |
6387 x * sin + y * cos); |
6352 } |
6388 } |
6353 var roots = [], |
6389 var roots = [], |
6354 count = Curve.solveCubic(rvc, 1, 0, roots, 0, 1); |
6390 count = Curve.solveCubic(rvc, 1, 0, roots, 0, 1); |
6355 for (var i = 0; i < count; i++) { |
6391 for (var i = 0; i < count; i++) { |
6356 var tc = roots[i], |
6392 var tc = roots[i], |
6357 x = Curve.getPoint(rvc, tc).x; |
6393 pc = Curve.getPoint(vc, tc), |
6358 if (x >= 0 && x <= rlx2) { |
6394 tl = Curve.getParameterOf(vl, pc); |
6359 var tl = Curve.getParameterOf(rvl, x, 0), |
6395 if (tl !== null) { |
6396 var pl = Curve.getPoint(vl, tl), |
|
6360 t1 = flip ? tl : tc, |
6397 t1 = flip ? tl : tc, |
6361 t2 = flip ? tc : tl; |
6398 t2 = flip ? tc : tl; |
6362 addLocation(locations, include, |
6399 if (!param.endConnected || t2 > Numerical.CURVETIME_EPSILON) { |
6363 curve1, t1, Curve.getPoint(v1, t1), |
6400 addLocation(locations, param, |
6364 curve2, t2, Curve.getPoint(v2, t2)); |
6401 v1, c1, t1, flip ? pl : pc, |
6365 } |
6402 v2, c2, t2, flip ? pc : pl); |
6366 } |
6403 } |
6367 } |
6404 } |
6368 |
6405 } |
6369 function addLineIntersection(v1, v2, curve1, curve2, locations, include) { |
6406 } |
6370 var point = Line.intersect( |
6407 |
6408 function addLineIntersection(v1, v2, c1, c2, locations, param) { |
|
6409 var pt = Line.intersect( |
|
6371 v1[0], v1[1], v1[6], v1[7], |
6410 v1[0], v1[1], v1[6], v1[7], |
6372 v2[0], v2[1], v2[6], v2[7]); |
6411 v2[0], v2[1], v2[6], v2[7]); |
6373 if (point) { |
6412 if (pt) { |
6374 var x = point.x, |
6413 addLocation(locations, param, v1, c1, null, pt, v2, c2, null, pt); |
6375 y = point.y; |
|
6376 addLocation(locations, include, |
|
6377 curve1, Curve.getParameterOf(v1, x, y), point, |
|
6378 curve2, Curve.getParameterOf(v2, x, y), point); |
|
6379 } |
6414 } |
6380 } |
6415 } |
6381 |
6416 |
6382 return { statics: { |
6417 return { statics: { |
6383 getIntersections: function(v1, v2, c1, c2, locations, include) { |
6418 _getIntersections: function(v1, v2, c1, c2, locations, param) { |
6384 var linear1 = Curve.isLinear(v1), |
6419 if (!v2) { |
6385 linear2 = Curve.isLinear(v2), |
6420 return Curve._getSelfIntersection(v1, c1, locations, param); |
6386 c1p1 = c1.getPoint1(), |
6421 } |
6387 c1p2 = c1.getPoint2(), |
6422 var c1p1x = v1[0], c1p1y = v1[1], |
6388 c2p1 = c2.getPoint1(), |
6423 c1p2x = v1[6], c1p2y = v1[7], |
6389 c2p2 = c2.getPoint2(), |
6424 c2p1x = v2[0], c2p1y = v2[1], |
6390 tolerance = 0.000001; |
6425 c2p2x = v2[6], c2p2y = v2[7], |
6391 if (c1p1.isClose(c2p1, tolerance)) |
6426 c1s1x = (3 * v1[2] + c1p1x) / 4, |
6392 addLocation(locations, include, c1, 0, c1p1, c2, 0, c1p1); |
6427 c1s1y = (3 * v1[3] + c1p1y) / 4, |
6393 if (c1p1.isClose(c2p2, tolerance)) |
6428 c1s2x = (3 * v1[4] + c1p2x) / 4, |
6394 addLocation(locations, include, c1, 0, c1p1, c2, 1, c1p1); |
6429 c1s2y = (3 * v1[5] + c1p2y) / 4, |
6395 (linear1 && linear2 |
6430 c2s1x = (3 * v2[2] + c2p1x) / 4, |
6431 c2s1y = (3 * v2[3] + c2p1y) / 4, |
|
6432 c2s2x = (3 * v2[4] + c2p2x) / 4, |
|
6433 c2s2y = (3 * v2[5] + c2p2y) / 4, |
|
6434 min = Math.min, |
|
6435 max = Math.max; |
|
6436 if (!( max(c1p1x, c1s1x, c1s2x, c1p2x) >= |
|
6437 min(c2p1x, c2s1x, c2s2x, c2p2x) && |
|
6438 min(c1p1x, c1s1x, c1s2x, c1p2x) <= |
|
6439 max(c2p1x, c2s1x, c2s2x, c2p2x) && |
|
6440 max(c1p1y, c1s1y, c1s2y, c1p2y) >= |
|
6441 min(c2p1y, c2s1y, c2s2y, c2p2y) && |
|
6442 min(c1p1y, c1s1y, c1s2y, c1p2y) <= |
|
6443 max(c2p1y, c2s1y, c2s2y, c2p2y))) |
|
6444 return locations; |
|
6445 if (!param.startConnected && !param.endConnected) { |
|
6446 var overlaps = Curve.getOverlaps(v1, v2); |
|
6447 if (overlaps) { |
|
6448 for (var i = 0; i < 2; i++) { |
|
6449 var overlap = overlaps[i]; |
|
6450 addLocation(locations, param, |
|
6451 v1, c1, overlap[0], null, |
|
6452 v2, c2, overlap[1], null, true); |
|
6453 } |
|
6454 return locations; |
|
6455 } |
|
6456 } |
|
6457 |
|
6458 var straight1 = Curve.isStraight(v1), |
|
6459 straight2 = Curve.isStraight(v2), |
|
6460 straight = straight1 && straight2, |
|
6461 epsilon = 1e-12, |
|
6462 before = locations.length; |
|
6463 (straight |
|
6396 ? addLineIntersection |
6464 ? addLineIntersection |
6397 : linear1 || linear2 |
6465 : straight1 || straight2 |
6398 ? addCurveLineIntersections |
6466 ? addCurveLineIntersections |
6399 : addCurveIntersections)( |
6467 : addCurveIntersections)( |
6400 v1, v2, c1, c2, locations, include, |
6468 v1, v2, c1, c2, locations, param, |
6401 0, 1, 0, 1, 0, false, 0); |
6469 0, 1, 0, 1, 0, false, 0); |
6402 if (c1p2.isClose(c2p1, tolerance)) |
6470 if (straight && locations.length > before) |
6403 addLocation(locations, include, c1, 1, c1p2, c2, 0, c1p2); |
6471 return locations; |
6404 if (c1p2.isClose(c2p2, tolerance)) |
6472 var c1p1 = new Point(c1p1x, c1p1y), |
6405 addLocation(locations, include, c1, 1, c1p2, c2, 1, c1p2); |
6473 c1p2 = new Point(c1p2x, c1p2y), |
6474 c2p1 = new Point(c2p1x, c2p1y), |
|
6475 c2p2 = new Point(c2p2x, c2p2y); |
|
6476 if (c1p1.isClose(c2p1, epsilon)) |
|
6477 addLocation(locations, param, v1, c1, 0, c1p1, v2, c2, 0, c2p1); |
|
6478 if (!param.startConnected && c1p1.isClose(c2p2, epsilon)) |
|
6479 addLocation(locations, param, v1, c1, 0, c1p1, v2, c2, 1, c2p2); |
|
6480 if (!param.endConnected && c1p2.isClose(c2p1, epsilon)) |
|
6481 addLocation(locations, param, v1, c1, 1, c1p2, v2, c2, 0, c2p1); |
|
6482 if (c1p2.isClose(c2p2, epsilon)) |
|
6483 addLocation(locations, param, v1, c1, 1, c1p2, v2, c2, 1, c2p2); |
|
6406 return locations; |
6484 return locations; |
6407 }, |
6485 }, |
6408 |
6486 |
6409 filterIntersections: function(locations, _expand) { |
6487 _getSelfIntersection: function(v1, c1, locations, param) { |
6410 var last = locations.length - 1, |
6488 var p1x = v1[0], p1y = v1[1], |
6411 tMax = 1 - 0.000001; |
6489 h1x = v1[2], h1y = v1[3], |
6412 for (var i = last; i >= 0; i--) { |
6490 h2x = v1[4], h2y = v1[5], |
6413 var loc = locations[i], |
6491 p2x = v1[6], p2y = v1[7]; |
6414 next = loc._curve.getNext(), |
6492 var line = new Line(p1x, p1y, p2x, p2y, false), |
6415 next2 = loc._curve2.getNext(); |
6493 side1 = line.getSide(new Point(h1x, h1y), true), |
6416 if (next && loc._parameter >= tMax) { |
6494 side2 = line.getSide(new Point(h2x, h2y), true); |
6417 loc._parameter = 0; |
6495 if (side1 === side2) { |
6418 loc._curve = next; |
6496 var edgeSum = (p1x - h2x) * (h1y - p2y) |
6497 + (h1x - p2x) * (h2y - p1y); |
|
6498 if (edgeSum * side1 > 0) |
|
6499 return locations; |
|
6500 } |
|
6501 var ax = p2x - 3 * h2x + 3 * h1x - p1x, |
|
6502 bx = h2x - 2 * h1x + p1x, |
|
6503 cx = h1x - p1x, |
|
6504 ay = p2y - 3 * h2y + 3 * h1y - p1y, |
|
6505 by = h2y - 2 * h1y + p1y, |
|
6506 cy = h1y - p1y, |
|
6507 ac = ay * cx - ax * cy, |
|
6508 ab = ay * bx - ax * by, |
|
6509 bc = by * cx - bx * cy; |
|
6510 if (ac * ac - 4 * ab * bc < 0) { |
|
6511 var roots = [], |
|
6512 tSplit, |
|
6513 count = Numerical.solveCubic( |
|
6514 ax * ax + ay * ay, |
|
6515 3 * (ax * bx + ay * by), |
|
6516 2 * (bx * bx + by * by) + ax * cx + ay * cy, |
|
6517 bx * cx + by * cy, |
|
6518 roots, 0, 1); |
|
6519 if (count > 0) { |
|
6520 for (var i = 0, maxCurvature = 0; i < count; i++) { |
|
6521 var curvature = Math.abs( |
|
6522 c1.getCurvatureAt(roots[i], true)); |
|
6523 if (curvature > maxCurvature) { |
|
6524 maxCurvature = curvature; |
|
6525 tSplit = roots[i]; |
|
6526 } |
|
6527 } |
|
6528 var parts = Curve.subdivide(v1, tSplit); |
|
6529 param.endConnected = true; |
|
6530 param.renormalize = function(t1, t2) { |
|
6531 return [t1 * tSplit, t2 * (1 - tSplit) + tSplit]; |
|
6532 }; |
|
6533 Curve._getIntersections(parts[0], parts[1], c1, c1, |
|
6534 locations, param); |
|
6419 } |
6535 } |
6420 if (next2 && loc._parameter2 >= tMax) { |
6536 } |
6421 loc._parameter2 = 0; |
6537 return locations; |
6422 loc._curve2 = next2; |
6538 }, |
6539 |
|
6540 getOverlaps: function(v1, v2) { |
|
6541 var abs = Math.abs, |
|
6542 timeEpsilon = 4e-7, |
|
6543 geomEpsilon = 2e-7, |
|
6544 straight1 = Curve.isStraight(v1), |
|
6545 straight2 = Curve.isStraight(v2), |
|
6546 straight = straight1 && straight2; |
|
6547 |
|
6548 function getLineLengthSquared(v) { |
|
6549 var x = v[6] - v[0], |
|
6550 y = v[7] - v[1]; |
|
6551 return x * x + y * y; |
|
6552 } |
|
6553 |
|
6554 if (straight) { |
|
6555 var flip = getLineLengthSquared(v1) < getLineLengthSquared(v2), |
|
6556 l1 = flip ? v2 : v1, |
|
6557 l2 = flip ? v1 : v2, |
|
6558 line = new Line(l1[0], l1[1], l1[6], l1[7]); |
|
6559 if (line.getDistance(new Point(l2[0], l2[1])) > geomEpsilon || |
|
6560 line.getDistance(new Point(l2[6], l2[7])) > geomEpsilon) |
|
6561 return null; |
|
6562 } else if (straight1 ^ straight2) { |
|
6563 return null; |
|
6564 } |
|
6565 |
|
6566 var v = [v1, v2], |
|
6567 pairs = []; |
|
6568 for (var i = 0, t1 = 0; |
|
6569 i < 2 && pairs.length < 2; |
|
6570 i += t1 === 0 ? 0 : 1, t1 = t1 ^ 1) { |
|
6571 var t2 = Curve.getParameterOf(v[i ^ 1], new Point( |
|
6572 v[i][t1 === 0 ? 0 : 6], |
|
6573 v[i][t1 === 0 ? 1 : 7])); |
|
6574 if (t2 != null) { |
|
6575 var pair = i === 0 ? [t1, t2] : [t2, t1]; |
|
6576 if (pairs.length === 0 || |
|
6577 abs(pair[0] - pairs[0][0]) > timeEpsilon && |
|
6578 abs(pair[1] - pairs[0][1]) > timeEpsilon) |
|
6579 pairs.push(pair); |
|
6423 } |
6580 } |
6424 } |
6581 if (i === 1 && pairs.length === 0) |
6425 |
6582 break; |
6426 function compare(loc1, loc2) { |
6583 } |
6427 var path1 = loc1.getPath(), |
6584 if (pairs.length !== 2) { |
6428 path2 = loc2.getPath(); |
6585 pairs = null; |
6429 return path1 === path2 |
6586 } else if (!straight) { |
6430 ? (loc1.getIndex() + loc1.getParameter()) |
6587 var o1 = Curve.getPart(v1, pairs[0][0], pairs[1][0]), |
6431 - (loc2.getIndex() + loc2.getParameter()) |
6588 o2 = Curve.getPart(v2, pairs[0][1], pairs[1][1]); |
6432 : path1._id - path2._id; |
6589 if (abs(o2[2] - o1[2]) > geomEpsilon || |
6433 } |
6590 abs(o2[3] - o1[3]) > geomEpsilon || |
6434 |
6591 abs(o2[4] - o1[4]) > geomEpsilon || |
6435 if (last > 0) { |
6592 abs(o2[5] - o1[5]) > geomEpsilon) |
6436 locations.sort(compare); |
6593 pairs = null; |
6437 for (var i = last; i > 0; i--) { |
6594 } |
6438 if (locations[i].equals(locations[i - 1])) { |
6595 return pairs; |
6439 locations.splice(i, 1); |
|
6440 last--; |
|
6441 } |
|
6442 } |
|
6443 } |
|
6444 if (_expand) { |
|
6445 for (var i = last; i >= 0; i--) |
|
6446 locations.push(locations[i].getIntersection()); |
|
6447 locations.sort(compare); |
|
6448 } |
|
6449 return locations; |
|
6450 } |
6596 } |
6451 }}; |
6597 }}; |
6452 }); |
6598 }); |
6453 |
6599 |
6454 var CurveLocation = Base.extend({ |
6600 var CurveLocation = Base.extend({ |
6455 _class: 'CurveLocation', |
6601 _class: 'CurveLocation', |
6456 beans: true, |
6602 beans: true, |
6457 |
6603 |
6458 initialize: function CurveLocation(curve, parameter, point, _curve2, |
6604 initialize: function CurveLocation(curve, parameter, point, |
6459 _parameter2, _point2, _distance) { |
6605 _overlap, _distance) { |
6606 if (parameter > 0.9999996) { |
|
6607 var next = curve.getNext(); |
|
6608 if (next) { |
|
6609 parameter = 0; |
|
6610 curve = next; |
|
6611 } |
|
6612 } |
|
6460 this._id = UID.get(CurveLocation); |
6613 this._id = UID.get(CurveLocation); |
6614 this._setCurve(curve); |
|
6615 this._parameter = parameter; |
|
6616 this._point = point || curve.getPointAt(parameter, true); |
|
6617 this._overlap = _overlap; |
|
6618 this._distance = _distance; |
|
6619 this._intersection = this._next = this._prev = null; |
|
6620 }, |
|
6621 |
|
6622 _setCurve: function(curve) { |
|
6461 var path = curve._path; |
6623 var path = curve._path; |
6462 this._version = path ? path._version : 0; |
6624 this._version = path ? path._version : 0; |
6463 this._curve = curve; |
6625 this._curve = curve; |
6464 this._parameter = parameter; |
6626 this._segment = null; |
6465 this._point = point || curve.getPointAt(parameter, true); |
|
6466 this._curve2 = _curve2; |
|
6467 this._parameter2 = _parameter2; |
|
6468 this._point2 = _point2; |
|
6469 this._distance = _distance; |
|
6470 this._segment1 = curve._segment1; |
6627 this._segment1 = curve._segment1; |
6471 this._segment2 = curve._segment2; |
6628 this._segment2 = curve._segment2; |
6472 }, |
6629 }, |
6473 |
6630 |
6474 getSegment: function(_preferFirst) { |
6631 _setSegment: function(segment) { |
6475 if (!this._segment) { |
6632 this._setCurve(segment.getCurve()); |
6476 var curve = this.getCurve(), |
6633 this._segment = segment; |
6477 parameter = this.getParameter(); |
6634 this._parameter = segment === this._segment1 ? 0 : 1; |
6478 if (parameter === 1) { |
6635 this._point = segment._point.clone(); |
6479 this._segment = curve._segment2; |
6636 }, |
6480 } else if (parameter === 0 || _preferFirst) { |
6637 |
6481 this._segment = curve._segment1; |
6638 getSegment: function() { |
6482 } else if (parameter == null) { |
6639 var curve = this.getCurve(), |
6483 return null; |
6640 segment = this._segment; |
6484 } else { |
6641 if (!segment) { |
6485 this._segment = curve.getPartLength(0, parameter) |
6642 var parameter = this.getParameter(); |
6643 if (parameter === 0) { |
|
6644 segment = curve._segment1; |
|
6645 } else if (parameter === 1) { |
|
6646 segment = curve._segment2; |
|
6647 } else if (parameter != null) { |
|
6648 segment = curve.getPartLength(0, parameter) |
|
6486 < curve.getPartLength(parameter, 1) |
6649 < curve.getPartLength(parameter, 1) |
6487 ? curve._segment1 |
6650 ? curve._segment1 |
6488 : curve._segment2; |
6651 : curve._segment2; |
6489 } |
6652 } |
6490 } |
6653 this._segment = segment; |
6491 return this._segment; |
6654 } |
6655 return segment; |
|
6492 }, |
6656 }, |
6493 |
6657 |
6494 getCurve: function() { |
6658 getCurve: function() { |
6495 var curve = this._curve, |
6659 var curve = this._curve, |
6496 path = curve && curve._path; |
6660 path = curve && curve._path, |
6661 that = this; |
|
6497 if (path && path._version !== this._version) { |
6662 if (path && path._version !== this._version) { |
6498 curve = null; |
6663 curve = this._parameter = this._curve = this._offset = null; |
6499 this._parameter = null; |
6664 } |
6500 } |
6665 |
6501 if (!curve) { |
6666 function trySegment(segment) { |
6502 curve = this._segment1.getCurve(); |
6667 var curve = segment && segment.getCurve(); |
6503 if (curve.getParameterOf(this._point) == null) |
6668 if (curve && (that._parameter = curve.getParameterOf(that._point)) |
6504 curve = this._segment2.getPrevious().getCurve(); |
6669 != null) { |
6505 this._curve = curve; |
6670 that._setCurve(curve); |
6506 path = curve._path; |
6671 that._segment = segment; |
6507 this._version = path ? path._version : 0; |
6672 return curve; |
6508 } |
6673 } |
6509 return curve; |
6674 } |
6675 |
|
6676 return curve |
|
6677 || trySegment(this._segment) |
|
6678 || trySegment(this._segment1) |
|
6679 || trySegment(this._segment2.getPrevious()); |
|
6510 }, |
6680 }, |
6511 |
6681 |
6512 getPath: function() { |
6682 getPath: function() { |
6513 var curve = this.getCurve(); |
6683 var curve = this.getCurve(); |
6514 return curve && curve._path; |
6684 return curve && curve._path; |
6530 getPoint: function() { |
6700 getPoint: function() { |
6531 return this._point; |
6701 return this._point; |
6532 }, |
6702 }, |
6533 |
6703 |
6534 getOffset: function() { |
6704 getOffset: function() { |
6535 var path = this.getPath(); |
6705 var offset = this._offset; |
6536 return path ? path._getOffset(this) : this.getCurveOffset(); |
6706 if (offset == null) { |
6707 offset = 0; |
|
6708 var path = this.getPath(), |
|
6709 index = this.getIndex(); |
|
6710 if (path && index != null) { |
|
6711 var curves = path.getCurves(); |
|
6712 for (var i = 0; i < index; i++) |
|
6713 offset += curves[i].getLength(); |
|
6714 } |
|
6715 this._offset = offset += this.getCurveOffset(); |
|
6716 } |
|
6717 return offset; |
|
6537 }, |
6718 }, |
6538 |
6719 |
6539 getCurveOffset: function() { |
6720 getCurveOffset: function() { |
6540 var curve = this.getCurve(), |
6721 var curve = this.getCurve(), |
6541 parameter = this.getParameter(); |
6722 parameter = this.getParameter(); |
6542 return parameter != null && curve && curve.getPartLength(0, parameter); |
6723 return parameter != null && curve && curve.getPartLength(0, parameter); |
6543 }, |
6724 }, |
6544 |
6725 |
6545 getIntersection: function() { |
6726 getIntersection: function() { |
6546 var intersection = this._intersection; |
6727 return this._intersection; |
6547 if (!intersection && this._curve2) { |
|
6548 this._intersection = intersection = new CurveLocation(this._curve2, |
|
6549 this._parameter2, this._point2 || this._point, this); |
|
6550 intersection._intersection = this; |
|
6551 } |
|
6552 return intersection; |
|
6553 }, |
6728 }, |
6554 |
6729 |
6555 getDistance: function() { |
6730 getDistance: function() { |
6556 return this._distance; |
6731 return this._distance; |
6557 }, |
6732 }, |
6558 |
6733 |
6559 divide: function() { |
6734 divide: function() { |
6560 var curve = this.getCurve(); |
6735 var curve = this.getCurve(), |
6561 return curve && curve.divide(this.getParameter(), true); |
6736 res = null; |
6737 if (curve) { |
|
6738 res = curve.divide(this.getParameter(), true); |
|
6739 if (res) |
|
6740 this._setSegment(res._segment1); |
|
6741 } |
|
6742 return res; |
|
6562 }, |
6743 }, |
6563 |
6744 |
6564 split: function() { |
6745 split: function() { |
6565 var curve = this.getCurve(); |
6746 var curve = this.getCurve(); |
6566 return curve && curve.split(this.getParameter(), true); |
6747 return curve ? curve.split(this.getParameter(), true) : null; |
6567 }, |
6748 }, |
6568 |
6749 |
6569 equals: function(loc) { |
6750 equals: function(loc, _ignoreOther) { |
6570 var abs = Math.abs, |
6751 var res = this === loc, |
6571 tolerance = 0.000001; |
6752 epsilon = 2e-7; |
6572 return this === loc |
6753 if (!res && loc instanceof CurveLocation |
6573 || loc instanceof CurveLocation |
6754 && this.getPath() === loc.getPath() |
6574 && this.getCurve() === loc.getCurve() |
6755 && this.getPoint().isClose(loc.getPoint(), epsilon)) { |
6575 && abs(this.getParameter() - loc.getParameter()) < tolerance |
6756 var c1 = this.getCurve(), |
6576 && this._curve2 === loc._curve2 |
6757 c2 = loc.getCurve(), |
6577 && abs(this._parameter2 - loc._parameter2) < tolerance |
6758 abs = Math.abs, |
6578 || false; |
6759 diff = abs( |
6760 ((c1.isLast() && c2.isFirst() ? -1 : c1.getIndex()) |
|
6761 + this.getParameter()) - |
|
6762 ((c2.isLast() && c1.isFirst() ? -1 : c2.getIndex()) |
|
6763 + loc.getParameter())); |
|
6764 res = (diff < 4e-7 |
|
6765 || ((diff = abs(this.getOffset() - loc.getOffset())) < epsilon |
|
6766 || abs(this.getPath().getLength() - diff) < epsilon)) |
|
6767 && (_ignoreOther |
|
6768 || (!this._intersection && !loc._intersection |
|
6769 || this._intersection && this._intersection.equals( |
|
6770 loc._intersection, true))); |
|
6771 } |
|
6772 return res; |
|
6579 }, |
6773 }, |
6580 |
6774 |
6581 toString: function() { |
6775 toString: function() { |
6582 var parts = [], |
6776 var parts = [], |
6583 point = this.getPoint(), |
6777 point = this.getPoint(), |
6591 if (parameter != null) |
6785 if (parameter != null) |
6592 parts.push('parameter: ' + f.number(parameter)); |
6786 parts.push('parameter: ' + f.number(parameter)); |
6593 if (this._distance != null) |
6787 if (this._distance != null) |
6594 parts.push('distance: ' + f.number(this._distance)); |
6788 parts.push('distance: ' + f.number(this._distance)); |
6595 return '{ ' + parts.join(', ') + ' }'; |
6789 return '{ ' + parts.join(', ') + ' }'; |
6790 }, |
|
6791 |
|
6792 isTouching: function() { |
|
6793 var inter = this._intersection; |
|
6794 if (inter && this.getTangent().isCollinear(inter.getTangent())) { |
|
6795 var curve1 = this.getCurve(), |
|
6796 curve2 = inter.getCurve(); |
|
6797 return !(curve1.isStraight() && curve2.isStraight() |
|
6798 && curve1.getLine().intersect(curve2.getLine())); |
|
6799 } |
|
6800 return false; |
|
6801 }, |
|
6802 |
|
6803 isCrossing: function() { |
|
6804 var inter = this._intersection; |
|
6805 if (!inter) |
|
6806 return false; |
|
6807 var t1 = this.getParameter(), |
|
6808 t2 = inter.getParameter(), |
|
6809 tMin = 4e-7, |
|
6810 tMax = 1 - tMin; |
|
6811 if (t1 >= tMin && t1 <= tMax || t2 >= tMin && t2 <= tMax) |
|
6812 return !this.isTouching(); |
|
6813 var c2 = this.getCurve(), |
|
6814 c1 = c2.getPrevious(), |
|
6815 c4 = inter.getCurve(), |
|
6816 c3 = c4.getPrevious(), |
|
6817 PI = Math.PI; |
|
6818 if (!c1 || !c3) |
|
6819 return false; |
|
6820 |
|
6821 function isInRange(angle, min, max) { |
|
6822 return min < max |
|
6823 ? angle > min && angle < max |
|
6824 : angle > min && angle <= PI || angle >= -PI && angle < max; |
|
6825 } |
|
6826 |
|
6827 var a1 = c1.getTangentAt(tMax, true).negate().getAngleInRadians(), |
|
6828 a2 = c2.getTangentAt(tMin, true).getAngleInRadians(), |
|
6829 a3 = c3.getTangentAt(tMax, true).negate().getAngleInRadians(), |
|
6830 a4 = c4.getTangentAt(tMin, true).getAngleInRadians(); |
|
6831 |
|
6832 return (isInRange(a3, a1, a2) ^ isInRange(a4, a1, a2)) |
|
6833 && (isInRange(a3, a2, a1) ^ isInRange(a4, a2, a1)); |
|
6834 }, |
|
6835 |
|
6836 isOverlap: function() { |
|
6837 return !!this._overlap; |
|
6596 } |
6838 } |
6597 }, Base.each(Curve.evaluateMethods, function(name) { |
6839 }, Base.each(Curve.evaluateMethods, function(name) { |
6598 if (name !== 'getPoint') { |
6840 var get = name + 'At'; |
6599 var get = name + 'At'; |
6841 this[name] = function() { |
6600 this[name] = function() { |
6842 var parameter = this.getParameter(), |
6601 var parameter = this.getParameter(), |
6843 curve = this.getCurve(); |
6602 curve = this.getCurve(); |
6844 return parameter != null && curve && curve[get](parameter, true); |
6603 return parameter != null && curve && curve[get](parameter, true); |
6845 }; |
6604 }; |
6846 }, { |
6605 } |
6847 preserve: true |
6606 }, {})); |
6848 }), |
6849 new function() { |
|
6850 |
|
6851 function insert(locations, loc, merge) { |
|
6852 var length = locations.length, |
|
6853 l = 0, |
|
6854 r = length - 1; |
|
6855 |
|
6856 function search(index, dir) { |
|
6857 for (var i = index + dir; i >= -1 && i <= length; i += dir) { |
|
6858 var loc2 = locations[((i % length) + length) % length]; |
|
6859 if (!loc.getPoint().isClose(loc2.getPoint(), |
|
6860 2e-7)) |
|
6861 break; |
|
6862 if (loc.equals(loc2)) |
|
6863 return loc2; |
|
6864 } |
|
6865 return null; |
|
6866 } |
|
6867 |
|
6868 while (l <= r) { |
|
6869 var m = (l + r) >>> 1, |
|
6870 loc2 = locations[m], |
|
6871 found; |
|
6872 if (merge && (found = loc.equals(loc2) ? loc2 |
|
6873 : (search(m, -1) || search(m, 1)))) { |
|
6874 if (loc._overlap) { |
|
6875 found._overlap = found._intersection._overlap = true; |
|
6876 } |
|
6877 return found; |
|
6878 } |
|
6879 var path1 = loc.getPath(), |
|
6880 path2 = loc2.getPath(), |
|
6881 diff = path1 === path2 |
|
6882 ? (loc.getIndex() + loc.getParameter()) |
|
6883 - (loc2.getIndex() + loc2.getParameter()) |
|
6884 : path1._id - path2._id; |
|
6885 if (diff < 0) { |
|
6886 r = m - 1; |
|
6887 } else { |
|
6888 l = m + 1; |
|
6889 } |
|
6890 } |
|
6891 locations.splice(l, 0, loc); |
|
6892 return loc; |
|
6893 } |
|
6894 |
|
6895 return { statics: { |
|
6896 insert: insert, |
|
6897 |
|
6898 expand: function(locations) { |
|
6899 var expanded = locations.slice(); |
|
6900 for (var i = 0, l = locations.length; i < l; i++) { |
|
6901 insert(expanded, locations[i]._intersection, false); |
|
6902 } |
|
6903 return expanded; |
|
6904 } |
|
6905 }}; |
|
6906 }); |
|
6607 |
6907 |
6608 var PathItem = Item.extend({ |
6908 var PathItem = Item.extend({ |
6609 _class: 'PathItem', |
6909 _class: 'PathItem', |
6610 |
6910 |
6611 initialize: function PathItem() { |
6911 initialize: function PathItem() { |
6612 }, |
6912 }, |
6613 |
6913 |
6614 getIntersections: function(path, _matrix, _expand) { |
6914 getIntersections: function(path, include, _matrix, _returnFirst) { |
6615 if (this === path) |
6915 var self = this === path || !path, |
6616 path = null; |
|
6617 var locations = [], |
|
6618 curves1 = this.getCurves(), |
|
6619 curves2 = path ? path.getCurves() : curves1, |
|
6620 matrix1 = this._matrix.orNullIfIdentity(), |
6916 matrix1 = this._matrix.orNullIfIdentity(), |
6621 matrix2 = path ? (_matrix || path._matrix).orNullIfIdentity() |
6917 matrix2 = self ? matrix1 |
6622 : matrix1, |
6918 : (_matrix || path._matrix).orNullIfIdentity(); |
6919 if (!self && !this.getBounds(matrix1).touches(path.getBounds(matrix2))) |
|
6920 return []; |
|
6921 var curves1 = this.getCurves(), |
|
6922 curves2 = self ? curves1 : path.getCurves(), |
|
6623 length1 = curves1.length, |
6923 length1 = curves1.length, |
6624 length2 = path ? curves2.length : length1, |
6924 length2 = self ? length1 : curves2.length, |
6625 values2 = [], |
6925 values2 = [], |
6626 tMin = 0.000001, |
6926 arrays = [], |
6627 tMax = 1 - tMin; |
6927 locations, |
6628 if (path && !this.getBounds(matrix1).touches(path.getBounds(matrix2))) |
6928 path; |
6629 return []; |
|
6630 for (var i = 0; i < length2; i++) |
6929 for (var i = 0; i < length2; i++) |
6631 values2[i] = curves2[i].getValues(matrix2); |
6930 values2[i] = curves2[i].getValues(matrix2); |
6632 for (var i = 0; i < length1; i++) { |
6931 for (var i = 0; i < length1; i++) { |
6633 var curve1 = curves1[i], |
6932 var curve1 = curves1[i], |
6634 values1 = path ? curve1.getValues(matrix1) : values2[i]; |
6933 values1 = self ? values2[i] : curve1.getValues(matrix1), |
6635 if (!path) { |
6934 path1 = curve1.getPath(); |
6636 var seg1 = curve1.getSegment1(), |
6935 if (path1 !== path) { |
6637 seg2 = curve1.getSegment2(), |
6936 path = path1; |
6638 h1 = seg1._handleOut, |
6937 locations = []; |
6639 h2 = seg2._handleIn; |
6938 arrays.push(locations); |
6640 if (new Line(seg1._point.subtract(h1), h1.multiply(2), true) |
6939 } |
6641 .intersect(new Line(seg2._point.subtract(h2), |
6940 if (self) { |
6642 h2.multiply(2), true), false)) { |
6941 Curve._getSelfIntersection(values1, curve1, locations, { |
6643 var parts = Curve.subdivide(values1); |
6942 include: include, |
6644 Curve.getIntersections( |
6943 startConnected: length1 === 1 && |
6645 parts[0], parts[1], curve1, curve1, locations, |
6944 curve1.getPoint1().equals(curve1.getPoint2()) |
6646 function(loc) { |
6945 }); |
6647 if (loc._parameter <= tMax) { |
6946 } |
6648 loc._parameter /= 2; |
6947 for (var j = self ? i + 1 : 0; j < length2; j++) { |
6649 loc._parameter2 = 0.5 + loc._parameter2 / 2; |
6948 if (_returnFirst && locations.length) |
6650 return true; |
6949 return locations; |
6651 } |
6950 var curve2 = curves2[j]; |
6652 } |
6951 Curve._getIntersections( |
6653 ); |
6952 values1, values2[j], curve1, curve2, locations, |
6654 } |
6953 { |
6655 } |
6954 include: include, |
6656 for (var j = path ? 0 : i + 1; j < length2; j++) { |
6955 startConnected: self && curve1.getPrevious() === curve2, |
6657 Curve.getIntersections( |
6956 endConnected: self && curve1.getNext() === curve2 |
6658 values1, values2[j], curve1, curves2[j], locations, |
6957 } |
6659 !path && (j === i + 1 || j === length2 - 1 && i === 0) |
|
6660 && function(loc) { |
|
6661 var t = loc._parameter; |
|
6662 return t >= tMin && t <= tMax; |
|
6663 } |
|
6664 ); |
6958 ); |
6665 } |
6959 } |
6666 } |
6960 } |
6667 return Curve.filterIntersections(locations, _expand); |
6961 locations = []; |
6962 for (var i = 0, l = arrays.length; i < l; i++) { |
|
6963 locations.push.apply(locations, arrays[i]); |
|
6964 } |
|
6965 return locations; |
|
6966 }, |
|
6967 |
|
6968 getCrossings: function(path) { |
|
6969 return this.getIntersections(path, function(inter) { |
|
6970 return inter.isCrossing(); |
|
6971 }); |
|
6668 }, |
6972 }, |
6669 |
6973 |
6670 _asPathItem: function() { |
6974 _asPathItem: function() { |
6671 return this; |
6975 return this; |
6672 }, |
6976 }, |
6838 _changed.base.call(this, flags); |
7142 _changed.base.call(this, flags); |
6839 if (flags & 8) { |
7143 if (flags & 8) { |
6840 var parent = this._parent; |
7144 var parent = this._parent; |
6841 if (parent) |
7145 if (parent) |
6842 parent._currentPath = undefined; |
7146 parent._currentPath = undefined; |
6843 this._length = this._clockwise = undefined; |
7147 this._length = this._area = this._clockwise = this._monoCurves = |
7148 undefined; |
|
6844 if (flags & 16) { |
7149 if (flags & 16) { |
6845 this._version++; |
7150 this._version++; |
6846 } else if (this._curves) { |
7151 } else if (this._curves) { |
6847 for (var i = 0, l = this._curves.length; i < l; i++) |
7152 for (var i = 0, l = this._curves.length; i < l; i++) |
6848 this._curves[i]._changed(); |
7153 this._curves[i]._changed(); |
6849 } |
7154 } |
6850 this._monoCurves = undefined; |
|
6851 } else if (flags & 32) { |
7155 } else if (flags & 32) { |
6852 this._bounds = undefined; |
7156 this._bounds = undefined; |
6853 } |
7157 } |
6854 }, |
7158 }, |
6855 |
7159 |
6975 |
7279 |
6976 isEmpty: function() { |
7280 isEmpty: function() { |
6977 return this._segments.length === 0; |
7281 return this._segments.length === 0; |
6978 }, |
7282 }, |
6979 |
7283 |
6980 isLinear: function() { |
|
6981 var segments = this._segments; |
|
6982 for (var i = 0, l = segments.length; i < l; i++) { |
|
6983 if (!segments[i].isLinear()) |
|
6984 return false; |
|
6985 } |
|
6986 return true; |
|
6987 }, |
|
6988 |
|
6989 hasHandles: function() { |
|
6990 var segments = this._segments; |
|
6991 for (var i = 0, l = segments.length; i < l; i++) { |
|
6992 if (segments[i].hasHandles()) |
|
6993 return true; |
|
6994 } |
|
6995 return false; |
|
6996 }, |
|
6997 |
|
6998 _transformContent: function(matrix) { |
7284 _transformContent: function(matrix) { |
6999 var coords = new Array(6); |
7285 var coords = new Array(6); |
7000 for (var i = 0, l = this._segments.length; i < l; i++) |
7286 for (var i = 0, l = this._segments.length; i < l; i++) |
7001 this._segments[i]._transformCoordinates(matrix, coords, true); |
7287 this._segments[i]._transformCoordinates(matrix, coords, true); |
7002 return true; |
7288 return true; |
7022 } else { |
7308 } else { |
7023 segments.splice.apply(segments, [index, 0].concat(segs)); |
7309 segments.splice.apply(segments, [index, 0].concat(segs)); |
7024 for (var i = index + amount, l = segments.length; i < l; i++) |
7310 for (var i = index + amount, l = segments.length; i < l; i++) |
7025 segments[i]._index = i; |
7311 segments[i]._index = i; |
7026 } |
7312 } |
7027 if (curves || segs._curves) { |
7313 if (curves) { |
7028 if (!curves) |
7314 var total = this._countCurves(), |
7029 curves = this._curves = []; |
7315 from = index + amount - 1 === total ? index - 1 : index, |
7030 var from = index > 0 ? index - 1 : index, |
|
7031 start = from, |
7316 start = from, |
7032 to = Math.min(from + amount, this._countCurves()); |
7317 to = Math.min(from + amount, total); |
7033 if (segs._curves) { |
7318 if (segs._curves) { |
7034 curves.splice.apply(curves, [from, 0].concat(segs._curves)); |
7319 curves.splice.apply(curves, [from, 0].concat(segs._curves)); |
7035 start += segs._curves.length; |
7320 start += segs._curves.length; |
7036 } |
7321 } |
7037 for (var i = start; i < to; i++) |
7322 for (var i = start; i < to; i++) |
7132 return removed; |
7417 return removed; |
7133 }, |
7418 }, |
7134 |
7419 |
7135 clear: '#removeSegments', |
7420 clear: '#removeSegments', |
7136 |
7421 |
7422 hasHandles: function() { |
|
7423 var segments = this._segments; |
|
7424 for (var i = 0, l = segments.length; i < l; i++) { |
|
7425 if (segments[i].hasHandles()) |
|
7426 return true; |
|
7427 } |
|
7428 return false; |
|
7429 }, |
|
7430 |
|
7431 clearHandles: function() { |
|
7432 var segments = this._segments; |
|
7433 for (var i = 0, l = segments.length; i < l; i++) |
|
7434 segments[i].clearHandles(); |
|
7435 }, |
|
7436 |
|
7137 getLength: function() { |
7437 getLength: function() { |
7138 if (this._length == null) { |
7438 if (this._length == null) { |
7139 var curves = this.getCurves(); |
7439 var curves = this.getCurves(), |
7140 this._length = 0; |
7440 length = 0; |
7141 for (var i = 0, l = curves.length; i < l; i++) |
7441 for (var i = 0, l = curves.length; i < l; i++) |
7142 this._length += curves[i].getLength(); |
7442 length += curves[i].getLength(); |
7443 this._length = length; |
|
7143 } |
7444 } |
7144 return this._length; |
7445 return this._length; |
7145 }, |
7446 }, |
7146 |
7447 |
7147 getArea: function() { |
7448 getArea: function() { |
7148 var curves = this.getCurves(); |
7449 if (this._area == null) { |
7149 var area = 0; |
7450 var segments = this._segments, |
7150 for (var i = 0, l = curves.length; i < l; i++) |
7451 count = segments.length, |
7151 area += curves[i].getArea(); |
7452 last = count - 1, |
7152 return area; |
7453 area = 0; |
7454 for (var i = 0, l = this._closed ? count : last; i < l; i++) { |
|
7455 area += Curve.getArea(Curve.getValues( |
|
7456 segments[i], segments[i < last ? i + 1 : 0])); |
|
7457 } |
|
7458 this._area = area; |
|
7459 } |
|
7460 return this._area; |
|
7461 }, |
|
7462 |
|
7463 isClockwise: function() { |
|
7464 if (this._clockwise !== undefined) |
|
7465 return this._clockwise; |
|
7466 return this.getArea() >= 0; |
|
7467 }, |
|
7468 |
|
7469 setClockwise: function(clockwise) { |
|
7470 if (this.isClockwise() != (clockwise = !!clockwise)) |
|
7471 this.reverse(); |
|
7472 this._clockwise = clockwise; |
|
7153 }, |
7473 }, |
7154 |
7474 |
7155 isFullySelected: function() { |
7475 isFullySelected: function() { |
7156 var length = this._segments.length; |
7476 var length = this._segments.length; |
7157 return this._selected && length > 0 && this._selectedSegmentState |
7477 return this._selected && length > 0 && this._selectedSegmentState |
7201 |
7521 |
7202 reduce: function() { |
7522 reduce: function() { |
7203 var curves = this.getCurves(); |
7523 var curves = this.getCurves(); |
7204 for (var i = curves.length - 1; i >= 0; i--) { |
7524 for (var i = curves.length - 1; i >= 0; i--) { |
7205 var curve = curves[i]; |
7525 var curve = curves[i]; |
7206 if (curve.isLinear() && curve.getLength() === 0) |
7526 if (!curve.hasHandles() && (curve.getLength() === 0 |
7527 || curve.isCollinear(curve.getNext()))) |
|
7207 curve.remove(); |
7528 curve.remove(); |
7208 } |
7529 } |
7209 return this; |
7530 return this; |
7210 }, |
7531 }, |
7211 |
7532 |
7226 if (!arg) |
7547 if (!arg) |
7227 return null |
7548 return null |
7228 index = arg.index; |
7549 index = arg.index; |
7229 parameter = arg.parameter; |
7550 parameter = arg.parameter; |
7230 } |
7551 } |
7231 var tolerance = 0.000001; |
7552 var tMin = 4e-7, |
7232 if (parameter >= 1 - tolerance) { |
7553 tMax = 1 - tMin; |
7554 if (parameter >= tMax) { |
|
7233 index++; |
7555 index++; |
7234 parameter--; |
7556 parameter--; |
7235 } |
7557 } |
7236 var curves = this.getCurves(); |
7558 var curves = this.getCurves(); |
7237 if (index >= 0 && index < curves.length) { |
7559 if (index >= 0 && index < curves.length) { |
7238 if (parameter > tolerance) { |
7560 if (parameter >= tMin) { |
7239 curves[index++].divide(parameter, true); |
7561 curves[index++].divide(parameter, true); |
7240 } |
7562 } |
7241 var segs = this.removeSegments(index, this._segments.length, true), |
7563 var segs = this.removeSegments(index, this._segments.length, true), |
7242 path; |
7564 path; |
7243 if (this._closed) { |
7565 if (this._closed) { |
7244 this.setClosed(false); |
7566 this.setClosed(false); |
7245 path = this; |
7567 path = this; |
7246 } else { |
7568 } else { |
7247 path = this._clone(new Path().insertAbove(this, true)); |
7569 path = new Path(Item.NO_INSERT); |
7570 path.insertAbove(this, true); |
|
7571 this._clone(path); |
|
7248 } |
7572 } |
7249 path._add(segs, 0); |
7573 path._add(segs, 0); |
7250 this.addSegment(segs[0]); |
7574 this.addSegment(segs[0]); |
7251 return path; |
7575 return path; |
7252 } |
7576 } |
7253 return null; |
7577 return null; |
7254 }, |
|
7255 |
|
7256 isClockwise: function() { |
|
7257 if (this._clockwise !== undefined) |
|
7258 return this._clockwise; |
|
7259 return Path.isClockwise(this._segments); |
|
7260 }, |
|
7261 |
|
7262 setClockwise: function(clockwise) { |
|
7263 if (this.isClockwise() != (clockwise = !!clockwise)) |
|
7264 this.reverse(); |
|
7265 this._clockwise = clockwise; |
|
7266 }, |
7578 }, |
7267 |
7579 |
7268 reverse: function() { |
7580 reverse: function() { |
7269 this._segments.reverse(); |
7581 this._segments.reverse(); |
7270 for (var i = 0, l = this._segments.length; i < l; i++) { |
7582 for (var i = 0, l = this._segments.length; i < l; i++) { |
7303 this._add(segments.slice(0, segments.length - 1), 0); |
7615 this._add(segments.slice(0, segments.length - 1), 0); |
7304 } else { |
7616 } else { |
7305 this._add(segments.slice()); |
7617 this._add(segments.slice()); |
7306 } |
7618 } |
7307 } |
7619 } |
7308 if (path.closed) |
7620 if (path._closed) |
7309 this._add([segments[0]]); |
7621 this._add([segments[0]]); |
7310 path.remove(); |
7622 path.remove(); |
7311 } |
7623 } |
7312 var first = this.getFirstSegment(), |
7624 var first = this.getFirstSegment(), |
7313 last = this.getLastSegment(); |
7625 last = this.getLastSegment(); |
7328 size, |
7640 size, |
7329 radius, |
7641 radius, |
7330 topCenter; |
7642 topCenter; |
7331 |
7643 |
7332 function isCollinear(i, j) { |
7644 function isCollinear(i, j) { |
7333 return segments[i].isCollinear(segments[j]); |
7645 var seg1 = segments[i], |
7646 seg2 = seg1.getNext(), |
|
7647 seg3 = segments[j], |
|
7648 seg4 = seg3.getNext(); |
|
7649 return seg1._handleOut.isZero() && seg2._handleIn.isZero() |
|
7650 && seg3._handleOut.isZero() && seg4._handleIn.isZero() |
|
7651 && seg2._point.subtract(seg1._point).isCollinear( |
|
7652 seg4._point.subtract(seg3._point)); |
|
7334 } |
7653 } |
7335 |
7654 |
7336 function isOrthogonal(i) { |
7655 function isOrthogonal(i) { |
7337 return segments[i].isOrthogonal(); |
7656 var seg2 = segments[i], |
7657 seg1 = seg2.getPrevious(), |
|
7658 seg3 = seg2.getNext(); |
|
7659 return seg1._handleOut.isZero() && seg2._handleIn.isZero() |
|
7660 && seg2._handleOut.isZero() && seg3._handleIn.isZero() |
|
7661 && seg2._point.subtract(seg1._point).isOrthogonal( |
|
7662 seg3._point.subtract(seg2._point)); |
|
7338 } |
7663 } |
7339 |
7664 |
7340 function isArc(i) { |
7665 function isArc(i) { |
7341 return segments[i].isOrthogonalArc(); |
7666 var seg1 = segments[i], |
7667 seg2 = seg1.getNext(), |
|
7668 handle1 = seg1._handleOut, |
|
7669 handle2 = seg2._handleIn, |
|
7670 kappa = 0.5522847498307936; |
|
7671 if (handle1.isOrthogonal(handle2)) { |
|
7672 var pt1 = seg1._point, |
|
7673 pt2 = seg2._point, |
|
7674 corner = new Line(pt1, handle1, true).intersect( |
|
7675 new Line(pt2, handle2, true), true); |
|
7676 return corner && Numerical.isZero(handle1.getLength() / |
|
7677 corner.subtract(pt1).getLength() - kappa) |
|
7678 && Numerical.isZero(handle2.getLength() / |
|
7679 corner.subtract(pt2).getLength() - kappa); |
|
7680 } |
|
7681 return false; |
|
7342 } |
7682 } |
7343 |
7683 |
7344 function getDistance(i, j) { |
7684 function getDistance(i, j) { |
7345 return segments[i]._point.getDistance(segments[j]._point); |
7685 return segments[i]._point.getDistance(segments[j]._point); |
7346 } |
7686 } |
7513 }; |
7853 }; |
7514 }, |
7854 }, |
7515 { |
7855 { |
7516 beans: false, |
7856 beans: false, |
7517 |
7857 |
7518 _getOffset: function(location) { |
|
7519 var index = location && location.getIndex(); |
|
7520 if (index != null) { |
|
7521 var curves = this.getCurves(), |
|
7522 offset = 0; |
|
7523 for (var i = 0; i < index; i++) |
|
7524 offset += curves[i].getLength(); |
|
7525 var curve = curves[index], |
|
7526 parameter = location.getParameter(); |
|
7527 if (parameter > 0) |
|
7528 offset += curve.getPartLength(0, parameter); |
|
7529 return offset; |
|
7530 } |
|
7531 return null; |
|
7532 }, |
|
7533 |
|
7534 getLocationOf: function() { |
7858 getLocationOf: function() { |
7535 var point = Point.read(arguments), |
7859 var point = Point.read(arguments), |
7536 curves = this.getCurves(); |
7860 curves = this.getCurves(); |
7537 for (var i = 0, l = curves.length; i < l; i++) { |
7861 for (var i = 0, l = curves.length; i < l; i++) { |
7538 var loc = curves[i].getLocationOf(point); |
7862 var loc = curves[i].getLocationOf(point); |
7561 length += curve.getLength(); |
7885 length += curve.getLength(); |
7562 if (length > offset) { |
7886 if (length > offset) { |
7563 return curve.getLocationAt(offset - start); |
7887 return curve.getLocationAt(offset - start); |
7564 } |
7888 } |
7565 } |
7889 } |
7566 if (offset <= this.getLength()) |
7890 if (curves.length > 0 && offset <= this.getLength()) |
7567 return new CurveLocation(curves[curves.length - 1], 1); |
7891 return new CurveLocation(curves[curves.length - 1], 1); |
7568 return null; |
7892 return null; |
7569 }, |
7893 }, |
7570 |
7894 |
7571 getNearestLocation: function() { |
7895 getNearestLocation: function() { |
7584 }, |
7908 }, |
7585 |
7909 |
7586 getNearestPoint: function() { |
7910 getNearestPoint: function() { |
7587 return this.getNearestLocation.apply(this, arguments).getPoint(); |
7911 return this.getNearestLocation.apply(this, arguments).getPoint(); |
7588 } |
7912 } |
7589 }), new function() { |
7913 }), |
7914 new function() { |
|
7590 |
7915 |
7591 function drawHandles(ctx, segments, matrix, size) { |
7916 function drawHandles(ctx, segments, matrix, size) { |
7592 var half = size / 2; |
7917 var half = size / 2; |
7593 |
7918 |
7594 function drawHandle(index) { |
7919 function drawHandle(index) { |
7748 drawSegments(ctx, this, matrix); |
8073 drawSegments(ctx, this, matrix); |
7749 ctx.stroke(); |
8074 ctx.stroke(); |
7750 drawHandles(ctx, this._segments, matrix, paper.settings.handleSize); |
8075 drawHandles(ctx, this._segments, matrix, paper.settings.handleSize); |
7751 } |
8076 } |
7752 }; |
8077 }; |
7753 }, new function() { |
8078 }, |
7754 |
8079 new function() { |
7755 function getFirstControlPoints(rhs) { |
8080 function getFirstControlPoints(rhs) { |
7756 var n = rhs.length, |
8081 var n = rhs.length, |
7757 x = [], |
8082 x = [], |
7758 tmp = [], |
8083 tmp = [], |
7759 b = 2; |
8084 b = 2; |
7841 var segment = this._segments[0]; |
8166 var segment = this._segments[0]; |
7842 segment.setHandleIn(handleIn.subtract(segment._point)); |
8167 segment.setHandleIn(handleIn.subtract(segment._point)); |
7843 } |
8168 } |
7844 } |
8169 } |
7845 }; |
8170 }; |
7846 }, new function() { |
8171 }, |
8172 new function() { |
|
7847 function getCurrentSegment(that) { |
8173 function getCurrentSegment(that) { |
7848 var segments = that._segments; |
8174 var segments = that._segments; |
7849 if (segments.length === 0) |
8175 if (segments.length === 0) |
7850 throw new Error('Use a moveTo() command first'); |
8176 throw new Error('Use a moveTo() command first'); |
7851 return segments[segments.length - 1]; |
8177 return segments[segments.length - 1]; |
7927 middle = from.add(to).divide(2), |
8253 middle = from.add(to).divide(2), |
7928 pt = from.subtract(middle).rotate(-rotation), |
8254 pt = from.subtract(middle).rotate(-rotation), |
7929 x = pt.x, |
8255 x = pt.x, |
7930 y = pt.y, |
8256 y = pt.y, |
7931 abs = Math.abs, |
8257 abs = Math.abs, |
7932 epsilon = 1e-12, |
|
7933 rx = abs(radius.width), |
8258 rx = abs(radius.width), |
7934 ry = abs(radius.height), |
8259 ry = abs(radius.height), |
7935 rxSq = rx * rx, |
8260 rxSq = rx * rx, |
7936 rySq = ry * ry, |
8261 rySq = ry * ry, |
7937 xSq = x * x, |
8262 xSq = x * x, |
7943 rxSq = rx * rx; |
8268 rxSq = rx * rx; |
7944 rySq = ry * ry; |
8269 rySq = ry * ry; |
7945 } |
8270 } |
7946 factor = (rxSq * rySq - rxSq * ySq - rySq * xSq) / |
8271 factor = (rxSq * rySq - rxSq * ySq - rySq * xSq) / |
7947 (rxSq * ySq + rySq * xSq); |
8272 (rxSq * ySq + rySq * xSq); |
7948 if (abs(factor) < epsilon) |
8273 if (abs(factor) < 1e-12) |
7949 factor = 0; |
8274 factor = 0; |
7950 if (factor < 0) |
8275 if (factor < 0) |
7951 throw new Error( |
8276 throw new Error( |
7952 'Cannot create an arc with the given arguments'); |
8277 'Cannot create an arc with the given arguments'); |
7953 center = new Point(rx * y / ry, -ry * x / rx) |
8278 center = new Point(rx * y / ry, -ry * x / rx) |
8073 return Path[getter](this._segments, this._closed, this.getStyle(), |
8398 return Path[getter](this._segments, this._closed, this.getStyle(), |
8074 matrix); |
8399 matrix); |
8075 }, |
8400 }, |
8076 |
8401 |
8077 statics: { |
8402 statics: { |
8078 isClockwise: function(segments) { |
|
8079 var sum = 0; |
|
8080 for (var i = 0, l = segments.length; i < l; i++) |
|
8081 sum += Curve.getEdgeSum(Curve.getValues( |
|
8082 segments[i], segments[i + 1 < l ? i + 1 : 0])); |
|
8083 return sum > 0; |
|
8084 }, |
|
8085 |
|
8086 getBounds: function(segments, closed, style, matrix, strokePadding) { |
8403 getBounds: function(segments, closed, style, matrix, strokePadding) { |
8087 var first = segments[0]; |
8404 var first = segments[0]; |
8088 if (!first) |
8405 if (!first) |
8089 return new Rectangle(); |
8406 return new Rectangle(); |
8090 var coords = new Array(6), |
8407 var coords = new Array(6), |
8425 } |
8742 } |
8426 } |
8743 } |
8427 }, |
8744 }, |
8428 |
8745 |
8429 insertChildren: function insertChildren(index, items, _preserve) { |
8746 insertChildren: function insertChildren(index, items, _preserve) { |
8747 for (var i = items.length - 1; i >= 0; i--) { |
|
8748 var item = items[i]; |
|
8749 if (item instanceof CompoundPath) { |
|
8750 items.splice.apply(items, [i, 1].concat(item.removeChildren())); |
|
8751 item.remove(); |
|
8752 } |
|
8753 } |
|
8430 items = insertChildren.base.call(this, index, items, _preserve, Path); |
8754 items = insertChildren.base.call(this, index, items, _preserve, Path); |
8431 for (var i = 0, l = !_preserve && items && items.length; i < l; i++) { |
8755 for (var i = 0, l = !_preserve && items && items.length; i < l; i++) { |
8432 var item = items[i]; |
8756 var item = items[i]; |
8433 if (item._clockwise === undefined) |
8757 if (item._clockwise === undefined) |
8434 item.setClockwise(item._index === 0); |
8758 item.setClockwise(item._index === 0); |
8446 for (var i = 0, l = this._children.length; i < l; i++) |
8770 for (var i = 0, l = this._children.length; i < l; i++) |
8447 this._children[i].smooth(); |
8771 this._children[i].smooth(); |
8448 }, |
8772 }, |
8449 |
8773 |
8450 reduce: function reduce() { |
8774 reduce: function reduce() { |
8451 if (this._children.length === 0) { |
8775 var children = this._children; |
8776 for (var i = children.length - 1; i >= 0; i--) { |
|
8777 var path = children[i].reduce(); |
|
8778 if (path.isEmpty()) |
|
8779 children.splice(i, 1); |
|
8780 } |
|
8781 if (children.length === 0) { |
|
8452 var path = new Path(Item.NO_INSERT); |
8782 var path = new Path(Item.NO_INSERT); |
8453 path.insertAbove(this); |
8783 path.insertAbove(this); |
8454 path.setStyle(this._style); |
8784 path.setStyle(this._style); |
8455 this.remove(); |
8785 this.remove(); |
8456 return path; |
8786 return path; |
8457 } else { |
8787 } |
8458 return reduce.base.call(this); |
8788 return reduce.base.call(this); |
8459 } |
|
8460 }, |
8789 }, |
8461 |
8790 |
8462 isClockwise: function() { |
8791 isClockwise: function() { |
8463 var child = this.getFirstChild(); |
8792 var child = this.getFirstChild(); |
8464 return child && child.isClockwise(); |
8793 return child && child.isClockwise(); |
8512 paths = []; |
8841 paths = []; |
8513 for (var i = 0, l = children.length; i < l; i++) { |
8842 for (var i = 0, l = children.length; i < l; i++) { |
8514 var child = children[i], |
8843 var child = children[i], |
8515 mx = child._matrix; |
8844 mx = child._matrix; |
8516 paths.push(child.getPathData(_matrix && !mx.isIdentity() |
8845 paths.push(child.getPathData(_matrix && !mx.isIdentity() |
8517 ? _matrix.chain(mx) : mx, _precision)); |
8846 ? _matrix.chain(mx) : _matrix, _precision)); |
8518 } |
8847 } |
8519 return paths.join(' '); |
8848 return paths.join(' '); |
8520 } |
8849 } |
8521 }, { |
8850 }, { |
8522 _getChildHitTestOptions: function(options) { |
8851 _getChildHitTestOptions: function(options) { |
8560 if (!selectedItems[child._id]) |
8889 if (!selectedItems[child._id]) |
8561 child._drawSelected(ctx, mx.isIdentity() ? matrix |
8890 child._drawSelected(ctx, mx.isIdentity() ? matrix |
8562 : matrix.chain(mx)); |
8891 : matrix.chain(mx)); |
8563 } |
8892 } |
8564 } |
8893 } |
8565 }, new function() { |
8894 }, |
8895 new function() { |
|
8566 function getCurrentPath(that, check) { |
8896 function getCurrentPath(that, check) { |
8567 var children = that._children; |
8897 var children = that._children; |
8568 if (check && children.length === 0) |
8898 if (check && children.length === 0) |
8569 throw new Error('Use a moveTo() command first'); |
8899 throw new Error('Use a moveTo() command first'); |
8570 return children[children.length - 1]; |
8900 return children[children.length - 1]; |
8571 } |
8901 } |
8572 |
8902 |
8573 var fields = { |
8903 var fields = { |
8574 moveTo: function() { |
8904 moveTo: function() { |
8575 var current = getCurrentPath(this), |
8905 var current = getCurrentPath(this), |
8576 path = current && current.isEmpty() ? current : new Path(); |
8906 path = current && current.isEmpty() ? current |
8907 : new Path(Item.NO_INSERT); |
|
8577 if (path !== current) |
8908 if (path !== current) |
8578 this.addChild(path); |
8909 this.addChild(path); |
8579 path.moveTo.apply(path, arguments); |
8910 path.moveTo.apply(path, arguments); |
8580 }, |
8911 }, |
8581 |
8912 |
8621 exclude: function(w) { |
8952 exclude: function(w) { |
8622 return w === 1; |
8953 return w === 1; |
8623 } |
8954 } |
8624 }; |
8955 }; |
8625 |
8956 |
8957 function preparePath(path, resolve) { |
|
8958 var res = path.clone(false).reduce().transform(null, true, true); |
|
8959 return resolve ? res.resolveCrossings().reorient() : res; |
|
8960 } |
|
8961 |
|
8962 function finishBoolean(ctor, paths, path1, path2, reduce) { |
|
8963 var result = new ctor(Item.NO_INSERT); |
|
8964 result.addChildren(paths, true); |
|
8965 if (reduce) |
|
8966 result = result.reduce(); |
|
8967 result.insertAbove(path2 && path1.isSibling(path2) |
|
8968 && path1.getIndex() < path2.getIndex() |
|
8969 ? path2 : path1); |
|
8970 result.setStyle(path1._style); |
|
8971 return result; |
|
8972 } |
|
8973 |
|
8626 function computeBoolean(path1, path2, operation) { |
8974 function computeBoolean(path1, path2, operation) { |
8627 var operator = operators[operation]; |
8975 if (!path1._children && !path1._closed) |
8628 function preparePath(path) { |
8976 return computeOpenBoolean(path1, path2, operation); |
8629 return path.clone(false).reduce().reorient().transform(null, true, |
8977 var _path1 = preparePath(path1, true), |
8630 true); |
8978 _path2 = path2 && path1 !== path2 && preparePath(path2, true); |
8631 } |
|
8632 |
|
8633 var _path1 = preparePath(path1), |
|
8634 _path2 = path2 && path1 !== path2 && preparePath(path2); |
|
8635 if (_path2 && /^(subtract|exclude)$/.test(operation) |
8979 if (_path2 && /^(subtract|exclude)$/.test(operation) |
8636 ^ (_path2.isClockwise() !== _path1.isClockwise())) |
8980 ^ (_path2.isClockwise() !== _path1.isClockwise())) |
8637 _path2.reverse(); |
8981 _path2.reverse(); |
8638 splitPath(_path1.getIntersections(_path2, null, true)); |
8982 var intersections = CurveLocation.expand( |
8639 |
8983 _path1.getIntersections(_path2, function(inter) { |
8640 var chain = [], |
8984 return _path2 && inter.isOverlap() || inter.isCrossing(); |
8641 segments = [], |
8985 }) |
8642 monoCurves = [], |
8986 ); |
8643 tolerance = 0.000001; |
8987 divideLocations(intersections); |
8988 |
|
8989 var segments = [], |
|
8990 monoCurves = []; |
|
8644 |
8991 |
8645 function collect(paths) { |
8992 function collect(paths) { |
8646 for (var i = 0, l = paths.length; i < l; i++) { |
8993 for (var i = 0, l = paths.length; i < l; i++) { |
8647 var path = paths[i]; |
8994 var path = paths[i]; |
8648 segments.push.apply(segments, path._segments); |
8995 segments.push.apply(segments, path._segments); |
8651 } |
8998 } |
8652 |
8999 |
8653 collect(_path1._children || [_path1]); |
9000 collect(_path1._children || [_path1]); |
8654 if (_path2) |
9001 if (_path2) |
8655 collect(_path2._children || [_path2]); |
9002 collect(_path2._children || [_path2]); |
8656 segments.sort(function(a, b) { |
9003 for (var i = 0, l = intersections.length; i < l; i++) { |
8657 var _a = a._intersection, |
9004 propagateWinding(intersections[i]._segment, _path1, _path2, |
8658 _b = b._intersection; |
9005 monoCurves, operation); |
8659 return !_a && !_b || _a && _b ? 0 : _a ? -1 : 1; |
9006 } |
8660 }); |
|
8661 for (var i = 0, l = segments.length; i < l; i++) { |
9007 for (var i = 0, l = segments.length; i < l; i++) { |
8662 var segment = segments[i]; |
9008 var segment = segments[i]; |
8663 if (segment._winding != null) |
9009 if (segment._winding == null) { |
8664 continue; |
9010 propagateWinding(segment, _path1, _path2, monoCurves, |
8665 chain.length = 0; |
9011 operation); |
8666 var startSeg = segment, |
9012 } |
8667 totalLength = 0, |
9013 } |
8668 windingSum = 0; |
9014 return finishBoolean(CompoundPath, tracePaths(segments, operation), |
8669 do { |
9015 path1, path2, true); |
8670 var length = segment.getCurve().getLength(); |
9016 } |
8671 chain.push({ segment: segment, length: length }); |
9017 |
8672 totalLength += length; |
9018 function computeOpenBoolean(path1, path2, operation) { |
8673 segment = segment.getNext(); |
9019 if (!path2 || !path2._children && !path2._closed |
8674 } while (segment && !segment._intersection && segment !== startSeg); |
9020 || !/^(subtract|intersect)$/.test(operation)) |
8675 for (var j = 0; j < 3; j++) { |
9021 return null; |
8676 var length = totalLength * (j + 1) / 4; |
9022 var _path1 = preparePath(path1, false), |
8677 for (var k = 0, m = chain.length; k < m; k++) { |
9023 _path2 = preparePath(path2, false), |
8678 var node = chain[k], |
9024 intersections = _path1.getIntersections(_path2, function(inter) { |
8679 curveLength = node.length; |
9025 return inter.isOverlap() || inter.isCrossing(); |
8680 if (length <= curveLength) { |
9026 }), |
8681 if (length < tolerance |
9027 sub = operation === 'subtract', |
8682 || curveLength - length < tolerance) |
9028 paths = []; |
8683 length = curveLength / 2; |
9029 |
8684 var curve = node.segment.getCurve(), |
9030 function addPath(path) { |
8685 pt = curve.getPointAt(length), |
9031 if (_path2.contains(path.getPointAt(path.getLength() / 2)) ^ sub) { |
8686 hor = curve.isLinear() && Math.abs(curve |
9032 paths.unshift(path); |
8687 .getTangentAt(0.5, true).y) < tolerance, |
9033 return true; |
8688 path = curve._path; |
9034 } |
8689 if (path._parent instanceof CompoundPath) |
9035 } |
8690 path = path._parent; |
9036 |
8691 windingSum += operation === 'subtract' && _path2 |
9037 for (var i = intersections.length - 1; i >= 0; i--) { |
8692 && (path === _path1 && _path2._getWinding(pt, hor) |
9038 var path = intersections[i].split(); |
8693 || path === _path2 && !_path1._getWinding(pt, hor)) |
9039 if (path) { |
8694 ? 0 |
9040 if (addPath(path)) |
8695 : getWinding(pt, monoCurves, hor); |
9041 path.getFirstSegment().setHandleIn(0, 0); |
8696 break; |
9042 _path1.getLastSegment().setHandleOut(0, 0); |
8697 } |
9043 } |
8698 length -= curveLength; |
9044 } |
9045 addPath(_path1); |
|
9046 return finishBoolean(Group, paths, path1, path2); |
|
9047 } |
|
9048 |
|
9049 function linkIntersections(from, to) { |
|
9050 var prev = from; |
|
9051 while (prev) { |
|
9052 if (prev === to) |
|
9053 return; |
|
9054 prev = prev._prev; |
|
9055 } |
|
9056 while (from._next && from._next !== to) |
|
9057 from = from._next; |
|
9058 if (!from._next) { |
|
9059 while (to._prev) |
|
9060 to = to._prev; |
|
9061 from._next = to; |
|
9062 to._prev = from; |
|
9063 } |
|
9064 } |
|
9065 |
|
9066 function divideLocations(locations) { |
|
9067 var tMin = 4e-7, |
|
9068 tMax = 1 - tMin, |
|
9069 noHandles = false, |
|
9070 clearSegments = [], |
|
9071 prevCurve, |
|
9072 prevT; |
|
9073 |
|
9074 for (var i = locations.length - 1; i >= 0; i--) { |
|
9075 var loc = locations[i], |
|
9076 curve = loc._curve, |
|
9077 t = loc._parameter, |
|
9078 origT = t; |
|
9079 if (curve !== prevCurve) { |
|
9080 noHandles = !curve.hasHandles(); |
|
9081 } else if (prevT > 0) { |
|
9082 t /= prevT; |
|
9083 } |
|
9084 var segment; |
|
9085 if (t < tMin) { |
|
9086 segment = curve._segment1; |
|
9087 } else if (t > tMax) { |
|
9088 segment = curve._segment2; |
|
9089 } else { |
|
9090 segment = curve.divide(t, true, true)._segment1; |
|
9091 if (noHandles) |
|
9092 clearSegments.push(segment); |
|
9093 } |
|
9094 loc._setSegment(segment); |
|
9095 var inter = segment._intersection, |
|
9096 dest = loc._intersection; |
|
9097 if (inter) { |
|
9098 linkIntersections(inter, dest); |
|
9099 var other = inter; |
|
9100 while (other) { |
|
9101 linkIntersections(other._intersection, inter); |
|
9102 other = other._next; |
|
8699 } |
9103 } |
8700 } |
|
8701 var winding = Math.round(windingSum / 3); |
|
8702 for (var j = chain.length - 1; j >= 0; j--) |
|
8703 chain[j].segment._winding = winding; |
|
8704 } |
|
8705 var result = new CompoundPath(Item.NO_INSERT); |
|
8706 result.insertAbove(path1); |
|
8707 result.addChildren(tracePaths(segments, operator), true); |
|
8708 result = result.reduce(); |
|
8709 result.setStyle(path1._style); |
|
8710 return result; |
|
8711 } |
|
8712 |
|
8713 function splitPath(intersections) { |
|
8714 var tMin = 0.000001, |
|
8715 tMax = 1 - tMin, |
|
8716 linearHandles; |
|
8717 |
|
8718 function resetLinear() { |
|
8719 for (var i = 0, l = linearHandles.length; i < l; i++) |
|
8720 linearHandles[i].set(0, 0); |
|
8721 } |
|
8722 |
|
8723 for (var i = intersections.length - 1, curve, prev; i >= 0; i--) { |
|
8724 var loc = intersections[i], |
|
8725 t = loc._parameter; |
|
8726 if (prev && prev._curve === loc._curve && prev._parameter > 0) { |
|
8727 t /= prev._parameter; |
|
8728 } else { |
9104 } else { |
8729 curve = loc._curve; |
9105 segment._intersection = dest; |
8730 if (linearHandles) |
9106 } |
8731 resetLinear(); |
9107 prevCurve = curve; |
8732 linearHandles = curve.isLinear() ? [ |
9108 prevT = origT; |
8733 curve._segment1._handleOut, |
9109 } |
8734 curve._segment2._handleIn |
9110 for (var i = 0, l = clearSegments.length; i < l; i++) { |
8735 ] : null; |
9111 clearSegments[i].clearHandles(); |
8736 } |
9112 } |
8737 var newCurve, |
|
8738 segment; |
|
8739 if (newCurve = curve.divide(t, true, true)) { |
|
8740 segment = newCurve._segment1; |
|
8741 curve = newCurve.getPrevious(); |
|
8742 if (linearHandles) |
|
8743 linearHandles.push(segment._handleOut, segment._handleIn); |
|
8744 } else { |
|
8745 segment = t < tMin |
|
8746 ? curve._segment1 |
|
8747 : t > tMax |
|
8748 ? curve._segment2 |
|
8749 : curve.getPartLength(0, t) < curve.getPartLength(t, 1) |
|
8750 ? curve._segment1 |
|
8751 : curve._segment2; |
|
8752 } |
|
8753 segment._intersection = loc.getIntersection(); |
|
8754 loc._segment = segment; |
|
8755 prev = loc; |
|
8756 } |
|
8757 if (linearHandles) |
|
8758 resetLinear(); |
|
8759 } |
9113 } |
8760 |
9114 |
8761 function getWinding(point, curves, horizontal, testContains) { |
9115 function getWinding(point, curves, horizontal, testContains) { |
8762 var tolerance = 0.000001, |
9116 var epsilon = 2e-7, |
8763 tMin = tolerance, |
9117 tMin = 4e-7, |
8764 tMax = 1 - tMin, |
9118 tMax = 1 - tMin, |
8765 px = point.x, |
9119 px = point.x, |
8766 py = point.y, |
9120 py = point.y, |
8767 windLeft = 0, |
9121 windLeft = 0, |
8768 windRight = 0, |
9122 windRight = 0, |
8769 roots = [], |
9123 roots = [], |
8770 abs = Math.abs; |
9124 abs = Math.abs; |
8771 if (horizontal) { |
9125 if (horizontal) { |
8772 var yTop = -Infinity, |
9126 var yTop = -Infinity, |
8773 yBottom = Infinity, |
9127 yBottom = Infinity, |
8774 yBefore = py - tolerance, |
9128 yBefore = py - epsilon, |
8775 yAfter = py + tolerance; |
9129 yAfter = py + epsilon; |
8776 for (var i = 0, l = curves.length; i < l; i++) { |
9130 for (var i = 0, l = curves.length; i < l; i++) { |
8777 var values = curves[i].values; |
9131 var values = curves[i].values; |
8778 if (Curve.solveCubic(values, 0, px, roots, 0, 1) > 0) { |
9132 if (Curve.solveCubic(values, 0, px, roots, 0, 1) > 0) { |
8779 for (var j = roots.length - 1; j >= 0; j--) { |
9133 for (var j = roots.length - 1; j >= 0; j--) { |
8780 var y = Curve.getPoint(values, roots[j]).y; |
9134 var y = Curve.getPoint(values, roots[j]).y; |
8787 } |
9141 } |
8788 } |
9142 } |
8789 yTop = (yTop + py) / 2; |
9143 yTop = (yTop + py) / 2; |
8790 yBottom = (yBottom + py) / 2; |
9144 yBottom = (yBottom + py) / 2; |
8791 if (yTop > -Infinity) |
9145 if (yTop > -Infinity) |
8792 windLeft = getWinding(new Point(px, yTop), curves); |
9146 windLeft = getWinding(new Point(px, yTop), curves, false, |
9147 testContains); |
|
8793 if (yBottom < Infinity) |
9148 if (yBottom < Infinity) |
8794 windRight = getWinding(new Point(px, yBottom), curves); |
9149 windRight = getWinding(new Point(px, yBottom), curves, false, |
9150 testContains); |
|
8795 } else { |
9151 } else { |
8796 var xBefore = px - tolerance, |
9152 var xBefore = px - epsilon, |
8797 xAfter = px + tolerance; |
9153 xAfter = px + epsilon; |
8798 var startCounted = false, |
9154 var startCounted = false, |
8799 prevCurve, |
9155 prevCurve, |
8800 prevT; |
9156 prevT; |
8801 for (var i = 0, l = curves.length; i < l; i++) { |
9157 for (var i = 0, l = curves.length; i < l; i++) { |
8802 var curve = curves[i], |
9158 var curve = curves[i], |
8812 || t < tMin && prevT > tMax |
9168 || t < tMin && prevT > tMax |
8813 && curve.previous === prevCurve)) { |
9169 && curve.previous === prevCurve)) { |
8814 var x = Curve.getPoint(values, t).x, |
9170 var x = Curve.getPoint(values, t).x, |
8815 slope = Curve.getTangent(values, t).y, |
9171 slope = Curve.getTangent(values, t).y, |
8816 counted = false; |
9172 counted = false; |
8817 if (Numerical.isZero(slope) && !Curve.isLinear(values) |
9173 if (Numerical.isZero(slope) && !Curve.isStraight(values) |
8818 || t < tMin && slope * Curve.getTangent( |
9174 || t < tMin && slope * Curve.getTangent( |
8819 curve.previous.values, 1).y < 0 |
9175 curve.previous.values, 1).y < 0 |
8820 || t > tMax && slope * Curve.getTangent( |
9176 || t > tMax && slope * Curve.getTangent( |
8821 curve.next.values, 0).y < 0) { |
9177 curve.next.values, 0).y < 0) { |
8822 if (testContains && x >= xBefore && x <= xAfter) { |
9178 if (testContains && x >= xBefore && x <= xAfter) { |
8840 } |
9196 } |
8841 } |
9197 } |
8842 return Math.max(abs(windLeft), abs(windRight)); |
9198 return Math.max(abs(windLeft), abs(windRight)); |
8843 } |
9199 } |
8844 |
9200 |
8845 function tracePaths(segments, operator, selfOp) { |
9201 function propagateWinding(segment, path1, path2, monoCurves, operation) { |
9202 var epsilon = 2e-7, |
|
9203 chain = [], |
|
9204 start = segment, |
|
9205 totalLength = 0, |
|
9206 windingSum = 0; |
|
9207 do { |
|
9208 var curve = segment.getCurve(), |
|
9209 length = curve.getLength(); |
|
9210 chain.push({ segment: segment, curve: curve, length: length }); |
|
9211 totalLength += length; |
|
9212 segment = segment.getNext(); |
|
9213 } while (segment && !segment._intersection && segment !== start); |
|
9214 for (var i = 0; i < 3; i++) { |
|
9215 var length = totalLength * (i + 1) / 4; |
|
9216 for (var k = 0, m = chain.length; k < m; k++) { |
|
9217 var node = chain[k], |
|
9218 curveLength = node.length; |
|
9219 if (length <= curveLength) { |
|
9220 if (length < epsilon || curveLength - length < epsilon) |
|
9221 length = curveLength / 2; |
|
9222 var curve = node.curve, |
|
9223 path = curve._path, |
|
9224 parent = path._parent, |
|
9225 pt = curve.getPointAt(length), |
|
9226 hor = curve.isHorizontal(); |
|
9227 if (parent instanceof CompoundPath) |
|
9228 path = parent; |
|
9229 windingSum += operation === 'subtract' && path2 |
|
9230 && (path === path1 && path2._getWinding(pt, hor) |
|
9231 || path === path2 && !path1._getWinding(pt, hor)) |
|
9232 ? 0 |
|
9233 : getWinding(pt, monoCurves, hor); |
|
9234 break; |
|
9235 } |
|
9236 length -= curveLength; |
|
9237 } |
|
9238 } |
|
9239 var winding = Math.round(windingSum / 3); |
|
9240 for (var j = chain.length - 1; j >= 0; j--) |
|
9241 chain[j].segment._winding = winding; |
|
9242 } |
|
9243 |
|
9244 function tracePaths(segments, operation) { |
|
8846 var paths = [], |
9245 var paths = [], |
8847 tMin = 0.000001, |
9246 start, |
8848 tMax = 1 - tMin; |
9247 otherStart, |
8849 for (var i = 0, seg, startSeg, l = segments.length; i < l; i++) { |
9248 operator = operators[operation], |
8850 seg = startSeg = segments[i]; |
9249 overlapWinding = { |
8851 if (seg._visited || !operator(seg._winding)) |
9250 unite: { 1: 2 }, |
9251 intersect: { 2: 1 } |
|
9252 }[operation]; |
|
9253 |
|
9254 function isValid(seg, adjusted) { |
|
9255 if (seg._visited) |
|
9256 return false; |
|
9257 if (!operator) |
|
9258 return true; |
|
9259 var winding = seg._winding, |
|
9260 inter = seg._intersection; |
|
9261 if (inter && adjusted && overlapWinding && inter.isOverlap()) |
|
9262 winding = overlapWinding[winding] || winding; |
|
9263 return operator(winding); |
|
9264 } |
|
9265 |
|
9266 function isStart(seg) { |
|
9267 return seg === start || seg === otherStart; |
|
9268 } |
|
9269 |
|
9270 function findBestIntersection(inter, strict) { |
|
9271 if (!inter._next) |
|
9272 return inter; |
|
9273 while (inter) { |
|
9274 var seg = inter._segment, |
|
9275 nextSeg = seg.getNext(), |
|
9276 nextInter = nextSeg._intersection; |
|
9277 if (isStart(nextSeg) |
|
9278 || !seg._visited && !nextSeg._visited |
|
9279 && (!operator |
|
9280 || (!strict || isValid(seg)) |
|
9281 && (!(strict && nextInter && nextInter.isOverlap()) |
|
9282 && isValid(nextSeg) |
|
9283 || !strict && nextInter |
|
9284 && isValid(nextInter._segment)) |
|
9285 )) |
|
9286 return inter; |
|
9287 inter = inter._next; |
|
9288 } |
|
9289 return null; |
|
9290 } |
|
9291 |
|
9292 function findStartSegment(inter, next) { |
|
9293 while (inter) { |
|
9294 var seg = inter._segment; |
|
9295 if (isStart(seg)) |
|
9296 return seg; |
|
9297 inter = inter[next ? '_next' : '_prev']; |
|
9298 } |
|
9299 } |
|
9300 |
|
9301 for (var i = 0, l = segments.length; i < l; i++) { |
|
9302 var seg = segments[i], |
|
9303 path = null, |
|
9304 finished = false; |
|
9305 if (!isValid(seg, true)) |
|
8852 continue; |
9306 continue; |
8853 var path = new Path(Item.NO_INSERT), |
9307 start = otherStart = null; |
8854 inter = seg._intersection, |
9308 while (!finished) { |
8855 startInterSeg = inter && inter._segment, |
9309 var inter = seg._intersection, |
8856 added = false, |
9310 handleIn = path && seg._handleIn; |
8857 dir = 1; |
9311 inter = inter && (findBestIntersection(inter, true) |
8858 do { |
9312 || findBestIntersection(inter, false)) || inter; |
8859 var handleIn = dir > 0 ? seg._handleIn : seg._handleOut, |
9313 var other = inter && inter._segment; |
8860 handleOut = dir > 0 ? seg._handleOut : seg._handleIn, |
9314 if (other && isValid(other)) |
8861 interSeg; |
9315 seg = other; |
8862 if (added && (!operator(seg._winding) || selfOp) |
9316 if (seg._visited) { |
8863 && (inter = seg._intersection) |
9317 finished = isStart(seg); |
8864 && (interSeg = inter._segment) |
9318 if (!finished && inter) { |
8865 && interSeg !== startSeg) { |
9319 var found = findStartSegment(inter, true) |
8866 if (selfOp) { |
9320 || findStartSegment(inter, false); |
8867 seg._visited = interSeg._visited; |
9321 if (found) { |
8868 seg = interSeg; |
9322 seg = found; |
8869 dir = 1; |
9323 finished = true; |
8870 } else { |
|
8871 var c1 = seg.getCurve(); |
|
8872 if (dir > 0) |
|
8873 c1 = c1.getPrevious(); |
|
8874 var t1 = c1.getTangentAt(dir < 1 ? tMin : tMax, true), |
|
8875 c4 = interSeg.getCurve(), |
|
8876 c3 = c4.getPrevious(), |
|
8877 t3 = c3.getTangentAt(tMax, true), |
|
8878 t4 = c4.getTangentAt(tMin, true), |
|
8879 w3 = t1.cross(t3), |
|
8880 w4 = t1.cross(t4); |
|
8881 if (w3 * w4 !== 0) { |
|
8882 var curve = w3 < w4 ? c3 : c4, |
|
8883 nextCurve = operator(curve._segment1._winding) |
|
8884 ? curve |
|
8885 : w3 < w4 ? c4 : c3, |
|
8886 nextSeg = nextCurve._segment1; |
|
8887 dir = nextCurve === c3 ? -1 : 1; |
|
8888 if (nextSeg._visited && seg._path !== nextSeg._path |
|
8889 || !operator(nextSeg._winding)) { |
|
8890 dir = 1; |
|
8891 } else { |
|
8892 seg._visited = interSeg._visited; |
|
8893 seg = interSeg; |
|
8894 if (nextSeg._visited) |
|
8895 dir = 1; |
|
8896 } |
|
8897 } else { |
|
8898 dir = 1; |
|
8899 } |
9324 } |
8900 } |
9325 } |
8901 handleOut = dir > 0 ? seg._handleOut : seg._handleIn; |
9326 break; |
8902 } |
9327 } |
8903 path.add(new Segment(seg._point, added && handleIn, handleOut)); |
9328 if (!path) { |
8904 added = true; |
9329 path = new Path(Item.NO_INSERT); |
9330 start = seg; |
|
9331 otherStart = other; |
|
9332 } |
|
9333 path.add(new Segment(seg._point, handleIn, seg._handleOut)); |
|
8905 seg._visited = true; |
9334 seg._visited = true; |
8906 seg = dir > 0 ? seg.getNext() : seg. getPrevious(); |
9335 seg = seg.getNext(); |
8907 } while (seg && !seg._visited |
9336 finished = isStart(seg); |
8908 && seg !== startSeg && seg !== startInterSeg |
9337 } |
8909 && (seg._intersection || operator(seg._winding))); |
9338 if (finished) { |
8910 if (seg && (seg === startSeg || seg === startInterSeg)) { |
9339 path.firstSegment.setHandleIn(seg._handleIn); |
8911 path.firstSegment.setHandleIn((seg === startInterSeg |
|
8912 ? startInterSeg : seg)._handleIn); |
|
8913 path.setClosed(true); |
9340 path.setClosed(true); |
8914 } else { |
9341 } else if (path) { |
8915 path.lastSegment._handleOut.set(0, 0); |
9342 console.error('Boolean operation resulted in open path', |
8916 } |
9343 'segments =', path._segments.length, |
8917 if (path._segments.length > |
9344 'length =', path.getLength()); |
8918 (path._closed ? path.isLinear() ? 2 : 0 : 1)) |
9345 path = null; |
9346 } |
|
9347 if (path && (path._segments.length > 8 |
|
9348 || !Numerical.isZero(path.getArea()))) { |
|
8919 paths.push(path); |
9349 paths.push(path); |
9350 path = null; |
|
9351 } |
|
8920 } |
9352 } |
8921 return paths; |
9353 return paths; |
8922 } |
9354 } |
8923 |
9355 |
8924 return { |
9356 return { |
8942 exclude: function(path) { |
9374 exclude: function(path) { |
8943 return computeBoolean(this, path, 'exclude'); |
9375 return computeBoolean(this, path, 'exclude'); |
8944 }, |
9376 }, |
8945 |
9377 |
8946 divide: function(path) { |
9378 divide: function(path) { |
8947 return new Group([this.subtract(path), this.intersect(path)]); |
9379 return finishBoolean(Group, |
9380 [this.subtract(path), this.intersect(path)], |
|
9381 this, path, true); |
|
9382 }, |
|
9383 |
|
9384 resolveCrossings: function() { |
|
9385 var crossings = this.getCrossings(); |
|
9386 if (!crossings.length) |
|
9387 return this; |
|
9388 divideLocations(CurveLocation.expand(crossings)); |
|
9389 var paths = this._children || [this], |
|
9390 segments = []; |
|
9391 for (var i = 0, l = paths.length; i < l; i++) { |
|
9392 segments.push.apply(segments, paths[i]._segments); |
|
9393 } |
|
9394 return finishBoolean(CompoundPath, tracePaths(segments), |
|
9395 this, null, false); |
|
8948 } |
9396 } |
8949 }; |
9397 }; |
8950 }); |
9398 }); |
8951 |
9399 |
8952 Path.inject({ |
9400 Path.inject({ |
8978 return; |
9426 return; |
8979 var y0 = v[1], |
9427 var y0 = v[1], |
8980 y1 = v[3], |
9428 y1 = v[3], |
8981 y2 = v[5], |
9429 y2 = v[5], |
8982 y3 = v[7]; |
9430 y3 = v[7]; |
8983 if (Curve.isLinear(v)) { |
9431 if (Curve.isStraight(v)) { |
8984 insertCurve(v); |
9432 insertCurve(v); |
8985 } else { |
9433 } else { |
8986 var a = 3 * (y1 - y2) - y0 + y3, |
9434 var a = 3 * (y1 - y2) - y0 + y3, |
8987 b = 2 * (y0 + y2) - 4 * y1, |
9435 b = 2 * (y0 + y2) - 4 * y1, |
8988 c = y1 - y0, |
9436 c = y1 - y0, |
8989 tolerance = 0.000001, |
9437 tMin = 4e-7, |
8990 roots = []; |
9438 tMax = 1 - tMin, |
8991 var count = Numerical.solveQuadratic(a, b, c, roots, tolerance, |
9439 roots = [], |
8992 1 - tolerance); |
9440 n = Numerical.solveQuadratic(a, b, c, roots, tMin, tMax); |
8993 if (count === 0) { |
9441 if (n === 0) { |
8994 insertCurve(v); |
9442 insertCurve(v); |
8995 } else { |
9443 } else { |
8996 roots.sort(); |
9444 roots.sort(); |
8997 var t = roots[0], |
9445 var t = roots[0], |
8998 parts = Curve.subdivide(v, t); |
9446 parts = Curve.subdivide(v, t); |
8999 insertCurve(parts[0]); |
9447 insertCurve(parts[0]); |
9000 if (count > 1) { |
9448 if (n > 1) { |
9001 t = (roots[1] - t) / (1 - t); |
9449 t = (roots[1] - t) / (1 - t); |
9002 parts = Curve.subdivide(parts[1], t); |
9450 parts = Curve.subdivide(parts[1], t); |
9003 insertCurve(parts[0]); |
9451 insertCurve(parts[0]); |
9004 } |
9452 } |
9005 insertCurve(parts[1]); |
9453 insertCurve(parts[1]); |
9110 } |
9558 } |
9111 |
9559 |
9112 function computeParts(curve, index, minT, maxT) { |
9560 function computeParts(curve, index, minT, maxT) { |
9113 if ((maxT - minT) > minDifference |
9561 if ((maxT - minT) > minDifference |
9114 && !Curve.isFlatEnough(curve, tolerance || 0.25)) { |
9562 && !Curve.isFlatEnough(curve, tolerance || 0.25)) { |
9115 var split = Curve.subdivide(curve), |
9563 var split = Curve.subdivide(curve, 0.5), |
9116 halfT = (minT + maxT) / 2; |
9564 halfT = (minT + maxT) / 2; |
9117 computeParts(split[0], index, minT, halfT); |
9565 computeParts(split[0], index, minT, halfT); |
9118 computeParts(split[1], index, halfT, maxT); |
9566 computeParts(split[1], index, halfT, maxT); |
9119 } else { |
9567 } else { |
9120 var x = curve[6] - curve[0], |
9568 var x = curve[6] - curve[0], |
9121 y = curve[7] - curve[1], |
9569 y = curve[7] - curve[1], |
9122 dist = Math.sqrt(x * x + y * y); |
9570 dist = Math.sqrt(x * x + y * y); |
9123 if (dist > 0.000001) { |
9571 if (dist > 1e-6) { |
9124 length += dist; |
9572 length += dist; |
9125 parts.push({ |
9573 parts.push({ |
9126 offset: length, |
9574 offset: length, |
9127 value: maxT, |
9575 value: maxT, |
9128 index: index |
9576 index: index |
9364 var pt = this.evaluate(3, curve, u), |
9812 var pt = this.evaluate(3, curve, u), |
9365 pt1 = this.evaluate(2, curve1, u), |
9813 pt1 = this.evaluate(2, curve1, u), |
9366 pt2 = this.evaluate(1, curve2, u), |
9814 pt2 = this.evaluate(1, curve2, u), |
9367 diff = pt.subtract(point), |
9815 diff = pt.subtract(point), |
9368 df = pt1.dot(pt1) + diff.dot(pt2); |
9816 df = pt1.dot(pt1) + diff.dot(pt2); |
9369 if (Math.abs(df) < 0.000001) |
9817 if (Math.abs(df) < 1e-6) |
9370 return u; |
9818 return u; |
9371 return u - diff.dot(pt1) / df; |
9819 return u - diff.dot(pt1) / df; |
9372 }, |
9820 }, |
9373 |
9821 |
9374 evaluate: function(degree, curve, t) { |
9822 evaluate: function(degree, curve, t) { |
9799 || 'radial' in arg |
10247 || 'radial' in arg |
9800 ? 'gradient' |
10248 ? 'gradient' |
9801 : 'gray' in arg |
10249 : 'gray' in arg |
9802 ? 'gray' |
10250 ? 'gray' |
9803 : 'rgb'; |
10251 : 'rgb'; |
9804 var properties = types[type]; |
10252 var properties = types[type], |
9805 parsers = componentParsers[type]; |
10253 parsers = componentParsers[type]; |
9806 this._components = components = []; |
10254 this._components = components = []; |
9807 for (var i = 0, l = properties.length; i < l; i++) { |
10255 for (var i = 0, l = properties.length; i < l; i++) { |
9808 var value = arg[properties[i]]; |
10256 var value = arg[properties[i]]; |
9809 if (value == null && i === 0 && type === 'gradient' |
10257 if (value == null && i === 0 && type === 'gradient' |
10001 var random = Math.random; |
10449 var random = Math.random; |
10002 return new Color(random(), random(), random()); |
10450 return new Color(random(), random(), random()); |
10003 } |
10451 } |
10004 } |
10452 } |
10005 }); |
10453 }); |
10006 }, new function() { |
10454 }, |
10455 new function() { |
|
10007 var operators = { |
10456 var operators = { |
10008 add: function(a, b) { |
10457 add: function(a, b) { |
10009 return a + b; |
10458 return a + b; |
10010 }, |
10459 }, |
10011 |
10460 |
10036 : null); |
10485 : null); |
10037 }; |
10486 }; |
10038 }, { |
10487 }, { |
10039 }); |
10488 }); |
10040 }); |
10489 }); |
10041 |
|
10042 Base.each(Color._types, function(properties, type) { |
|
10043 var ctor = this[Base.capitalize(type) + 'Color'] = function(arg) { |
|
10044 var argType = arg != null && typeof arg, |
|
10045 components = argType === 'object' && arg.length != null |
|
10046 ? arg |
|
10047 : argType === 'string' |
|
10048 ? null |
|
10049 : arguments; |
|
10050 return components |
|
10051 ? new Color(type, components) |
|
10052 : new Color(arg); |
|
10053 }; |
|
10054 if (type.length == 3) { |
|
10055 var acronym = type.toUpperCase(); |
|
10056 Color[acronym] = this[acronym + 'Color'] = ctor; |
|
10057 } |
|
10058 }, Base.exports); |
|
10059 |
10490 |
10060 var Gradient = Base.extend({ |
10491 var Gradient = Base.extend({ |
10061 _class: 'Gradient', |
10492 _class: 'Gradient', |
10062 |
10493 |
10063 initialize: function Gradient(stops, radial) { |
10494 initialize: function Gradient(stops, radial) { |
10679 this._animate = false; |
11110 this._animate = false; |
10680 this._frameItems = {}; |
11111 this._frameItems = {}; |
10681 return true; |
11112 return true; |
10682 }, |
11113 }, |
10683 |
11114 |
10684 _events: { |
11115 _events: Base.each(['onResize', 'onMouseDown', 'onMouseUp', 'onMouseMove'], |
10685 onFrame: { |
11116 function(name) { |
10686 install: function() { |
11117 this[name] = { |
10687 this.play(); |
11118 install: function(type) { |
10688 }, |
11119 this._installEvent(type); |
10689 |
11120 }, |
10690 uninstall: function() { |
11121 |
10691 this.pause(); |
11122 uninstall: function(type) { |
10692 } |
11123 this._uninstallEvent(type); |
10693 }, |
11124 } |
10694 |
11125 }; |
10695 onResize: {} |
11126 }, { |
10696 }, |
11127 onFrame: { |
11128 install: function() { |
|
11129 this.play(); |
|
11130 }, |
|
11131 |
|
11132 uninstall: function() { |
|
11133 this.pause(); |
|
11134 } |
|
11135 } |
|
11136 } |
|
11137 ), |
|
10697 |
11138 |
10698 _animate: false, |
11139 _animate: false, |
10699 _time: 0, |
11140 _time: 0, |
10700 _count: 0, |
11141 _count: 0, |
10701 |
11142 |
10886 if (typeof element === 'string') |
11327 if (typeof element === 'string') |
10887 element = document.getElementById(element); |
11328 element = document.getElementById(element); |
10888 return new CanvasView(project, element); |
11329 return new CanvasView(project, element); |
10889 } |
11330 } |
10890 } |
11331 } |
10891 }, new function() { |
11332 }, |
11333 new function() { |
|
10892 var tool, |
11334 var tool, |
10893 prevFocus, |
11335 prevFocus, |
10894 tempFocus, |
11336 tempFocus, |
10895 dragging = false; |
11337 dragging = false; |
10896 |
11338 |
11009 |
11451 |
11010 DomEvent.add(window, { |
11452 DomEvent.add(window, { |
11011 load: updateFocus |
11453 load: updateFocus |
11012 }); |
11454 }); |
11013 |
11455 |
11456 var mouseFlags = { |
|
11457 mousedown: { |
|
11458 mousedown: 1, |
|
11459 mousedrag: 1, |
|
11460 click: 1, |
|
11461 doubleclick: 1 |
|
11462 }, |
|
11463 mouseup: { |
|
11464 mouseup: 1, |
|
11465 mousedrag: 1, |
|
11466 click: 1, |
|
11467 doubleclick: 1 |
|
11468 }, |
|
11469 mousemove: { |
|
11470 mousedrag: 1, |
|
11471 mousemove: 1, |
|
11472 mouseenter: 1, |
|
11473 mouseleave: 1 |
|
11474 } |
|
11475 }; |
|
11476 |
|
11014 return { |
11477 return { |
11015 _viewEvents: viewEvents, |
11478 _viewEvents: viewEvents, |
11016 |
11479 |
11017 _handleEvent: function() {}, |
11480 _handleEvent: function() {}, |
11481 |
|
11482 _installEvent: function(type) { |
|
11483 var counters = this._eventCounters; |
|
11484 if (counters) { |
|
11485 for (var key in mouseFlags) { |
|
11486 counters[key] = (counters[key] || 0) |
|
11487 + (mouseFlags[key][type] || 0); |
|
11488 } |
|
11489 } |
|
11490 }, |
|
11491 |
|
11492 _uninstallEvent: function(type) { |
|
11493 var counters = this._eventCounters; |
|
11494 if (counters) { |
|
11495 for (var key in mouseFlags) |
|
11496 counters[key] -= mouseFlags[key][type] || 0; |
|
11497 } |
|
11498 }, |
|
11018 |
11499 |
11019 statics: { |
11500 statics: { |
11020 updateFocus: updateFocus |
11501 updateFocus: updateFocus |
11021 } |
11502 } |
11022 }; |
11503 }; |
11103 ctx.clearRect(0, 0, size.width + 1, size.height + 1); |
11584 ctx.clearRect(0, 0, size.width + 1, size.height + 1); |
11104 project.draw(ctx, this._matrix, this._pixelRatio); |
11585 project.draw(ctx, this._matrix, this._pixelRatio); |
11105 project._needsUpdate = false; |
11586 project._needsUpdate = false; |
11106 return true; |
11587 return true; |
11107 } |
11588 } |
11108 }, new function() { |
11589 }, |
11109 |
11590 new function() { |
11110 var downPoint, |
11591 var downPoint, |
11111 lastPoint, |
11592 lastPoint, |
11112 overPoint, |
11593 overPoint, |
11113 downItem, |
11594 downItem, |
11114 lastItem, |
11595 lastItem, |
11549 return this._minDistance == this._maxDistance |
12030 return this._minDistance == this._maxDistance |
11550 ? this._minDistance : null; |
12031 ? this._minDistance : null; |
11551 }, |
12032 }, |
11552 |
12033 |
11553 setFixedDistance: function(distance) { |
12034 setFixedDistance: function(distance) { |
11554 this._minDistance = distance; |
12035 this._minDistance = this._maxDistance = distance; |
11555 this._maxDistance = distance; |
|
11556 }, |
12036 }, |
11557 |
12037 |
11558 _updateEvent: function(type, point, minDistance, maxDistance, start, |
12038 _updateEvent: function(type, point, minDistance, maxDistance, start, |
11559 needsChange, matchMaxDistance) { |
12039 needsChange, matchMaxDistance) { |
11560 if (!start) { |
12040 if (!start) { |