1 /* 2 *Copyright (c) 2009, TellurianRing.com 3 *All rights reserved. 4 * 5 *Redistribution and use in source and binary forms, with or without modification, 6 *are permitted provided that the following conditions are met: 7 * 8 * Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * Neither the name of the Organization (TellurianRing.com) nor the names of 14 * its contributors may be used to endorse or promote products derived from 15 * this software without specific prior written permission. 16 * 17 *THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 *ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 *WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 *DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 21 *ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 *(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 *LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 24 *ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 *(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 *SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 /** 29 * @class A Path builder which can be used to draw pretty much any shape. Due to 30 * current Canvas path restrictions, a Path's width and height are always equal 31 * to the width and height passed into the Path factory function. This is unlike 32 * other Groups in where the Groups bounds is calculated based on it's sub content. 33 * @extends Group 34 * @example // A Paths bounds is equal to the bounds passed in. 35 * var path = Path({ x: 10, y: 10, w: 25, h: 30 }); 36 * alert(path.getBounds().x == 10 && path.getBounds().w == 25); // alerts true 37 * path.moveBy({ x: 10 }); 38 * alert(path.getBounds().x == 20); // alerts true 39 * <script type="text/javascript"> 40 * function path_example_1() { 41 * var path = Path({ x: 10, y: 10, w: 25, h: 30 }); 42 * alert(path.getBounds().x == 10 && path.getBounds().w == 25); // alerts true 43 * path.moveBy({ x: 10 }); 44 * alert(path.getBounds().x == 20); // alerts true 45 * } 46 * </script> 47 * <button onclick="path_example_1()">Prove It!</button> 48 * @example 49 * // Creating a circle. 50 * Path({ fill: "#CCCCCC" }).jumpTo(75, 50) 51 * .arc(50, 50, 25, 0, 360 * (Math.PI / 180), true) 52 * <div id="path_example_2"></div> 53 * <script type="text/javascript"> 54 * Stage({ 55 * container_id: "path_example_2", 56 * width: 100, height: 100, 57 * update_time: 10000, 58 * scene: Scene({ 59 * content: [ 60 * Path({ fill: "#CCCCCC" }).jumpTo(75, 50) 61 * .arc(50, 50, 25, 0, 360 * (Math.PI / 180), true) 62 * ] 63 * }) 64 * }); 65 * </script> 66 * @example 67 * // Creating a box. 68 * Path({x: 10, y: 10, stroke: "#333333"}) 69 * .lineTo(50, 50) 70 * .lineTo(50, 90) 71 * .lineTo(10, 90) 72 * .lineTo(10, 10) 73 * <div id="path_example_3"></div> 74 * <script type="text/javascript"> 75 * Stage({ 76 * container_id: "path_example_3", 77 * width: 100, height: 100, 78 * update_time: 10000, 79 * scene: Scene({ 80 * content: [ 81 * Path({x: 25, y: 25, stroke: "#333333"}) 82 * .lineTo(25, 75) 83 * .lineTo(75, 75) 84 * .lineTo(75, 25) 85 * .lineTo(25, 25) 86 * ] 87 * }) 88 * }); 89 * </script> 90 * @example 91 * // Splitting the builder across multiple lines. 92 * var path = Path({x: 10, y: 10, stroke: "#333333"}); 93 * path.lineTo(50, 50); 94 * path.lineTo(50, 90); 95 * path.lineTo(10, 90); 96 * path.lineTo(10, 10); 97 * <div id="path_example_4"></div> 98 * <script type="text/javascript"> 99 * var path = Path({x: 10, y: 10, stroke: "#333333"}); 100 * path.lineTo(50, 50); 101 * path.lineTo(50, 90); 102 * path.lineTo(10, 90); 103 * path.lineTo(10, 10); 104 * 105 * Stage({ 106 * container_id: "path_example_4", 107 * width: 100, height: 100, 108 * update_time: 10000, 109 * scene: Scene({ 110 * content: [ path ] 111 * }) 112 * }); 113 * </script> 114 * @extends Group 115 * @param {Object} _details A JSON Object with these properties. 116 * <table cellpadding="0" cellspacing="1" border="0" class="constructor_details"> 117 * <tr><th>Property</th> <th>Required</th> <th>Default</th></tr> 118 * <tr><td>fill</td> <td>no</td> <td>transparent</td></tr> 119 * <tr><td>stroke</td> <td>no</td> <td>#000000</td></tr> 120 * <tr><td>stroke_width</td> <td>no</td> <td>1</td></tr> 121 * </table> 122 */ 123 function Path(_details) { // {{{ 124 /** @constructs */ 125 function _Path(_details) { // {{{ 126 // Private members {{{ 127 var fill = check(_details.fill, "transparent"); 128 var stroke = check(_details.stroke, "#000000"); 129 var stroke_width = check(_details.stroke_width, 1); 130 // }}} 131 // Public Members {{{ 132 /** 133 * Dreates an arc (based on a circle) in this path. 134 * @function 135 * @param _x The center x coord. of the circle. 136 * @param _y The center y coord. of the circle. 137 * @param _start The starting angle of this arc in radians. 138 * @param _end The ending angle of this arc in radians. 139 * @param _clockwise Should this arc go clockwise. 140 */ 141 this.arc = function(_x, _y, _radius, _start, _end, _clockwise) { 142 this.getContent().push(Arc({x: _x, y: _y, radius: _radius, start: _start, end: _end, clockwise: _clockwise})); 143 return this; 144 } 145 /** 146 * Draws a bezier curve from the current position of the path to the 147 * given coordinates. 148 * @function 149 * @param _cpx1 The control point x1. 150 * @param _cpy1 The control point y1. 151 * @param _cpx2 The control point x2. 152 * @param _cpy2 The control point y2. 153 * @param _x The x coord. to draw to. 154 * @param _y The y coord. to draw to. 155 */ 156 this.beziTo = function(_cpx1, _cpy1, _cpx2, _cpy2, _x, _y) { // {{{ 157 this.getContent().push(BezierTo({cpx1: _cpx1, cpy1: _cpy1, cpx2: _cpx2, cpy2: _cpy2, x: _x, y: _y})); 158 return this; 159 } // }}} lineTo 160 161 // Overridden to draw this path. 162 this.draw = function(_context) { 163 var bounds = this.getBounds(); 164 var content = this.getContent(); 165 _context.save(); 166 _context.beginPath(); 167 _context.moveTo(bounds.x, bounds.y); 168 for(var index in content) { 169 var component = content[index]; 170 component.draw(_context); 171 } 172 if(exists(fill)) { 173 _context.fillStyle = fill; 174 _context.fill(); 175 } 176 if(exists(stroke)) { 177 _context.strokeStyle = stroke; 178 _context.lineWidth = stroke_width; 179 _context.stroke(); 180 } 181 _context.closePath(); 182 _context.restore(); 183 } 184 /** 185 * Moves the next part of this path to the given coordinates. 186 * <b>This function does not draw anything.</b> 187 * @function 188 * @param _x The x coord. to jump to. 189 * @param _y The y coord. to jump to. 190 */ 191 this.jumpTo = function(_x, _y) { // {{{ 192 this.getContent().push(JumpTo({x: _x, y: _y})); 193 return this; 194 } // }}} jumpTo 195 /** 196 * Draws a line from the current position of the path to the given coordinates. 197 * @function 198 * @param _x The x coord. to draw to. 199 * @param _y The y coord. to draw to. 200 */ 201 this.lineTo = function(_x, _y) { // {{{ 202 this.getContent().push(LineTo({x: _x, y: _y})); 203 return this; 204 } // }}} lineTo 205 /** 206 * Draws a quadratic curve from the current position of the path to the 207 * given coordinates. 208 * @function 209 * @param _cpx The control point x. 210 * @param _cpy The control point x. 211 * @param _x The x coord. to draw to. 212 * @param _y The y coord. to draw to. 213 */ 214 this.quadTo = function(_cpx, _cpy, _x, _y) { // {{{ 215 this.getContent().push(QuadraticTo({cpx: _cpx, cpy: _cpy, x: _x, y: _y})); 216 return this; 217 } // }}} quadTo 218 219 // Overriden because Group's update method calculates bounds based on 220 // sub content. 221 this.update = function(_runtime) { 222 this.updateBase(_runtime); 223 } 224 // }}} 225 } // }}} _Path 226 227 228 /** 229 * Draws an arc from the current location to a new one. 230 * @private 231 */ 232 function Arc(_details) { // {{{ 233 /** @constructs */ 234 function _Arc(_details) { // {{{ 235 // Private Members {{{ 236 var radius = check(_details.radius, 0); 237 var start = check(_details.start, 0); 238 var end = check(_details.end, 0); 239 var clockwise = check(_details.clockwise, true); 240 // }}} Private Members 241 // Public Members {{{ 242 this.draw = function(_context) { // {{{ 243 var bounds = this.getBounds(); 244 // arc function asks for anti-clockwise parameters. So we just 245 // negate the clockwise value. 246 _context.arc(bounds.x, bounds.y, radius, start, end, !clockwise); 247 } // }}} draw 248 // }}} Public Members 249 } // }}} _Arc 250 SceneContent(_details).extend(_Arc); 251 var theArc = new _Arc(_details); 252 return theArc; 253 } // }}} Arc 254 255 /** 256 * Draws a Bezier curve from the current location to a new one. 257 * @private 258 */ 259 function BezierTo(_details) { // {{{ 260 /** @constructs */ 261 function _BezierTo(_details) { // {{{ 262 // Private Members {{{ 263 var cpx1 = check(_details.cpx1, 0); 264 var cpy1 = check(_details.cpy1, 0); 265 var cpx2 = check(_details.cpx2, 0); 266 var cpy2 = check(_details.cpy2, 0); 267 // }}} Private Members 268 // Public Members {{{ 269 this.draw = function(_context) { // {{{ 270 var bounds = this.getBounds(); 271 _context.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, bounds.x, bounds.y); 272 } // }}} draw 273 // }}} Public Members 274 } // }}} _BezierTo 275 SceneContent(_details).extend(_BezierTo); 276 var theBezierTo = new _BezierTo(_details); 277 return theBezierTo; 278 } // }}} BezierTo 279 280 /** 281 * Moves the context to a new location. 282 * @private 283 */ 284 function JumpTo(_details) { // {{{ 285 /** @constructs */ 286 function _JumpTo(_details) { // {{{ 287 // Public Members {{{ 288 this.draw = function(_context) { // {{{ 289 var bounds = this.getBounds(); 290 _context.moveTo(bounds.x, bounds.y); 291 } // }}} draw 292 // }}} Public Members 293 } // }}} _JumpTo 294 SceneContent(_details).extend(_JumpTo); 295 var theJumpTo = new _JumpTo(_details); 296 return theJumpTo; 297 } // }}} JumpTo 298 299 /** 300 * Draws a line from the current position to a new one. 301 * @private 302 */ 303 function LineTo(_details) { // {{{ 304 /** @constructs */ 305 function _LineTo(_details) { // {{{ 306 // Public Members {{{ 307 this.draw = function(_context) { // {{{ 308 var bounds = this.getBounds(); 309 _context.lineTo(bounds.x, bounds.y); 310 } // }}} draw 311 // }}} Public Members 312 } // }}} _LineTo 313 SceneContent(_details).extend(_LineTo); 314 var theLineTo = new _LineTo(_details); 315 return theLineTo; 316 } // }}} LineTo 317 318 /** 319 * Draws a Quadratic curve from the current location to a new one. 320 * @private 321 */ 322 function QuadraticTo(_details) { // {{{ 323 /** @constructs */ 324 function _QuadraticTo(_details) { // {{{ 325 // Private Members {{{ 326 var cpx = check(_details.cpx, 0); 327 var cpy = check(_details.cpy, 0); 328 // }}} Private Members 329 // Public Members {{{ 330 this.draw = function(_context) { // {{{ 331 var bounds = this.getBounds(); 332 _context.quadraticCurveTo(cpx, cpy, bounds.x, bounds.y); 333 } // }}} draw 334 // }}} Public Members 335 } // }}} _QuadTo 336 SceneContent(_details).extend(_QuadraticTo); 337 var theQuadTo = new _QuadraticTo(_details); 338 return theQuadTo; 339 } // }}} QuadTo 340 341 Group(_details).extend(_Path); 342 var thePath = new _Path(_details); 343 return thePath; 344 } // }}} Path 345 // These properties are for jEdit - Programmer's Text Editor. 346 // Load this file in jEdit to see what they do. 347 // ::folding=explicit:mode=javascript:noTabs=true:collapseFolds=4:: 348