1 /****************************************************************************
  2  Copyright (c) 2010-2012 cocos2d-x.org
  3  Copyright (c) 2008-2010 Ricardo Quesada
  4  Copyright (c) 2011      Zynga Inc.
  5 
  6  http://www.cocos2d-x.org
  7 
  8  Permission is hereby granted, free of charge, to any person obtaining a copy
  9  of this software and associated documentation files (the "Software"), to deal
 10  in the Software without restriction, including without limitation the rights
 11  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12  copies of the Software, and to permit persons to whom the Software is
 13  furnished to do so, subject to the following conditions:
 14 
 15  The above copyright notice and this permission notice shall be included in
 16  all copies or substantial portions of the Software.
 17 
 18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24  THE SOFTWARE.
 25  ****************************************************************************/
 26 
 27 /**
 28  * <p>
 29  *   Drawing primitives Utility Class. this class is base class, it contain some render type version: Canvas, WebGL, DOM.<br/>
 30  *   this class contain some primitive Drawing Method: <br/>
 31  *     - drawPoint<br/>
 32  *     - drawLine<br/>
 33  *     - drawPoly<br/>
 34  *     - drawCircle<br/>
 35  *     - drawQuadBezier<br/>
 36  *     - drawCubicBezier<br/>
 37  *     You can change the color, width and other property by calling these WebGL API:<br/>
 38  *     glColor4ub(), glLineWidth(), glPointSize().<br/>
 39  * </p>
 40  * @class
 41  * @extends cc.Class
 42  * @warning These functions draws the Line, Point, Polygon, immediately. They aren't batched. <br/>
 43  *   If you are going to make a game that depends on these primitives, I suggest creating a batch.
 44  */
 45 cc.DrawingPrimitive = cc.Class.extend(/** @lends cc.DrawingPrimitive# */{
 46     _renderContext:null,
 47 
 48     /**
 49      * set render context of drawing primitive
 50      * @param context
 51      */
 52     setRenderContext:function (context) {
 53         this._renderContext = context;
 54     },
 55 
 56     /**
 57      * returns render context of drawing primitive
 58      * @return {CanvasContext}
 59      */
 60     getRenderContext:function () {
 61         return this._renderContext;
 62     },
 63 
 64     /**
 65      * Constructor
 66      * @param {CanvasContext} renderContext
 67      */
 68     ctor:function (renderContext) {
 69         this._renderContext = renderContext;
 70     },
 71 
 72     /**
 73      * draws a point given x and y coordinate measured in points
 74      * @param {cc.Point} point
 75      */
 76     drawPoint:function (point) {
 77         cc.log("DrawingPrimitive.drawPoint() not implement!");
 78     },
 79 
 80     /**
 81      * draws an array of points.
 82      * @param {Array} points point of array
 83      * @param {Number} numberOfPoints
 84      */
 85     drawPoints:function (points, numberOfPoints) {
 86         cc.log("DrawingPrimitive.drawPoints() not implement!");
 87     },
 88 
 89     /**
 90      * draws a line given the origin and destination point measured in points
 91      * @param {cc.Point} origin
 92      * @param {cc.Point} destination
 93      */
 94     drawLine:function (origin, destination) {
 95         cc.log("DrawingPrimitive.drawLine() not implement!");
 96     },
 97 
 98     /**
 99      * draws a poligon given a pointer to cc.Point coordiantes and the number of vertices measured in points.
100      * @param {Array} vertices a pointer to cc.Point coordiantes
101      * @param {Number} numOfVertices the number of vertices measured in points
102      * @param {Boolean} closePolygon The polygon can be closed or open
103      * @param {Boolean} fill The polygon can be closed or open and optionally filled with current color
104      */
105     drawPoly:function (vertices, numOfVertices, closePolygon, fill) {
106         cc.log("DrawingPrimitive.drawPoly() not implement!");
107     },
108 
109     /**
110      * draws a circle given the center, radius and number of segments.
111      * @param {cc.Point} center center of circle
112      * @param {Number} radius
113      * @param {Number} angle angle in radians
114      * @param {Number} segments
115      * @param {Boolean} drawLineToCenter
116      */
117     drawCircle:function (center, radius, angle, segments, drawLineToCenter) {
118         //WEBGL version
119         if ((segments == "undefined") || (segments == 0)) {
120             return;
121         }
122         var additionalSegment = 1;
123         if (drawLineToCenter) {
124             ++additionalSegment;
125         }
126 
127         var coef = 2.0 * Math.PI / segments;
128 
129         var vertices = [];
130 
131         for (var i = 0; i <= segments; i++) {
132             var rads = i * coef;
133             var j = radius * Math.cos(rads + angle) + center.x;
134             var k = radius * Math.sin(rads + angle) + center.y;
135             var addPoint = cc.p(j * cc.CONTENT_SCALE_FACTOR(), k * cc.CONTENT_SCALE_FACTOR());
136             vertices.push(addPoint);
137         }
138 
139         if (drawLineToCenter) {
140             var lastPoint = cc.p(center.x * cc.CONTENT_SCALE_FACTOR(), center.y * cc.CONTENT_SCALE_FACTOR());
141             vertices.push(lastPoint);
142         }
143 
144         this.drawPoly(vertices, segments + 2, true, false);
145     },
146 
147     /**
148      * draws a quad bezier path
149      * @param {cc.Point} origin
150      * @param {cc.Point} control
151      * @param {cc.Point} destination
152      * @param {Number} segments
153      */
154     drawQuadBezier:function (origin, control, destination, segments) {
155         cc.log("DrawingPrimitive.drawQuadBezier() not implement!");
156     },
157 
158     /**
159      * draws a cubic bezier path
160      * @param {cc.Point} origin
161      * @param {cc.Point} control1
162      * @param {cc.Point} control2
163      * @param {cc.Point} destination
164      * @param {Number} segments
165      */
166     drawCubicBezier:function (origin, control1, control2, destination, segments) {
167         cc.log("DrawingPrimitive.drawCubicBezier() not implement!");
168     },
169 
170     /**
171      * draw a catmull rom line
172      * @param {cc.PointArray} points
173      * @param {Number} segments
174      */
175     drawCatmullRom:function (points, segments) {
176         cc.log("DrawingPrimitive.drawCardinalSpline() not implement!");
177     },
178 
179     /**
180      * draw a cardinal spline path
181      * @param {cc.PointArray} config
182      * @param {Number} tension
183      * @param {Number} segments
184      */
185     drawCardinalSpline:function (config, tension, segments) {
186         cc.log("DrawingPrimitive.drawCardinalSpline() not implement!");
187     }
188 });
189 
190 /**
191  * Canvas of DrawingPrimitive implement version
192  * @class
193  * @extends cc.DrawingPrimitive
194  */
195 cc.DrawingPrimitiveCanvas = cc.DrawingPrimitive.extend(/** @lends cc.DrawingPrimitiveCanvas# */{
196     /**
197      * draws a point given x and y coordinate measured in points
198      * @override
199      * @param {cc.Point} point
200      */
201     drawPoint:function (point, size) {
202         if (!size) {
203             size = 1;
204         }
205         var newPoint = cc.p(point.x * cc.CONTENT_SCALE_FACTOR(), point.y * cc.CONTENT_SCALE_FACTOR());
206         this._renderContext.beginPath();
207         this._renderContext.arc(newPoint.x, -newPoint.y, size * cc.CONTENT_SCALE_FACTOR(), 0, Math.PI * 2, false);
208         this._renderContext.closePath();
209         this._renderContext.fill();
210     },
211 
212     /**
213      * draws an array of points.
214      * @override
215      * @param {Array} points point of array
216      * @param {Number} numberOfPoints
217      */
218     drawPoints:function (points, numberOfPoints, size) {
219         if (points == null) {
220             return;
221         }
222         if (!size) {
223             size = 1;
224         }
225 
226         this._renderContext.beginPath();
227         for (var i = 0; i < points.length; i++) {
228             this._renderContext.arc(points[i].x * cc.CONTENT_SCALE_FACTOR(), -points[i].y * cc.CONTENT_SCALE_FACTOR(),
229                 size * cc.CONTENT_SCALE_FACTOR(), 0, Math.PI * 2, false);
230         }
231         this._renderContext.closePath();
232         this._renderContext.fill();
233     },
234 
235     /**
236      * draws a line given the origin and destination point measured in points
237      * @override
238      * @param {cc.Point} origin
239      * @param {cc.Point} destination
240      */
241     drawLine:function (origin, destination) {
242         this._renderContext.beginPath();
243         this._renderContext.moveTo(origin.x * cc.CONTENT_SCALE_FACTOR(), -origin.y * cc.CONTENT_SCALE_FACTOR());
244         this._renderContext.lineTo(destination.x * cc.CONTENT_SCALE_FACTOR(), -destination.y * cc.CONTENT_SCALE_FACTOR());
245         this._renderContext.closePath();
246         this._renderContext.stroke();
247     },
248 
249     /**
250      * draws a poligon given a pointer to cc.Point coordiantes and the number of vertices measured in points.
251      * @override
252      * @param {Array} vertices a pointer to cc.Point coordiantes
253      * @param {Number} numOfVertices the number of vertices measured in points
254      * @param {Boolean} closePolygon The polygon can be closed or open
255      * @param {Boolean} fill The polygon can be closed or open and optionally filled with current color
256      */
257     drawPoly:function (vertices, numOfVertices, closePolygon, fill) {
258         if (fill == 'undefined') {
259             fill = false;
260         }
261 
262         if (vertices == null) {
263             return;
264         }
265         if (vertices.length < 3) {
266             throw new Error("Polygon's point must greater than 2");
267         }
268 
269         var firstPoint = vertices[0];
270         this._renderContext.beginPath();
271         this._renderContext.moveTo(firstPoint.x * cc.CONTENT_SCALE_FACTOR(), -firstPoint.y * cc.CONTENT_SCALE_FACTOR());
272         for (var i = 1; i < vertices.length; i++) {
273             this._renderContext.lineTo(vertices[i].x * cc.CONTENT_SCALE_FACTOR(), -vertices[i].y * cc.CONTENT_SCALE_FACTOR());
274         }
275         if (closePolygon) {
276             this._renderContext.closePath();
277         }
278 
279         if (fill) {
280             this._renderContext.fill();
281         } else {
282             this._renderContext.stroke();
283         }
284     },
285 
286     /**
287      * draws a circle given the center, radius and number of segments.
288      * @override
289      * @param {cc.Point} center center of circle
290      * @param {Number} radius
291      * @param {Number} angle angle in radians
292      * @param {Number} segments
293      * @param {Boolean} drawLineToCenter
294      */
295     drawCircle:function (center, radius, angle, segments, drawLineToCenter) {
296         this._renderContext.beginPath();
297         var endAngle = angle - Math.PI * 2;
298         this._renderContext.arc(0 | center.x, 0 | -(center.y), radius, -angle, -endAngle, false);
299         if (drawLineToCenter) {
300             this._renderContext.lineTo(0 | center.x, 0 | -(center.y));
301         }
302         this._renderContext.stroke();
303     },
304 
305     /**
306      * draws a quad bezier path
307      * @override
308      * @param {cc.Point} origin
309      * @param {cc.Point} control
310      * @param {cc.Point} destination
311      * @param {Number} segments
312      */
313     drawQuadBezier:function (origin, control, destination, segments) {
314         //this is OpenGL Algorithm
315         var vertices = [];
316 
317         var t = 0.0;
318         for (var i = 0; i < segments; i++) {
319             var x = Math.pow(1 - t, 2) * origin.x + 2.0 * (1 - t) * t * control.x + t * t * destination.x;
320             var y = Math.pow(1 - t, 2) * origin.y + 2.0 * (1 - t) * t * control.y + t * t * destination.y;
321             vertices.push(cc.p(x * cc.CONTENT_SCALE_FACTOR(), y * cc.CONTENT_SCALE_FACTOR()));
322             t += 1.0 / segments;
323         }
324         vertices.push(cc.p(destination.x * cc.CONTENT_SCALE_FACTOR(), destination.y * cc.CONTENT_SCALE_FACTOR()));
325 
326         this.drawPoly(vertices, segments + 1, false, false);
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      */
338     drawCubicBezier:function (origin, control1, control2, destination, segments) {
339         //this is OpenGL Algorithm
340         var vertices = [];
341 
342         var t = 0;
343         for (var i = 0; i < segments; i++) {
344             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;
345             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;
346             vertices.push(cc.p(x * cc.CONTENT_SCALE_FACTOR(), y * cc.CONTENT_SCALE_FACTOR()));
347             t += 1.0 / segments;
348         }
349         vertices.push(cc.p(destination.x * cc.CONTENT_SCALE_FACTOR(), destination.y * cc.CONTENT_SCALE_FACTOR()));
350 
351         this.drawPoly(vertices, segments + 1, false, false);
352     },
353 
354     /**
355      * draw a CatmullRom curve
356      * @override
357      * @param {cc.PointArray} points
358      * @param {Number} segments
359      */
360     drawCatmullRom:function (points, segments) {
361         this.drawCardinalSpline(points, 0.5, segments);
362     },
363 
364     /**
365      * draw a cardinal spline path
366      * @override
367      * @param {cc.PointArray} config
368      * @param {Number} tension
369      * @param {Number} segments
370      */
371     drawCardinalSpline:function (config, tension, segments) {
372         //lazy_init();
373         cc.renderContext.strokeStyle = "rgba(255,255,255,1)";
374         var points = [];
375         var p, lt;
376         var deltaT = 1.0 / config.length;
377 
378         for (var i = 0; i < segments + 1; i++) {
379             var dt = i / segments;
380 
381             // border
382             if (dt == 1) {
383                 p = config.length - 1;
384                 lt = 1;
385             } else {
386                 p = 0 | (dt / deltaT);
387                 lt = (dt - deltaT * p) / deltaT;
388             }
389 
390             // Interpolate
391             var newPos = cc.CardinalSplineAt(
392                 cc.getControlPointAt( config, p - 1),
393                 cc.getControlPointAt( config, p - 0),
394                 cc.getControlPointAt( config, p + 1),
395                 cc.getControlPointAt( config, p + 2),
396                 tension, lt);
397             points.push(newPos);
398         }
399         this.drawPoly(points, segments + 1, false, false);
400     },
401 
402     /**
403      * draw an image
404      * @override
405      * @param {HTMLImageElement|HTMLCanvasElement} image
406      * @param {cc.Point} sourcePoint
407      * @param {cc.Size} sourceSize
408      * @param {cc.Point} destPoint
409      * @param {cc.Size} destSize
410      */
411     drawImage:function (image, sourcePoint, sourceSize, destPoint, destSize) {
412         var len = arguments.length;
413         switch (len) {
414             case 2:
415                 var height = image.height;
416                 this._renderContext.drawImage(image, sourcePoint.x, -(sourcePoint.y + height));
417                 break;
418             case 3:
419                 this._renderContext.drawImage(image, sourcePoint.x, -(sourcePoint.y + sourceSize.height), sourceSize.width, sourceSize.height);
420                 break;
421             case 5:
422                 this._renderContext.drawImage(image, sourcePoint.x, sourcePoint.y, sourceSize.width, sourceSize.height, destPoint.x, -(destPoint.y + destSize.height),
423                     destSize.width, destSize.height);
424                 break;
425             default:
426                 throw new Error("Argument must be non-nil");
427                 break;
428         }
429     },
430 
431     /**
432      * draw a star
433      * @param {CanvasContext} ctx canvas context
434      * @param {Number} radius
435      * @param {cc.Color3B|cc.Color4B|cc.Color4F} color
436      */
437     drawStar:function (ctx, radius, color) {
438         var context = ctx || this._renderContext;
439         if (color instanceof cc.Color4F)
440             color = new cc.Color3B(0 | (color.r * 255), 0 | (color.g * 255), 0 | (color.b * 255));
441         var colorStr = "rgba(" + color.r + "," + color.g + "," + color.b;
442         context.fillStyle = colorStr + ",1)";
443         var subRadius = radius / 10;
444 
445         context.beginPath();
446         context.moveTo(-radius, radius);
447         context.lineTo(0, subRadius);
448         context.lineTo(radius, radius);
449         context.lineTo(subRadius, 0);
450         context.lineTo(radius, -radius);
451         context.lineTo(0, -subRadius);
452         context.lineTo(-radius, -radius);
453         context.lineTo(-subRadius, 0);
454         context.lineTo(-radius, radius);
455         context.closePath();
456         context.fill();
457 
458         var g1 = context.createRadialGradient(0, 0, subRadius, 0, 0, radius);
459         g1.addColorStop(0, colorStr + ", 1)");
460         g1.addColorStop(0.3, colorStr + ", 0.8)");
461         g1.addColorStop(1.0, colorStr + ", 0.0)");
462         context.fillStyle = g1;
463         context.beginPath();
464         var startAngle_1 = 0;
465         var endAngle_1 = cc.PI2;
466         context.arc(0, 0, radius - subRadius, startAngle_1, endAngle_1, false);
467         context.closePath();
468         context.fill();
469     },
470 
471     /**
472      * draw a color ball
473      * @param {CanvasContext} ctx canvas context
474      * @param {Number} radius
475      * @param {cc.Color3B|cc.Color4B|cc.Color4F} color
476      */
477     drawColorBall:function (ctx, radius, color) {
478         var context = ctx || this._renderContext;
479         if (color instanceof cc.Color4F)
480             color = new cc.Color3B(0 | (color.r * 255), 0 | (color.g * 255), 0 | (color.b * 255));
481         var colorStr = "rgba(" + color.r + "," + color.g + "," + color.b;
482         var subRadius = radius / 10;
483 
484         var g1 = context.createRadialGradient(0, 0, subRadius, 0, 0, radius);
485         g1.addColorStop(0, colorStr + ", 1)");
486         g1.addColorStop(0.3, colorStr + ", 0.8)");
487         g1.addColorStop(0.6, colorStr + ", 0.4)");
488         g1.addColorStop(1.0, colorStr + ", 0.0)");
489         context.fillStyle = g1;
490         context.beginPath();
491         var startAngle_1 = 0;
492         var endAngle_1 = cc.PI2;
493         context.arc(0, 0, radius, startAngle_1, endAngle_1, false);
494         context.closePath();
495         context.fill();
496     },
497 
498     /**
499      * fill text
500      * @param {String} strText
501      * @param {Number} x
502      * @param {Number} y
503      */
504     fillText:function (strText, x, y) {
505         this._renderContext.fillText(strText, x, -y);
506     }
507 });
508 
509 cc.PI2 = Math.PI * 2;
510