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) 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 of cc.DrawNode for Canvas
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 
872         this._ensureCapacity(2*3);
873 
874         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));
875         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));
876         this._dirty = true;
877     },
878 
879     drawDots: function(points, radius,color) {
880         if(!points || points.length == 0)
881             return;
882         color = color || this.getDrawColor();
883         if (color.a == null)
884             color.a = 255;
885         for(var i = 0, len = points.length; i < len; i++)
886             this.drawDot(points[i], radius, color);
887     },
888 
889     drawSegment:function (from, to, radius, color) {
890         color = color || this.getDrawColor();
891         if (color.a == null)
892             color.a = 255;
893         radius = radius || (this._lineWidth * 0.5);
894         var vertexCount = 6*3;
895         this._ensureCapacity(vertexCount);
896 
897         var c4bColor = {r: 0 | color.r, g: 0 | color.g, b: 0 | color.b, a: 0 | color.a};
898         var a = cc.__v2f(from), b = cc.__v2f(to);
899         var n = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(b, a))), t = cc.v2fperp(n);
900         var nw = cc.v2fmult(n, radius), tw = cc.v2fmult(t, radius);
901 
902         var v0 = cc.v2fsub(b, cc.v2fadd(nw, tw));
903         var v1 = cc.v2fadd(b, cc.v2fsub(nw, tw));
904         var v2 = cc.v2fsub(b, nw);
905         var v3 = cc.v2fadd(b, nw);
906         var v4 = cc.v2fsub(a, nw);
907         var v5 = cc.v2fadd(a, nw);
908         var v6 = cc.v2fsub(a, cc.v2fsub(nw, tw));
909         var v7 = cc.v2fadd(a, cc.v2fadd(nw, tw));
910 
911         var TriangleLength = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT, triangleBuffer = this._trianglesArrayBuffer, locBuffer = this._buffer;
912         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v0, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(cc.v2fadd(n, t)))},
913             {vertices: v1, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(n, t))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))},
914             triangleBuffer, locBuffer.length * TriangleLength));
915 
916         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)},
917             {vertices: v1, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(n, t))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))},
918             triangleBuffer, locBuffer.length * TriangleLength));
919 
920         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)},
921             {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))},
922             triangleBuffer, locBuffer.length * TriangleLength));
923 
924         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)},
925             {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)},
926             triangleBuffer, locBuffer.length * TriangleLength));
927 
928         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v6, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(t, n))},
929             {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)},
930             triangleBuffer, locBuffer.length * TriangleLength));
931 
932         locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v6, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(t, n))},
933             {vertices: v7, colors: c4bColor, texCoords: cc.__t(cc.v2fadd(n, t))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)},
934             triangleBuffer, locBuffer.length * TriangleLength));
935         this._dirty = true;
936     },
937 
938     drawPoly:function (verts, fillColor, borderWidth, borderColor) {
939         if(fillColor == null){
940             this._drawSegments(verts, borderWidth, borderColor, true);
941             return;
942         }
943         if (fillColor.a == null)
944             fillColor.a = 255;
945         if (borderColor.a == null)
946             borderColor.a = 255;
947         borderWidth = borderWidth || this._lineWidth;
948         borderWidth *= 0.5;
949         var c4bFillColor = {r: 0 | fillColor.r, g: 0 | fillColor.g, b: 0 | fillColor.b, a: 0 | fillColor.a};
950         var c4bBorderColor = {r: 0 | borderColor.r, g: 0 | borderColor.g, b: 0 | borderColor.b, a: 0 | borderColor.a};
951         var extrude = [], i, v0, v1, v2, count = verts.length;
952         for (i = 0; i < count; i++) {
953             v0 = cc.__v2f(verts[(i - 1 + count) % count]);
954             v1 = cc.__v2f(verts[i]);
955             v2 = cc.__v2f(verts[(i + 1) % count]);
956             var n1 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v1, v0)));
957             var n2 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v2, v1)));
958             var offset = cc.v2fmult(cc.v2fadd(n1, n2), 1.0 / (cc.v2fdot(n1, n2) + 1.0));
959             extrude[i] = {offset: offset, n: n2};
960         }
961         var outline = (borderWidth > 0.0), triangleCount = 3 * count - 2, vertexCount = 3 * triangleCount;
962         this._ensureCapacity(vertexCount);
963 
964         var triangleBytesLen = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT, trianglesBuffer = this._trianglesArrayBuffer;
965         var locBuffer = this._buffer;
966         var inset = (outline == false ? 0.5 : 0.0);
967         for (i = 0; i < count - 2; i++) {
968             v0 = cc.v2fsub(cc.__v2f(verts[0]), cc.v2fmult(extrude[0].offset, inset));
969             v1 = cc.v2fsub(cc.__v2f(verts[i + 1]), cc.v2fmult(extrude[i + 1].offset, inset));
970             v2 = cc.v2fsub(cc.__v2f(verts[i + 2]), cc.v2fmult(extrude[i + 2].offset, inset));
971             locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())},
972                 {vertices: v1, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, {vertices: v2, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())},
973                 trianglesBuffer, locBuffer.length * triangleBytesLen));
974         }
975 
976         for (i = 0; i < count; i++) {
977             var j = (i + 1) % count;
978             v0 = cc.__v2f(verts[i]);
979             v1 = cc.__v2f(verts[j]);
980 
981             var n0 = extrude[i].n;
982             var offset0 = extrude[i].offset;
983             var offset1 = extrude[j].offset;
984             var inner0 = outline ? cc.v2fsub(v0, cc.v2fmult(offset0, borderWidth)) : cc.v2fsub(v0, cc.v2fmult(offset0, 0.5));
985             var inner1 = outline ? cc.v2fsub(v1, cc.v2fmult(offset1, borderWidth)) : cc.v2fsub(v1, cc.v2fmult(offset1, 0.5));
986             var outer0 = outline ? cc.v2fadd(v0, cc.v2fmult(offset0, borderWidth)) : cc.v2fadd(v0, cc.v2fmult(offset0, 0.5));
987             var outer1 = outline ? cc.v2fadd(v1, cc.v2fmult(offset1, borderWidth)) : cc.v2fadd(v1, cc.v2fmult(offset1, 0.5));
988 
989             if (outline) {
990                 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))},
991                     {vertices: inner1, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)},
992                     trianglesBuffer, locBuffer.length * triangleBytesLen));
993                 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))},
994                     {vertices: outer0, colors: c4bBorderColor, texCoords: cc.__t(n0)}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)},
995                     trianglesBuffer, locBuffer.length * triangleBytesLen));
996             } else {
997                 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())},
998                     {vertices: inner1, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, {vertices: outer1, colors: c4bFillColor, texCoords: cc.__t(n0)},
999                     trianglesBuffer, locBuffer.length * triangleBytesLen));
1000                 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())},
1001                     {vertices: outer0, colors: c4bFillColor, texCoords: cc.__t(n0)}, {vertices: outer1, colors: c4bFillColor, texCoords: cc.__t(n0)},
1002                     trianglesBuffer, locBuffer.length * triangleBytesLen));
1003             }
1004         }
1005         extrude = null;
1006         this._dirty = true;
1007     },
1008 
1009     _drawSegments: function(verts, borderWidth, borderColor, closePoly){
1010         borderWidth = borderWidth || this._lineWidth;
1011         borderColor = borderColor || this._drawColor;
1012         if(borderColor.a == null)
1013             borderColor.a = 255;
1014         borderWidth *= 0.5;
1015         if (borderWidth <= 0)
1016             return;
1017 
1018         var c4bBorderColor = {r: 0 | borderColor.r, g: 0 | borderColor.g, b: 0 | borderColor.b, a: 0 | borderColor.a };
1019         var extrude = [], i, v0, v1, v2, count = verts.length;
1020         for (i = 0; i < count; i++) {
1021             v0 = cc.__v2f(verts[(i - 1 + count) % count]);
1022             v1 = cc.__v2f(verts[i]);
1023             v2 = cc.__v2f(verts[(i + 1) % count]);
1024             var n1 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v1, v0)));
1025             var n2 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v2, v1)));
1026             var offset = cc.v2fmult(cc.v2fadd(n1, n2), 1.0 / (cc.v2fdot(n1, n2) + 1.0));
1027             extrude[i] = {offset: offset, n: n2};
1028         }
1029 
1030         var triangleCount = 3 * count - 2, vertexCount = 3 * triangleCount;
1031         this._ensureCapacity(vertexCount);
1032 
1033         var triangleBytesLen = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT, trianglesBuffer = this._trianglesArrayBuffer;
1034         var locBuffer = this._buffer;
1035         var len = closePoly ? count : count - 1;
1036         for (i = 0; i < len; i++) {
1037             var j = (i + 1) % count;
1038             v0 = cc.__v2f(verts[i]);
1039             v1 = cc.__v2f(verts[j]);
1040 
1041             var n0 = extrude[i].n;
1042             var offset0 = extrude[i].offset;
1043             var offset1 = extrude[j].offset;
1044             var inner0 = cc.v2fsub(v0, cc.v2fmult(offset0, borderWidth));
1045             var inner1 = cc.v2fsub(v1, cc.v2fmult(offset1, borderWidth));
1046             var outer0 = cc.v2fadd(v0, cc.v2fmult(offset0, borderWidth));
1047             var outer1 = cc.v2fadd(v1, cc.v2fmult(offset1, borderWidth));
1048             locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))},
1049                 {vertices: inner1, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)},
1050                 trianglesBuffer, locBuffer.length * triangleBytesLen));
1051             locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))},
1052                 {vertices: outer0, colors: c4bBorderColor, texCoords: cc.__t(n0)}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)},
1053                 trianglesBuffer, locBuffer.length * triangleBytesLen));
1054         }
1055         extrude = null;
1056         this._dirty = true;
1057     },
1058 
1059     clear:function () {
1060         this._buffer.length = 0;
1061         this._dirty = true;
1062     }
1063 });
1064 
1065 cc.DrawNode = cc._renderType == cc._RENDER_TYPE_WEBGL ? cc.DrawNodeWebGL : cc.DrawNodeCanvas;
1066 
1067 /**
1068  * Creates a DrawNode
1069  * @return {cc.DrawNode}
1070  */
1071 cc.DrawNode.create = function () {
1072     return new cc.DrawNode();
1073 };
1074 
1075 cc._DrawNodeElement = function (type, verts, fillColor, lineWidth, lineColor, lineCap, isClosePolygon, isFill, isStroke) {
1076     var _t = this;
1077     _t.type = type;
1078     _t.verts = verts || null;
1079     _t.fillColor = fillColor || null;
1080     _t.lineWidth = lineWidth || 0;
1081     _t.lineColor = lineColor || null;
1082     _t.lineCap = lineCap || "butt";
1083     _t.isClosePolygon = isClosePolygon || false;
1084     _t.isFill = isFill || false;
1085     _t.isStroke = isStroke || false;
1086 };
1087 
1088 cc.DrawNode.TYPE_DOT = 0;
1089 cc.DrawNode.TYPE_SEGMENT = 1;
1090 cc.DrawNode.TYPE_POLY = 2;
1091