1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2008-2010 Ricardo Quesada
  4  Copyright (c) 2011      Zynga Inc.
  5  Copyright (c) 2012 Scott Lembcke and Howling Moon Software
  6 
  7  http://www.cocos2d-x.org
  8 
  9  Permission is hereby granted, free of charge, to any person obtaining a copy
 10  of this software and associated documentation files (the "Software"), to deal
 11  in the Software without restriction, including without limitation the rights
 12  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 13  copies of the Software, and to permit persons to whom the Software is
 14  furnished to do so, subject to the following conditions:
 15 
 16  The above copyright notice and this permission notice shall be included in
 17  all copies or substantial portions of the Software.
 18 
 19  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 20  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 21  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 22  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 23  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 24  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 25  THE SOFTWARE.
 26  ****************************************************************************/
 27 
 28 /**
 29  * Code copied & pasted from SpacePatrol game https://github.com/slembcke/SpacePatrol
 30  *
 31  * Renamed and added some changes for cocos2d
 32  *
 33  */
 34 cc.v2fzero = function () {
 35     return {x: 0, y: 0};
 36 };
 37 
 38 cc.v2f = function (x, y) {
 39     return {x: x, y: y};
 40 };
 41 
 42 cc.v2fadd = function (v0, v1) {
 43     return cc.v2f(v0.x + v1.x, v0.y + v1.y);
 44 };
 45 
 46 cc.v2fsub = function (v0, v1) {
 47     return cc.v2f(v0.x - v1.x, v0.y - v1.y);
 48 };
 49 
 50 cc.v2fmult = function (v, s) {
 51     return cc.v2f(v.x * s, v.y * s);
 52 };
 53 
 54 cc.v2fperp = function (p0) {
 55     return cc.v2f(-p0.y, p0.x);
 56 };
 57 
 58 cc.v2fneg = function (p0) {
 59     return cc.v2f(-p0.x, -p0.y);
 60 };
 61 
 62 cc.v2fdot = function (p0, p1) {
 63     return  p0.x * p1.x + p0.y * p1.y;
 64 };
 65 
 66 cc.v2fforangle = function (_a_) {
 67     return cc.v2f(Math.cos(_a_), Math.sin(_a_));
 68 };
 69 
 70 cc.v2fnormalize = function (p) {
 71     var r = cc.pNormalize(cc.p(p.x, p.y));
 72     return cc.v2f(r.x, r.y);
 73 };
 74 
 75 cc.__v2f = function (v) {
 76     return cc.v2f(v.x, v.y);
 77 };
 78 
 79 cc.__t = function (v) {
 80     return {u: v.x, v: v.y};
 81 };
 82 
 83 /**
 84  * <p>CCDrawNode                                                <br/>
 85  * Node that draws dots, segments and polygons.                        <br/>
 86  * Faster than the "drawing primitives" since they it draws everything in one single batch.</p>
 87  * @class
 88  * @name cc.DrawNode
 89  * @extends cc.Node
 90  */
 91 cc.DrawNodeCanvas = cc.Node.extend(/** @lends cc.DrawNode# */{
 92     _buffer: null,
 93     _blendFunc: null,
 94     _lineWidth: 0,
 95     _drawColor: null,
 96     _className:"DrawNodeCanvas",
 97     ctor: function () {
 98         cc.Node.prototype.ctor.call(this);
 99         this._buffer = [];
100         this._lineWidth = 1;
101         this._drawColor = cc.color(255, 255, 255, 255);
102         this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST);
103     },
104 
105     // ----common function start ----
106     getBlendFunc: function () {
107         return this._blendFunc;
108     },
109 
110     setBlendFunc: function (blendFunc) {
111         this._blendFunc = blendFunc;
112     },
113 
114     /**
115      * line width setter
116      * @param {Number} width
117      */
118     setLineWidth: function (width) {
119         this._lineWidth = width;
120     },
121 
122     /**
123      * line width getter
124      * @returns {Number}
125      */
126     getLineWidth: function () {
127         return this._lineWidth;
128     },
129 
130     /**
131      * draw color setter
132      * @param {cc.Color} color
133      */
134     setDrawColor: function (color) {
135         this._drawColor.r = color.r;
136         this._drawColor.g = color.g;
137         this._drawColor.b = color.b;
138         this._drawColor.a = color.a;
139     },
140 
141     /**
142      * draw color getter
143      * @returns {cc.Color}
144      */
145     getDrawColor: function () {
146         return  cc.color(this._drawColor.r, this._drawColor.g, this._drawColor.b, this._drawColor.a);
147     },
148     // ----common function end ----
149 
150 
151     /**
152      * draws a rectangle given the origin and destination point measured in points.
153      * @param {cc.Point} origin
154      * @param {cc.Point} destination
155      *  @param {cc.Color} fillColor
156      * @param {Number} lineWidth
157      * @param {cc.Color} lineColor
158      */
159     drawRect: function (origin, destination, fillColor, lineWidth, lineColor) {
160         lineWidth = lineWidth || this._lineWidth;
161         lineColor = lineColor || this.getDrawColor();
162         var vertices = [
163             origin,
164             cc.p(destination.x, origin.y),
165             destination,
166             cc.p(origin.x, destination.y)
167         ];
168         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
169         element.verts = vertices;
170         element.lineWidth = lineWidth;
171         element.lineColor = lineColor;
172         element.isClosePolygon = true;
173         element.isStroke = true;
174         element.lineCap = "butt";
175         element.fillColor = fillColor;
176         if (fillColor) {
177             element.isFill = true;
178         }
179         this._buffer.push(element);
180     },
181 
182     /**
183      * draws a circle given the center, radius and number of segments.
184      * @override
185      * @param {cc.Point} center center of circle
186      * @param {Number} radius
187      * @param {Number} angle angle in radians
188      * @param {Number} segments
189      * @param {Boolean} drawLineToCenter
190      * @param {Number} lineWidth
191      * @param {cc.Color} color
192      */
193     drawCircle: function (center, radius, angle, segments, drawLineToCenter, lineWidth, color) {
194         lineWidth = lineWidth || this._lineWidth;
195         color = color || this.getDrawColor();
196 
197         var coef = 2.0 * Math.PI / segments;
198         var vertices = [];
199         for (var i = 0; i <= segments; i++) {
200             var rads = i * coef;
201             var j = radius * Math.cos(rads + angle) + center.x;
202             var k = radius * Math.sin(rads + angle) + center.y;
203             vertices.push(cc.p(j, k));
204         }
205         if (drawLineToCenter) {
206             vertices.push(cc.p(center.x, center.y));
207         }
208 
209         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
210         element.verts = vertices;
211         element.lineWidth = lineWidth;
212         element.lineColor = color;
213         element.isClosePolygon = true;
214         element.isStroke = true;
215         this._buffer.push(element);
216     },
217 
218     /**
219      * draws a quad bezier path
220      * @override
221      * @param {cc.Point} origin
222      * @param {cc.Point} control
223      * @param {cc.Point} destination
224      * @param {Number} segments
225      * @param {Number} lineWidth
226      * @param {cc.Color} color
227      */
228     drawQuadBezier: function (origin, control, destination, segments, lineWidth, color) {
229         lineWidth = lineWidth || this._lineWidth;
230         color = color || this.getDrawColor();
231 
232         var vertices = [];
233         var t = 0.0;
234         for (var i = 0; i < segments; i++) {
235             var x = Math.pow(1 - t, 2) * origin.x + 2.0 * (1 - t) * t * control.x + t * t * destination.x;
236             var y = Math.pow(1 - t, 2) * origin.y + 2.0 * (1 - t) * t * control.y + t * t * destination.y;
237             vertices.push(cc.p(x, y));
238             t += 1.0 / segments;
239         }
240         vertices.push(cc.p(destination.x, destination.y));
241 
242         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
243         element.verts = vertices;
244         element.lineWidth = lineWidth;
245         element.lineColor = color;
246         element.isStroke = true;
247         element.lineCap = "round";
248         this._buffer.push(element);
249     },
250 
251     /**
252      * draws a cubic bezier path
253      * @override
254      * @param {cc.Point} origin
255      * @param {cc.Point} control1
256      * @param {cc.Point} control2
257      * @param {cc.Point} destination
258      * @param {Number} segments
259      * @param {Number} lineWidth
260      * @param {cc.Color} color
261      */
262     drawCubicBezier: function (origin, control1, control2, destination, segments, lineWidth, color) {
263         lineWidth = lineWidth || this._lineWidth;
264         color = color || this.getDrawColor();
265 
266         var vertices = [];
267         var t = 0;
268         for (var i = 0; i < segments; i++) {
269             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;
270             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;
271             vertices.push(cc.p(x, y));
272             t += 1.0 / segments;
273         }
274         vertices.push(cc.p(destination.x, destination.y));
275 
276         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
277         element.verts = vertices;
278         element.lineWidth = lineWidth;
279         element.lineColor = color;
280         element.isStroke = true;
281         element.lineCap = "round";
282         this._buffer.push(element);
283     },
284 
285     /**
286      * draw a CatmullRom curve
287      * @override
288      * @param {Array} points
289      * @param {Number} segments
290      * @param {Number} lineWidth
291      * @param {cc.Color} color
292      */
293     drawCatmullRom: function (points, segments, lineWidth, color) {
294         this.drawCardinalSpline(points, 0.5, segments, lineWidth, color);
295     },
296 
297     /**
298      * draw a cardinal spline path
299      * @override
300      * @param {Array} config
301      * @param {Number} tension
302      * @param {Number} segments
303      * @param {Number} lineWidth
304      * @param {cc.Color} color
305      */
306     drawCardinalSpline: function (config, tension, segments, lineWidth, color) {
307         lineWidth = lineWidth || this._lineWidth;
308         color = color || this.getDrawColor();
309 
310         var vertices = [];
311         var p, lt;
312         var deltaT = 1.0 / config.length;
313 
314         for (var i = 0; i < segments + 1; i++) {
315             var dt = i / segments;
316 
317             // border
318             if (dt == 1) {
319                 p = config.length - 1;
320                 lt = 1;
321             } else {
322                 p = 0 | (dt / deltaT);
323                 lt = (dt - deltaT * p) / deltaT;
324             }
325 
326             // Interpolate
327             var newPos = cc.CardinalSplineAt(
328                 cc.getControlPointAt(config, p - 1),
329                 cc.getControlPointAt(config, p - 0),
330                 cc.getControlPointAt(config, p + 1),
331                 cc.getControlPointAt(config, p + 2),
332                 tension, lt);
333             vertices.push(newPos);
334         }
335 
336         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
337         element.verts = vertices;
338         element.lineWidth = lineWidth;
339         element.lineColor = color;
340         element.isStroke = true;
341         element.lineCap = "round";
342         this._buffer.push(element);
343     },
344 
345     /**
346      *  draw a dot at a position, with a given radius and color
347      * @param {cc.Point} pos
348      * @param {Number} radius
349      * @param {cc.Color} color
350      */
351     drawDot: function (pos, radius, color) {
352         color = color || this.getDrawColor();
353         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_DOT);
354         element.verts = [pos];
355         element.lineWidth = radius;
356         element.fillColor = color;
357         this._buffer.push(element);
358     },
359 
360     /**
361      * draw a segment with a radius and color
362      * @param {cc.Point} from
363      * @param {cc.Point} to
364      * @param {Number} lineWidth
365      * @param {cc.Color} color
366      */
367     drawSegment: function (from, to, lineWidth, color) {
368         lineWidth = lineWidth || this._lineWidth;
369         color = color || this.getDrawColor();
370 
371         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
372         element.verts = [from, to];
373         element.lineWidth = lineWidth;
374         element.lineColor = color;
375         element.isStroke = true;
376         element.lineCap = "round";
377         this._buffer.push(element);
378     },
379 
380     /**
381      * draw a polygon with a fill color and line color without copying the vertex list
382      * @param {Array} verts
383      * @param {cc.Color} fillColor
384      * @param {Number} lineWidth
385      * @param {cc.Color} color
386      */
387     drawPoly_: function (verts, fillColor, lineWidth, color) {
388         lineWidth = lineWidth || this._lineWidth;
389         color = color || this.getDrawColor();
390         var element = new cc._DrawNodeElement(cc.DrawNode.TYPE_POLY);
391         
392         element.verts = verts;
393         element.fillColor = fillColor;
394         element.lineWidth = lineWidth;
395         element.lineColor = color;
396         element.isClosePolygon = true;
397         element.isStroke = true;
398         element.lineCap = "round";
399         if (fillColor) {
400             element.isFill = true;
401         }
402         this._buffer.push(element);
403     },
404     
405     /**
406      * draw a polygon with a fill color and line color, copying the vertex list
407      * @param {Array} verts
408      * @param {cc.Color} fillColor
409      * @param {Number} lineWidth
410      * @param {cc.Color} color
411      */
412     drawPoly: function (verts, fillColor, lineWidth, color) {
413         var vertsCopy = [];
414         for (var i=0; i < verts.length; i++) {
415             vertsCopy.push(cc.p(verts[i].x, verts[i].y));
416         }
417         return this.drawPoly_(vertsCopy, fillColor, lineWidth, color);     
418     },
419 
420     draw: function (ctx) {
421         var context = ctx || cc._renderContext;
422         if ((this._blendFunc && (this._blendFunc.src == cc.SRC_ALPHA) && (this._blendFunc.dst == cc.ONE)))
423             context.globalCompositeOperation = 'lighter';
424 
425         for (var i = 0; i < this._buffer.length; i++) {
426             var element = this._buffer[i];
427             switch (element.type) {
428                 case cc.DrawNode.TYPE_DOT:
429                     this._drawDot(context, element);
430                     break;
431                 case cc.DrawNode.TYPE_SEGMENT:
432                     this._drawSegment(context, element);
433                     break;
434                 case cc.DrawNode.TYPE_POLY:
435                     this._drawPoly(context, element);
436                     break;
437             }
438         }
439     },
440 
441     _drawDot: function (ctx, element) {
442         var locColor = element.fillColor;
443         var locPos = element.verts[0];
444         var locRadius = element.lineWidth;
445         var locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY();
446 
447         ctx.fillStyle = "rgba(" + (0 | locColor.r) + "," + (0 | locColor.g) + "," + (0 | locColor.b) + "," + locColor.a / 255 + ")";
448         ctx.beginPath();
449         ctx.arc(locPos.x * locScaleX, -locPos.y * locScaleY, locRadius * locScaleX, 0, Math.PI * 2, false);
450         ctx.closePath();
451         ctx.fill();
452     },
453 
454     _drawSegment: function (ctx, element) {
455         var locColor = element.lineColor;
456         var locFrom = element.verts[0];
457         var locTo = element.verts[1];
458         var locLineWidth = element.lineWidth;
459         var locLineCap = element.lineCap;
460         var locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY();
461 
462         ctx.strokeStyle = "rgba(" + (0 | locColor.r) + "," + (0 | locColor.g) + "," + (0 | locColor.b) + "," + locColor.a / 255 + ")";
463         ctx.lineWidth = locLineWidth * locScaleX;
464         ctx.beginPath();
465         ctx.lineCap = locLineCap;
466         ctx.moveTo(locFrom.x * locScaleX, -locFrom.y * locScaleY);
467         ctx.lineTo(locTo.x * locScaleX, -locTo.y * locScaleY);
468         ctx.stroke();
469     },
470 
471     _drawPoly: function (ctx, element) {
472         var locVertices = element.verts;
473         var locLineCap = element.lineCap;
474         var locFillColor = element.fillColor;
475         var locLineWidth = element.lineWidth;
476         var locLineColor = element.lineColor;
477         var locIsClosePolygon = element.isClosePolygon;
478         var locIsFill = element.isFill;
479         var locIsStroke = element.isStroke;
480         if (locVertices == null)
481             return;
482 
483         var firstPoint = locVertices[0];
484         var locScaleX = cc.view.getScaleX(), locScaleY = cc.view.getScaleY();
485 
486         ctx.lineCap = locLineCap;
487 
488         if (locFillColor) {
489             ctx.fillStyle = "rgba(" + (0 | locFillColor.r) + "," + (0 | locFillColor.g) + ","
490                 + (0 | locFillColor.b) + "," + locFillColor.a / 255 + ")";
491         }
492 
493         if (locLineWidth) {
494             ctx.lineWidth = locLineWidth * locScaleX;
495         }
496         if (locLineColor) {
497             ctx.strokeStyle = "rgba(" + (0 | locLineColor.r) + "," + (0 | locLineColor.g) + ","
498                 + (0 | locLineColor.b) + "," + locLineColor.a / 255 + ")";
499         }
500         ctx.beginPath();
501         ctx.moveTo(firstPoint.x * locScaleX, -firstPoint.y * locScaleY);
502         for (var i = 1, len = locVertices.length; i < len; i++)
503             ctx.lineTo(locVertices[i].x * locScaleX, -locVertices[i].y * locScaleY);
504 
505         if (locIsClosePolygon)
506             ctx.closePath();
507 
508         if (locIsFill)
509             ctx.fill();
510         if (locIsStroke)
511             ctx.stroke();
512     },
513 
514     /**
515      * Clear the geometry in the node's buffer.
516      */
517     clear: function () {
518         this._buffer.length = 0;
519     }
520 });
521 
522 cc.DrawNodeWebGL = cc.Node.extend({
523     _bufferCapacity:0,
524     _buffer:null,
525 
526     _trianglesArrayBuffer:null,
527     _trianglesWebBuffer:null,
528     _trianglesReader:null,
529 
530     _blendFunc:null,
531     _dirty:false,
532     _className:"DrawNodeWebGL",
533 
534     // ----common function start ----
535     getBlendFunc:function () {
536         return this._blendFunc;
537     },
538 
539     setBlendFunc:function (blendFunc) {
540         this._blendFunc = blendFunc;
541     },
542     // ----common function end ----
543 
544     ctor:function () {
545         cc.Node.prototype.ctor.call(this);
546         this._buffer = [];
547         this._blendFunc = new cc.BlendFunc(cc.BLEND_SRC, cc.BLEND_DST);
548     },
549 
550     init:function () {
551         if (cc.Node.prototype.init.call(this)) {
552             this.shaderProgram = cc.shaderCache.programForKey(cc.SHADER_POSITION_LENGTHTEXTURECOLOR);
553             this._ensureCapacity(512);
554             this._trianglesWebBuffer = cc._renderContext.createBuffer();
555             this._dirty = true;
556             return true;
557         }
558         return false;
559     },
560 
561     _render:function () {
562         var gl = cc._renderContext;
563 
564         cc.glEnableVertexAttribs(cc.VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);
565         gl.bindBuffer(gl.ARRAY_BUFFER, this._trianglesWebBuffer);
566         if (this._dirty) {
567             gl.bufferData(gl.ARRAY_BUFFER, this._trianglesArrayBuffer, gl.STREAM_DRAW);
568             this._dirty = false;
569         }
570         var triangleSize = cc.V2F_C4B_T2F.BYTES_PER_ELEMENT;
571 
572         // vertex
573         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_POSITION, 2, gl.FLOAT, false, triangleSize, 0);
574         // color
575         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_COLOR, 4, gl.UNSIGNED_BYTE, true, triangleSize, 8);
576         // texcood
577         gl.vertexAttribPointer(cc.VERTEX_ATTRIB_TEX_COORDS, 2, gl.FLOAT, false, triangleSize, 12);
578 
579         gl.drawArrays(gl.TRIANGLES, 0, this._buffer.length * 3);
580         cc.INCREMENT_GL_DRAWS(1);
581         //cc.CHECK_GL_ERROR_DEBUG();
582     },
583 
584     _ensureCapacity:function(count){
585         if(this._buffer.length + count > this._bufferCapacity){
586             var TriangleLength = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT;
587             this._bufferCapacity += Math.max(this._bufferCapacity, count);
588             //re alloc
589             if((this._buffer == null) || (this._buffer.length === 0)){
590                 //init
591                 this._buffer = [];
592                 this._trianglesArrayBuffer = new ArrayBuffer(TriangleLength * this._bufferCapacity);
593                 this._trianglesReader = new Uint8Array(this._trianglesArrayBuffer);
594             } else {
595                 var newTriangles = this._buffer;
596                 newTriangles.length = 0;
597                 var newArrayBuffer = new ArrayBuffer(TriangleLength * this._bufferCapacity);
598 
599                 for(var i = 0; i < this._buffer.length;i++){
600                     newTriangles[i] = new cc.V2F_C4B_T2F_Triangle(this._buffer[i].a,this._buffer[i].b,this._buffer[i].c,
601                         newArrayBuffer,i * TriangleLength);
602                 }
603                 this._trianglesReader = new Uint8Array(newArrayBuffer);
604                 this._trianglesArrayBuffer = newArrayBuffer;
605             }
606         }
607     },
608 
609     draw:function () {
610         cc.glBlendFunc(this._blendFunc.src, this._blendFunc.dst);
611         this._shaderProgram.use();
612         this._shaderProgram.setUniformsForBuiltins();
613         this._render();
614     },
615 
616     drawDot:function (pos, radius, color) {
617         var c4bColor = {r: 0 | color.r, g: 0 | color.g, b: 0 | color.b, a: 0 | color.a};
618         var a = {vertices: {x: pos.x - radius, y: pos.y - radius}, colors: c4bColor, texCoords: {u: -1.0, v: -1.0}};
619         var b = {vertices: {x: pos.x - radius, y: pos.y + radius}, colors: c4bColor, texCoords: {u: -1.0, v: 1.0}};
620         var c = {vertices: {x: pos.x + radius, y: pos.y + radius}, colors: c4bColor, texCoords: {u: 1.0, v: 1.0}};
621         var d = {vertices: {x: pos.x + radius, y: pos.y - radius}, colors: c4bColor, texCoords: {u: 1.0, v: -1.0}};
622         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));
623         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));
624         this._dirty = true;
625     },
626 
627     drawSegment:function (from, to, radius, color) {
628         var vertexCount = 6*3;
629         this._ensureCapacity(vertexCount);
630 
631         var c4bColor = {r: 0 | color.r, g: 0 | color.g, b: 0 | color.b, a: 0 | color.a};
632         var a = cc.__v2f(from);
633         var b = cc.__v2f(to);
634 
635         var n = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(b, a)));
636         var t = cc.v2fperp(n);
637 
638         var nw = cc.v2fmult(n, radius);
639         var tw = cc.v2fmult(t, radius);
640         var v0 = cc.v2fsub(b, cc.v2fadd(nw, tw));
641         var v1 = cc.v2fadd(b, cc.v2fsub(nw, tw));
642         var v2 = cc.v2fsub(b, nw);
643         var v3 = cc.v2fadd(b, nw);
644         var v4 = cc.v2fsub(a, nw);
645         var v5 = cc.v2fadd(a, nw);
646         var v6 = cc.v2fsub(a, cc.v2fsub(nw, tw));
647         var v7 = cc.v2fadd(a, cc.v2fadd(nw, tw));
648 
649         var TriangleLength = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT, triangleBuffer = this._trianglesArrayBuffer;
650         this._buffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v0, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(cc.v2fadd(n, t)))},
651             {vertices: v1, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(n, t))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))},
652             triangleBuffer, this._buffer.length * TriangleLength));
653 
654         this._buffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)},
655             {vertices: v1, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(n, t))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))},
656             triangleBuffer, this._buffer.length * TriangleLength));
657 
658         this._buffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)},
659             {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v2, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))},
660             triangleBuffer, this._buffer.length * TriangleLength));
661 
662         this._buffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v3, colors: c4bColor, texCoords: cc.__t(n)},
663             {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)},
664             triangleBuffer, this._buffer.length * TriangleLength));
665 
666         this._buffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v6, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(t, n))},
667             {vertices: v4, colors: c4bColor, texCoords: cc.__t(cc.v2fneg(n))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)},
668             triangleBuffer, this._buffer.length * TriangleLength));
669 
670         this._buffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v6, colors: c4bColor, texCoords: cc.__t(cc.v2fsub(t, n))},
671             {vertices: v7, colors: c4bColor, texCoords: cc.__t(cc.v2fadd(n, t))}, {vertices: v5, colors: c4bColor, texCoords: cc.__t(n)},
672             triangleBuffer, this._buffer.length * TriangleLength));
673         this._dirty = true;
674     },
675 
676     drawPoly:function (verts, fillColor, borderWidth, borderColor) {
677         var c4bFillColor = {r: 0 | fillColor.r, g: 0 | fillColor.g, b: 0 | fillColor.b, a: 0 | fillColor.a};
678         var c4bBorderColor = {r: 0 | borderColor.r, g: 0 | borderColor.g, b: 0 | borderColor.b, a: 0 | borderColor.a};
679         var extrude = [], i;
680         var v0, v1, v2;
681         var count = verts.length;
682         for (i = 0; i < count; i++) {
683             v0 = cc.__v2f(verts[(i - 1 + count) % count]);
684             v1 = cc.__v2f(verts[i]);
685             v2 = cc.__v2f(verts[(i + 1) % count]);
686             var n1 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v1, v0)));
687             var n2 = cc.v2fnormalize(cc.v2fperp(cc.v2fsub(v2, v1)));
688             var offset = cc.v2fmult(cc.v2fadd(n1, n2), 1.0 / (cc.v2fdot(n1, n2) + 1.0));
689             extrude[i] = {offset: offset, n: n2};
690         }
691         var outline = (borderWidth > 0.0);
692 
693         var triangleCount = 3 * count -2;
694         var vertexCount = 3 * triangleCount;
695         this._ensureCapacity(vertexCount);
696 
697         var triangleBytesLen = cc.V2F_C4B_T2F_Triangle.BYTES_PER_ELEMENT, trianglesBuffer = this._trianglesArrayBuffer;
698         var locBuffer = this._buffer;
699         var inset = (outline == false ? 0.5 : 0.0);
700         for (i = 0; i < count - 2; i++) {
701             v0 = cc.v2fsub(cc.__v2f(verts[0]), cc.v2fmult(extrude[0].offset, inset));
702             v1 = cc.v2fsub(cc.__v2f(verts[i + 1]), cc.v2fmult(extrude[i + 1].offset, inset));
703             v2 = cc.v2fsub(cc.__v2f(verts[i + 2]), cc.v2fmult(extrude[i + 2].offset, inset));
704             locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: v0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())},
705                 {vertices: v1, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, {vertices: v2, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())},
706                 trianglesBuffer, locBuffer.length * triangleBytesLen));
707         }
708 
709         for (i = 0; i < count; i++) {
710             var j = (i + 1) % count;
711             v0 = cc.__v2f(verts[i]);
712             v1 = cc.__v2f(verts[j]);
713 
714             var n0 = extrude[i].n;
715             var offset0 = extrude[i].offset;
716             var offset1 = extrude[j].offset;
717             var inner0 = outline ? cc.v2fsub(v0, cc.v2fmult(offset0, borderWidth)) : cc.v2fsub(v0, cc.v2fmult(offset0, 0.5));
718             var inner1 = outline ? cc.v2fsub(v1, cc.v2fmult(offset1, borderWidth)) : cc.v2fsub(v1, cc.v2fmult(offset1, 0.5));
719             var outer0 = outline ? cc.v2fadd(v0, cc.v2fmult(offset0, borderWidth)) : cc.v2fadd(v0, cc.v2fmult(offset0, 0.5));
720             var outer1 = outline ? cc.v2fadd(v1, cc.v2fmult(offset1, borderWidth)) : cc.v2fadd(v1, cc.v2fmult(offset1, 0.5));
721 
722             if (outline) {
723                 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))},
724                     {vertices: inner1, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)},
725                     trianglesBuffer, locBuffer.length * triangleBytesLen));
726                 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bBorderColor, texCoords: cc.__t(cc.v2fneg(n0))},
727                     {vertices: outer0, colors: c4bBorderColor, texCoords: cc.__t(n0)}, {vertices: outer1, colors: c4bBorderColor, texCoords: cc.__t(n0)},
728                     trianglesBuffer, locBuffer.length * triangleBytesLen));
729             } else {
730                 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())},
731                     {vertices: inner1, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())}, {vertices: outer1, colors: c4bFillColor, texCoords: cc.__t(n0)},
732                     trianglesBuffer, locBuffer.length * triangleBytesLen));
733                 locBuffer.push(new cc.V2F_C4B_T2F_Triangle({vertices: inner0, colors: c4bFillColor, texCoords: cc.__t(cc.v2fzero())},
734                     {vertices: outer0, colors: c4bFillColor, texCoords: cc.__t(n0)}, {vertices: outer1, colors: c4bFillColor, texCoords: cc.__t(n0)},
735                     trianglesBuffer, locBuffer.length * triangleBytesLen));
736             }
737         }
738         extrude = null;
739         this._dirty = true;
740     },
741 
742     clear:function () {
743         this._buffer.length = 0;
744         this._dirty = true;
745     }
746 });
747 
748 cc.DrawNode = cc._renderType == cc._RENDER_TYPE_WEBGL ? cc.DrawNodeWebGL : cc.DrawNodeCanvas;
749 
750 /**
751  * Creates a DrawNode
752  * @return {cc.DrawNode}
753  */
754 cc.DrawNode.create = function () {
755     var ret = new cc.DrawNode();
756     if (ret && ret.init())
757         return ret;
758     return null;
759 };
760 
761 cc._DrawNodeElement = function (type, verts, fillColor, lineWidth, lineColor, lineCap, isClosePolygon, isFill, isStroke) {
762     this.type = type;
763     this.verts = verts || null;
764     this.fillColor = fillColor || null;
765     this.lineWidth = lineWidth || 0;
766     this.lineColor = lineColor || null;
767     this.lineCap = lineCap || "butt";
768     this.isClosePolygon = isClosePolygon || false;
769     this.isFill = isFill || false;
770     this.isStroke = isStroke || false;
771 };
772 
773 cc.DrawNode.TYPE_DOT = 0;
774 cc.DrawNode.TYPE_SEGMENT = 1;
775 cc.DrawNode.TYPE_POLY = 2;
776