API Docs for: 3.8.0
Show:

File: graphics/js/CanvasDrawing.js

  1. var IMPLEMENTATION = "canvas",
  2.     SHAPE = "shape",
  3.         SPLITPATHPATTERN = /[a-z][^a-z]*/ig,
  4.     SPLITARGSPATTERN = /[-]?[0-9]*[0-9|\.][0-9]*/g,
  5.     DOCUMENT = Y.config.doc,
  6.     Y_LANG = Y.Lang,
  7.     AttributeLite = Y.AttributeLite,
  8.         CanvasShape,
  9.         CanvasPath,
  10.         CanvasRect,
  11.     CanvasEllipse,
  12.         CanvasCircle,
  13.     CanvasPieSlice,
  14.     Y_DOM = Y.DOM,
  15.     Y_Color = Y.Color,
  16.     PARSE_INT = parseInt,
  17.     PARSE_FLOAT = parseFloat,
  18.     IS_NUMBER = Y_LANG.isNumber,
  19.     RE = RegExp,
  20.     TORGB = Y_Color.toRGB,
  21.     TOHEX = Y_Color.toHex,
  22.     _getClassName = Y.ClassNameManager.getClassName;

  23. /**
  24.  * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> implementation of the <a href="Drawing.html">`Drawing`</a> class.
  25.  * `CanvasDrawing` is not intended to be used directly. Instead, use the <a href="Drawing.html">`Drawing`</a> class.
  26.  * If the browser lacks <a href="http://www.w3.org/TR/SVG/">SVG</a> capabilities but has
  27.  * <a href="http://www.w3.org/TR/html5/the-canvas-element.html">Canvas</a> capabilities, the <a href="Drawing.html">`Drawing`</a>
  28.  * class will point to the `CanvasDrawing` class.
  29.  *
  30.  * @module graphics
  31.  * @class CanvasDrawing
  32.  * @constructor
  33.  */
  34. function CanvasDrawing()
  35. {
  36. }

  37. CanvasDrawing.prototype = {
  38.     /**
  39.      * Maps path to methods
  40.      *
  41.      * @property _pathSymbolToMethod
  42.      * @type Object
  43.      * @private
  44.      */
  45.     _pathSymbolToMethod: {
  46.         M: "moveTo",
  47.         m: "relativeMoveTo",
  48.         L: "lineTo",
  49.         l: "relativeLineTo",
  50.         C: "curveTo",
  51.         c: "relativeCurveTo",
  52.         Q: "quadraticCurveTo",
  53.         q: "relativeQuadraticCurveTo",
  54.         z: "closePath",
  55.         Z: "closePath"
  56.     },

  57.     /**
  58.      * Current x position of the drawing.
  59.      *
  60.      * @property _currentX
  61.      * @type Number
  62.      * @private
  63.      */
  64.     _currentX: 0,

  65.     /**
  66.      * Current y position of the drqwing.
  67.      *
  68.      * @property _currentY
  69.      * @type Number
  70.      * @private
  71.      */
  72.     _currentY: 0,

  73.     /**
  74.      * Parses hex color string and alpha value to rgba
  75.      *
  76.      * @method _toRGBA
  77.      * @param {Object} val Color value to parse. Can be hex string, rgb or name.
  78.      * @param {Number} alpha Numeric value between 0 and 1 representing the alpha level.
  79.      * @private
  80.      */
  81.     _toRGBA: function(val, alpha) {
  82.         alpha = (alpha !== undefined) ? alpha : 1;
  83.         if (!Y_Color.re_RGB.test(val)) {
  84.             val = TOHEX(val);
  85.         }

  86.         if(Y_Color.re_hex.exec(val)) {
  87.             val = 'rgba(' + [
  88.                 PARSE_INT(RE.$1, 16),
  89.                 PARSE_INT(RE.$2, 16),
  90.                 PARSE_INT(RE.$3, 16)
  91.             ].join(',') + ',' + alpha + ')';
  92.         }
  93.         return val;
  94.     },

  95.     /**
  96.      * Converts color to rgb format
  97.      *
  98.      * @method _toRGB
  99.      * @param val Color value to convert.
  100.      * @private
  101.      */
  102.     _toRGB: function(val) {
  103.         return TORGB(val);
  104.     },

  105.     /**
  106.      * Sets the size of the graphics object.
  107.      *
  108.      * @method setSize
  109.      * @param w {Number} width to set for the instance.
  110.      * @param h {Number} height to set for the instance.
  111.      * @private
  112.      */
  113.         setSize: function(w, h) {
  114.         if(this.get("autoSize"))
  115.         {
  116.             if(w > this.node.getAttribute("width"))
  117.             {
  118.                 this.node.style.width = w + "px";
  119.                 this.node.setAttribute("width", w);
  120.             }
  121.             if(h > this.node.getAttribute("height"))
  122.             {
  123.                 this.node.style.height = h + "px";
  124.                 this.node.setAttribute("height", h);
  125.             }
  126.         }
  127.     },

  128.         /**
  129.      * Tracks coordinates. Used to calculate the start point of dashed lines.
  130.      *
  131.      * @method _updateCoords
  132.      * @param {Number} x x-coordinate
  133.      * @param {Number} y y-coordinate
  134.          * @private
  135.          */
  136.     _updateCoords: function(x, y)
  137.     {
  138.         this._xcoords.push(x);
  139.         this._ycoords.push(y);
  140.         this._currentX = x;
  141.         this._currentY = y;
  142.     },

  143.         /**
  144.      * Clears the coordinate arrays. Called at the end of a drawing operation.
  145.          *
  146.      * @method _clearAndUpdateCoords
  147.      * @private
  148.          */
  149.     _clearAndUpdateCoords: function()
  150.     {
  151.         var x = this._xcoords.pop() || 0,
  152.             y = this._ycoords.pop() || 0;
  153.         this._updateCoords(x, y);
  154.     },

  155.         /**
  156.      * Moves the shape's dom node.
  157.      *
  158.      * @method _updateNodePosition
  159.          * @private
  160.          */
  161.     _updateNodePosition: function()
  162.     {
  163.         var node = this.get("node"),
  164.             x = this.get("x"),
  165.             y = this.get("y");
  166.         node.style.position = "absolute";
  167.         node.style.left = (x + this._left) + "px";
  168.         node.style.top = (y + this._top) + "px";
  169.     },

  170.     /**
  171.      * Queues up a method to be executed when a shape redraws.
  172.      *
  173.      * @method _updateDrawingQueue
  174.      * @param {Array} val An array containing data that can be parsed into a method and arguments. The value at zero-index
  175.      * of the array is a string reference of the drawing method that will be called. All subsequent indices are argument for
  176.      * that method. For example, `lineTo(10, 100)` would be structured as:
  177.      * `["lineTo", 10, 100]`.
  178.      * @private
  179.      */
  180.     _updateDrawingQueue: function(val)
  181.     {
  182.         this._methods.push(val);
  183.     },

  184.     /**
  185.      * Draws a line segment from the current drawing position to the specified x and y coordinates.
  186.      *
  187.      * @method lineTo
  188.      * @param {Number} point1 x-coordinate for the end point.
  189.      * @param {Number} point2 y-coordinate for the end point.
  190.      */
  191.     lineTo: function()
  192.     {
  193.         this._lineTo.apply(this, [Y.Array(arguments), false]);
  194.     },

  195.     /**
  196.      * Draws a line segment from the current drawing position to the relative x and y coordinates.
  197.      *
  198.      * @method lineTo
  199.      * @param {Number} point1 x-coordinate for the end point.
  200.      * @param {Number} point2 y-coordinate for the end point.
  201.      */
  202.     relativeLineTo: function()
  203.     {
  204.         this._lineTo.apply(this, [Y.Array(arguments), true]);
  205.     },

  206.     /**
  207.      * Implements lineTo methods.
  208.      *
  209.      * @method _lineTo
  210.      * @param {Array} args The arguments to be used.
  211.      * @param {Boolean} relative Indicates whether or not to use relative coordinates.
  212.      * @private
  213.      */
  214.     _lineTo: function(args, relative)
  215.     {
  216.         var point1 = args[0],
  217.             i,
  218.             len,
  219.             x,
  220.             y,
  221.             wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
  222.             relativeX = relative ? parseFloat(this._currentX) : 0,
  223.             relativeY = relative ? parseFloat(this._currentY) : 0;
  224.         if(!this._lineToMethods)
  225.         {
  226.             this._lineToMethods = [];
  227.         }
  228.         len = args.length - 1;
  229.         if (typeof point1 === 'string' || typeof point1 === 'number') {
  230.             for (i = 0; i < len; i = i + 2) {
  231.                 x = parseFloat(args[i]);
  232.                 y = parseFloat(args[i + 1]);
  233.                 x = x + relativeX;
  234.                 y = y + relativeY;
  235.                 this._updateDrawingQueue(["lineTo", x, y]);
  236.                 this._trackSize(x - wt, y - wt);
  237.                 this._trackSize(x + wt, y + wt);
  238.                 this._updateCoords(x, y);
  239.             }
  240.         }
  241.         else
  242.         {
  243.             for (i = 0; i < len; i = i + 1)
  244.             {
  245.                 x = parseFloat(args[i][0]);
  246.                 y = parseFloat(args[i][1]);
  247.                 this._updateDrawingQueue(["lineTo", x, y]);
  248.                 this._lineToMethods[this._lineToMethods.length] = this._methods[this._methods.length - 1];
  249.                 this._trackSize(x - wt, y - wt);
  250.                 this._trackSize(x + wt, y + wt);
  251.                 this._updateCoords(x, y);
  252.             }
  253.         }
  254.         this._drawingComplete = false;
  255.         return this;
  256.     },

  257.     /**
  258.      * Moves the current drawing position to specified x and y coordinates.
  259.      *
  260.      * @method moveTo
  261.      * @param {Number} x x-coordinate for the end point.
  262.      * @param {Number} y y-coordinate for the end point.
  263.      */
  264.     moveTo: function()
  265.     {
  266.         this._moveTo.apply(this, [Y.Array(arguments), false]);
  267.     },

  268.     /**
  269.      * Moves the current drawing position relative to specified x and y coordinates.
  270.      *
  271.      * @method relativeMoveTo
  272.      * @param {Number} x x-coordinate for the end point.
  273.      * @param {Number} y y-coordinate for the end point.
  274.      */
  275.     relativeMoveTo: function()
  276.     {
  277.         this._moveTo.apply(this, [Y.Array(arguments), true]);
  278.     },

  279.     /**
  280.      * Implements moveTo methods.
  281.      *
  282.      * @method _moveTo
  283.      * @param {Array} args The arguments to be used.
  284.      * @param {Boolean} relative Indicates whether or not to use relative coordinates.
  285.      * @private
  286.      */
  287.     _moveTo: function(args, relative) {
  288.         var wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
  289.             relativeX = relative ? parseFloat(this._currentX) : 0,
  290.             relativeY = relative ? parseFloat(this._currentY) : 0,
  291.             x = parseFloat(args[0]) + relativeX,
  292.             y = parseFloat(args[1]) + relativeY;
  293.         this._updateDrawingQueue(["moveTo", x, y]);
  294.         this._trackSize(x - wt, y - wt);
  295.         this._trackSize(x + wt, y + wt);
  296.         this._updateCoords(x, y);
  297.         this._drawingComplete = false;
  298.         return this;
  299.     },

  300.     /**
  301.      * Draws a bezier curve.
  302.      *
  303.      * @method curveTo
  304.      * @param {Number} cp1x x-coordinate for the first control point.
  305.      * @param {Number} cp1y y-coordinate for the first control point.
  306.      * @param {Number} cp2x x-coordinate for the second control point.
  307.      * @param {Number} cp2y y-coordinate for the second control point.
  308.      * @param {Number} x x-coordinate for the end point.
  309.      * @param {Number} y y-coordinate for the end point.
  310.      */
  311.     curveTo: function() {
  312.         this._curveTo.apply(this, [Y.Array(arguments), false]);
  313.     },

  314.     /**
  315.      * Draws a bezier curve relative to the current coordinates.
  316.      *
  317.      * @method curveTo
  318.      * @param {Number} cp1x x-coordinate for the first control point.
  319.      * @param {Number} cp1y y-coordinate for the first control point.
  320.      * @param {Number} cp2x x-coordinate for the second control point.
  321.      * @param {Number} cp2y y-coordinate for the second control point.
  322.      * @param {Number} x x-coordinate for the end point.
  323.      * @param {Number} y y-coordinate for the end point.
  324.      */
  325.     relativeCurveTo: function() {
  326.         this._curveTo.apply(this, [Y.Array(arguments), true]);
  327.     },

  328.     /**
  329.      * Implements curveTo methods.
  330.      *
  331.      * @method _curveTo
  332.      * @param {Array} args The arguments to be used.
  333.      * @param {Boolean} relative Indicates whether or not to use relative coordinates.
  334.      * @private
  335.      */
  336.     _curveTo: function(args, relative) {
  337.         var w,
  338.             h,
  339.             cp1x,
  340.             cp1y,
  341.             cp2x,
  342.             cp2y,
  343.             x,
  344.             y,
  345.             pts,
  346.             right,
  347.             left,
  348.             bottom,
  349.             top,
  350.             i,
  351.             len,
  352.             relativeX = relative ? parseFloat(this._currentX) : 0,
  353.             relativeY = relative ? parseFloat(this._currentY) : 0;
  354.         len = args.length - 5;
  355.         for(i = 0; i < len; i = i + 6)
  356.         {
  357.             cp1x = parseFloat(args[i]) + relativeX;
  358.             cp1y = parseFloat(args[i + 1]) + relativeY;
  359.             cp2x = parseFloat(args[i + 2]) + relativeX;
  360.             cp2y = parseFloat(args[i + 3]) + relativeY;
  361.             x = parseFloat(args[i + 4]) + relativeX;
  362.             y = parseFloat(args[i + 5]) + relativeY;
  363.             this._updateDrawingQueue(["bezierCurveTo", cp1x, cp1y, cp2x, cp2y, x, y]);
  364.             this._drawingComplete = false;
  365.             right = Math.max(x, Math.max(cp1x, cp2x));
  366.             bottom = Math.max(y, Math.max(cp1y, cp2y));
  367.             left = Math.min(x, Math.min(cp1x, cp2x));
  368.             top = Math.min(y, Math.min(cp1y, cp2y));
  369.             w = Math.abs(right - left);
  370.             h = Math.abs(bottom - top);
  371.             pts = [[this._currentX, this._currentY] , [cp1x, cp1y], [cp2x, cp2y], [x, y]];
  372.             this._setCurveBoundingBox(pts, w, h);
  373.             this._currentX = x;
  374.             this._currentY = y;
  375.         }
  376.     },

  377.     /**
  378.      * Draws a quadratic bezier curve.
  379.      *
  380.      * @method quadraticCurveTo
  381.      * @param {Number} cpx x-coordinate for the control point.
  382.      * @param {Number} cpy y-coordinate for the control point.
  383.      * @param {Number} x x-coordinate for the end point.
  384.      * @param {Number} y y-coordinate for the end point.
  385.      */
  386.     quadraticCurveTo: function() {
  387.         this._quadraticCurveTo.apply(this, [Y.Array(arguments), false]);
  388.     },

  389.     /**
  390.      * Draws a quadratic bezier curve relative to the current position.
  391.      *
  392.      * @method relativeQuadraticCurveTo
  393.      * @param {Number} cpx x-coordinate for the control point.
  394.      * @param {Number} cpy y-coordinate for the control point.
  395.      * @param {Number} x x-coordinate for the end point.
  396.      * @param {Number} y y-coordinate for the end point.
  397.      */
  398.     relativeQuadraticCurveTo: function() {
  399.         this._quadraticCurveTo.apply(this, [Y.Array(arguments), true]);
  400.     },

  401.     /**
  402.      * Implements quadraticCurveTo methods.
  403.      *
  404.      * @method _quadraticCurveTo
  405.      * @param {Array} args The arguments to be used.
  406.      * @param {Boolean} relative Indicates whether or not to use relative coordinates.
  407.      * @private
  408.      */
  409.     _quadraticCurveTo: function(args, relative) {
  410.         var cpx,
  411.             cpy,
  412.             x,
  413.             y,
  414.             w,
  415.             h,
  416.             pts,
  417.             right,
  418.             left,
  419.             bottom,
  420.             top,
  421.             i,
  422.             len = args.length - 3,
  423.             wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
  424.             relativeX = relative ? parseFloat(this._currentX) : 0,
  425.             relativeY = relative ? parseFloat(this._currentY) : 0;
  426.         for(i = 0; i < len; i = i + 4)
  427.         {
  428.             cpx = parseFloat(args[i]) + relativeX;
  429.             cpy = parseFloat(args[i + 1]) + relativeY;
  430.             x = parseFloat(args[i + 2]) + relativeX;
  431.             y = parseFloat(args[i + 3]) + relativeY;
  432.             this._drawingComplete = false;
  433.             right = Math.max(x, cpx);
  434.             bottom = Math.max(y, cpy);
  435.             left = Math.min(x, cpx);
  436.             top = Math.min(y, cpy);
  437.             w = Math.abs(right - left);
  438.             h = Math.abs(bottom - top);
  439.             pts = [[this._currentX, this._currentY] , [cpx, cpy], [x, y]];
  440.             this._setCurveBoundingBox(pts, w, h);
  441.             this._updateDrawingQueue(["quadraticCurveTo", cpx, cpy, x, y]);
  442.             this._updateCoords(x, y);
  443.         }
  444.         return this;
  445.     },

  446.     /**
  447.      * Draws a circle. Used internally by `CanvasCircle` class.
  448.      *
  449.      * @method drawCircle
  450.      * @param {Number} x y-coordinate
  451.      * @param {Number} y x-coordinate
  452.      * @param {Number} r radius
  453.      * @protected
  454.      */
  455.         drawCircle: function(x, y, radius) {
  456.         var startAngle = 0,
  457.             endAngle = 2 * Math.PI,
  458.             wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
  459.             circum = radius * 2;
  460.             circum += wt;
  461.         this._drawingComplete = false;
  462.         this._trackSize(x + circum, y + circum);
  463.         this._trackSize(x - wt, y - wt);
  464.         this._updateCoords(x, y);
  465.         this._updateDrawingQueue(["arc", x + radius, y + radius, radius, startAngle, endAngle, false]);
  466.         return this;
  467.     },

  468.     /**
  469.      * Draws a diamond.
  470.      *
  471.      * @method drawDiamond
  472.      * @param {Number} x y-coordinate
  473.      * @param {Number} y x-coordinate
  474.      * @param {Number} width width
  475.      * @param {Number} height height
  476.      * @protected
  477.      */
  478.     drawDiamond: function(x, y, width, height)
  479.     {
  480.         var midWidth = width * 0.5,
  481.             midHeight = height * 0.5;
  482.         this.moveTo(x + midWidth, y);
  483.         this.lineTo(x + width, y + midHeight);
  484.         this.lineTo(x + midWidth, y + height);
  485.         this.lineTo(x, y + midHeight);
  486.         this.lineTo(x + midWidth, y);
  487.         return this;
  488.     },

  489.     /**
  490.      * Draws an ellipse. Used internally by `CanvasEllipse` class.
  491.      *
  492.      * @method drawEllipse
  493.      * @param {Number} x x-coordinate
  494.      * @param {Number} y y-coordinate
  495.      * @param {Number} w width
  496.      * @param {Number} h height
  497.      * @protected
  498.      */
  499.         drawEllipse: function(x, y, w, h) {
  500.         var l = 8,
  501.             theta = -(45/180) * Math.PI,
  502.             angle = 0,
  503.             angleMid,
  504.             radius = w/2,
  505.             yRadius = h/2,
  506.             i,
  507.             centerX = x + radius,
  508.             centerY = y + yRadius,
  509.             ax, ay, bx, by, cx, cy,
  510.             wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0;

  511.         ax = centerX + Math.cos(0) * radius;
  512.         ay = centerY + Math.sin(0) * yRadius;
  513.         this.moveTo(ax, ay);
  514.         for(i = 0; i < l; i++)
  515.         {
  516.             angle += theta;
  517.             angleMid = angle - (theta / 2);
  518.             bx = centerX + Math.cos(angle) * radius;
  519.             by = centerY + Math.sin(angle) * yRadius;
  520.             cx = centerX + Math.cos(angleMid) * (radius / Math.cos(theta / 2));
  521.             cy = centerY + Math.sin(angleMid) * (yRadius / Math.cos(theta / 2));
  522.             this._updateDrawingQueue(["quadraticCurveTo", cx, cy, bx, by]);
  523.         }
  524.         this._trackSize(x + w + wt, y + h + wt);
  525.         this._trackSize(x - wt, y - wt);
  526.         this._updateCoords(x, y);
  527.         return this;
  528.     },

  529.     /**
  530.      * Draws a rectangle.
  531.      *
  532.      * @method drawRect
  533.      * @param {Number} x x-coordinate
  534.      * @param {Number} y y-coordinate
  535.      * @param {Number} w width
  536.      * @param {Number} h height
  537.      */
  538.     drawRect: function(x, y, w, h) {
  539.         var wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0;
  540.         this._drawingComplete = false;
  541.         this.moveTo(x, y);
  542.         this.lineTo(x + w, y);
  543.         this.lineTo(x + w, y + h);
  544.         this.lineTo(x, y + h);
  545.         this.lineTo(x, y);
  546.         return this;
  547.     },

  548.     /**
  549.      * Draws a rectangle with rounded corners.
  550.      *
  551.      * @method drawRect
  552.      * @param {Number} x x-coordinate
  553.      * @param {Number} y y-coordinate
  554.      * @param {Number} w width
  555.      * @param {Number} h height
  556.      * @param {Number} ew width of the ellipse used to draw the rounded corners
  557.      * @param {Number} eh height of the ellipse used to draw the rounded corners
  558.      */
  559.     drawRoundRect: function(x, y, w, h, ew, eh) {
  560.         var wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0;
  561.         this._drawingComplete = false;
  562.         this.moveTo( x, y + eh);
  563.         this.lineTo(x, y + h - eh);
  564.         this.quadraticCurveTo(x, y + h, x + ew, y + h);
  565.         this.lineTo(x + w - ew, y + h);
  566.         this.quadraticCurveTo(x + w, y + h, x + w, y + h - eh);
  567.         this.lineTo(x + w, y + eh);
  568.         this.quadraticCurveTo(x + w, y, x + w - ew, y);
  569.         this.lineTo(x + ew, y);
  570.         this.quadraticCurveTo(x, y, x, y + eh);
  571.         return this;
  572.     },

  573.     /**
  574.      * Draws a wedge.
  575.      *
  576.      * @method drawWedge
  577.      * @param {Number} x x-coordinate of the wedge's center point
  578.      * @param {Number} y y-coordinate of the wedge's center point
  579.      * @param {Number} startAngle starting angle in degrees
  580.      * @param {Number} arc sweep of the wedge. Negative values draw clockwise.
  581.      * @param {Number} radius radius of wedge. If [optional] yRadius is defined, then radius is the x radius.
  582.      * @param {Number} yRadius [optional] y radius for wedge.
  583.      * @private
  584.      */
  585.     drawWedge: function(x, y, startAngle, arc, radius, yRadius)
  586.     {
  587.         var wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
  588.             segs,
  589.             segAngle,
  590.             theta,
  591.             angle,
  592.             angleMid,
  593.             ax,
  594.             ay,
  595.             bx,
  596.             by,
  597.             cx,
  598.             cy,
  599.             i = 0;
  600.         yRadius = yRadius || radius;

  601.         this._drawingComplete = false;
  602.         // move to x,y position
  603.         this._updateDrawingQueue(["moveTo", x, y]);

  604.         yRadius = yRadius || radius;

  605.         // limit sweep to reasonable numbers
  606.         if(Math.abs(arc) > 360)
  607.         {
  608.             arc = 360;
  609.         }

  610.         // First we calculate how many segments are needed
  611.         // for a smooth arc.
  612.         segs = Math.ceil(Math.abs(arc) / 45);

  613.         // Now calculate the sweep of each segment.
  614.         segAngle = arc / segs;

  615.         // The math requires radians rather than degrees. To convert from degrees
  616.         // use the formula (degrees/180)*Math.PI to get radians.
  617.         theta = -(segAngle / 180) * Math.PI;

  618.         // convert angle startAngle to radians
  619.         angle = (startAngle / 180) * Math.PI;

  620.         // draw the curve in segments no larger than 45 degrees.
  621.         if(segs > 0)
  622.         {
  623.             // draw a line from the center to the start of the curve
  624.             ax = x + Math.cos(startAngle / 180 * Math.PI) * radius;
  625.             ay = y + Math.sin(startAngle / 180 * Math.PI) * yRadius;
  626.             this.lineTo(ax, ay);
  627.             // Loop for drawing curve segments
  628.             for(i = 0; i < segs; ++i)
  629.             {
  630.                 angle += theta;
  631.                 angleMid = angle - (theta / 2);
  632.                 bx = x + Math.cos(angle) * radius;
  633.                 by = y + Math.sin(angle) * yRadius;
  634.                 cx = x + Math.cos(angleMid) * (radius / Math.cos(theta / 2));
  635.                 cy = y + Math.sin(angleMid) * (yRadius / Math.cos(theta / 2));
  636.                 this._updateDrawingQueue(["quadraticCurveTo", cx, cy, bx, by]);
  637.             }
  638.             // close the wedge by drawing a line to the center
  639.             this._updateDrawingQueue(["lineTo", x, y]);
  640.         }
  641.         this._trackSize(-wt , -wt);
  642.         this._trackSize((radius * 2) + wt, (radius * 2) + wt);
  643.         return this;
  644.     },

  645.     /**
  646.      * Completes a drawing operation.
  647.      *
  648.      * @method end
  649.      */
  650.     end: function() {
  651.         this._closePath();
  652.         return this;
  653.     },

  654.     /**
  655.      * Ends a fill and stroke
  656.      *
  657.      * @method closePath
  658.      */
  659.     closePath: function()
  660.     {
  661.         this._updateDrawingQueue(["closePath"]);
  662.         this._updateDrawingQueue(["beginPath"]);
  663.     },

  664.         /**
  665.          * Clears the graphics object.
  666.          *
  667.          * @method clear
  668.          */

  669.     /**
  670.      * Returns a linear gradient fill
  671.      *
  672.      * @method _getLinearGradient
  673.      * @return CanvasGradient
  674.      * @private
  675.      */
  676.     _getLinearGradient: function() {
  677.         var isNumber = Y.Lang.isNumber,
  678.             fill = this.get("fill"),
  679.             stops = fill.stops,
  680.             opacity,
  681.             color,
  682.             stop,
  683.             i,
  684.             len = stops.length,
  685.             gradient,
  686.             x = 0,
  687.             y = 0,
  688.             w = this.get("width"),
  689.             h = this.get("height"),
  690.             r = fill.rotation || 0,
  691.             x1, x2, y1, y2,
  692.             cx = x + w/2,
  693.             cy = y + h/2,
  694.             offset,
  695.             radCon = Math.PI/180,
  696.             tanRadians = parseFloat(parseFloat(Math.tan(r * radCon)).toFixed(8));
  697.         if(Math.abs(tanRadians) * w/2 >= h/2)
  698.         {
  699.             if(r < 180)
  700.             {
  701.                 y1 = y;
  702.                 y2 = y + h;
  703.             }
  704.             else
  705.             {
  706.                 y1 = y + h;
  707.                 y2 = y;
  708.             }
  709.             x1 = cx - ((cy - y1)/tanRadians);
  710.             x2 = cx - ((cy - y2)/tanRadians);
  711.         }
  712.         else
  713.         {
  714.             if(r > 90 && r < 270)
  715.             {
  716.                 x1 = x + w;
  717.                 x2 = x;
  718.             }
  719.             else
  720.             {
  721.                 x1 = x;
  722.                 x2 = x + w;
  723.             }
  724.             y1 = ((tanRadians * (cx - x1)) - cy) * -1;
  725.             y2 = ((tanRadians * (cx - x2)) - cy) * -1;
  726.         }
  727.         gradient = this._context.createLinearGradient(x1, y1, x2, y2);
  728.         for(i = 0; i < len; ++i)
  729.         {
  730.             stop = stops[i];
  731.             opacity = stop.opacity;
  732.             color = stop.color;
  733.             offset = stop.offset;
  734.             if(isNumber(opacity))
  735.             {
  736.                 opacity = Math.max(0, Math.min(1, opacity));
  737.                 color = this._toRGBA(color, opacity);
  738.             }
  739.             else
  740.             {
  741.                 color = TORGB(color);
  742.             }
  743.             offset = stop.offset || i/(len - 1);
  744.             gradient.addColorStop(offset, color);
  745.         }
  746.         return gradient;
  747.     },

  748.     /**
  749.      * Returns a radial gradient fill
  750.      *
  751.      * @method _getRadialGradient
  752.      * @return CanvasGradient
  753.      * @private
  754.      */
  755.     _getRadialGradient: function() {
  756.         var isNumber = Y.Lang.isNumber,
  757.             fill = this.get("fill"),
  758.             r = fill.r,
  759.             fx = fill.fx,
  760.             fy = fill.fy,
  761.             stops = fill.stops,
  762.             opacity,
  763.             color,
  764.             stop,
  765.             i,
  766.             len = stops.length,
  767.             gradient,
  768.             x = 0,
  769.             y = 0,
  770.             w = this.get("width"),
  771.             h = this.get("height"),
  772.             x1, x2, y1, y2, r2,
  773.             xc, yc, xn, yn, d,
  774.             offset,
  775.             ratio,
  776.             stopMultiplier;
  777.         xc = x + w/2;
  778.         yc = y + h/2;
  779.         x1 = w * fx;
  780.         y1 = h * fy;
  781.         x2 = x + w/2;
  782.         y2 = y + h/2;
  783.         r2 = w * r;
  784.         d = Math.sqrt( Math.pow(Math.abs(xc - x1), 2) + Math.pow(Math.abs(yc - y1), 2) );
  785.         if(d >= r2)
  786.         {
  787.             ratio = d/r2;
  788.             //hack. gradient won't show if it is exactly on the edge of the arc
  789.             if(ratio === 1)
  790.             {
  791.                 ratio = 1.01;
  792.             }
  793.             xn = (x1 - xc)/ratio;
  794.             yn = (y1 - yc)/ratio;
  795.             xn = xn > 0 ? Math.floor(xn) : Math.ceil(xn);
  796.             yn = yn > 0 ? Math.floor(yn) : Math.ceil(yn);
  797.             x1 = xc + xn;
  798.             y1 = yc + yn;
  799.         }

  800.         //If the gradient radius is greater than the circle's, adjusting the radius stretches the gradient properly.
  801.         //If the gradient radius is less than the circle's, adjusting the radius of the gradient will not work.
  802.         //Instead, adjust the color stops to reflect the smaller radius.
  803.         if(r >= 0.5)
  804.         {
  805.             gradient = this._context.createRadialGradient(x1, y1, r, x2, y2, r * w);
  806.             stopMultiplier = 1;
  807.         }
  808.         else
  809.         {
  810.             gradient = this._context.createRadialGradient(x1, y1, r, x2, y2, w/2);
  811.             stopMultiplier = r * 2;
  812.         }
  813.         for(i = 0; i < len; ++i)
  814.         {
  815.             stop = stops[i];
  816.             opacity = stop.opacity;
  817.             color = stop.color;
  818.             offset = stop.offset;
  819.             if(isNumber(opacity))
  820.             {
  821.                 opacity = Math.max(0, Math.min(1, opacity));
  822.                 color = this._toRGBA(color, opacity);
  823.             }
  824.             else
  825.             {
  826.                 color = TORGB(color);
  827.             }
  828.             offset = stop.offset || i/(len - 1);
  829.             offset *= stopMultiplier;
  830.             if(offset <= 1)
  831.             {
  832.                 gradient.addColorStop(offset, color);
  833.             }
  834.         }
  835.         return gradient;
  836.     },


  837.     /**
  838.      * Clears all values
  839.      *
  840.      * @method _initProps
  841.      * @private
  842.      */
  843.     _initProps: function() {
  844.         this._methods = [];
  845.         this._lineToMethods = [];
  846.         this._xcoords = [0];
  847.                 this._ycoords = [0];
  848.                 this._width = 0;
  849.         this._height = 0;
  850.         this._left = 0;
  851.         this._top = 0;
  852.         this._right = 0;
  853.         this._bottom = 0;
  854.         this._currentX = 0;
  855.         this._currentY = 0;
  856.     },

  857.     /**
  858.      * Indicates a drawing has completed.
  859.      *
  860.      * @property _drawingComplete
  861.      * @type Boolean
  862.      * @private
  863.      */
  864.     _drawingComplete: false,

  865.     /**
  866.      * Creates canvas element
  867.      *
  868.      * @method _createGraphic
  869.      * @return HTMLCanvasElement
  870.      * @private
  871.      */
  872.     _createGraphic: function(config) {
  873.         var graphic = Y.config.doc.createElement('canvas');
  874.         return graphic;
  875.     },

  876.     /**
  877.      * Returns the points on a curve
  878.      *
  879.      * @method getBezierData
  880.      * @param Array points Array containing the begin, end and control points of a curve.
  881.      * @param Number t The value for incrementing the next set of points.
  882.      * @return Array
  883.      * @private
  884.      */
  885.     getBezierData: function(points, t) {
  886.         var n = points.length,
  887.             tmp = [],
  888.             i,
  889.             j;

  890.         for (i = 0; i < n; ++i){
  891.             tmp[i] = [points[i][0], points[i][1]]; // save input
  892.         }

  893.         for (j = 1; j < n; ++j) {
  894.             for (i = 0; i < n - j; ++i) {
  895.                 tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
  896.                 tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
  897.             }
  898.         }
  899.         return [ tmp[0][0], tmp[0][1] ];
  900.     },

  901.     /**
  902.      * Calculates the bounding box for a curve
  903.      *
  904.      * @method _setCurveBoundingBox
  905.      * @param Array pts Array containing points for start, end and control points of a curve.
  906.      * @param Number w Width used to calculate the number of points to describe the curve.
  907.      * @param Number h Height used to calculate the number of points to describe the curve.
  908.      * @private
  909.      */
  910.     _setCurveBoundingBox: function(pts, w, h)
  911.     {
  912.         var i = 0,
  913.             left = this._currentX,
  914.             right = left,
  915.             top = this._currentY,
  916.             bottom = top,
  917.             len = Math.round(Math.sqrt((w * w) + (h * h))),
  918.             t = 1/len,
  919.             wt = this._stroke && this._strokeWeight ? this._strokeWeight : 0,
  920.             xy;
  921.         for(i = 0; i < len; ++i)
  922.         {
  923.             xy = this.getBezierData(pts, t * i);
  924.             left = isNaN(left) ? xy[0] : Math.min(xy[0], left);
  925.             right = isNaN(right) ? xy[0] : Math.max(xy[0], right);
  926.             top = isNaN(top) ? xy[1] : Math.min(xy[1], top);
  927.             bottom = isNaN(bottom) ? xy[1] : Math.max(xy[1], bottom);
  928.         }
  929.         left = Math.round(left * 10)/10;
  930.         right = Math.round(right * 10)/10;
  931.         top = Math.round(top * 10)/10;
  932.         bottom = Math.round(bottom * 10)/10;
  933.         this._trackSize(right + wt, bottom + wt);
  934.         this._trackSize(left - wt, top - wt);
  935.     },

  936.     /**
  937.      * Updates the size of the graphics object
  938.      *
  939.      * @method _trackSize
  940.      * @param {Number} w width
  941.      * @param {Number} h height
  942.      * @private
  943.      */
  944.     _trackSize: function(w, h) {
  945.         if (w > this._right) {
  946.             this._right = w;
  947.         }
  948.         if(w < this._left)
  949.         {
  950.             this._left = w;
  951.         }
  952.         if (h < this._top)
  953.         {
  954.             this._top = h;
  955.         }
  956.         if (h > this._bottom)
  957.         {
  958.             this._bottom = h;
  959.         }
  960.         this._width = this._right - this._left;
  961.         this._height = this._bottom - this._top;
  962.     }
  963. };
  964. Y.CanvasDrawing = CanvasDrawing;

  965.