4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-draw-Draw'>/**
19 </span> * @class Ext.draw.Draw
20 * Base Drawing class. Provides base drawing functions.
23 Ext.define('Ext.draw.Draw', {
24 /* Begin Definitions */
28 requires: ['Ext.draw.Color'],
32 pathToStringRE: /,?([achlmqrstvxz]),?/gi,
33 pathCommandRE: /([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,
34 pathValuesRE: /(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,
36 radian: Math.PI / 180,
39 along: "along",
41 "clip-rect": "csv",
44 fill: "color",
45 "fill-opacity": null,
46 "font-size": null,
49 path: "path",
51 rotation: "csv",
54 scale: "csv",
55 stroke: "color",
56 "stroke-opacity": null,
57 "stroke-width": null,
58 translation: "csv",
64 is: function(o, type) {
65 type = String(type).toLowerCase();
66 return (type == "object" && o === Object(o)) ||
67 (type == "undefined" && typeof o == type) ||
68 (type == "null" && o === null) ||
69 (type == "array" && Array.isArray && Array.isArray(o)) ||
70 (Object.prototype.toString.call(o).toLowerCase().slice(8, -1)) == type;
73 ellipsePath: function(sprite) {
74 var attr = sprite.attr;
75 return Ext.String.format("M{0},{1}A{2},{3},0,1,1,{0},{4}A{2},{3},0,1,1,{0},{1}z", attr.x, attr.y - attr.ry, attr.rx, attr.ry, attr.y + attr.ry);
78 rectPath: function(sprite) {
79 var attr = sprite.attr;
81 return Ext.String.format("M{0},{1}l{2},0a{3},{3},0,0,1,{3},{3}l0,{5}a{3},{3},0,0,1,{4},{3}l{6},0a{3},{3},0,0,1,{4},{4}l0,{7}a{3},{3},0,0,1,{3},{4}z", attr.x + attr.radius, attr.y, attr.width - attr.radius * 2, attr.radius, -attr.radius, attr.height - attr.radius * 2, attr.radius * 2 - attr.width, attr.radius * 2 - attr.height);
84 return Ext.String.format("M{0},{1}l{2},0,0,{3},{4},0z", attr.x, attr.y, attr.width, attr.height, -attr.width);
88 // To be deprecated, converts itself (an arrayPath) to a proper SVG path string
89 path2string: function () {
90 return this.join(",").replace(Ext.draw.Draw.pathToStringRE, "$1");
93 // Convert the passed arrayPath to a proper SVG path string (d attribute)
94 pathToString: function(arrayPath) {
95 return arrayPath.join(",").replace(Ext.draw.Draw.pathToStringRE, "$1");
98 parsePathString: function (pathString) {
102 var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0},
105 if (me.is(pathString, "array") && me.is(pathString[0], "array")) { // rough assumption
106 data = me.pathClone(pathString);
109 String(pathString).replace(me.pathCommandRE, function (a, b, c) {
111 name = b.toLowerCase();
112 c.replace(me.pathValuesRE, function (a, b) {
113 b && params.push(+b);
115 if (name == "m" && params.length > 2) {
116 data.push([b].concat(Ext.Array.splice(params, 0, 2)));
117 name = "l";
118 b = (b == "m") ? "l" : "L";
120 while (params.length >= paramCounts[name]) {
121 data.push([b].concat(Ext.Array.splice(params, 0, paramCounts[name])));
122 if (!paramCounts[name]) {
128 data.toString = me.path2string;
132 mapPath: function (path, matrix) {
136 var x, y, i, ii, j, jj, pathi;
137 path = this.path2curve(path);
138 for (i = 0, ii = path.length; i < ii; i++) {
140 for (j = 1, jj = pathi.length; j < jj-1; j += 2) {
141 x = matrix.x(pathi[j], pathi[j + 1]);
142 y = matrix.y(pathi[j], pathi[j + 1]);
150 pathClone: function(pathArray) {
153 if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { // rough assumption
154 pathArray = this.parsePathString(pathArray);
156 for (i = 0, ii = pathArray.length; i < ii; i++) {
158 for (j = 0, jj = pathArray[i].length; j < jj; j++) {
159 res[i][j] = pathArray[i][j];
162 res.toString = this.path2string;
166 pathToAbsolute: function (pathArray) {
167 if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { // rough assumption
168 pathArray = this.parsePathString(pathArray);
176 ln = pathArray.length,
177 r, pathSegment, j, ln2;
178 // MoveTo initial x/y position
179 if (ln && pathArray[0][0] == "M") {
180 x = +pathArray[0][1];
181 y = +pathArray[0][2];
185 res[0] = ["M", x, y];
187 for (; i < ln; i++) {
189 pathSegment = pathArray[i];
190 if (pathSegment[0] != pathSegment[0].toUpperCase()) {
191 r[0] = pathSegment[0].toUpperCase();
195 r[1] = pathSegment[1];
196 r[2] = pathSegment[2];
197 r[3] = pathSegment[3];
198 r[4] = pathSegment[4];
199 r[5] = pathSegment[5];
200 r[6] = +(pathSegment[6] + x);
201 r[7] = +(pathSegment[7] + y);
205 r[1] = +pathSegment[1] + y;
209 r[1] = +pathSegment[1] + x;
213 mx = +pathSegment[1] + x;
214 my = +pathSegment[2] + y;
217 ln2 = pathSegment.length;
218 for (; j < ln2; j++) {
219 r[j] = +pathSegment[j] + ((j % 2) ? x : y);
225 ln2 = pathSegment.length;
226 for (; j < ln2; j++) {
227 res[i][j] = pathSegment[j];
246 pathSegment = res[i];
247 ln2 = pathSegment.length;
248 mx = pathSegment[ln2 - 2];
249 my = pathSegment[ln2 - 1];
251 pathSegment = res[i];
252 ln2 = pathSegment.length;
253 x = pathSegment[ln2 - 2];
254 y = pathSegment[ln2 - 1];
257 res.toString = this.path2string;
262 pathToRelative: function (pathArray) {
263 if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) {
264 pathArray = this.parsePathString(pathArray);
272 if (pathArray[0][0] == "M") {
278 res.push(["M", x, y]);
280 for (var i = start, ii = pathArray.length; i < ii; i++) {
283 if (pa[0] != pa[0].toLowerCase()) {
284 r[0] = pa[0].toLowerCase();
292 r[6] = +(pa[6] - x).toFixed(3);
293 r[7] = +(pa[7] - y).toFixed(3);
296 r[1] = +(pa[1] - y).toFixed(3);
302 for (var j = 1, jj = pa.length; j < jj; j++) {
303 r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
308 if (pa[0] == "m") {
312 for (var k = 0, kk = pa.length; k < kk; k++) {
316 var len = res[i].length;
323 x += +res[i][len - 1];
326 y += +res[i][len - 1];
329 x += +res[i][len - 2];
330 y += +res[i][len - 1];
333 res.toString = this.path2string;
337 // Returns a path converted to a set of curveto commands
338 path2curve: function (path) {
340 points = me.pathToAbsolute(path),
342 attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
343 i, seg, segLn, point;
345 for (i = 0; i < ln; i++) {
346 points[i] = me.command2curve(points[i], attrs);
347 if (points[i].length > 7) {
350 while (point.length) {
351 Ext.Array.splice(points, i++, 0, ["C"].concat(Ext.Array.splice(point, 0, 6)));
353 Ext.Array.erase(points, i, 1);
358 attrs.x = seg[segLn - 2];
359 attrs.y = seg[segLn - 1];
360 attrs.bx = parseFloat(seg[segLn - 4]) || attrs.x;
361 attrs.by = parseFloat(seg[segLn - 3]) || attrs.y;
366 interpolatePaths: function (path, path2) {
368 p = me.pathToAbsolute(path),
369 p2 = me.pathToAbsolute(path2),
370 attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
371 attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
372 fixArc = function (pp, i) {
373 if (pp[i].length > 7) {
377 Ext.Array.splice(pp, i++, 0, ["C"].concat(Ext.Array.splice(pi, 0, 6)));
379 Ext.Array.erase(pp, i, 1);
380 ii = Math.max(p.length, p2.length || 0);
383 fixM = function (path1, path2, a1, a2, i) {
384 if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
385 Ext.Array.splice(path2, i, 0, ["M", a2.x, a2.y]);
390 ii = Math.max(p.length, p2.length || 0);
393 for (var i = 0, ii = Math.max(p.length, p2.length || 0); i < ii; i++) {
394 p[i] = me.command2curve(p[i], attrs);
396 (p2[i] = me.command2curve(p2[i], attrs2));
398 fixM(p, p2, attrs, attrs2, i);
399 fixM(p2, p, attrs2, attrs, i);
403 seg2len = seg2.length;
404 attrs.x = seg[seglen - 2];
405 attrs.y = seg[seglen - 1];
406 attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x;
407 attrs.by = parseFloat(seg[seglen - 3]) || attrs.y;
408 attrs2.bx = (parseFloat(seg2[seg2len - 4]) || attrs2.x);
409 attrs2.by = (parseFloat(seg2[seg2len - 3]) || attrs2.y);
410 attrs2.x = seg2[seg2len - 2];
411 attrs2.y = seg2[seg2len - 1];
416 //Returns any path command as a curveto command based on the attrs passed
417 command2curve: function (pathCommand, d) {
420 return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
422 if (pathCommand[0] != "T" && pathCommand[0] != "Q") {
425 switch (pathCommand[0]) {
427 d.X = pathCommand[1];
428 d.Y = pathCommand[2];
431 pathCommand = ["C"].concat(me.arc2curve.apply(me, [d.x, d.y].concat(pathCommand.slice(1))));
434 pathCommand = ["C", d.x + (d.x - (d.bx || d.x)), d.y + (d.y - (d.by || d.y))].concat(pathCommand.slice(1));
437 d.qx = d.x + (d.x - (d.qx || d.x));
438 d.qy = d.y + (d.y - (d.qy || d.y));
439 pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, d.qx, d.qy, pathCommand[1], pathCommand[2]));
442 d.qx = pathCommand[1];
443 d.qy = pathCommand[2];
444 pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[3], pathCommand[4]));
447 pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[1], pathCommand[2]);
450 pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], d.y, pathCommand[1], d.y);
453 pathCommand = ["C"].concat(d.x, d.y, d.x, pathCommand[1], d.x, pathCommand[1]);
456 pathCommand = ["C"].concat(d.x, d.y, d.X, d.Y, d.X, d.Y);
462 quadratic2curve: function (x1, y1, ax, ay, x2, y2) {
475 rotate: function (x, y, rad) {
476 var cos = Math.cos(rad),
478 X = x * cos - y * sin,
479 Y = x * sin + y * cos;
483 arc2curve: function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
484 // for more information of where this Math came from visit:
485 // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
489 _120 = PI * 120 / 180,
490 rad = radian * (+angle || 0),
498 xy, cos, sin, x, y, h, rx2, ry2, k, cx, cy, f1, f2, df, c1, s1, c2, s2,
499 t, hx, hy, m1, m2, m3, m4, newres, i, ln, f2old, x2old, y2old;
501 xy = me.rotate(x1, y1, -rad);
504 xy = me.rotate(x2, y2, -rad);
507 cos = mcos(radian * angle);
508 sin = msin(radian * angle);
511 h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
519 k = (large_arc_flag == sweep_flag ? -1 : 1) *
520 msqrt(mabs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x)));
521 cx = k * rx * y / ry + (x1 + x2) / 2;
522 cy = k * -ry * x / rx + (y1 + y2) / 2;
523 f1 = masin(((y1 - cy) / ry).toFixed(7));
524 f2 = masin(((y2 - cy) / ry).toFixed(7));
526 f1 = x1 < cx ? PI - f1 : f1;
527 f2 = x2 < cx ? PI - f2 : f2;
534 if (sweep_flag && f1 > f2) {
537 if (!sweep_flag && f2 > f1) {
548 if (mabs(df) > _120) {
552 f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
553 x2 = cx + rx * mcos(f2);
554 y2 = cy + ry * msin(f2);
555 res = me.arc2curve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
562 t = math.tan(df / 4);
566 m2 = [x1 + hx * s1, y1 - hy * c1];
567 m3 = [x2 + hx * s2, y2 - hy * c2];
569 m2[0] = 2 * m1[0] - m2[0];
570 m2[1] = 2 * m1[1] - m2[1];
572 return [m2, m3, m4].concat(res);
575 res = [m2, m3, m4].concat(res).join().split(",");
578 for (i = 0; i < ln; i++) {
579 newres[i] = i % 2 ? me.rotate(res[i - 1], res[i], rad).y : me.rotate(res[i], res[i + 1], rad).x;
586 rotateAndTranslatePath: function (sprite) {
587 var alpha = sprite.rotation.degrees,
588 cx = sprite.rotation.x,
589 cy = sprite.rotation.y,
590 dx = sprite.translation.x,
591 dy = sprite.translation.y,
598 if (!alpha && !dx && !dy) {
599 return this.pathToAbsolute(sprite.attr.path);
603 path = this.pathToAbsolute(sprite.attr.path);
604 for (i = path.length; i--;) {
605 p = res[i] = path[i].slice();
606 if (p[0] == "A") {
607 xy = this.rotatePoint(p[6], p[7], alpha, cx, cy);
612 while (p[j + 1] != null) {
613 xy = this.rotatePoint(p[j], p[j + 1], alpha, cx, cy);
615 p[j + 1] = xy.y + dy;
624 rotatePoint: function (x, y, alpha, cx, cy) {
635 alpha = alpha * this.radian;
636 var cos = Math.cos(alpha),
637 sin = Math.sin(alpha);
639 x: x * cos - y * sin + cx,
640 y: x * sin + y * cos + cy
644 pathDimensions: function (path) {
645 if (!path || !(path + "")) {
646 return {x: 0, y: 0, width: 0, height: 0};
648 path = this.path2curve(path);
656 for (; i < ln; i++) {
658 if (p[0] == "M") {
665 dim = this.curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
666 X = X.concat(dim.min.x, dim.max.x);
667 Y = Y.concat(dim.min.y, dim.max.y);
672 xmin = Math.min.apply(0, X);
673 ymin = Math.min.apply(0, Y);
678 width: Math.max.apply(0, X) - xmin,
679 height: Math.max.apply(0, Y) - ymin
683 intersectInside: function(path, cp1, cp2) {
684 return (cp2[0] - cp1[0]) * (path[1] - cp1[1]) > (cp2[1] - cp1[1]) * (path[0] - cp1[0]);
687 intersectIntersection: function(s, e, cp1, cp2) {
689 dcx = cp1[0] - cp2[0],
690 dcy = cp1[1] - cp2[1],
693 n1 = cp1[0] * cp2[1] - cp1[1] * cp2[0],
694 n2 = s[0] * e[1] - s[1] * e[0],
695 n3 = 1 / (dcx * dpy - dcy * dpx);
697 p[0] = (n1 * dpx - n2 * dcx) * n3;
698 p[1] = (n1 * dpy - n2 * dcy) * n3;
702 intersect: function(subjectPolygon, clipPolygon) {
705 ln = clipPolygon.length,
706 cp1 = clipPolygon[ln - 1],
707 outputList = subjectPolygon,
708 cp2, s, e, point, ln2, inputList, j;
709 for (; i < ln; ++i) {
710 cp2 = clipPolygon[i];
711 inputList = outputList;
713 s = inputList[inputList.length - 1];
715 ln2 = inputList.length;
716 for (; j < ln2; j++) {
718 if (me.intersectInside(e, cp1, cp2)) {
719 if (!me.intersectInside(s, cp1, cp2)) {
720 outputList.push(me.intersectIntersection(s, e, cp1, cp2));
724 else if (me.intersectInside(s, cp1, cp2)) {
725 outputList.push(me.intersectIntersection(s, e, cp1, cp2));
734 curveDim: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
735 var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
736 b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
738 t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a,
739 t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a,
743 if (Math.abs(t1) > 1e12) {
746 if (Math.abs(t2) > 1e12) {
749 if (t1 > 0 && t1 < 1) {
750 dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
754 if (t2 > 0 && t2 < 1) {
755 dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
759 a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
760 b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
762 t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a;
763 t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a;
764 if (Math.abs(t1) > 1e12) {
767 if (Math.abs(t2) > 1e12) {
770 if (t1 > 0 && t1 < 1) {
771 dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
775 if (t2 > 0 && t2 < 1) {
776 dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
781 min: {x: Math.min.apply(0, x), y: Math.min.apply(0, y)},
782 max: {x: Math.max.apply(0, x), y: Math.max.apply(0, y)}
786 <span id='Ext-draw-Draw-method-getAnchors'> /**
789 * Calculates bezier curve control anchor points for a particular point in a path, with a
790 * smoothing curve applied. The smoothness of the curve is controlled by the 'value' parameter.
791 * Note that this algorithm assumes that the line being smoothed is normalized going from left
792 * to right; it makes special adjustments assuming this orientation.
794 * @param {Number} prevX X coordinate of the previous point in the path
795 * @param {Number} prevY Y coordinate of the previous point in the path
796 * @param {Number} curX X coordinate of the current point in the path
797 * @param {Number} curY Y coordinate of the current point in the path
798 * @param {Number} nextX X coordinate of the next point in the path
799 * @param {Number} nextY Y coordinate of the next point in the path
800 * @param {Number} value A value to control the smoothness of the curve; this is used to
801 * divide the distance between points, so a value of 2 corresponds to
802 * half the distance between points (a very smooth line) while higher values
803 * result in less smooth curves. Defaults to 4.
804 * @return {Object} Object containing x1, y1, x2, y2 bezier control anchor points; x1 and y1
805 * are the control point for the curve toward the previous path point, and
806 * x2 and y2 are the control point for the curve toward the next path point.
808 getAnchors: function (prevX, prevY, curX, curY, nextX, nextY, value) {
817 control1Length, control2Length, control1Angle, control2Angle,
818 control1X, control1Y, control2X, control2Y, alpha;
820 // Find the length of each control anchor line, by dividing the horizontal distance
821 // between points by the value parameter.
822 control1Length = (curX - prevX) / value;
823 control2Length = (nextX - curX) / value;
825 // Determine the angle of each control anchor line. If the middle point is a vertical
826 // turnaround then we force it to a flat horizontal angle to prevent the curve from
827 // dipping above or below the middle point. Otherwise we use an angle that points
828 // toward the previous/next target point.
829 if ((curY >= prevY && curY >= nextY) || (curY <= prevY && curY <= nextY)) {
830 control1Angle = control2Angle = halfPI;
832 control1Angle = atan((curX - prevX) / abs(curY - prevY));
833 if (prevY < curY) {
834 control1Angle = PI - control1Angle;
836 control2Angle = atan((nextX - curX) / abs(curY - nextY));
837 if (nextY < curY) {
838 control2Angle = PI - control2Angle;
842 // Adjust the calculated angles so they point away from each other on the same line
843 alpha = halfPI - ((control1Angle + control2Angle) % (PI * 2)) / 2;
844 if (alpha > halfPI) {
847 control1Angle += alpha;
848 control2Angle += alpha;
850 // Find the control anchor points from the angles and length
851 control1X = curX - control1Length * sin(control1Angle);
852 control1Y = curY + control1Length * cos(control1Angle);
853 control2X = curX + control2Length * sin(control2Angle);
854 control2Y = curY + control2Length * cos(control2Angle);
856 // One last adjustment, make sure that no control anchor point extends vertically past
857 // its target prev/next point, as that results in curves dipping above or below and
858 // bending back strangely. If we find this happening we keep the control angle but
859 // reduce the length of the control line so it stays within bounds.
860 if ((curY > prevY && control1Y < prevY) || (curY < prevY && control1Y > prevY)) {
861 control1X += abs(prevY - control1Y) * (control1X - curX) / (control1Y - curY);
864 if ((curY > nextY && control2Y < nextY) || (curY < nextY && control2Y > nextY)) {
865 control2X -= abs(nextY - control2Y) * (control2X - curX) / (control2Y - curY);
877 /* Smoothing function for a path. Converts a path into cubic beziers. Value defines the divider of the distance between points.
878 * Defaults to a value of 4.
880 smooth: function (originalPath, value) {
881 var path = this.path2curve(originalPath),
894 for (; i < ii; i++) {
896 pathil = pathi.length,
897 pathim = path[i - 1],
898 pathiml = pathim.length,
899 pathip = path[i + 1],
900 pathipl = pathip && pathip.length;
901 if (pathi[0] == "M") {
905 while (path[j][0] != "C") {
910 newp.push(["M", mx, my]);
916 if (pathi[pathil - 2] == mx && pathi[pathil - 1] == my && (!pathip || pathip[0] == "M")) {
917 var begl = newp[beg].length;
918 points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], mx, my, newp[beg][begl - 2], newp[beg][begl - 1], value);
919 newp[beg][1] = points.x2;
920 newp[beg][2] = points.y2;
922 else if (!pathip || pathip[0] == "M") {
924 x1: pathi[pathil - 2],
925 y1: pathi[pathil - 1]
928 points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], pathi[pathil - 2], pathi[pathil - 1], pathip[pathipl - 2], pathip[pathipl - 1], value);
930 newp.push(["C", x, y, points.x1, points.y1, pathi[pathil - 2], pathi[pathil - 1]]);
937 findDotAtSegment: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
940 x: Math.pow(t1, 3) * p1x + Math.pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + Math.pow(t, 3) * p2x,
941 y: Math.pow(t1, 3) * p1y + Math.pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + Math.pow(t, 3) * p2y
945 <span id='Ext-draw-Draw-method-snapEnds'> /**
946 </span> * A utility method to deduce an appropriate tick configuration for the data set of given
949 * @param {Number/Date} from The minimum value in the data
950 * @param {Number/Date} to The maximum value in the data
951 * @param {Number} stepsMax The maximum number of ticks
952 * @return {Object} The calculated step and ends info; When `from` and `to` are Dates, refer to the
953 * return value of {@link #snapEndsByDate}. For numerical `from` and `to` the return value contains:
954 * @return {Number} return.from The result start value, which may be lower than the original start value
955 * @return {Number} return.to The result end value, which may be higher than the original end value
956 * @return {Number} return.power The calculate power.
957 * @return {Number} return.step The value size of each step
958 * @return {Number} return.steps The number of steps.
960 snapEnds: function (from, to, stepsMax) {
961 if (Ext.isDate(from)) {
962 return this.snapEndsByDate(from, to, stepsMax);
964 var step = (to - from) / stepsMax,
965 level = Math.floor(Math.log(step) / Math.LN10) + 1,
966 m = Math.pow(10, level),
968 modulo = Math.round((step % m) * Math.pow(10, 2 - level)),
969 interval = [[0, 15], [20, 4], [30, 2], [40, 4], [50, 9], [60, 4], [70, 2], [80, 4], [100, 15]],
976 ln = interval.length;
977 cur = from = Math.floor(from / m) * m;
978 for (i = 0; i < ln; i++) {
979 value = interval[i][0];
980 weight = (value - modulo) < 0 ? 1e6 : (value - modulo) / interval[i][1];
981 if (weight < topWeight) {
986 step = Math.floor(step * Math.pow(10, -level)) * Math.pow(10, level) + topValue * Math.pow(10, level - 2);
987 while (cur < to) {
991 to = +cur.toFixed(10);
1001 <span id='Ext-draw-Draw-method-snapEndsByDate'> /**
1002 </span> * A utility method to deduce an appropriate tick configuration for the data set of given
1003 * feature when data is Dates. Refer to {@link #snapEnds} for numeric data.
1005 * @param {Date} from The minimum value in the data
1006 * @param {Date} to The maximum value in the data
1007 * @param {Number} stepsMax The maximum number of ticks
1008 * @param {Boolean} lockEnds If true, the 'from' and 'to' parameters will be used as fixed end values
1009 * and will not be adjusted
1010 * @return {Object} The calculated step and ends info; properties are:
1011 * @return {Date} return.from The result start value, which may be lower than the original start value
1012 * @return {Date} return.to The result end value, which may be higher than the original end value
1013 * @return {Number} return.step The value size of each step
1014 * @return {Number} return.steps The number of steps.
1015 * NOTE: the steps may not divide the from/to range perfectly evenly;
1016 * there may be a smaller distance between the last step and the end value than between prior
1017 * steps, particularly when the `endsLocked` param is true. Therefore it is best to not use
1018 * the `steps` result when finding the axis tick points, instead use the `step`, `to`, and
1019 * `from` to find the correct point for each tick.
1021 snapEndsByDate: function (from, to, stepsMax, lockEnds) {
1022 var selectedStep = false, scales = [
1023 [Ext.Date.MILLI, [1, 2, 3, 5, 10, 20, 30, 50, 100, 200, 300, 500]],
1024 [Ext.Date.SECOND, [1, 2, 3, 5, 10, 15, 30]],
1025 [Ext.Date.MINUTE, [1, 2, 3, 5, 10, 20, 30]],
1026 [Ext.Date.HOUR, [1, 2, 3, 4, 6, 12]],
1027 [Ext.Date.DAY, [1, 2, 3, 7, 14]],
1028 [Ext.Date.MONTH, [1, 2, 3, 4, 6]]
1031 // Find the most desirable scale
1032 Ext.each(scales, function(scale, i) {
1033 for (j = 0; j < scale[1].length; j++) {
1034 if (to < Ext.Date.add(from, scale[0], scale[1][j] * stepsMax)) {
1035 selectedStep = [scale[0], scale[1][j]];
1040 if (!selectedStep) {
1041 yearDiff = this.snapEnds(from.getFullYear(), to.getFullYear() + 1, stepsMax, lockEnds);
1042 selectedStep = [Date.YEAR, Math.round(yearDiff.step)];
1044 return this.snapEndsByDateAndStep(from, to, selectedStep, lockEnds);
1048 <span id='Ext-draw-Draw-method-snapEndsByDateAndStep'> /**
1049 </span> * A utility method to deduce an appropriate tick configuration for the data set of given
1050 * feature and specific step size.
1051 * @param {Date} from The minimum value in the data
1052 * @param {Date} to The maximum value in the data
1053 * @param {Array} step An array with two components: The first is the unit of the step (day, month, year, etc).
1054 * The second one is the number of units for the step (1, 2, etc.).
1055 * @param {Boolean} lockEnds If true, the 'from' and 'to' parameters will be used as fixed end values
1056 * and will not be adjusted
1057 * @return {Object} See the return value of {@link #snapEndsByDate}.
1059 snapEndsByDateAndStep: function(from, to, step, lockEnds) {
1060 var fromStat = [from.getFullYear(), from.getMonth(), from.getDate(),
1061 from.getHours(), from.getMinutes(), from.getSeconds(), from.getMilliseconds()],
1062 steps = 0, testFrom, testTo;
1067 case Ext.Date.MILLI:
1068 testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3],
1069 fromStat[4], fromStat[5], Math.floor(fromStat[6] / step[1]) * step[1]);
1071 case Ext.Date.SECOND:
1072 testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3],
1073 fromStat[4], Math.floor(fromStat[5] / step[1]) * step[1], 0);
1075 case Ext.Date.MINUTE:
1076 testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3],
1077 Math.floor(fromStat[4] / step[1]) * step[1], 0, 0);
1080 testFrom = new Date(fromStat[0], fromStat[1], fromStat[2],
1081 Math.floor(fromStat[3] / step[1]) * step[1], 0, 0, 0);
1084 testFrom = new Date(fromStat[0], fromStat[1],
1085 Math.floor(fromStat[2] - 1 / step[1]) * step[1] + 1, 0, 0, 0, 0);
1087 case Ext.Date.MONTH:
1088 testFrom = new Date(fromStat[0], Math.floor(fromStat[1] / step[1]) * step[1], 1, 0, 0, 0, 0);
1090 default: // Ext.Date.YEAR
1091 testFrom = new Date(Math.floor(fromStat[0] / step[1]) * step[1], 0, 1, 0, 0, 0, 0);
1097 // TODO(zhangbei) : We can do it better somehow...
1098 while (testTo < to) {
1099 testTo = Ext.Date.add(testTo, step[0], step[1]);
1109 step : (testTo - testFrom) / steps,
1114 sorter: function (a, b) {
1115 return a.offset - b.offset;
1118 rad: function(degrees) {
1119 return degrees % 360 * Math.PI / 180;
1122 degrees: function(radian) {
1123 return radian * 180 / Math.PI % 360;
1126 withinBox: function(x, y, bbox) {
1128 return (x >= bbox.x && x <= (bbox.x + bbox.width) && y >= bbox.y && y <= (bbox.y + bbox.height));
1131 parseGradient: function(gradient) {
1133 type = gradient.type || 'linear',
1134 angle = gradient.angle || 0,
1136 stops = gradient.stops,
1143 if (type == 'linear') {
1144 vector = [0, 0, Math.cos(angle * radian), Math.sin(angle * radian)];
1145 max = 1 / (Math.max(Math.abs(vector[2]), Math.abs(vector[3])) || 1);
1148 if (vector[2] < 0) {
1149 vector[0] = -vector[2];
1152 if (vector[3] < 0) {
1153 vector[1] = -vector[3];
1158 for (stop in stops) {
1159 if (stops.hasOwnProperty(stop) && me.stopsRE.test(stop)) {
1161 offset: parseInt(stop, 10),
1162 color: Ext.draw.Color.toHex(stops[stop].color) || '#ffffff',
1163 opacity: stops[stop].opacity || 1
1165 stopsArr.push(stopObj);
1168 // Sort by pct property
1169 Ext.Array.sort(stopsArr, me.sorter);
1170 if (type == 'linear') {
1182 centerX: gradient.centerX,
1183 centerY: gradient.centerY,
1184 focalX: gradient.focalX,
1185 focalY: gradient.focalY,
1186 radius: gradient.radius,