1 /****************************************************************************
  2  Copyright (c) 2008-2010 Ricardo Quesada
  3  Copyright (c) 2011-2012 cocos2d-x.org
  4  Copyright (c) 2013-2014 Chukong Technologies Inc.
  5  Copyright (c) 2010      Lam Pham
  6 
  7  http://www.cocos2d-x.org
  8 
  9  Permission is hereby granted, free of charge, to any person obtaining a copy
 10  of this software and associated documentation files (the "Software"), to deal
 11  in the Software without restriction, including without limitation the rights
 12  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 13  copies of the Software, and to permit persons to whom the Software is
 14  furnished to do so, subject to the following conditions:
 15 
 16  The above copyright notice and this permission notice shall be included in
 17  all copies or substantial portions of the Software.
 18 
 19  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 20  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 21  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 22  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 23  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 24  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 25  THE SOFTWARE.
 26  ****************************************************************************/
 27 
 28 /**
 29  * cc.Progresstimer is a subclass of cc.Node.   <br/>
 30  * It renders the inner sprite according to the percentage.<br/>
 31  * The progress can be Radial, Horizontal or vertical.
 32  * @class
 33  * @extends cc.Node
 34  *
 35  * @property {cc.Point}     midPoint        <p>- Midpoint is used to modify the progress start position.<br/>
 36  *                                          If you're using radials type then the midpoint changes the center point<br/>
 37  *                                          If you're using bar type the the midpoint changes the bar growth<br/>
 38  *                                              it expands from the center but clamps to the sprites edge so:<br/>
 39  *                                              you want a left to right then set the midpoint all the way to cc.p(0,y)<br/>
 40  *                                              you want a right to left then set the midpoint all the way to cc.p(1,y)<br/>
 41  *                                              you want a bottom to top then set the midpoint all the way to cc.p(x,0)<br/>
 42  *                                              you want a top to bottom then set the midpoint all the way to cc.p(x,1)</p>
 43  * @property {cc.Point}     barChangeRate   - This allows the bar type to move the component at a specific rate.
 44  * @property {enum}         type            - Type of the progress timer: cc.ProgressTimer.TYPE_RADIAL|cc.ProgressTimer.TYPE_BAR.
 45  * @property {Number}       percentage      - Percentage to change progress, from 0 to 100.
 46  * @property {cc.Sprite}    sprite          - The sprite to show the progress percentage.
 47  * @property {Boolean}      reverseDir      - Indicate whether the direction is reversed.
 48  *
 49  */
 50 cc.ProgressTimer = cc.Node.extend(/** @lends cc.ProgressTimer# */{
 51     _type:null,
 52     _percentage:0.0,
 53     _sprite:null,
 54 
 55     _midPoint:null,
 56     _barChangeRate:null,
 57     _reverseDirection:false,
 58     _className:"ProgressTimer",
 59 
 60     /**
 61      *    Midpoint is used to modify the progress start position.
 62      *    If you're using radials type then the midpoint changes the center point
 63      *    If you're using bar type the the midpoint changes the bar growth
 64      *        it expands from the center but clamps to the sprites edge so:
 65      *        you want a left to right then set the midpoint all the way to cc.p(0,y)
 66      *        you want a right to left then set the midpoint all the way to cc.p(1,y)
 67      *        you want a bottom to top then set the midpoint all the way to cc.p(x,0)
 68      *        you want a top to bottom then set the midpoint all the way to cc.p(x,1)
 69      *  @return {cc.Point}
 70      */
 71     getMidpoint:function () {
 72         return cc.p(this._midPoint.x, this._midPoint.y);
 73     },
 74 
 75     /**
 76      * Midpoint setter
 77      * @param {cc.Point} mpoint
 78      */
 79     setMidpoint:function (mpoint) {
 80         this._midPoint = cc.pClamp(mpoint, cc.p(0, 0), cc.p(1, 1));
 81     },
 82 
 83     /**
 84      *    This allows the bar type to move the component at a specific rate
 85      *    Set the component to 0 to make sure it stays at 100%.
 86      *    For example you want a left to right bar but not have the height stay 100%
 87      *    Set the rate to be cc.p(0,1); and set the midpoint to = cc.p(0,.5f);
 88      *  @return {cc.Point}
 89      */
 90     getBarChangeRate:function () {
 91         return cc.p(this._barChangeRate.x, this._barChangeRate.y);
 92     },
 93 
 94     /**
 95      * @param {cc.Point} barChangeRate
 96      */
 97     setBarChangeRate:function (barChangeRate) {
 98         this._barChangeRate = cc.pClamp(barChangeRate, cc.p(0, 0), cc.p(1, 1));
 99     },
100 
101     /**
102      *  Change the percentage to change progress
103      * @return {cc.ProgressTimer.TYPE_RADIAL|cc.ProgressTimer.TYPE_BAR}
104      */
105     getType:function () {
106         return this._type;
107     },
108 
109     /**
110      * Percentages are from 0 to 100
111      * @return {Number}
112      */
113     getPercentage:function () {
114         return this._percentage;
115     },
116 
117     /**
118      * The image to show the progress percentage, retain
119      * @return {cc.Sprite}
120      */
121     getSprite:function () {
122         return this._sprite;
123     },
124 
125     /**
126      * from 0-100
127      * @param {Number} percentage
128      */
129     setPercentage:function (percentage) {
130         if (this._percentage != percentage) {
131             this._percentage = cc.clampf(percentage, 0, 100);
132             this._updateProgress();
133         }
134     },
135     /**
136      * only use for jsbinding
137      * @param bValue
138      */
139     setOpacityModifyRGB:function (bValue) {
140     },
141     /**
142      * only use for jsbinding
143      * @returns {boolean}
144      */
145     isOpacityModifyRGB:function () {
146         return false;
147     },
148     /**
149      * return if reverse direction
150      * @returns {boolean}
151      */
152     isReverseDirection:function () {
153         return this._reverseDirection;
154     },
155 
156     _boundaryTexCoord:function (index) {
157         if (index < cc.ProgressTimer.TEXTURE_COORDS_COUNT) {
158             var locProTextCoords = cc.ProgressTimer.TEXTURE_COORDS;
159             if (this._reverseDirection)
160                 return cc.p((locProTextCoords >> (7 - (index << 1))) & 1, (locProTextCoords >> (7 - ((index << 1) + 1))) & 1);
161             else
162                 return cc.p((locProTextCoords >> ((index << 1) + 1)) & 1, (locProTextCoords >> (index << 1)) & 1);
163         }
164         return cc.p(0,0);
165     },
166 
167     _origin:null,
168     _startAngle:270,
169     _endAngle:270,
170     _radius:0,
171     _counterClockWise:false,
172     _barRect:null,
173 
174     _vertexDataCount:0,
175     _vertexData:null,
176     _vertexArrayBuffer:null,
177     _vertexWebGLBuffer:null,
178     _vertexDataDirty:false,
179 
180     /**
181      * constructor of cc.cc.ProgressTimer
182      * @function
183      * @param {cc.Sprite} sprite
184      */
185     ctor: null,
186 
187     _ctorForCanvas: function (sprite) {
188         cc.Node.prototype.ctor.call(this);
189 
190         this._type = cc.ProgressTimer.TYPE_RADIAL;
191         this._percentage = 0.0;
192         this._midPoint = cc.p(0, 0);
193         this._barChangeRate = cc.p(0, 0);
194         this._reverseDirection = false;
195 
196         this._sprite = null;
197 
198         this._origin = cc.p(0,0);
199         this._startAngle = 270;
200         this._endAngle = 270;
201         this._radius = 0;
202         this._counterClockWise = false;
203         this._barRect = cc.rect(0, 0, 0, 0);
204 
205         sprite && this._initWithSpriteForCanvas(sprite);
206     },
207 
208     _ctorForWebGL: function (sprite) {
209         cc.Node.prototype.ctor.call(this);
210         this._type = cc.ProgressTimer.TYPE_RADIAL;
211         this._percentage = 0.0;
212         this._midPoint = cc.p(0, 0);
213         this._barChangeRate = cc.p(0, 0);
214         this._reverseDirection = false;
215 
216         this._sprite = null;
217 
218         this._vertexWebGLBuffer = cc._renderContext.createBuffer();
219         this._vertexDataCount = 0;
220         this._vertexData = null;
221         this._vertexArrayBuffer = null;
222         this._vertexDataDirty = false;
223 
224         sprite && this._initWithSpriteForWebGL(sprite);
225     },
226 
227     /**
228      * set color of sprite
229      * @param {cc.Color} color
230      */
231     setColor:function (color) {
232         this._sprite.color = color;
233         this._updateColor();
234     },
235 
236     /**
237      *  set opacity of sprite
238      * @param {Number} opacity
239      */
240     setOpacity:function (opacity) {
241         this._sprite.opacity = opacity;
242         this._updateColor();
243     },
244 
245     /**
246      * return color of sprite
247      * @return {cc.Color}
248      */
249     getColor:function () {
250         return this._sprite.color;
251     },
252 
253     /**
254      * return Opacity of sprite
255      * @return {Number}
256      */
257     getOpacity:function () {
258         return this._sprite.opacity;
259     },
260 
261     /**
262      * set reverse cc.ProgressTimer
263      * @function
264      * @param {Boolean} reverse
265      */
266     setReverseProgress:null,
267 
268     _setReverseProgressForCanvas:function (reverse) {
269         if (this._reverseDirection !== reverse)
270             this._reverseDirection = reverse;
271     },
272 
273     _setReverseProgressForWebGL:function (reverse) {
274         if (this._reverseDirection !== reverse) {
275             this._reverseDirection = reverse;
276 
277             //    release all previous information
278             this._vertexData = null;
279             this._vertexArrayBuffer = null;
280             this._vertexDataCount = 0;
281         }
282     },
283 
284     /**
285      * set sprite for cc.ProgressTimer
286      * @function
287      * @param {cc.Sprite} sprite
288      */
289     setSprite:null,
290 
291     _setSpriteForCanvas:function (sprite) {
292         if (this._sprite != sprite) {
293             this._sprite = sprite;
294             this.width = this._sprite.width;
295 	        this.height = this._sprite.height;
296         }
297     },
298 
299     _setSpriteForWebGL:function (sprite) {
300         if (sprite && this._sprite != sprite) {
301             this._sprite = sprite;
302             this.width = sprite.width;
303 	        this.height = sprite.height;
304 
305             //	Everytime we set a new sprite, we free the current vertex data
306             if (this._vertexData) {
307                 this._vertexData = null;
308                 this._vertexArrayBuffer = null;
309                 this._vertexDataCount = 0;
310             }
311         }
312     },
313 
314     /**
315      * set Progress type of cc.ProgressTimer
316      * @function
317      * @param {cc.ProgressTimer.TYPE_RADIAL|cc.ProgressTimer.TYPE_BAR} type
318      */
319     setType:null,
320 
321     _setTypeForCanvas:function (type) {
322         if (type !== this._type)
323             this._type = type;
324     },
325 
326     _setTypeForWebGL:function (type) {
327         if (type !== this._type) {
328             //	release all previous information
329             if (this._vertexData) {
330                 this._vertexData = null;
331                 this._vertexArrayBuffer = null;
332                 this._vertexDataCount = 0;
333             }
334             this._type = type;
335         }
336     },
337 
338     /**
339      * Reverse Progress setter
340      * @function
341      * @param {Boolean} reverse
342      */
343     setReverseDirection: null,
344 
345     _setReverseDirectionForCanvas: function (reverse) {
346         if (this._reverseDirection !== reverse)
347             this._reverseDirection = reverse;
348     },
349 
350     _setReverseDirectionForWebGL: function (reverse) {
351         if (this._reverseDirection !== reverse) {
352             this._reverseDirection = reverse;
353             //release all previous information
354             this._vertexData = null;
355             this._vertexArrayBuffer = null;
356             this._vertexDataCount = 0;
357         }
358     },
359 
360     /**
361      * @param {cc.Point} alpha
362      * @return {cc.Vertex2F | Object} the vertex position from the texture coordinate
363      * @private
364      */
365     _textureCoordFromAlphaPoint:function (alpha) {
366         var locSprite = this._sprite;
367         if (!locSprite) {
368             return {u:0, v:0}; //new cc.Tex2F(0, 0);
369         }
370         var quad = locSprite.quad;
371         var min = cc.p(quad.bl.texCoords.u, quad.bl.texCoords.v);
372         var max = cc.p(quad.tr.texCoords.u, quad.tr.texCoords.v);
373 
374         //  Fix bug #1303 so that progress timer handles sprite frame texture rotation
375         if (locSprite.textureRectRotated) {
376             var temp = alpha.x;
377             alpha.x = alpha.y;
378             alpha.y = temp;
379         }
380         return {u: min.x * (1 - alpha.x) + max.x * alpha.x, v: min.y * (1 - alpha.y) + max.y * alpha.y};
381     },
382 
383     _vertexFromAlphaPoint:function (alpha) {
384         if (!this._sprite) {
385             return {x: 0, y: 0};
386         }
387         var quad = this._sprite.quad;
388         var min = cc.p(quad.bl.vertices.x, quad.bl.vertices.y);
389         var max = cc.p(quad.tr.vertices.x, quad.tr.vertices.y);
390         return {x: min.x * (1 - alpha.x) + max.x * alpha.x, y: min.y * (1 - alpha.y) + max.y * alpha.y};
391     },
392 
393     /**
394      * Initializes a progress timer with the sprite as the shape the timer goes through
395      * @function
396      * @param {cc.Sprite} sprite
397      * @return {Boolean}
398      */
399     initWithSprite:null,
400 
401     _initWithSpriteForCanvas:function (sprite) {
402         this.percentage = 0;
403         this.anchorX = 0.5;
404 	    this.anchorY = 0.5;
405 
406         this._type = cc.ProgressTimer.TYPE_RADIAL;
407         this._reverseDirection = false;
408 	    this.midPoint = cc.p(0.5, 0.5);
409 	    this.barChangeRate = cc.p(1, 1);
410 	    this.sprite = sprite;
411 
412         return true;
413     },
414 
415     _initWithSpriteForWebGL:function (sprite) {
416         this.percentage = 0;
417         this._vertexData = null;
418         this._vertexArrayBuffer = null;
419         this._vertexDataCount = 0;
420         this.anchorX = 0.5;
421 	    this.anchorY = 0.5;
422 
423         this._type = cc.ProgressTimer.TYPE_RADIAL;
424         this._reverseDirection = false;
425         this.midPoint = cc.p(0.5, 0.5);
426         this.barChangeRate = cc.p(1, 1);
427         this.sprite = sprite;
428 
429         //shader program
430         this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_TEXTURECOLOR);
431         return true;
432     },
433 
434     /**
435      * Stuff gets drawn here
436      * @function
437      * @param {CanvasRenderingContext2D} ctx
438      */
439     draw:null,
440 
441     _drawForCanvas:function (ctx) {
442         var context = ctx || cc._renderContext;
443 
444         var locSprite = this._sprite;
445         if (locSprite._blendFuncStr != "source")
446             context.globalCompositeOperation = locSprite._blendFuncStr;
447 
448         var locEGL_ScaleX = cc.view.getScaleX(), locEGL_ScaleY = cc.view.getScaleY();
449 
450         context.globalAlpha = locSprite._displayedOpacity / 255;
451         var locRect = locSprite._rect, locContentSize = locSprite._contentSize, locOffsetPosition = locSprite._offsetPosition, locDrawSizeCanvas = locSprite._drawSize_Canvas;
452         var flipXOffset = 0 | (locOffsetPosition.x), flipYOffset = -locOffsetPosition.y - locRect.height, locTextureCoord = locSprite._textureRect_Canvas;
453         locDrawSizeCanvas.width = locRect.width * locEGL_ScaleX;
454         locDrawSizeCanvas.height = locRect.height * locEGL_ScaleY;
455 
456         context.save();
457         if (locSprite._flippedX) {
458             flipXOffset = -locOffsetPosition.x - locRect.width;
459             context.scale(-1, 1);
460         }
461         if (locSprite._flippedY) {
462             flipYOffset = locOffsetPosition.y;
463             context.scale(1, -1);
464         }
465 
466         flipXOffset *= locEGL_ScaleX;
467         flipYOffset *= locEGL_ScaleY;
468 
469         //clip
470         if (this._type == cc.ProgressTimer.TYPE_BAR) {
471             var locBarRect = this._barRect;
472             context.beginPath();
473             context.rect(locBarRect.x * locEGL_ScaleX, locBarRect.y * locEGL_ScaleY, locBarRect.width * locEGL_ScaleX, locBarRect.height * locEGL_ScaleY);
474             context.clip();
475             context.closePath();
476         } else if (this._type == cc.ProgressTimer.TYPE_RADIAL) {
477             var locOriginX = this._origin.x * locEGL_ScaleX;
478             var locOriginY = this._origin.y * locEGL_ScaleY;
479             context.beginPath();
480             context.arc(locOriginX, locOriginY, this._radius * locEGL_ScaleY, (Math.PI / 180) * this._startAngle, (Math.PI / 180) * this._endAngle, this._counterClockWise);
481             context.lineTo(locOriginX, locOriginY);
482             context.clip();
483             context.closePath();
484         }
485 
486         //draw sprite
487         if (locSprite._texture && locTextureCoord.validRect) {
488             var image = locSprite._texture.getHtmlElementObj();
489             if (locSprite._colorized) {
490                 context.drawImage(image,
491                     0, 0, locTextureCoord.width, locTextureCoord.height,
492                     flipXOffset, flipYOffset, locDrawSizeCanvas.width, locDrawSizeCanvas.height);
493             } else {
494                 context.drawImage(image,
495                     locTextureCoord.x, locTextureCoord.y, locTextureCoord.width,  locTextureCoord.height,
496                     flipXOffset, flipYOffset, locDrawSizeCanvas.width , locDrawSizeCanvas.height);
497             }
498         } else if (locContentSize.width !== 0) {
499             var curColor = this.color;
500             context.fillStyle = "rgba(" + curColor.r + "," + curColor.g + "," + curColor.b + ",1)";
501             context.fillRect(flipXOffset, flipYOffset, locContentSize.width * locEGL_ScaleX, locContentSize.height * locEGL_ScaleY);
502         }
503 
504         context.restore();
505         cc.incrementGLDraws(1);
506     },
507 
508     _drawForWebGL:function (ctx) {
509         var context = ctx || cc._renderContext;
510         if (!this._vertexData || !this._sprite)
511             return;
512 
513         cc.nodeDrawSetup(this);
514 
515         var blendFunc = this._sprite.getBlendFunc();
516         cc.glBlendFunc(blendFunc.src, blendFunc.dst);
517         cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);
518 
519         cc.glBindTexture2D(this._sprite.texture);
520 
521         context.bindBuffer(context.ARRAY_BUFFER, this._vertexWebGLBuffer);
522         if(this._vertexDataDirty){
523             context.bufferData(context.ARRAY_BUFFER, this._vertexArrayBuffer, context.DYNAMIC_DRAW);
524             this._vertexDataDirty = false;
525         }
526         var locVertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT;
527         context.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, context.FLOAT, false, locVertexDataLen, 0);
528         context.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, context.UNSIGNED_BYTE, true, locVertexDataLen, 8);
529         context.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, context.FLOAT, false, locVertexDataLen, 12);
530 
531         if (this._type === cc.ProgressTimer.TYPE_RADIAL)
532             context.drawArrays(context.TRIANGLE_FAN, 0, this._vertexDataCount);
533         else if (this._type == cc.ProgressTimer.TYPE_BAR) {
534             if (!this._reverseDirection)
535                 context.drawArrays(context.TRIANGLE_STRIP, 0, this._vertexDataCount);
536             else {
537                 context.drawArrays(context.TRIANGLE_STRIP, 0, this._vertexDataCount / 2);
538                 context.drawArrays(context.TRIANGLE_STRIP, 4, this._vertexDataCount / 2);
539                 // 2 draw calls
540                 cc.g_NumberOfDraws++;
541             }
542         }
543         cc.g_NumberOfDraws++;
544     },
545 
546     /**
547      * <p>
548      *    Update does the work of mapping the texture onto the triangles            <br/>
549      *    It now doesn't occur the cost of free/alloc data every update cycle.      <br/>
550      *    It also only changes the percentage point but no other points if they have not been modified.       <br/>
551      *                                                                              <br/>
552      *    It now deals with flipped texture. If you run into this problem, just use the                       <br/>
553      *    sprite property and enable the methods flipX, flipY.                      <br/>
554      * </p>
555      * @private
556      */
557     _updateRadial:function () {
558         if (!this._sprite)
559             return;
560 
561         var i, locMidPoint = this._midPoint;
562         var alpha = this._percentage / 100;
563         var angle = 2 * (cc.PI) * ( this._reverseDirection ? alpha : 1.0 - alpha);
564 
565         //    We find the vector to do a hit detection based on the percentage
566         //    We know the first vector is the one @ 12 o'clock (top,mid) so we rotate
567         //    from that by the progress angle around the m_tMidpoint pivot
568         var topMid = cc.p(locMidPoint.x, 1);
569         var percentagePt = cc.pRotateByAngle(topMid, locMidPoint, angle);
570 
571         var index = 0;
572         var hit;
573 
574         if (alpha == 0) {
575             //    More efficient since we don't always need to check intersection
576             //    If the alpha is zero then the hit point is top mid and the index is 0.
577             hit = topMid;
578             index = 0;
579         } else if (alpha == 1) {
580             //    More efficient since we don't always need to check intersection
581             //    If the alpha is one then the hit point is top mid and the index is 4.
582             hit = topMid;
583             index = 4;
584         } else {
585             //    We run a for loop checking the edges of the texture to find the
586             //    intersection point
587             //    We loop through five points since the top is split in half
588 
589             var min_t = cc.FLT_MAX;
590             var locProTextCoordsCount = cc.ProgressTimer.TEXTURE_COORDS_COUNT;
591             for (i = 0; i <= locProTextCoordsCount; ++i) {
592                 var pIndex = (i + (locProTextCoordsCount - 1)) % locProTextCoordsCount;
593 
594                 var edgePtA = this._boundaryTexCoord(i % locProTextCoordsCount);
595                 var edgePtB = this._boundaryTexCoord(pIndex);
596 
597                 //    Remember that the top edge is split in half for the 12 o'clock position
598                 //    Let's deal with that here by finding the correct endpoints
599                 if (i == 0)
600                     edgePtB = cc.pLerp(edgePtA, edgePtB, 1 - locMidPoint.x);
601                 else if (i == 4)
602                     edgePtA = cc.pLerp(edgePtA, edgePtB, 1 - locMidPoint.x);
603 
604                 // retPoint are returned by ccpLineIntersect
605                 var retPoint = cc.p(0, 0);
606                 if (cc.pLineIntersect(edgePtA, edgePtB, locMidPoint, percentagePt, retPoint)) {
607                     //    Since our hit test is on rays we have to deal with the top edge
608                     //    being in split in half so we have to test as a segment
609                     if ((i == 0 || i == 4)) {
610                         //    s represents the point between edgePtA--edgePtB
611                         if (!(0 <= retPoint.x && retPoint.x <= 1))
612                             continue;
613                     }
614                     //    As long as our t isn't negative we are at least finding a
615                     //    correct hitpoint from m_tMidpoint to percentagePt.
616                     if (retPoint.y >= 0) {
617                         //    Because the percentage line and all the texture edges are
618                         //    rays we should only account for the shortest intersection
619                         if (retPoint.y < min_t) {
620                             min_t = retPoint.y;
621                             index = i;
622                         }
623                     }
624                 }
625             }
626 
627             //    Now that we have the minimum magnitude we can use that to find our intersection
628             hit = cc.pAdd(locMidPoint, cc.pMult(cc.pSub(percentagePt, locMidPoint), min_t));
629         }
630 
631         //    The size of the vertex data is the index from the hitpoint
632         //    the 3 is for the m_tMidpoint, 12 o'clock point and hitpoint position.
633         var sameIndexCount = true;
634         if (this._vertexDataCount != index + 3) {
635             sameIndexCount = false;
636             this._vertexData = null;
637             this._vertexArrayBuffer = null;
638             this._vertexDataCount = 0;
639         }
640 
641         if (!this._vertexData) {
642             this._vertexDataCount = index + 3;
643             var locCount = this._vertexDataCount, vertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT;
644             this._vertexArrayBuffer = new ArrayBuffer(locCount * vertexDataLen);
645             var locData = [];
646             for (i = 0; i < locCount; i++)
647                 locData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * vertexDataLen);
648 
649             this._vertexData = locData;
650             if(!this._vertexData){
651                 cc.log( "cc.ProgressTimer._updateRadial() : Not enough memory");
652                 return;
653             }
654         }
655         this._updateColor();
656 
657         var locVertexData = this._vertexData;
658         if (!sameIndexCount) {
659             //    First we populate the array with the m_tMidpoint, then all
660             //    vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint
661             locVertexData[0].texCoords = this._textureCoordFromAlphaPoint(locMidPoint);
662             locVertexData[0].vertices = this._vertexFromAlphaPoint(locMidPoint);
663 
664             locVertexData[1].texCoords = this._textureCoordFromAlphaPoint(topMid);
665             locVertexData[1].vertices = this._vertexFromAlphaPoint(topMid);
666 
667             for (i = 0; i < index; i++) {
668                 var alphaPoint = this._boundaryTexCoord(i);
669                 locVertexData[i + 2].texCoords = this._textureCoordFromAlphaPoint(alphaPoint);
670                 locVertexData[i + 2].vertices = this._vertexFromAlphaPoint(alphaPoint);
671             }
672         }
673 
674         //    hitpoint will go last
675         locVertexData[this._vertexDataCount - 1].texCoords = this._textureCoordFromAlphaPoint(hit);
676         locVertexData[this._vertexDataCount - 1].vertices = this._vertexFromAlphaPoint(hit);
677     },
678 
679     /**
680      * <p>
681      *    Update does the work of mapping the texture onto the triangles for the bar                            <br/>
682      *    It now doesn't occur the cost of free/alloc data every update cycle.                                  <br/>
683      *    It also only changes the percentage point but no other points if they have not been modified.         <br/>
684      *                                                                                                          <br/>
685      *    It now deals with flipped texture. If you run into this problem, just use the                         <br/>
686      *    sprite property and enable the methods flipX, flipY.                                                  <br/>
687      * </p>
688      * @private
689      */
690     _updateBar:function () {
691         if (!this._sprite)
692             return;
693 
694         var i;
695         var alpha = this._percentage / 100.0;
696         var locBarChangeRate = this._barChangeRate;
697         var alphaOffset = cc.pMult(cc.p((1.0 - locBarChangeRate.x) + alpha * locBarChangeRate.x,
698             (1.0 - locBarChangeRate.y) + alpha * locBarChangeRate.y), 0.5);
699         var min = cc.pSub(this._midPoint, alphaOffset);
700         var max = cc.pAdd(this._midPoint, alphaOffset);
701 
702         if (min.x < 0) {
703             max.x += -min.x;
704             min.x = 0;
705         }
706 
707         if (max.x > 1) {
708             min.x -= max.x - 1;
709             max.x = 1;
710         }
711 
712         if (min.y < 0) {
713             max.y += -min.y;
714             min.y = 0;
715         }
716 
717         if (max.y > 1) {
718             min.y -= max.y - 1;
719             max.y = 1;
720         }
721 
722         var locVertexData;
723         if (!this._reverseDirection) {
724             if (!this._vertexData) {
725                 this._vertexDataCount = 4;
726                 var vertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT, locCount = 4;
727                 this._vertexArrayBuffer = new ArrayBuffer(locCount * vertexDataLen);
728                 this._vertexData = [];
729                 for (i = 0; i < locCount; i++)
730                     this._vertexData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * vertexDataLen);
731             }
732 
733             locVertexData = this._vertexData;
734             //    TOPLEFT
735             locVertexData[0].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, max.y));
736             locVertexData[0].vertices = this._vertexFromAlphaPoint(cc.p(min.x, max.y));
737 
738             //    BOTLEFT
739             locVertexData[1].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, min.y));
740             locVertexData[1].vertices = this._vertexFromAlphaPoint(cc.p(min.x, min.y));
741 
742             //    TOPRIGHT
743             locVertexData[2].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, max.y));
744             locVertexData[2].vertices = this._vertexFromAlphaPoint(cc.p(max.x, max.y));
745 
746             //    BOTRIGHT
747             locVertexData[3].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, min.y));
748             locVertexData[3].vertices = this._vertexFromAlphaPoint(cc.p(max.x, min.y));
749         } else {
750             if (!this._vertexData) {
751                 this._vertexDataCount = 8;
752                 var rVertexDataLen = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT, rLocCount = 8;
753                 this._vertexArrayBuffer = new ArrayBuffer(rLocCount * rVertexDataLen);
754                 var rTempData = [];
755                 for (i = 0; i < rLocCount; i++)
756                     rTempData[i] = new cc.V2F_C4B_T2F(null, null, null, this._vertexArrayBuffer, i * rVertexDataLen);
757                 //    TOPLEFT 1
758                 rTempData[0].texCoords = this._textureCoordFromAlphaPoint(cc.p(0, 1));
759                 rTempData[0].vertices = this._vertexFromAlphaPoint(cc.p(0, 1));
760 
761                 //    BOTLEFT 1
762                 rTempData[1].texCoords = this._textureCoordFromAlphaPoint(cc.p(0, 0));
763                 rTempData[1].vertices = this._vertexFromAlphaPoint(cc.p(0, 0));
764 
765                 //    TOPRIGHT 2
766                 rTempData[6].texCoords = this._textureCoordFromAlphaPoint(cc.p(1, 1));
767                 rTempData[6].vertices = this._vertexFromAlphaPoint(cc.p(1, 1));
768 
769                 //    BOTRIGHT 2
770                 rTempData[7].texCoords = this._textureCoordFromAlphaPoint(cc.p(1, 0));
771                 rTempData[7].vertices = this._vertexFromAlphaPoint(cc.p(1, 0));
772 
773                 this._vertexData = rTempData;
774             }
775 
776             locVertexData = this._vertexData;
777             //    TOPRIGHT 1
778             locVertexData[2].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, max.y));
779             locVertexData[2].vertices = this._vertexFromAlphaPoint(cc.p(min.x, max.y));
780 
781             //    BOTRIGHT 1
782             locVertexData[3].texCoords = this._textureCoordFromAlphaPoint(cc.p(min.x, min.y));
783             locVertexData[3].vertices = this._vertexFromAlphaPoint(cc.p(min.x, min.y));
784 
785             //    TOPLEFT 2
786             locVertexData[4].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, max.y));
787             locVertexData[4].vertices = this._vertexFromAlphaPoint(cc.p(max.x, max.y));
788 
789             //    BOTLEFT 2
790             locVertexData[5].texCoords = this._textureCoordFromAlphaPoint(cc.p(max.x, min.y));
791             locVertexData[5].vertices = this._vertexFromAlphaPoint(cc.p(max.x, min.y));
792         }
793         this._updateColor();
794     },
795 
796     _updateColor:function () {
797         if (!this._sprite || !this._vertexData)
798             return;
799 
800         var sc = this._sprite.quad.tl.colors;
801         var locVertexData = this._vertexData;
802         for (var i = 0, len = this._vertexDataCount; i < len; ++i)
803             locVertexData[i].colors = sc;
804         this._vertexDataDirty = true;
805     },
806 
807     _updateProgress:null,
808 
809     _updateProgressForCanvas:function () {
810         var locSprite = this._sprite;
811         var sw = locSprite.width, sh = locSprite.height;
812         var locMidPoint = this._midPoint;
813 
814         if (this._type == cc.ProgressTimer.TYPE_RADIAL) {
815             this._radius = Math.round(Math.sqrt(sw * sw + sh * sh));
816             var locStartAngle, locEndAngle, locCounterClockWise = false, locOrigin = this._origin;
817             locOrigin.x = sw * locMidPoint.x;
818             locOrigin.y = -sh * locMidPoint.y;
819 
820             if (this._reverseDirection) {
821                 locEndAngle = 270;
822                 locStartAngle = 270 - 3.6 * this._percentage;
823             } else {
824                 locStartAngle = -90;
825                 locEndAngle = -90 + 3.6 * this._percentage;
826             }
827 
828             if (locSprite._flippedX) {
829                 locOrigin.x -= sw * (this._midPoint.x * 2);
830                 locStartAngle= -locStartAngle;
831                 locEndAngle= -locEndAngle;
832                 locStartAngle -= 180;
833                 locEndAngle -= 180;
834                 locCounterClockWise = !locCounterClockWise;
835             }
836             if (locSprite._flippedY) {
837                 locOrigin.y+=sh*(this._midPoint.y*2);
838                 locCounterClockWise = !locCounterClockWise;
839                 locStartAngle= -locStartAngle;
840                 locEndAngle= -locEndAngle;
841             }
842 
843             this._startAngle = locStartAngle;
844             this._endAngle = locEndAngle;
845             this._counterClockWise = locCounterClockWise;
846         } else {
847             var locBarChangeRate = this._barChangeRate;
848             var percentageF = this._percentage / 100;
849             var locBarRect = this._barRect;
850 
851             var drawedSize = cc.size((sw * (1 - locBarChangeRate.x)), (sh * (1 - locBarChangeRate.y)));
852             var drawingSize = cc.size((sw - drawedSize.width) * percentageF, (sh - drawedSize.height) * percentageF);
853             var currentDrawSize = cc.size(drawedSize.width + drawingSize.width, drawedSize.height + drawingSize.height);
854 
855             var startPoint = cc.p(sw * locMidPoint.x, sh * locMidPoint.y);
856 
857             var needToLeft = startPoint.x - currentDrawSize.width / 2;
858             if (locMidPoint.x > 0.5) {
859                 if (currentDrawSize.width / 2 >= sw - startPoint.x) {
860                     needToLeft = sw - currentDrawSize.width;
861                 }
862             }
863 
864             var needToTop = startPoint.y - currentDrawSize.height / 2;
865             if (locMidPoint.y > 0.5) {
866                 if (currentDrawSize.height / 2 >= sh - startPoint.y) {
867                     needToTop = sh - currentDrawSize.height;
868                 }
869             }
870 
871             //left pos
872             locBarRect.x = 0;
873             var flipXNeed = 1;
874             if (locSprite._flippedX) {
875                 locBarRect.x -= currentDrawSize.width;
876                 flipXNeed = -1;
877             }
878 
879             if (needToLeft > 0)
880                 locBarRect.x += needToLeft * flipXNeed;
881 
882             //right pos
883             locBarRect.y = 0;
884             var flipYNeed = 1;
885             if (locSprite._flippedY) {
886                 locBarRect.y += currentDrawSize.height;
887                 flipYNeed = -1;
888             }
889 
890             if (needToTop > 0)
891                 locBarRect.y -= needToTop * flipYNeed;
892 
893             //clip width and clip height
894             locBarRect.width = currentDrawSize.width;
895             locBarRect.height = -currentDrawSize.height;
896         }
897     },
898 
899     _updateProgressForWebGL:function () {
900         var locType = this._type;
901         if(locType === cc.ProgressTimer.TYPE_RADIAL)
902             this._updateRadial();
903         else if(locType === cc.ProgressTimer.TYPE_BAR)
904             this._updateBar();
905         this._vertexDataDirty = true;
906     }
907 });
908 
909 var _p = cc.ProgressTimer.prototype;
910 if(cc._renderType == cc._RENDER_TYPE_WEBGL){
911     _p.ctor = _p._ctorForWebGL;
912     _p.setReverseProgress = _p._setReverseProgressForWebGL;
913     _p.setSprite = _p._setSpriteForWebGL;
914     _p.setType = _p._setTypeForWebGL;
915     _p.setReverseDirection = _p._setReverseDirectionForWebGL;
916     _p.initWithSprite = _p._initWithSpriteForWebGL;
917     _p.draw = _p._drawForWebGL;
918     _p._updateProgress = _p._updateProgressForWebGL;
919 } else {
920     _p.ctor = _p._ctorForCanvas;
921     _p.setReverseProgress = _p._setReverseProgressForCanvas;
922     _p.setSprite = _p._setSpriteForCanvas;
923     _p.setType = _p._setTypeForCanvas;
924     _p.setReverseDirection = _p._setReverseDirectionForCanvas;
925     _p.initWithSprite = _p._initWithSpriteForCanvas;
926     _p.draw = _p._drawForCanvas;
927     _p._updateProgress = cc.ProgressTimer.prototype._updateProgressForCanvas;
928 }
929 
930 // Extended properties
931 /** @expose */
932 _p.midPoint;
933 cc.defineGetterSetter(_p, "midPoint", _p.getMidpoint, _p.setMidpoint);
934 /** @expose */
935 _p.barChangeRate;
936 cc.defineGetterSetter(_p, "barChangeRate", _p.getBarChangeRate, _p.setBarChangeRate);
937 /** @expose */
938 _p.type;
939 cc.defineGetterSetter(_p, "type", _p.getType, _p.setType);
940 /** @expose */
941 _p.percentage;
942 cc.defineGetterSetter(_p, "percentage", _p.getPercentage, _p.setPercentage);
943 /** @expose */
944 _p.sprite;
945 cc.defineGetterSetter(_p, "sprite", _p.getSprite, _p.setSprite);
946 /** @expose */
947 _p.reverseDir;
948 cc.defineGetterSetter(_p, "reverseDir", _p.isReverseDirection, _p.setReverseDirection);
949 
950 
951 /**
952  * create a progress timer object with image file name that renders the inner sprite according to the percentage
953  * @deprecated since v3.0,please use new cc.ProgressTimer(sprite) instead.
954  * @param {cc.Sprite} sprite
955  * @return {cc.ProgressTimer}
956  * @example
957  * // Example
958  * var progress = cc.ProgressTimer.create('progress.png')
959  */
960 cc.ProgressTimer.create = function (sprite) {
961     return new cc.ProgressTimer(sprite);
962 };
963 
964 /**
965  * @constant
966  * @type Number
967  */
968 cc.ProgressTimer.TEXTURE_COORDS_COUNT = 4;
969 
970 /**
971  * @constant
972  * @type Number
973  */
974 cc.ProgressTimer.TEXTURE_COORDS = 0x4b;
975 
976 /**
977  * Radial Counter-Clockwise
978  * @type Number
979  * @constant
980  */
981 cc.ProgressTimer.TYPE_RADIAL = 0;
982 
983 /**
984  * Bar
985  * @type Number
986  * @constant
987  */
988 cc.ProgressTimer.TYPE_BAR = 1;
989