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