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