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