1 //= require "object.class"
  2 
  3 (function(global) {
  4   
  5   "use strict";
  6   
  7   var fabric  = global.fabric || (global.fabric = { }),
  8       piBy2   = Math.PI * 2,
  9       extend = fabric.util.object.extend;
 10   
 11   if (fabric.Circle) {
 12     fabric.warn('fabric.Circle is already defined.');
 13     return;
 14   }
 15 
 16   /** 
 17    * @class Circle
 18    * @extends fabric.Object
 19    */
 20   fabric.Circle = fabric.util.createClass(fabric.Object, /** @scope fabric.Circle.prototype */ {
 21     
 22     /**
 23      * @property
 24      * @type String
 25      */
 26     type: 'circle',
 27     
 28     /**
 29      * Constructor
 30      * @method initialize
 31      * @param {Object} [options] Options object
 32      * @return {fabric.Circle} thisArg
 33      */
 34     initialize: function(options) {
 35       options = options || { };
 36       
 37       this.set('radius', options.radius || 0);
 38       this.callSuper('initialize', options);
 39       
 40       var radiusBy2ByScale = this.get('radius') * 2 * this.get('scaleX');
 41       this.set('width', radiusBy2ByScale).set('height', radiusBy2ByScale);
 42     },
 43     
 44     /**
 45      * Returns object representation of an instance
 46      * @method toObject
 47      * @return {Object} object representation of an instance
 48      */
 49     toObject: function() {
 50       return extend(this.callSuper('toObject'), {
 51         radius: this.get('radius')
 52       });
 53     },
 54     
 55     /**
 56      * @private
 57      * @method _render
 58      * @param ctx {CanvasRenderingContext2D} context to render on
 59      */
 60     _render: function(ctx, noTransform) {
 61       ctx.beginPath();
 62       // multiply by currently set alpha (the one that was set by path group where this object is contained, for example)
 63       ctx.globalAlpha *= this.opacity;
 64       ctx.arc(noTransform ? this.left : 0, noTransform ? this.top : 0, this.radius, 0, piBy2, false);
 65       ctx.closePath();
 66       if (this.fill) {
 67         ctx.fill();
 68       }
 69       if (this.stroke) {
 70         ctx.stroke();
 71       }
 72     },
 73     
 74     /**
 75      * Returns horizontal radius of an object (according to how an object is scaled)
 76      * @method getRadiusX
 77      * @return {Number}
 78      */
 79     getRadiusX: function() {
 80       return this.get('radius') * this.get('scaleX');
 81     },
 82     
 83     /**
 84      * Returns vertical radius of an object (according to how an object is scaled)
 85      * @method getRadiusY
 86      * @return {Number}
 87      */
 88     getRadiusY: function() {
 89       return this.get('radius') * this.get('scaleY');
 90     },
 91     
 92     /**
 93      * Returns complexity of an instance
 94      * @method complexity
 95      * @return {Number} complexity of this instance
 96      */
 97     complexity: function() {
 98       return 1;
 99     }
100   });
101   
102   /**
103    * List of attribute names to account for when parsing SVG element (used by {@link fabric.Circle.fromElement})
104    * @static
105    * @see: http://www.w3.org/TR/SVG/shapes.html#CircleElement
106    */
107   fabric.Circle.ATTRIBUTE_NAMES = 'cx cy r fill fill-opacity opacity stroke stroke-width transform'.split(' ');
108   
109   /**
110    * Returns {@link fabric.Circle} instance from an SVG element
111    * @static
112    * @method fabric.Circle.fromElement
113    * @param element {SVGElement} element to parse
114    * @param options {Object} options object
115    * @throws {Error} If value of `r` attribute is missing or invalid
116    * @return {Object} instance of fabric.Circle
117    */
118   fabric.Circle.fromElement = function(element, options) {
119     options || (options = { });
120     var parsedAttributes = fabric.parseAttributes(element, fabric.Circle.ATTRIBUTE_NAMES);
121     if (!isValidRadius(parsedAttributes)) {
122       throw Error('value of `r` attribute is required and can not be negative');
123     }
124     if ('left' in parsedAttributes) {
125       parsedAttributes.left -= (options.width / 2) || 0;
126     }
127     if ('top' in parsedAttributes) {
128       parsedAttributes.top -= (options.height / 2) || 0;
129     }
130     return new fabric.Circle(extend(parsedAttributes, options));
131   };
132   
133   /**
134    * @private
135    */
136   function isValidRadius(attributes) {
137     return (('radius' in attributes) && (attributes.radius > 0));
138   }
139   
140   /**
141    * Returns {@link fabric.Circle} instance from an object representation
142    * @static
143    * @method fabric.Circle.fromObject
144    * @param {Object} object Object to create an instance from
145    * @return {Object} Instance of fabric.Circle
146    */
147   fabric.Circle.fromObject = function(object) {
148     return new fabric.Circle(object);
149   };
150   
151 })(this);