1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2008-2010 Ricardo Quesada
  4  Copyright (c) 2011      Zynga Inc.
  5  Copyright (c) 2012 Scott Lembcke and Howling Moon Software
  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  * Code copied & pasted from SpacePatrol game https://github.com/slembcke/SpacePatrol
 30  *
 31  * Renamed and added some changes for cocos2d
 32  *
 33  */
 34 cc.v2fzero = function () {
 35     return {x: 0, y: 0};
 36 };
 37 
 38 cc.v2f = function (x, y) {
 39     return {x: x, y: y};
 40 };
 41 
 42 cc.v2fadd = function (v0, v1) {
 43     return cc.v2f(v0.x + v1.x, v0.y + v1.y);
 44 };
 45 
 46 cc.v2fsub = function (v0, v1) {
 47     return cc.v2f(v0.x - v1.x, v0.y - v1.y);
 48 };
 49 
 50 cc.v2fmult = function (v, s) {
 51     return cc.v2f(v.x * s, v.y * s);
 52 };
 53 
 54 cc.v2fperp = function (p0) {
 55     return cc.v2f(-p0.y, p0.x);
 56 };
 57 
 58 cc.v2fneg = function (p0) {
 59     return cc.v2f(-p0.x, -p0.y);
 60 };
 61 
 62 cc.v2fdot = function (p0, p1) {
 63     return  p0.x * p1.x + p0.y * p1.y;
 64 };
 65 
 66 cc.v2fforangle = function (_a_) {
 67     return cc.v2f(Math.cos(_a_), Math.sin(_a_));
 68 };
 69 
 70 cc.v2fnormalize = function (p) {
 71     var r = cc.pNormalize(cc.p(p.x, p.y));
 72     return cc.v2f(r.x, r.y);
 73 };
 74 
 75 cc.__v2f = function (v) {
 76     return cc.v2f(v.x, v.y);
 77 };
 78 
 79 cc.__t = function (v) {
 80     return {u: v.x, v: v.y};
 81 };
 82 
 83 /**
 84  * <p>CCDrawNode                                                <br/>
 85  * Node that draws dots, segments and polygons.                        <br/>
 86  * Faster than the "drawing primitives" since they it draws everything in one single batch.</p>
 87  * @class
 88  * @name cc.DrawNode
 89  * @extends cc.Node
 90  */
 91 cc.DrawNodeCanvas = cc.Node.extend(/** @lends cc.DrawNode# */{
 92     _buffer: null,
 93     _blendFunc: null,
 94     _lineWidth: 1,
 95     _drawColor: null,
 96     _className:"DrawNodeCanvas",
 97 
 98 	/*
 99 	 * @constructor
100 	 */
101     ctor: function () {
102         cc.Node.prototype.ctor.call(this);
103         this._buffer = [];
104         this._drawColor = cc.color(255, 255, 255, 255);
105         this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST);
106 
107 		this.init();
108     },
109 
110     // ----common function start ----
111     getBlendFunc: function () {
112         return this._blendFunc;
113     },
114 
115     setBlendFunc: function (blendFunc, dst) {
116         if (dst === undefined) {
117             this._blendFunc.src = blendFunc.src;
118             this._blendFunc.dst = blendFunc.dst;
119         } else {
120             this._blendFunc.src = blendFunc;
121             this._blendFunc.dst = dst;
122         }
123     },
124 
125     /**
126      * line width setter
127      * @param {Number} width
128      */
129     setLineWidth: function (width) {
130         this._lineWidth = width;
131     },
132 
133     /**
134      * line width getter
135      * @returns {Number}
136      */
137     getLineWidth: function () {
138         return this._lineWidth;
139     },
140 
141     /**
142      * draw color setter
143      * @param {cc.Color} color
144      */
145     setDrawColor: function (color) {
146         var locDrawColor = this._drawColor;
147         locDrawColor.r = color.r;
148         locDrawColor.g = color.g;
149         locDrawColor.b = color.b;
150         locDrawColor.a = (color.a == null) ? 255 : color.a;
151     },
152 
153     /**
154      * draw color getter
155      * @returns {cc.Color}
156      */
157     getDrawColor: function () {
158         return  cc.color(this._drawColor.r, this._drawColor.g, this._drawColor.b, this._drawColor.a);
159     },
160     // ----common function end ----
161 
162 
163     /**
164      * draws a rectangle given the origin and destination point measured in points.
165      * @param {cc.Point} origin
166      * @param {cc.Point} destination
167      *  @param {cc.Color} fillColor
168      * @param {Number} lineWidth
169      * @param {cc.Color} lineColor
170      */
171     drawRect: function (origin, destination, fillColor, lineWidth, lineColor) {
172         lineWidth = lineWidth || this._lineWidth;
173         lineColor = lineColor || this.getDrawColor();
174         if(lineColor.a == null)
175             lineColor.a = 255;
176 
177         var vertices = [
178             origin,
179             cc.p(destination.x, origin.y),
180             destination,
181             cc.p(origin.x, destination.y)
182         ];
183         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
184         element.verts = vertices;
185         element.lineWidth = lineWidth;
186         element.lineColor = lineColor;
187         element.isClosePolygon = true;
188         element.isStroke = true;
189         element.lineCap = "butt";
190         element.fillColor = fillColor;
191         if (fillColor) {
192             if(fillColor.a == null)
193                 fillColor.a = 255;
194             element.isFill = true;
195         }
196         this._buffer.push(element);
197     },
198 
199     /**
200      * draws a circle given the center, radius and number of segments.
201      * @override
202      * @param {cc.Point} center center of circle
203      * @param {Number} radius
204      * @param {Number} angle angle in radians
205      * @param {Number} segments
206      * @param {Boolean} drawLineToCenter
207      * @param {Number} lineWidth
208      * @param {cc.Color} color
209      */
210     drawCircle: function (center, radius, angle, segments, drawLineToCenter, lineWidth, color) {
211         lineWidth = lineWidth || this._lineWidth;
212         color = color || this.getDrawColor();
213         if (color.a == null)
214             color.a = 255;
215 
216         var coef = 2.0 * Math.PI / segments;
217         var vertices = [];
218         for (var i = 0; i <= segments; i++) {
219             var rads = i * coef;
220             var j = radius * Math.cos(rads + angle) + center.x;
221             var k = radius * Math.sin(rads + angle) + center.y;
222             vertices.push(cc.p(j, k));
223         }
224         if (drawLineToCenter) {
225             vertices.push(cc.p(center.x, center.y));
226         }
227 
228         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
229         element.verts = vertices;
230         element.lineWidth = lineWidth;
231         element.lineColor = color;
232         element.isClosePolygon = true;
233         element.isStroke = true;
234         this._buffer.push(element);
235     },
236 
237     /**
238      * draws a quad bezier path
239      * @override
240      * @param {cc.Point} origin
241      * @param {cc.Point} control
242      * @param {cc.Point} destination
243      * @param {Number} segments
244      * @param {Number} lineWidth
245      * @param {cc.Color} color
246      */
247     drawQuadBezier: function (origin, control, destination, segments, lineWidth, color) {
248         lineWidth = lineWidth || this._lineWidth;
249         color = color || this.getDrawColor();
250         if (color.a == null)
251             color.a = 255;
252 
253         var vertices = [], t = 0.0;
254         for (var i = 0; i < segments; i++) {
255             var x = Math.pow(1 - t, 2) * origin.x + 2.0 * (1 - t) * t * control.x + t * t * destination.x;
256             var y = Math.pow(1 - t, 2) * origin.y + 2.0 * (1 - t) * t * control.y + t * t * destination.y;
257             vertices.push(cc.p(x, y));
258             t += 1.0 / segments;
259         }
260         vertices.push(cc.p(destination.x, destination.y));
261 
262         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
263         element.verts = vertices;
264         element.lineWidth = lineWidth;
265         element.lineColor = color;
266         element.isStroke = true;
267         element.lineCap = "round";
268         this._buffer.push(element);
269     },
270 
271     /**
272      * draws a cubic bezier path
273      * @override
274      * @param {cc.Point} origin
275      * @param {cc.Point} control1
276      * @param {cc.Point} control2
277      * @param {cc.Point} destination
278      * @param {Number} segments
279      * @param {Number} lineWidth
280      * @param {cc.Color} color
281      */
282     drawCubicBezier: function (origin, control1, control2, destination, segments, lineWidth, color) {
283         lineWidth = lineWidth || this._lineWidth;
284         color = color || this.getDrawColor();
285         if (color.a == null)
286             color.a = 255;
287 
288         var vertices = [], t = 0;
289         for (var i = 0; i < segments; i++) {
290             var x = Math.pow(1 - t, 3) * origin.x + 3.0 * Math.pow(1 - t, 2) * t * control1.x + 3.0 * (1 - t) * t * t * control2.x + t * t * t * destination.x;
291             var y = Math.pow(1 - t, 3) * origin.y + 3.0 * Math.pow(1 - t, 2) * t * control1.y + 3.0 * (1 - t) * t * t * control2.y + t * t * t * destination.y;
292             vertices.push(cc.p(x, y));
293             t += 1.0 / segments;
294         }
295         vertices.push(cc.p(destination.x, destination.y));
296 
297         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
298         element.verts = vertices;
299         element.lineWidth = lineWidth;
300         element.lineColor = color;
301         element.isStroke = true;
302         element.lineCap = "round";
303         this._buffer.push(element);
304     },
305 
306     /**
307      * draw a CatmullRom curve
308      * @override
309      * @param {Array} points
310      * @param {Number} segments
311      * @param {Number} lineWidth
312      * @param {cc.Color} color
313      */
314     drawCatmullRom: function (points, segments, lineWidth, color) {
315         this.drawCardinalSpline(points, 0.5, segments, lineWidth, color);
316     },
317 
318     /**
319      * draw a cardinal spline path
320      * @override
321      * @param {Array} config
322      * @param {Number} tension
323      * @param {Number} segments
324      * @param {Number} lineWidth
325      * @param {cc.Color} color
326      */
327     drawCardinalSpline: function (config, tension, segments, lineWidth, color) {
328         lineWidth = lineWidth || this._lineWidth;
329         color = color || this.getDrawColor();
330         if(color.a == null)
331             color.a = 255;
332 
333         var vertices = [], p, lt, deltaT = 1.0 / config.length;
334         for (var i = 0; i < segments + 1; i++) {
335             var dt = i / segments;
336             // border
337             if (dt == 1) {
338                 p = config.length - 1;
339                 lt = 1;
340             } else {
341                 p = 0 | (dt / deltaT);
342                 lt = (dt - deltaT * p) / deltaT;
343             }
344 
345             // Interpolate
346             var newPos = cc.cardinalSplineAt(
347                 cc.getControlPointAt(config, p - 1),
348                 cc.getControlPointAt(config, p - 0),
349                 cc.getControlPointAt(config, p + 1),
350                 cc.getControlPointAt(config, p + 2),
351                 tension, lt);
352             vertices.push(newPos);
353         }
354 
355         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
356         element.verts = vertices;
357         element.lineWidth = lineWidth;
358         element.lineColor = color;
359         element.isStroke = true;
360         element.lineCap = "round";
361         this._buffer.push(element);
362     },
363 
364     /**
365      *  draw a dot at a position, with a given radius and color
366      * @param {cc.Point} pos
367      * @param {Number} radius
368      * @param {cc.Color} color
369      */
370     drawDot: function (pos, radius, color) {
371         color = color || this.getDrawColor();
372         if (color.a == null)
373             color.a = 255;
374         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_DOT);
375         element.verts = [pos];
376         element.lineWidth = radius;
377         element.fillColor = color;
378         this._buffer.push(element);
379     },
380 
381     /**
382      * draws an array of points.
383      * @override
384      * @param {Array} points point of array
385      * @param {Number} radius
386      * @param {cc.Color} color
387      */
388     drawDots: function(points, radius, color){
389         if(!points || points.length == 0)
390             return;
391         color = color || this.getDrawColor();
392         if (color.a == null)
393             color.a = 255;
394         for(var i = 0, len = points.length; i < len; i++)
395            this.drawDot(points[i], radius, color);
396     },
397 
398     /**
399      * draw a segment with a radius and color
400      * @param {cc.Point} from
401      * @param {cc.Point} to
402      * @param {Number} lineWidth
403      * @param {cc.Color} color
404      */
405     drawSegment: function (from, to, lineWidth, color) {
406         lineWidth = lineWidth || this._lineWidth;
407         color = color || this.getDrawColor();
408         if (color.a == null)
409             color.a = 255;
410         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
411         element.verts = [from, to];
412         element.lineWidth = lineWidth * 2;
413         element.lineColor = color;
414         element.isStroke = true;
415         element.lineCap = "round";
416         this._buffer.push(element);
417     },
418 
419     /**
420      * draw a polygon with a fill color and line color without copying the vertex list
421      * @param {Array} verts
422      * @param {cc.Color} fillColor
423      * @param {Number} lineWidth
424      * @param {cc.Color} color
425      */
426     drawPoly_: function (verts, fillColor, lineWidth, color) {
427         lineWidth = lineWidth || this._lineWidth;
428         color = color || this.getDrawColor();
429         if (color.a == null)
430             color.a = 255;
431         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
432         
433         element.verts = verts;
434         element.fillColor = fillColor;
435         element.lineWidth = lineWidth;
436         element.lineColor = color;
437         element.isClosePolygon = true;
438         element.isStroke = true;
439         element.lineCap = "round";
440         if (fillColor)
441             element.isFill = true;
442         this._buffer.push(element);
443     },
444     
445     /**
446      * draw a polygon with a fill color and line color, copying the vertex list
447      * @param {Array} verts
448      * @param {cc.Color} fillColor
449      * @param {Number} lineWidth
450      * @param {cc.Color} color
451      */
452     drawPoly: function (verts, fillColor, lineWidth, color) {
453         var vertsCopy = [];
454         for (var i=0; i < verts.length; i++) {
455             vertsCopy.push(cc.p(verts[i].x, verts[i].y));
456         }
457         return this.drawPoly_(vertsCopy, fillColor, lineWidth, color);     
458     },
459 
460     draw: function (ctx) {
461         var context = ctx || cc._renderContext, _t = this;
462         if ((_t._blendFunc && (_t._blendFunc.src == cc.SRC_ALPHA) && (_t._blendFunc.dst == cc.ONE)))
463             context.globalCompositeOperation = 'lighter';
464 
465         for (var i = 0; i < _t._buffer.length; i++) {
466             var element = _t._buffer[i];
467             switch (element.type) {
468                 case cc.DrawNode.TYPE_DOT:
469                     _t._drawDot(context, element);
470                     break;
471                 case cc.DrawNode.TYPE_SEGMENT:
472                     _t._drawSegment(context, element);
473                     break;
474                 case cc.DrawNode.TYPE_POLY:
475                     _t._drawPoly(context, element);
476                     break;
477             }
478         }
479     },
480 
481     _drawDot: function (ctx, element) {
482         var locColor = element.fillColor, locPos = element.verts[0], locRadius = element.lineWidth;
483         var locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY();
484 
485         ctx.fillStyle = "rgba(" + (0 | locColor.r) + "," + (0 | locColor.g) + "," + (0 | locColor.b) + "," + locColor.a / 255 + ")";
486         ctx.beginPath();
487         ctx.arc(locPos.x * locScaleX, -locPos.y * locScaleY, locRadius * locScaleX, 0, Math.PI * 2, false);
488         ctx.closePath();
489         ctx.fill();
490     },
491 
492     _drawSegment: function (ctx, element) {
493         var locColor = element.lineColor;
494         var locFrom = element.verts[0];
495         var locTo = element.verts[1];
496         var locLineWidth = element.lineWidth;
497         var locLineCap = element.lineCap;
498         var locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY();
499 
500         ctx.strokeStyle = "rgba(" + (0 | locColor.r) + "," + (0 | locColor.g) + "," + (0 | locColor.b) + "," + locColor.a / 255 + ")";
501         ctx.lineWidth = locLineWidth * locScaleX;
502         ctx.beginPath();
503         ctx.lineCap = locLineCap;
504         ctx.moveTo(locFrom.x * locScaleX, -locFrom.y * locScaleY);
505         ctx.lineTo(locTo.x * locScaleX, -locTo.y * locScaleY);
506         ctx.stroke();
507     },
508 
509     _drawPoly: function (ctx, element) {
510         var locVertices = element.verts;
511         var locLineCap = element.lineCap;
512         var locFillColor = element.fillColor;
513         var locLineWidth = element.lineWidth;
514         var locLineColor = element.lineColor;
515         var locIsClosePolygon = element.isClosePolygon;
516         var locIsFill = element.isFill;
517         var locIsStroke = element.isStroke;
518         if (locVertices == null)
519             return;
520 
521         var firstPoint = locVertices[0];
522         var locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY();
523 
524         ctx.lineCap = locLineCap;
525 
526         if (locFillColor) {
527             ctx.fillStyle = "rgba(" + (0 | locFillColor.r) + "," + (0 | locFillColor.g) + ","
528                 + (0 | locFillColor.b) + "," + locFillColor.a / 255 + ")";
529         }
530 
531         if (locLineWidth) {
532             ctx.lineWidth = locLineWidth * locScaleX;
533         }
534         if (locLineColor) {
535             ctx.strokeStyle = "rgba(" + (0 | locLineColor.r) + "," + (0 | locLineColor.g) + ","
536                 + (0 | locLineColor.b) + "," + locLineColor.a / 255 + ")";
537         }
538         ctx.beginPath();
539         ctx.moveTo(firstPoint.x * locScaleX, -firstPoint.y * locScaleY);
540         for (var i = 1, len = locVertices.length; i < len; i++)
541             ctx.lineTo(locVertices[i].x * locScaleX, -locVertices[i].y * locScaleY);
542 
543         if (locIsClosePolygon)
544             ctx.closePath();
545 
546         if (locIsFill)
547             ctx.fill();
548         if (locIsStroke)
549             ctx.stroke();
550     },
551 
552     /**
553      * Clear the geometry in the node's buffer.
554      */
555     clear: function () {
556         this._buffer.length = 0;
557     }
558 });
559 
560 cc.DrawNodeWebGL = cc.Node.extend({
561     _bufferCapacity:0,
562     _buffer:null,
563 
564     _trianglesArrayBuffer:null,
565     _trianglesWebBuffer:null,
566     _trianglesReader:null,
567 
568     _lineWidth: 1,
569     _drawColor: null,
570 
571     _blendFunc:null,
572     _dirty:false,
573     _className:"DrawNodeWebGL",
574 
575     // ----common function start ----
576     getBlendFunc:function () {
577         return this._blendFunc;
578     },
579 
580     setBlendFunc:function (blendFunc, dst) {
581         if (dst === undefined) {
582             this._blendFunc.src = blendFunc.src;
583             this._blendFunc.dst = blendFunc.dst;
584         } else {
585             this._blendFunc.src = blendFunc;
586             this._blendFunc.dst = dst;
587         }
588     },
589     // ----common function end ----
590 
591     ctor:function () {
592         cc.Node.prototype.ctor.call(this);
593         this._buffer = [];
594         this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST);
595         this._drawColor = cc.color(255,255,255,255);
596 
597 	    this.init();
598     },
599 
600     init:function () {
601         if (cc.Node.prototype.init.call(this)) {
602             this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_LENGTHTEXTURECOLOR);
603             this._ensureCapacity(64);
604             this._trianglesWebBuffer = cc._renderContext.createBuffer();
605             this._dirty = true;
606             return true;
607         }
608         return false;
609     },
610 
611     /**
612      * line width setter
613      * @param {Number} width
614      */
615     setLineWidth: function (width) {
616         this._lineWidth = width;
617     },
618 
619     /**
620      * line width getter
621      * @returns {Number}
622      */
623     getLineWidth: function () {
624         return this._lineWidth;
625     },
626 
627     /**
628      * draw color setter
629      * @param {cc.Color} color
630      */
631     setDrawColor: function (color) {
632         var locDrawColor = this._drawColor;
633         locDrawColor.r = color.r;
634         locDrawColor.g = color.g;
635         locDrawColor.b = color.b;
636         locDrawColor.a = color.a;
637     },
638 
639     /**
640      * draw color getter
641      * @returns {cc.Color}
642      */
643     getDrawColor: function () {
644         return  cc.color(this._drawColor.r, this._drawColor.g, this._drawColor.b, this._drawColor.a);
645     },
646 
647     /**
648      * draws a rectangle given the origin and destination point measured in points.
649      * @param {cc.Point} origin
650      * @param {cc.Point} destination
651      *  @param {cc.Color} fillColor
652      * @param {Number} lineWidth
653      * @param {cc.Color} lineColor
654      */
655     drawRect: function (origin, destination, fillColor, lineWidth, lineColor) {
656         lineWidth = lineWidth || this._lineWidth;
657         lineColor = lineColor || this.getDrawColor();
658         if (lineColor.a == null)
659             lineColor.a = 255;
660         var vertices = [origin, cc.p(destination.x, origin.y), destination, cc.p(origin.x, destination.y)];
661         if(fillColor == null)
662             this._drawSegments(vertices, lineWidth, lineColor, true);
663         else
664             this.drawPoly(vertices, fillColor, lineWidth, lineColor);
665     },
666 
667     /**
668      * draws a circle given the center, radius and number of segments.
669      * @override
670      * @param {cc.Point} center center of circle
671      * @param {Number} radius
672      * @param {Number} angle angle in radians
673      * @param {Number} segments
674      * @param {Boolean} drawLineToCenter
675      * @param {Number} lineWidth
676      * @param {cc.Color} color
677      */
678     drawCircle: function (center, radius, angle, segments, drawLineToCenter, lineWidth, color) {
679         lineWidth = lineWidth || this._lineWidth;
680         color = color || this.getDrawColor();
681         if (color.a == null)
682             color.a = 255;
683         var coef = 2.0 * Math.PI / segments, vertices = [], i, len;
684         for (i = 0; i <= segments; i++) {
685             var rads = i * coef;
686             var j = radius * Math.cos(rads + angle) + center.x;
687             var k = radius * Math.sin(rads + angle) + center.y;
688             vertices.push(cc.p(j, k));
689         }
690         if (drawLineToCenter)
691             vertices.push(cc.p(center.x, center.y));
692 
693         lineWidth *= 0.5;
694         for (i = 0, len = vertices.length; i < len - 1; i++)
695             this.drawSegment(vertices[i], vertices[i + 1], lineWidth, color);
696     },
697 
698     /**
699      * draws a quad bezier path
700      * @override
701      * @param {cc.Point} origin
702      * @param {cc.Point} control
703      * @param {cc.Point} destination
704      * @param {Number} segments
705      * @param {Number} lineWidth
706      * @param {cc.Color} color
707      */
708     drawQuadBezier: function (origin, control, destination, segments, lineWidth, color) {
709         lineWidth = lineWidth || this._lineWidth;
710         color = color || this.getDrawColor();
711         if (color.a == null)
712             color.a = 255;
713         var vertices = [], t = 0.0;
714         for (var i = 0; i < segments; i++) {
715             var x = Math.pow(1 - t, 2) * origin.x + 2.0 * (1 - t) * t * control.x + t * t * destination.x;
716             var y = Math.pow(1 - t, 2) * origin.y + 2.0 * (1 - t) * t * control.y + t * t * destination.y;
717             vertices.push(cc.p(x, y));
718             t += 1.0 / segments;
719         }
720         vertices.push(cc.p(destination.x, destination.y));
721         this._drawSegments(vertices, lineWidth, color, false);
722     },
723 
724     /**
725      * draws a cubic bezier path
726      * @override
727      * @param {cc.Point} origin
728      * @param {cc.Point} control1
729      * @param {cc.Point} control2
730      * @param {cc.Point} destination
731      * @param {Number} segments
732      * @param {Number} lineWidth
733      * @param {cc.Color} color
734      */
735     drawCubicBezier: function (origin, control1, control2, destination, segments, lineWidth, color) {
736         lineWidth = lineWidth || this._lineWidth;
737         color = color || this.getDrawColor();
738         if (color.a == null)
739             color.a = 255;
740         var vertices = [], t = 0;
741         for (var i = 0; i < segments; i++) {
742             var x = Math.pow(1 - t, 3) * origin.x + 3.0 * Math.pow(1 - t, 2) * t * control1.x + 3.0 * (1 - t) * t * t * control2.x + t * t * t * destination.x;
743             var y = Math.pow(1 - t, 3) * origin.y + 3.0 * Math.pow(1 - t, 2) * t * control1.y + 3.0 * (1 - t) * t * t * control2.y + t * t * t * destination.y;
744             vertices.push(cc.p(x, y));
745             t += 1.0 / segments;
746         }
747         vertices.push(cc.p(destination.x, destination.y));
748         this._drawSegments(vertices, lineWidth, color, false);
749     },
750 
751     /**
752      * draw a CatmullRom curve
753      * @override
754      * @param {Array} points
755      * @param {Number} segments
756      * @param {Number} lineWidth
757      * @param {cc.Color} color
758      */
759     drawCatmullRom: function (points, segments, lineWidth, color) {
760         this.drawCardinalSpline(points, 0.5, segments, lineWidth, color);
761     },
762 
763     /**
764      * draw a cardinal spline path
765      * @override
766      * @param {Array} config
767      * @param {Number} tension
768      * @param {Number} segments
769      * @param {Number} lineWidth
770      * @param {cc.Color} color
771      */
772     drawCardinalSpline: function (config, tension, segments, lineWidth, color) {
773         lineWidth = lineWidth || this._lineWidth;
774         color = color || this.getDrawColor();
775         if (color.a == null)
776             color.a = 255;
777         var vertices = [], p, lt, deltaT = 1.0 / config.length;
778 
779         for (var i = 0; i < segments + 1; i++) {
780             var dt = i / segments;
781 
782             // border
783             if (dt == 1) {
784                 p = config.length - 1;
785                 lt = 1;
786             } else {
787                 p = 0 | (dt / deltaT);
788                 lt = (dt - deltaT * p) / deltaT;
789             }
790 
791             // Interpolate
792             var newPos = cc.cardinalSplineAt(
793                 cc.getControlPointAt(config, p - 1),
794                 cc.getControlPointAt(config, p - 0),
795                 cc.getControlPointAt(config, p + 1),
796                 cc.getControlPointAt(config, p + 2),
797                 tension, lt);
798             vertices.push(newPos);
799         }
800 
801         lineWidth *= 0.5;
802         for (var j = 0, len = vertices.length; j < len - 1; j++)
803             this.drawSegment(vertices[j], vertices[j + 1], lineWidth, color);
804     },
805 
806     _render:function () {
807         var gl = cc._renderContext;
808 
809         cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);
810         gl.bindBuffer(gl.ARRAY_BUFFER, this._trianglesWebBuffer);
811         if (this._dirty) {
812             gl.bufferData(gl.ARRAY_BUFFER, this._trianglesArrayBuffer, gl.STREAM_DRAW);
813             this._dirty = false;
814         }
815         var triangleSize = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT;
816 
817         // vertex
818         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, gl.FLOAT, false, triangleSize, 0);
819         // color
820         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, triangleSize, 8);
821         // texcood
822         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, triangleSize, 12);
823 
824         gl.drawArrays(gl.TRIANGLES, 0, this._buffer.length * 3);
825         cc.incrementGLDraws(1);
826         //cc.checkGLErrorDebug();
827     },
828 
829     _ensureCapacity:function(count){
830         var _t = this;
831         var locBuffer = _t._buffer;
832         if(locBuffer.length + count > _t._bufferCapacity){
833             var TriangleLength = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT;
834             _t._bufferCapacity += Math.max(_t._bufferCapacity, count);
835             //re alloc
836             if((locBuffer == null) || (locBuffer.length === 0)){
837                 //init
838                 _t._buffer = [];
839                 _t._trianglesArrayBuffer = new ArrayBuffer(TriangleLength * _t._bufferCapacity);
840                 _t._trianglesReader = new Uint8Array(_t._trianglesArrayBuffer);
841             } else {
842                 var newTriangles = [];
843                 var newArrayBuffer = new ArrayBuffer(TriangleLength * _t._bufferCapacity);
844                 for(var i = 0; i < locBuffer.length;i++){
845                     newTriangles[i] = new cc.V2F_C4B_T2F_Triangle(locBuffer[i].a,locBuffer[i].b,locBuffer[i].c,
846                         newArrayBuffer, i * TriangleLength);
847                 }
848                 _t._trianglesReader = new Uint8Array(newArrayBuffer);
849                 _t._trianglesArrayBuffer = newArrayBuffer;
850                 _t._buffer = newTriangles;
851             }
852         }
853     },
854 
855     draw:function () {
856         cc.glBlendFunc(this._blendFunc.src, this._blendFunc.dst);
857         this._shaderProgram.use();
858         this._shaderProgram.setUniformsForBuiltins();
859         this._render();
860     },
861 
862     drawDot:function (pos, radius, color) {
863         color = color || this.getDrawColor();
864         if (color.a == null)
865             color.a = 255;
866         var c4bColor = {r: 0 | color.r, g: 0 | color.g, b: 0 | color.b, a: 0 | color.a};
867         var a = {vertices: {x: pos.x - radius, y: pos.y - radius}, colors: c4bColor, texCoords: {u: -1.0, v: -1.0}};
868         var b = {vertices: {x: pos.x - radius, y: pos.y + radius}, colors: c4bColor, texCoords: {u: -1.0, v: 1.0}};
869         var c = {vertices: {x: pos.x + radius, y: pos.y + radius}, colors: c4bColor, texCoords: {u: 1.0, v: 1.0}};
870         var d = {vertices: {x: pos.x + radius, y: pos.y - radius}, colors: c4bColor, texCoords: {u: 1.0, v: -1.0}};
871         this._buffer.push(new cc.V2F_C4B_T2F_Triangle(a, b, c, this._trianglesArrayBuffer, this._buffer.length * cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT));
872         this._buffer.push(new cc.V2F_C4B_T2F_Triangle(a, c, d, this._trianglesArrayBuffer, this._buffer.length * cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT));
873         this._dirty = true;
874     },
875 
876     drawDots: function(points, radius,color) {
877         if(!points || points.length == 0)
878             return;
879         color = color || this.getDrawColor();
880         if (color.a == null)
881             color.a = 255;
882         for(var i = 0, len = points.length; i < len; i++)
883             this.drawDot(points[i], radius, color);
884     },
885 
886     drawSegment:function (from, to, radius, color) {
887         color = color || this.getDrawColor();
888         if (color.a == null)
889             color.a = 255;
890         radius = radius || (this._lineWidth * 0.5);
891         var vertexCount = 6*3;
892         this._ensureCapacity(vertexCount);
893 
894         var c4bColor = {r: 0 | color.r, g: 0 | color.g, b: 0 | color.b, a: 0 | color.a};
895         var a = cc.__v2f(from), b = cc.__v2f(to);
896         var n = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(b, a))), t = cc.v2fperp(n);
897         var nw = cc.v2fmult(n, radius), tw = cc.v2fmult(t, radius);
898 
899         var v0 = cc.v2fsub(b, cc.v2fadd(nw, tw));
900         var v1 = cc.v2fadd(b, cc.v2fsub(nw, tw));
901         var v2 = cc.v2fsub(b, nw);
902         var v3 = cc.v2fadd(b, nw);
903         var v4 = cc.v2fsub(a, nw);
904         var v5 = cc.v2fadd(a, nw);
905         var v6 = cc.v2fsub(a, cc.v2fsub(nw, tw));
906         var v7 = cc.v2fadd(a, cc.v2fadd(nw, tw));
907 
908         var TriangleLength = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT, triangleBuffer = this._trianglesArrayBuffer, locBuffer = this._buffer;
909         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v0, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(cc.v2fadd(n, t)))},
910             {vertices: v1, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(n, t))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))},
911             triangleBuffer, locBuffer.length * TriangleLength));
912 
913         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)},
914             {vertices: v1, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(n, t))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))},
915             triangleBuffer, locBuffer.length * TriangleLength));
916 
917         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)},
918             {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))},
919             triangleBuffer, locBuffer.length * TriangleLength));
920 
921         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)},
922             {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)},
923             triangleBuffer, locBuffer.length * TriangleLength));
924 
925         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v6, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(t, n))},
926             {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)},
927             triangleBuffer, locBuffer.length * TriangleLength));
928 
929         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v6, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(t, n))},
930             {vertices: v7, colors: c4bColor, texCoords: cc.__t(cc.v2fadd(n, t))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)},
931             triangleBuffer, locBuffer.length * TriangleLength));
932         this._dirty = true;
933     },
934 
935     drawPoly:function (verts, fillColor, borderWidth, borderColor) {
936         if(fillColor == null){
937             this._drawSegments(verts, borderWidth, borderColor, true);
938             return;
939         }
940         if (fillColor.a == null)
941             fillColor.a = 255;
942         if (borderColor.a == null)
943             borderColor.a = 255;
944         borderWidth = borderWidth || this._lineWidth;
945         borderWidth *= 0.5;
946         var c4bFillColor = {r: 0 | fillColor.r, g: 0 | fillColor.g, b: 0 | fillColor.b, a: 0 | fillColor.a};
947         var c4bBorderColor = {r: 0 | borderColor.r, g: 0 | borderColor.g, b: 0 | borderColor.b, a: 0 | borderColor.a};
948         var extrude = [], i, v0, v1, v2, count = verts.length;
949         for (i = 0; i < count; i++) {
950             v0 = cc.__v2f(verts[(i - 1 + count) % count]);
951             v1 = cc.__v2f(verts[i]);
952             v2 = cc.__v2f(verts[(i + 1) % count]);
953             var n1 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v1, v0)));
954             var n2 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v2, v1)));
955             var offset = cc.v2fmult(cc.v2fadd(n1, n2), 1.0 / (cc.v2fdot(n1, n2) + 1.0));
956             extrude[i] = {offset: offset, n: n2};
957         }
958         var outline = (borderWidth > 0.0), triangleCount = 3 * count - 2, vertexCount = 3 * triangleCount;
959         this._ensureCapacity(vertexCount);
960 
961         var triangleBytesLen = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT, trianglesBuffer = this._trianglesArrayBuffer;
962         var locBuffer = this._buffer;
963         var inset = (outline == false ? 0.5 : 0.0);
964         for (i = 0; i < count - 2; i++) {
965             v0 = cc.v2fsub(cc.__v2f(verts[0]), cc.v2fmult(extrude[0].offset, inset));
966             v1 = cc.v2fsub(cc.__v2f(verts[i + 1]), cc.v2fmult(extrude[i + 1].offset, inset));
967             v2 = cc.v2fsub(cc.__v2f(verts[i + 2]), cc.v2fmult(extrude[i + 2].offset, inset));
968             locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())},
969                 {vertices: v1, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, {vertices: v2, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())},
970                 trianglesBuffer, locBuffer.length * triangleBytesLen));
971         }
972 
973         for (i = 0; i < count; i++) {
974             var j = (i + 1) % count;
975             v0 = cc.__v2f(verts[i]);
976             v1 = cc.__v2f(verts[j]);
977 
978             var n0 = extrude[i].n;
979             var offset0 = extrude[i].offset;
980             var offset1 = extrude[j].offset;
981             var inner0 = outline ? cc.v2fsub(v0, cc.v2fmult(offset0, borderWidth)) : cc.v2fsub(v0, cc.v2fmult(offset0, 0.5));
982             var inner1 = outline ? cc.v2fsub(v1, cc.v2fmult(offset1, borderWidth)) : cc.v2fsub(v1, cc.v2fmult(offset1, 0.5));
983             var outer0 = outline ? cc.v2fadd(v0, cc.v2fmult(offset0, borderWidth)) : cc.v2fadd(v0, cc.v2fmult(offset0, 0.5));
984             var outer1 = outline ? cc.v2fadd(v1, cc.v2fmult(offset1, borderWidth)) : cc.v2fadd(v1, cc.v2fmult(offset1, 0.5));
985 
986             if (outline) {
987                 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))},
988                     {vertices: inner1, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)},
989                     trianglesBuffer, locBuffer.length * triangleBytesLen));
990                 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))},
991                     {vertices: outer0, colors: c4bBorderColor, texCoords: cc.__t(n0)}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)},
992                     trianglesBuffer, locBuffer.length * triangleBytesLen));
993             } else {
994                 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())},
995                     {vertices: inner1, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, {vertices: outer1, colors: c4bFillColor, texCoords: cc.__t(n0)},
996                     trianglesBuffer, locBuffer.length * triangleBytesLen));
997                 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())},
998                     {vertices: outer0, colors: c4bFillColor, texCoords: cc.__t(n0)}, {vertices: outer1, colors: c4bFillColor, texCoords: cc.__t(n0)},
999                     trianglesBuffer, locBuffer.length * triangleBytesLen));
1000             }
1001         }
1002         extrude = null;
1003         this._dirty = true;
1004     },
1005 
1006     _drawSegments: function(verts, borderWidth, borderColor, closePoly){
1007         borderWidth = borderWidth || this._lineWidth;
1008         borderColor = borderColor || this._drawColor;
1009         if(borderColor.a == null)
1010             borderColor.a = 255;
1011         borderWidth *= 0.5;
1012         if (borderWidth <= 0)
1013             return;
1014 
1015         var c4bBorderColor = {r: 0 | borderColor.r, g: 0 | borderColor.g, b: 0 | borderColor.b, a: 0 | borderColor.a };
1016         var extrude = [], i, v0, v1, v2, count = verts.length;
1017         for (i = 0; i < count; i++) {
1018             v0 = cc.__v2f(verts[(i - 1 + count) % count]);
1019             v1 = cc.__v2f(verts[i]);
1020             v2 = cc.__v2f(verts[(i + 1) % count]);
1021             var n1 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v1, v0)));
1022             var n2 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v2, v1)));
1023             var offset = cc.v2fmult(cc.v2fadd(n1, n2), 1.0 / (cc.v2fdot(n1, n2) + 1.0));
1024             extrude[i] = {offset: offset, n: n2};
1025         }
1026 
1027         var triangleCount = 3 * count - 2, vertexCount = 3 * triangleCount;
1028         this._ensureCapacity(vertexCount);
1029 
1030         var triangleBytesLen = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT, trianglesBuffer = this._trianglesArrayBuffer;
1031         var locBuffer = this._buffer;
1032         var len = closePoly ? count : count - 1;
1033         for (i = 0; i < len; i++) {
1034             var j = (i + 1) % count;
1035             v0 = cc.__v2f(verts[i]);
1036             v1 = cc.__v2f(verts[j]);
1037 
1038             var n0 = extrude[i].n;
1039             var offset0 = extrude[i].offset;
1040             var offset1 = extrude[j].offset;
1041             var inner0 = cc.v2fsub(v0, cc.v2fmult(offset0, borderWidth));
1042             var inner1 = cc.v2fsub(v1, cc.v2fmult(offset1, borderWidth));
1043             var outer0 = cc.v2fadd(v0, cc.v2fmult(offset0, borderWidth));
1044             var outer1 = cc.v2fadd(v1, cc.v2fmult(offset1, borderWidth));
1045             locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))},
1046                 {vertices: inner1, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)},
1047                 trianglesBuffer, locBuffer.length * triangleBytesLen));
1048             locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))},
1049                 {vertices: outer0, colors: c4bBorderColor, texCoords: cc.__t(n0)}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)},
1050                 trianglesBuffer, locBuffer.length * triangleBytesLen));
1051         }
1052         extrude = null;
1053         this._dirty = true;
1054     },
1055 
1056     clear:function () {
1057         this._buffer.length = 0;
1058         this._dirty = true;
1059     }
1060 });
1061 
1062 cc.DrawNode = cc._renderType == cc._RENDER_TYPE_WEBGL ? cc.DrawNodeWebGL : cc.DrawNodeCanvas;
1063 
1064 /**
1065  * Creates a DrawNode
1066  * @return {cc.DrawNode}
1067  */
1068 cc.DrawNode.create = function () {
1069     return new cc.DrawNode();
1070 };
1071 
1072 cc._DrawNodeElement = function (type, verts, fillColor, lineWidth, lineColor, lineCap, isClosePolygon, isFill, isStroke) {
1073     var _t = this;
1074     _t.type = type;
1075     _t.verts = verts || null;
1076     _t.fillColor = fillColor || null;
1077     _t.lineWidth = lineWidth || 0;
1078     _t.lineColor = lineColor || null;
1079     _t.lineCap = lineCap || "butt";
1080     _t.isClosePolygon = isClosePolygon || false;
1081     _t.isFill = isFill || false;
1082     _t.isStroke = isStroke || false;
1083 };
1084 
1085 cc.DrawNode.TYPE_DOT = 0;
1086 cc.DrawNode.TYPE_SEGMENT = 1;
1087 cc.DrawNode.TYPE_POLY = 2;
1088