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