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